明树Git Lab

Commit b5720d31 authored by zhanghan's avatar zhanghan

测试名称修改

parent bc9d6526
Pipeline #106882 passed with stage
in 19 seconds
...@@ -9,7 +9,9 @@ ...@@ -9,7 +9,9 @@
<div class="header-right"> <div class="header-right">
<div> <div>
<el-dropdown> <el-dropdown>
<span class="username">管理员</span> <span class="username">
{{ userInfo.name }}
</span>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item @click="handleLogout" <el-dropdown-item @click="handleLogout"
...@@ -37,23 +39,27 @@ ...@@ -37,23 +39,27 @@
</template> </template>
<script setup> <script setup>
import { computed, onMounted, getCurrentInstance } from "vue"; import { computed, ref, onMounted, getCurrentInstance } from "vue";
import { useRouter, useRoute } from "vue-router"; import { useRouter, useRoute } from "vue-router";
import { useUserStore } from "@/stores/user.js"; import { useUserStore } from "@/stores/user.js";
import LeftMenu from "./leftMenu.vue"; import LeftMenu from "./leftMenu.vue";
const userStore = useUserStore(); const userStore = useUserStore();
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
let userInfo = ref(
sessionStorage.getItem("userInfo")
? JSON.parse(sessionStorage.getItem("userInfo"))
: [],
);
const router = useRouter(); const router = useRouter();
// 获取资源库数据 // 获取资源库数据
const getResourceData = () => { const getResourceData = () => {
proxy.$post({ proxy.$post({
url: "/api/resource/listResourceAll", url: "/api/resource/listResourceAll",
data: {}, data: {},
callback: (data) => { callback: (data) => {
sessionStorage.setItem("resourceData", JSON.stringify(data)); sessionStorage.setItem("resourceData", JSON.stringify(data));
} },
}); });
}; };
onMounted(() => { onMounted(() => {
...@@ -63,13 +69,13 @@ onMounted(() => { ...@@ -63,13 +69,13 @@ onMounted(() => {
const handleLogout = () => { const handleLogout = () => {
// 清除登录状态 // 清除登录状态
proxy.$post({ proxy.$post({
url: "/api/user/logout", url: "/api/user/logout",
data: {}, data: {},
callback: (data) => { callback: (data) => {
userStore.clearUserInfo(); userStore.clearUserInfo();
router.replace("/login"); router.replace("/login");
} },
}) });
}; };
</script> </script>
...@@ -79,7 +85,7 @@ const handleLogout = () => { ...@@ -79,7 +85,7 @@ const handleLogout = () => {
overflow: hidden; overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.container-main{ .container-main {
flex: 1; flex: 1;
height: 0; height: 0;
} }
......
...@@ -635,7 +635,7 @@ ...@@ -635,7 +635,7 @@
<div class="annualPlans"> <div class="annualPlans">
<annualPlan <annualPlan
v-model="formData.xmndjh" v-model="formData.xmndjh"
:dynamic-time-list="dynamicTimeList" :dynamic-time-list="annualDynamicTimeList"
:is-preview="isPreview" :is-preview="isPreview"
@handleAnnualPlanChange="handleAnnualPlanTableChange" @handleAnnualPlanChange="handleAnnualPlanTableChange"
></annualPlan> ></annualPlan>
...@@ -785,6 +785,20 @@ import { ElMessage } from "element-plus"; ...@@ -785,6 +785,20 @@ import { ElMessage } from "element-plus";
import annualPlan from "./annualPlan.vue"; import annualPlan from "./annualPlan.vue";
import DynamicTable from "@/components/FormDynamicTable/index.vue"; import DynamicTable from "@/components/FormDynamicTable/index.vue";
const annualDynamicTimeList = ref([
"2025及以前",
"2026",
"2026-小记",
"2026-一季度",
"2026-二季度",
"2026-三季度",
"2026-四季度",
"2027",
"2028",
"2029",
"2030",
"2031",
]);
// ========== 动态表格列配置(参股单位出资修正) ========== // ========== 动态表格列配置(参股单位出资修正) ==========
const transferColumns = ref([ const transferColumns = ref([
{ {
...@@ -1342,6 +1356,12 @@ const getJsqtzjcDetail = () => { ...@@ -1342,6 +1356,12 @@ const getJsqtzjcDetail = () => {
Object.assign(formData, data); // 回填基础字段 Object.assign(formData, data); // 回填基础字段
if (data.xmndjh && Array.isArray(data.xmndjh) && data.xmndjh.length > 0) {
annualDynamicTimeList.value = Object.keys(data.xmndjh[0]).filter(
(key) => /^\d{4}.*/.test(key),
);
}
// 步骤1:提取/生成时间列表(共用表头) // 步骤1:提取/生成时间列表(共用表头)
if (data.kyjcxx && Array.isArray(data.kyjcxx) && data.kyjcxx.length > 0) { if (data.kyjcxx && Array.isArray(data.kyjcxx) && data.kyjcxx.length > 0) {
dynamicTimeList.value = Object.keys(data.kyjcxx[0]).filter((key) => dynamicTimeList.value = Object.keys(data.kyjcxx[0]).filter((key) =>
...@@ -1370,6 +1390,9 @@ const getJsqtzjcDetail = () => { ...@@ -1370,6 +1390,9 @@ const getJsqtzjcDetail = () => {
// ========== 保存表单:两个表格的提交数据完全独立拆分 ========== // ========== 保存表单:两个表格的提交数据完全独立拆分 ==========
const saveClick = () => { const saveClick = () => {
console.log(formData.xmndjh, "formData.xmndjh");
console.log(annualDynamicTimeList, "formData.xmndjh");
if (!formData.projectId) return ElMessage.warning("请选择项目信息"); if (!formData.projectId) return ElMessage.warning("请选择项目信息");
loading.value = true; loading.value = true;
const url = rcCgqyglId.value const url = rcCgqyglId.value
...@@ -1394,7 +1417,9 @@ const saveClick = () => { ...@@ -1394,7 +1417,9 @@ const saveClick = () => {
// 2. 项目年度计划表格:专属提交数据(仅自身字段,子组件绑定的xmndjh) // 2. 项目年度计划表格:专属提交数据(仅自身字段,子组件绑定的xmndjh)
xmndjh: formData.xmndjh.map((row) => { xmndjh: formData.xmndjh.map((row) => {
const filterRow = { id: row.id, total: row.total }; const filterRow = { id: row.id, total: row.total };
dynamicTimeList.value.forEach((time) => (filterRow[time] = row[time])); annualDynamicTimeList.value.forEach(
(time) => (filterRow[time] = row[time]),
);
return filterRow; return filterRow;
}), }),
}; };
......
...@@ -140,27 +140,92 @@ ...@@ -140,27 +140,92 @@
</template> </template>
<script setup> <script setup>
import { defineProps, defineEmits, watch, onMounted } from "vue"; import { defineProps, computed, defineEmits, watch, onMounted } from "vue";
// 🔥 核心:接收父组件参数,v-model双向绑定核心(modelValue对应父组件v-model值)
const props = defineProps({ const props = defineProps({
// 双向绑定的表格数据源(父组件formData.xmndjh) modelValue: { type: Array, default: () => [] },
modelValue: { dynamicTimeList: { type: Array, default: () => [] },
type: Array, isPreview: { type: Boolean, default: false },
default: () => [], });
}, // ✅ 新增1:处理时间列表 - 去重+非空,避免异常列,兼容所有自定义字符串字段
// 父组件传递的动态时间列表(替代子组件内部定义) const validDynamicTimeList = computed(() => {
dynamicTimeList: { return [...new Set(props.dynamicTimeList)].filter(
type: Array, (time) => !!time && typeof time === "string",
default: () => [], );
});
// ✅ 新增2:核心方法 - 初始化行的时间字段,无值则补0(解决2025及以前无默认值问题)
const initRowTimeField = (row) => {
if (!row) return;
validDynamicTimeList.value.forEach((time) => {
// 字段不存在/非数字,自动补0,确保v-model绑定有效
if (row[time] === undefined || isNaN(Number(row[time]))) {
row[time] = 0;
}
});
};
// ✅ 新增3:核心方法 - 计算行合计(基于处理后的时间列表)
const calcRowTotal = (row) => {
if (!row) return 0;
return validDynamicTimeList.value.reduce((sum, time) => {
return sum + (Number(row[time]) || 0);
}, 0);
};
// 监听数据源变化 - 优化:新增字段初始化+容错
watch(
() => props.modelValue,
(newVal) => {
if (newVal.length === 0) return;
const newData = [...newVal]; // 深拷贝避免响应式引用问题
newData.forEach((row) => {
if (row) {
initRowTimeField(row); // ✅ 新增:初始化字段(补0)
row.total = calcRowTotal(row); // 改用统一的合计方法
}
});
emit("update:modelValue", newData); // 同步父组件
}, },
// 预览状态(禁用输入) { deep: true, immediate: true },
isPreview: { );
type: Boolean,
default: false, // ✅ 新增4:监听时间列表变化 - 新增/修改2025及以前这类字段时,自动初始化所有行
watch(
() => validDynamicTimeList.value,
() => {
if (props.modelValue.length === 0) return;
const newData = [...props.modelValue];
newData.forEach((row) => {
initRowTimeField(row);
row.total = calcRowTotal(row);
});
emit("update:modelValue", newData);
emit("handleAnnualPlanChange", newData);
}, },
{ deep: true, immediate: true },
);
// 获取对应行的合计值 - 优化:增加数字校验,避免报错
const getRowTotal = (index) => {
const row = props.modelValue[index - 1];
if (!row || isNaN(Number(row.total))) return "0.00";
return Number(row.total).toFixed(2);
};
onMounted(() => {
console.log("兼容后的时间列表:", validDynamicTimeList.value);
}); });
// ✅ 修复5:handleChange - 输入后同步父组件,左侧合计实时刷新(核心!)
const handleChange = (row) => {
if (props.isPreview || !row) return;
row.total = calcRowTotal(row); // 计算合计
const newData = [...props.modelValue]; // 深拷贝
// 触发双向绑定+原有事件,保证兼容
emit("update:modelValue", newData);
emit("handleAnnualPlanChange", newData);
};
// 🔥 核心:触发双向绑定更新+原有事件兼容 // 🔥 核心:触发双向绑定更新+原有事件兼容
const emit = defineEmits([ const emit = defineEmits([
"update:modelValue", // v-model必须的更新事件,同步数据给父组件 "update:modelValue", // v-model必须的更新事件,同步数据给父组件
...@@ -187,22 +252,6 @@ watch( ...@@ -187,22 +252,6 @@ watch(
); );
// 获取对应行的合计值(父组件数据源,保留2位小数) // 获取对应行的合计值(父组件数据源,保留2位小数)
const getRowTotal = (index) => {
const row = props.modelValue[index - 1];
return row?.total ? row.total.toFixed(2) : "0.00";
};
onMounted(() => {
console.log(props.dynamicTimeList, " props.dynamicTimeList");
});
// 右侧数据变更:计算行合计 + 双向同步父组件 + 触发原有事件
const handleChange = (row) => {
// 计算当前行合计(适配父组件动态时间列表)
row.total = props.dynamicTimeList.reduce((sum, time) => {
return sum + (Number(row[time]) || 0);
}, 0);
};
// 合计行样式(和父组件表格样式统一) // 合计行样式(和父组件表格样式统一)
const tableCellStyle = ({ row }) => const tableCellStyle = ({ row }) =>
...@@ -226,6 +275,7 @@ const tableCellStyle = ({ row }) => ...@@ -226,6 +275,7 @@ const tableCellStyle = ({ row }) =>
.left-table { .left-table {
flex-shrink: 0; flex-shrink: 0;
} }
// 左侧原生表格样式(160px列宽 + 48px行高 + 无缝边框) // 左侧原生表格样式(160px列宽 + 48px行高 + 无缝边框)
.investment-table { .investment-table {
width: fit-content; width: fit-content;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment