明树Git Lab

Commit 0380bcfc authored by zhanghan's avatar zhanghan

年度计划k

parent 5bc37918
Pipeline #106888 passed with stage
in 19 seconds
...@@ -162,6 +162,13 @@ const routes = [ ...@@ -162,6 +162,13 @@ const routes = [
title: "运营期投资检查", title: "运营期投资检查",
component: () => import("@/views/castbehind/runningPeriod.vue"), component: () => import("@/views/castbehind/runningPeriod.vue"),
}, },
{
path: "/investmentCecovery",
name: "investmentCecovery",
title: "运营期投资回收",
component: () =>
import("@/views/castbehind/investmentCecovery.vue"),
},
{ {
path: "/runningPeriodAdd", path: "/runningPeriodAdd",
name: "runningPeriodAdd", name: "runningPeriodAdd",
...@@ -298,6 +305,34 @@ const routes = [ ...@@ -298,6 +305,34 @@ const routes = [
// }, // },
], ],
}, },
{
path: "/elseManage",
name: "elseManage",
title: "其他管理",
redirect: "/share",
children: [
{
path: "/cost",
name: "cost",
title: "成本管理",
component: () => import("@/views/elseManage/cost.vue"),
},
{
path: "/property",
name: "property",
title: "成本管理",
component: () => import("@/views/elseManage/property.vue"),
},
{
path: "/link",
name: "link",
title: "成本管理",
component: () => import("@/views/elseManage/link.vue"),
},
],
},
{ {
path: "/systemManage", path: "/systemManage",
name: "systemManage", name: "systemManage",
......
<template>
<div class="building-container">
<img src="@/assets/images/building.png" alt="" />
<div class="title">努力开发中……</div>
</div>
</template>
<script setup></script>
<style lang="less">
.building-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
img {
max-width: 300px;
}
.title {
font-size: 18px;
color: #999;
}
}
</style>
<template>
<div class="building-container">
<img src="@/assets/images/building.png" alt="" />
<div class="title">努力开发中……</div>
</div>
</template>
<script setup></script>
<style lang="less">
.building-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
img {
max-width: 300px;
}
.title {
font-size: 18px;
color: #999;
}
}
</style>
<template>
<div class="building-container">
<img src="@/assets/images/building.png" alt="" />
<div class="title">努力开发中……</div>
</div>
</template>
<script setup></script>
<style lang="less">
.building-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
img {
max-width: 300px;
}
.title {
font-size: 18px;
color: #999;
}
}
</style>
<template>
<div class="building-container">
<img src="@/assets/images/building.png" alt="" />
<div class="title">努力开发中……</div>
</div>
</template>
<script setup></script>
<style lang="less">
.building-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
img {
max-width: 300px;
}
.title {
font-size: 18px;
color: #999;
}
}
</style>
...@@ -487,7 +487,7 @@ ...@@ -487,7 +487,7 @@
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="新建/续建"> <el-form-item label="新建/续建">
<CommonSelector v-model="formData.xjXj" dictName="sf" /> <CommonSelector v-model="formData.xjXj" dictName="xj" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
...@@ -785,6 +785,7 @@ import { ElMessage } from "element-plus"; ...@@ -785,6 +785,7 @@ 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([ const annualDynamicTimeList = ref([
"2025及以前", "2025及以前",
"2026", "2026",
...@@ -823,7 +824,6 @@ const transferColumns = ref([ ...@@ -823,7 +824,6 @@ const transferColumns = ref([
placeholder: "请输入差异说明", placeholder: "请输入差异说明",
}, },
]); ]);
const cgdwczqkxz = ref([]);
// ========== 路由/实例/全局方法 ========== // ========== 路由/实例/全局方法 ==========
const router = useRouter(); const router = useRouter();
...@@ -849,12 +849,11 @@ const isPreview = ref(!!route.query.isPreview); ...@@ -849,12 +849,11 @@ const isPreview = ref(!!route.query.isPreview);
const projectList = ref([]); const projectList = ref([]);
const rcCgqyglId = ref(route.query.id || ""); const rcCgqyglId = ref(route.query.id || "");
const timeHeaderType = ref("year"); // year/ month 切换时间表头类型 const timeHeaderType = ref("year"); // year/ month 切换时间表头类型
const dynamicTimeList = ref([]); // 动态时间列表(两个表格共用表头,无其他数据关联) const dynamicTimeList = ref([]); // 可研表格专属时间列表,与年度计划彻底隔离
// ========== 数据源完全隔离:可研表格 & 年度计划表格 各自独立 ========== // ========== 数据源完全隔离:可研表格 & 年度计划表格 各自独立 ==========
// 1. 可研/决策信息表格:专属数据源(独立) // 1. 可研/决策信息表格:专属数据源(独立)
const financialIndicators = ref([]); // 可研表格渲染数据源 const financialIndicators = ref([]); // 可研表格渲染数据源
// 2. 项目年度计划表格:仅用子组件双向绑定的formData.xmndjh,移除冗余的annualPlanIndicators(彻底解耦)
// ========== 可研/决策信息表格:原始配置+专属初始化/方法(完全独立) ========== // ========== 可研/决策信息表格:原始配置+专属初始化/方法(完全独立) ==========
const financialOriginConfig = ref([ const financialOriginConfig = ref([
...@@ -1175,17 +1174,22 @@ const initFinancialTable = () => { ...@@ -1175,17 +1174,22 @@ const initFinancialTable = () => {
initFinancialRowTotal(); initFinancialRowTotal();
}; };
// ========== 【专属方法】项目年度计划表格:初始化(独立,子组件专属17行) ========== // ========== 【专属方法】项目年度计划表格:初始化(独立,仅新增模式执行,不覆盖接口数据) ==========
const initAnnualPlanTable = () => { const initAnnualPlanTable = () => {
if (dynamicTimeList.value.length === 0) return; // 仅新增模式初始化,编辑模式由接口回填,不执行此方法
// 生成17行带唯一id的独立数据(子组件专属,与可研表格无任何关联) if (rcCgqyglId.value) return;
if (annualDynamicTimeList.value.length === 0) return;
// 生成17行带唯一id的独立数据(保留基础id,子组件会保留所有字段)
const tableData = Array.from({ length: 17 }, (_, index) => ({ const tableData = Array.from({ length: 17 }, (_, index) => ({
id: index + 1, // 子组件row-key="id" 专属,唯一标识 id: index + 1, // 子组件唯一标识,必须保留
total: 0, total: 0,
...dynamicTimeList.value.reduce((obj, time) => ({ ...obj, [time]: 0 }), {}), ...annualDynamicTimeList.value.reduce(
(obj, time) => ({ ...obj, [time]: 0 }),
{},
),
})); }));
// 仅赋值给年度计划表格的绑定字段,与可研表格无任何引用
formData.xmndjh = tableData; formData.xmndjh = tableData;
console.log("新增模式初始化xmndjh:", formData.xmndjh);
}; };
// ========== 【专属方法】可研/决策信息表格:单行合计计算(独立) ========== // ========== 【专属方法】可研/决策信息表格:单行合计计算(独立) ==========
...@@ -1241,11 +1245,11 @@ const handleFinancialChange = (currentRow) => { ...@@ -1241,11 +1245,11 @@ const handleFinancialChange = (currentRow) => {
// ========== 【专属方法】项目年度计划表格:子组件回调(独立,仅影响自身) ========== // ========== 【专属方法】项目年度计划表格:子组件回调(独立,仅影响自身) ==========
const handleAnnualPlanTableChange = (newData) => { const handleAnnualPlanTableChange = (newData) => {
if (isPreview.value) return; if (isPreview.value) return;
// 仅接收子组件的年度计划数据,与可研表格无任何交互 formData.xmndjh = newData;
formData.xmndjh = [...newData]; console.log("子组件回调更新xmndjh:", formData.xmndjh);
}; };
// ========== 动态时间生成:仅生成一次,两个表格共用表头(无数据关联) ========== // ========== 可研表格动态时间生成:仅可研用,与年度计划无关 ==========
const generateDynamicTime = () => { const generateDynamicTime = () => {
const now = new Date(); const now = new Date();
const currentYear = now.getFullYear(); const currentYear = now.getFullYear();
...@@ -1261,9 +1265,8 @@ const generateDynamicTime = () => { ...@@ -1261,9 +1265,8 @@ const generateDynamicTime = () => {
} }
} }
dynamicTimeList.value = timeList; dynamicTimeList.value = timeList;
// 两个表格分别独立初始化,无互相调用 // 仅初始化可研表格,年度计划表格单独初始化
initFinancialTable(); initFinancialTable();
initAnnualPlanTable();
}; };
// ========== 回填逻辑:完全隔离,各自处理自身表格 ========== // ========== 回填逻辑:完全隔离,各自处理自身表格 ==========
...@@ -1286,21 +1289,23 @@ const fillFinancialTable = (backfillData) => { ...@@ -1286,21 +1289,23 @@ const fillFinancialTable = (backfillData) => {
initFinancialRowTotal(); initFinancialRowTotal();
}; };
// 2. 项目年度计划表格:专属回填(独立,按id匹配,精准无错位) // 2. 年度计划表格:专属回填【核心:用子组件的annualDynamicTimeList,不与可研混用】
const fillAnnualPlanTable = (backfillData) => { const fillAnnualPlanTable = (backfillData) => {
if (!Array.isArray(backfillData) || backfillData.length === 0) return; if (!Array.isArray(backfillData) || backfillData.length === 0) return;
// 先初始化年度计划表格的空数据 console.log("接口返回的xmndjh回填数据:", backfillData);
initAnnualPlanTable(); // 按唯一id匹配回填,保留所有原有字段,仅更新数字字段
// 按唯一id匹配回填,避免indicatorName重复导致的错位
formData.xmndjh.forEach((frontRow) => { formData.xmndjh.forEach((frontRow) => {
const backfillRow = backfillData.find((item) => item.id === frontRow.id); const backfillRow = backfillData.find((item) => item.id === frontRow.id);
if (!backfillRow) return; if (!backfillRow) return;
// 仅赋值total,不碰其他字段
frontRow.total = Number(backfillRow.total) || 0; frontRow.total = Number(backfillRow.total) || 0;
dynamicTimeList.value.forEach((time) => { // 仅遍历子组件的年度时间列表赋值,其他字段完全保留
annualDynamicTimeList.value.forEach((time) => {
frontRow[time] = frontRow[time] =
backfillRow[time] !== undefined ? Number(backfillRow[time]) || 0 : 0; backfillRow[time] !== undefined ? Number(backfillRow[time]) || 0 : 0;
}); });
}); });
console.log("回填后的xmndjh(保留所有字段):", formData.xmndjh);
}; };
// ========== 业务方法:获取项目列表(通用) ========== // ========== 业务方法:获取项目列表(通用) ==========
...@@ -1322,8 +1327,6 @@ const changeProject = (val) => { ...@@ -1322,8 +1327,6 @@ const changeProject = (val) => {
data: { id: val }, data: { id: val },
callback: (data) => { callback: (data) => {
loading.value = false; loading.value = false;
console.log(data, "项目详情数据");
formData.projectName = data.projectName || ""; formData.projectName = data.projectName || "";
formData.sbdw = data.sbdw || ""; formData.sbdw = data.sbdw || "";
formData.xmgsmc = data.xmgsmc || ""; formData.xmgsmc = data.xmgsmc || "";
...@@ -1342,7 +1345,7 @@ const changeProject = (val) => { ...@@ -1342,7 +1345,7 @@ const changeProject = (val) => {
if (selectItem) formData.projectName = selectItem.projectName; if (selectItem) formData.projectName = selectItem.projectName;
}; };
// ========== 获取详情(编辑/预览):两个表格分别独立回填 ========== // ========== 获取详情(编辑/预览):核心修复【不覆盖接口返回的xmndjh,不混用时间列表】 ==========
const getJsqtzjcDetail = () => { const getJsqtzjcDetail = () => {
if (!rcCgqyglId.value) return; if (!rcCgqyglId.value) return;
loading.value = true; loading.value = true;
...@@ -1352,17 +1355,11 @@ const getJsqtzjcDetail = () => { ...@@ -1352,17 +1355,11 @@ const getJsqtzjcDetail = () => {
callback: (data) => { callback: (data) => {
loading.value = false; loading.value = false;
if (!data) return ElMessage.error("未查询到数据"); if (!data) return ElMessage.error("未查询到数据");
console.log(data, "data"); console.log("接口返回完整数据:", 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. 回填基础字段,保留xmndjh的完整结构
Object.assign(formData, data);
// 2. 可研表格:初始化时间+回填(与年度计划无关)
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) =>
/^\d{4}(-\d{2})?$/.test(key), /^\d{4}(-\d{2})?$/.test(key),
...@@ -1370,16 +1367,14 @@ const getJsqtzjcDetail = () => { ...@@ -1370,16 +1367,14 @@ const getJsqtzjcDetail = () => {
} else { } else {
generateDynamicTime(); generateDynamicTime();
} }
initFinancialTable();
if (data.kyjcxx) fillFinancialTable(data.kyjcxx);
// 步骤2:两个表格分别独立初始化 // 3. 年度计划表格:【核心】直接使用接口返回的xmndjh,不重新初始化/覆盖
if (dynamicTimeList.value.length > 0) { if (data.xmndjh && Array.isArray(data.xmndjh) && data.xmndjh.length > 0) {
initFinancialTable(); formData.xmndjh = data.xmndjh; // 保留接口返回的所有字段
initAnnualPlanTable(); fillAnnualPlanTable(data.xmndjh); // 仅更新数字字段,不改变结构
} }
// 步骤3:两个表格分别独立回填(完全隔离)
if (data.kyjcxx) fillFinancialTable(data.kyjcxx);
if (data.xmndjh) fillAnnualPlanTable(data.xmndjh);
}, },
error: () => { error: () => {
loading.value = false; loading.value = false;
...@@ -1388,22 +1383,19 @@ const getJsqtzjcDetail = () => { ...@@ -1388,22 +1383,19 @@ const getJsqtzjcDetail = () => {
}); });
}; };
// ========== 保存表单:两个表格的提交数据完全独立拆分 ========== // ========== 保存表单:核心修复【移除xmndjh的字段过滤,传递完整数据】 ==========
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
? "/api/project/updateTzjh" ? "/api/project/updateTzjh"
: "/api/project/createTzjh"; : "/api/project/createTzjh";
// 组装提交数据:两个表格的字段完全独立拆分,无任何混叠 // 组装提交数据:【核心】xmndjh直接传递,不做任何过滤,保留所有字段
const submitData = { const submitData = {
...formData, ...formData,
projectId: String(formData.projectId), projectId: String(formData.projectId),
// 1. 可研/决策信息表格:专属提交数据(仅自身字段 // 可研表格:按需过滤(保留原有逻辑
kyjcxx: financialIndicators.value.map((row) => { kyjcxx: financialIndicators.value.map((row) => {
const filterRow = { const filterRow = {
serialNumber: row.serialNumber, serialNumber: row.serialNumber,
...@@ -1414,15 +1406,10 @@ const saveClick = () => { ...@@ -1414,15 +1406,10 @@ const saveClick = () => {
dynamicTimeList.value.forEach((time) => (filterRow[time] = row[time])); dynamicTimeList.value.forEach((time) => (filterRow[time] = row[time]));
return filterRow; return filterRow;
}), }),
// 2. 项目年度计划表格:专属提交数据(仅自身字段,子组件绑定的xmndjh) // 年度计划表格:【关键修复】直接传完整的xmndjh,不做map过滤!
xmndjh: formData.xmndjh.map((row) => { xmndjh: formData.xmndjh,
const filterRow = { id: row.id, total: row.total };
annualDynamicTimeList.value.forEach(
(time) => (filterRow[time] = row[time]),
);
return filterRow;
}),
}; };
console.log("提交的xmndjh(完整字段):", submitData.xmndjh);
proxy.$post({ proxy.$post({
url: url, url: url,
...@@ -1444,14 +1431,16 @@ const backClick = () => router.back(-1); ...@@ -1444,14 +1431,16 @@ const backClick = () => router.back(-1);
const tableCellStyle = ({ row }) => const tableCellStyle = ({ row }) =>
row.isTotal ? { background: "#f5f7fa", fontWeight: "bold" } : {}; row.isTotal ? { background: "#f5f7fa", fontWeight: "bold" } : {};
// ========== 页面初始化:两个表格分别独立处理 ========== // ========== 页面初始化:新增/编辑分离,不覆盖接口数据 ==========
onMounted(() => { onMounted(() => {
getProjectData(); getProjectData();
if (rcCgqyglId.value) { if (rcCgqyglId.value) {
// 编辑/预览:从接口获取完整数据,不执行本地初始化
setTimeout(() => getJsqtzjcDetail(), 100); setTimeout(() => getJsqtzjcDetail(), 100);
} else { } else {
// 新增模式:仅生成一次时间,两个表格分别独立初始化 // 新增模式:分别初始化两个表格,互不影响
generateDynamicTime(); generateDynamicTime(); // 可研表格时间
initAnnualPlanTable(); // 年度计划表格初始化
} }
}); });
</script> </script>
......
...@@ -105,10 +105,10 @@ ...@@ -105,10 +105,10 @@
</table> </table>
</div> </div>
<!-- 右侧:Element 时间输入表格(绑定父组件传递的数据源+动态时间) --> <!-- 右侧:Element 时间输入表格(绑定**内部响应式数据**,不再直接绑props) -->
<div class="right-table"> <div class="right-table">
<el-table <el-table
:data="modelValue" :data="tableData"
style="width: 100%" style="width: 100%"
border border
:cell-style="tableCellStyle" :cell-style="tableCellStyle"
...@@ -116,7 +116,7 @@ ...@@ -116,7 +116,7 @@
:header-row-style="{ height: '48px' }" :header-row-style="{ height: '48px' }"
> >
<el-table-column <el-table-column
v-for="time in dynamicTimeList" v-for="time in validDynamicTimeList"
:key="time" :key="time"
:label="time" :label="time"
width="160" width="160"
...@@ -128,7 +128,7 @@ ...@@ -128,7 +128,7 @@
:min="0" :min="0"
:precision="2" :precision="2"
controls-position="right" controls-position="right"
@change="handleChange(row)" @change="() => handleChange(row)"
:disabled="isPreview" :disabled="isPreview"
style="width: 100%" style="width: 100%"
/> />
...@@ -140,165 +140,171 @@ ...@@ -140,165 +140,171 @@
</template> </template>
<script setup> <script setup>
import { defineProps, computed, defineEmits, watch, onMounted } from "vue"; // 修复1:删除错误的deepClone导入,Vue无此API
import { defineProps, computed, defineEmits, watch, onMounted, ref } from "vue";
// 1. 定义props和emit(保持不变)
const props = defineProps({ const props = defineProps({
modelValue: { type: Array, default: () => [] }, modelValue: { type: Array, default: () => [] },
dynamicTimeList: { type: Array, default: () => [] }, dynamicTimeList: { type: Array, default: () => [] },
isPreview: { type: Boolean, default: false }, isPreview: { type: Boolean, default: false },
}); });
// ✅ 新增1:处理时间列表 - 去重+非空,避免异常列,兼容所有自定义字符串字段
const emit = defineEmits([
"update:modelValue", // v-model双向绑定
"handleAnnualPlanChange", // 原有业务事件
]);
// 2. 核心:组件内部响应式数据,隔离props循环
const tableData = ref([]);
// 3. 处理时间列表 - 去重+非空+去除隐形空白字符(保持原有逻辑)
const validDynamicTimeList = computed(() => { const validDynamicTimeList = computed(() => {
return [...new Set(props.dynamicTimeList)].filter( return [...new Set(props.dynamicTimeList)]
(time) => !!time && typeof time === "string", .map((time) => {
); return typeof time === "string" ? time.trim() : "";
})
.filter((time) => !!time);
}); });
// ✅ 新增2:核心方法 - 初始化行的时间字段,无值则补0(解决2025及以前无默认值问题) // 工具方法:初始化行的时间字段,无值则补0【仅赋值年份字段,不碰其他字段】
const initRowTimeField = (row) => { const initRowTimeField = (row) => {
if (!row) return; if (!row || typeof row !== "object") return;
validDynamicTimeList.value.forEach((time) => { validDynamicTimeList.value.forEach((time) => {
// 字段不存在/非数字,自动补0,确保v-model绑定有效 // 仅对年份字段做处理,其他字段完全不碰
if (row[time] === undefined || isNaN(Number(row[time]))) { if (
row[time] === undefined ||
row[time] === null ||
isNaN(Number(row[time]))
) {
row[time] = 0; row[time] = 0;
} else {
// 确保是数字类型,不修改原有值
row[time] = Number(row[time]);
} }
}); });
}; };
// ✅ 新增3:核心方法 - 计算行合计(基于处理后的时间列表) // 工具方法:计算行合计【仅读取年份字段,不碰其他字段】
const calcRowTotal = (row) => { const calcRowTotal = (row) => {
if (!row) return 0; if (!row || typeof row !== "object") return 0;
return validDynamicTimeList.value.reduce((sum, time) => { return validDynamicTimeList.value.reduce((sum, time) => {
return sum + (Number(row[time]) || 0); return sum + (Number(row[time]) || 0);
}, 0); }, 0);
}; };
// 监听数据源变化 - 优化:新增字段初始化+容错 // 工具方法:深拷贝+数据处理【强制保留所有原有字段,仅新增/更新total、处理年份】
const handleTableData = (sourceData) => {
if (!Array.isArray(sourceData) || sourceData.length === 0) return [];
// 深拷贝:保留原始数据的所有字段(id、名称等)
const newData = JSON.parse(JSON.stringify(sourceData));
newData.forEach((row) => {
initRowTimeField(row); // 仅处理年份字段
row.total = calcRowTotal(row); // 新增/更新total字段,不删除其他字段
});
return newData;
};
// 监听props.modelValue变化,仅同步到内部tableData,不emit【打印日志调试字段是否丢失】
watch( watch(
() => props.modelValue, () => props.modelValue,
(newVal) => { (newVal) => {
if (newVal.length === 0) return; console.log("父组件传递的原始modelValue:", newVal); // 调试:看父组件传的是否有id/名称字段
const newData = [...newVal]; // 深拷贝避免响应式引用问题 if (newVal.length > 0) {
newData.forEach((row) => { console.log("父组件传的第一行字段:", Object.keys(newVal[0])); // 关键调试:看是否丢失字段
if (row) { }
initRowTimeField(row); // ✅ 新增:初始化字段(补0) const newData = handleTableData(newVal);
row.total = calcRowTotal(row); // 改用统一的合计方法 tableData.value = newData; // 只更新内部数据,不emit
}
});
emit("update:modelValue", newData); // 同步父组件
}, },
{ deep: true, immediate: true }, { deep: true, immediate: true },
); );
// ✅ 新增4:监听时间列表变化 - 新增/修改2025及以前这类字段时,自动初始化所有行 // 监听时间列表变化,更新内部数据后统一emit【保留所有字段emit】
watch( watch(
() => validDynamicTimeList.value, () => validDynamicTimeList.value,
() => { () => {
if (props.modelValue.length === 0) return; if (tableData.value.length === 0) return;
const newData = [...props.modelValue]; const newData = handleTableData(tableData.value);
newData.forEach((row) => { tableData.value = newData;
initRowTimeField(row); emitDataChange(newData);
row.total = calcRowTotal(row);
});
emit("update:modelValue", newData);
emit("handleAnnualPlanChange", newData);
}, },
{ deep: true, immediate: true }, { deep: true, immediate: true },
); );
// 获取对应行的合计值 - 优化:增加数字校验,避免报错 // 工具方法:数据变化校验+深拷贝emit【完整保留所有字段emit给父组件】
const emitDataChange = (newData) => {
if (props.isPreview) return;
const emitData = JSON.parse(JSON.stringify(newData)); // 深拷贝保留所有字段
emit("update:modelValue", emitData);
emit("handleAnnualPlanChange", emitData);
console.log("子组件emit的完整数据:", emitData); // 调试:看emit的是否有所有字段
};
// 7. 获取对应行的合计值 - 从内部tableData取值
const getRowTotal = (index) => { const getRowTotal = (index) => {
const row = props.modelValue[index - 1]; const row = tableData.value[index - 1];
if (!row || isNaN(Number(row.total))) return "0.00"; if (!row || isNaN(Number(row.total))) return "0.00";
return Number(row.total).toFixed(2); return Number(row.total).toFixed(2);
}; };
onMounted(() => { // 8. 输入变化处理 - 更新内部数据后emit【仅更新年份/total,保留其他字段】
console.log("兼容后的时间列表:", validDynamicTimeList.value);
});
// ✅ 修复5:handleChange - 输入后同步父组件,左侧合计实时刷新(核心!)
const handleChange = (row) => { const handleChange = (row) => {
if (props.isPreview || !row) return; if (props.isPreview || !row || typeof row !== "object") return;
row.total = calcRowTotal(row); // 计算合计 row.total = calcRowTotal(row); // 仅更新total
const newData = [...props.modelValue]; // 深拷贝 emitDataChange(tableData.value); // 完整emit所有字段
// 触发双向绑定+原有事件,保证兼容
emit("update:modelValue", newData);
emit("handleAnnualPlanChange", newData);
}; };
// 🔥 核心:触发双向绑定更新+原有事件兼容 // 合计行样式(保持不变)
const emit = defineEmits([
"update:modelValue", // v-model必须的更新事件,同步数据给父组件
"handleAnnualPlanChange", // 保留原有事件,兼容父组件逻辑
]);
// 监听数据源变化(父组件回填时触发),确保合计值正确
watch(
() => props.modelValue,
(newVal) => {
if (newVal.length === 0) return;
// 父组件回填数据后,重新计算所有行合计
newVal.forEach((row) => {
if (row) {
row.total = props.dynamicTimeList.reduce((sum, time) => {
return sum + (Number(row[time]) || 0);
}, 0);
}
});
// 同步更新父组件数据
emit("update:modelValue", newVal);
},
{ deep: true, immediate: true },
);
// 获取对应行的合计值(父组件数据源,保留2位小数)
// 合计行样式(和父组件表格样式统一)
const tableCellStyle = ({ row }) => const tableCellStyle = ({ row }) =>
row.isTotal ? { background: "#f5f7fa", fontWeight: "bold" } : {}; row?.isTotal ? { background: "#f5f7fa", fontWeight: "bold" } : {};
// 调试日志(打印内部数据字段,确认是否保留)
onMounted(() => {
console.log("处理后的有效时间字段:", validDynamicTimeList.value);
console.log("子组件内部tableData:", tableData.value);
if (tableData.value.length > 0) {
console.log(
"子组件tableData第一行所有字段:",
Object.keys(tableData.value[0]),
);
}
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
// 核心:左右布局样式 // 所有样式保持不变
.annual-plan-wrap { .annual-plan-wrap {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
gap: 0; // 左右无缝衔接,视觉成一个整体 gap: 0;
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
padding: 0; padding: 0;
margin: 0; margin: 0;
overflow-x: auto; // 整体横向滚动,避免左右分离滚动 overflow-x: auto;
} }
// 左侧原生表格容器:固定宽度不挤压
.left-table { .left-table {
flex-shrink: 0; flex-shrink: 0;
} }
// 左侧原生表格样式(160px列宽 + 48px行高 + 无缝边框)
.investment-table { .investment-table {
width: fit-content; width: fit-content;
border-collapse: collapse; border-collapse: collapse;
border: 1px solid #ebeef5; // 和父组件表格边框色统一 border: 1px solid #ebeef5;
border-right: 0; // 取消右侧边框,和右侧EL表格无缝衔接 border-right: 0;
} }
.investment-table td { .investment-table td {
width: 160px; // 和右侧EL表格列宽完全统一 width: 160px;
height: 49px; // 匹配原有行高,避免错位 height: 49px;
border: 1px solid #ebeef5; // 和父组件表格边框色统一 border: 1px solid #ebeef5;
padding: 0 !important; // 取消内边距,文字居中更整齐 padding: 0 !important;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
word-break: break-all; word-break: break-all;
box-sizing: border-box; box-sizing: border-box;
} }
// 左侧表格层级背景色(保留原有样式)
.first-col { .first-col {
font-weight: bold; font-weight: bold;
background-color: #f5f7fa; // 和父组件合计行背景统一 background-color: #f5f7fa;
} }
.second-col { .second-col {
background-color: #f5f7fa; background-color: #f5f7fa;
...@@ -307,42 +313,32 @@ const tableCellStyle = ({ row }) => ...@@ -307,42 +313,32 @@ const tableCellStyle = ({ row }) =>
font-weight: 500; font-weight: 500;
background-color: #f5f7fa; background-color: #f5f7fa;
} }
// 右侧EL表格样式优化(和父组件完全统一:行高/边框/内边距)
:deep(.el-table) { :deep(.el-table) {
--el-table-border-color: #ebeef5; // 边框色和父组件表格一致 --el-table-border-color: #ebeef5;
--el-table-row-height: 48px !important; // 行高和左侧强制统一 --el-table-row-height: 48px !important;
border-left: 0; // 取消左侧边框,和左侧无缝衔接 border-left: 0;
} }
// 表头行高也强制48px,避免表头错位
:deep(.el-table__header tr), :deep(.el-table__header tr),
:deep(.el-table__body tr) { :deep(.el-table__body tr) {
height: 48px !important; height: 48px !important;
} }
:deep(.el-table-cell) { :deep(.el-table-cell) {
padding: 0 !important; // 内边距和左侧一致 padding: 0 !important;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
box-sizing: border-box; box-sizing: border-box;
} }
// 输入框适配行高,居中显示
:deep(.el-input-number) { :deep(.el-input-number) {
width: 100%; width: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
:deep(.el-input-number__input) { :deep(.el-input-number__input) {
text-align: right; text-align: right;
padding-right: 25px; padding-right: 25px;
width: 90%; // 微调宽度,避免输入框溢出 width: 90%;
} }
// 去除多余样式
.flex { .flex {
display: flex; display: flex;
} }
......
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