明树Git Lab

Commit bc9d6526 authored by zhanghan's avatar zhanghan

年度计划开发完毕

parent 826a8f44
Pipeline #106881 passed with stage
in 20 seconds
......@@ -65,7 +65,37 @@ let tableData = ref([]);
let tableColumns = ref([
{
prop: "projectName",
label: "项目信息",
label: "项目名称",
showOverflowTooltip: true,
},
{
prop: "projectForeignName",
label: "项目外文名称",
showOverflowTooltip: true,
},
{
prop: "sbdw",
label: "申报单位",
showOverflowTooltip: true,
},
{
prop: "ssejqy",
label: "所属二级企业",
showOverflowTooltip: true,
},
{
prop: "xmgsmc",
label: "项目公司名称",
showOverflowTooltip: true,
},
{
prop: "xmkgsjyj",
label: "项目预计起始时间",
showOverflowTooltip: true,
},
{
prop: "xmjgsjyj",
label: "目预计完成时间",
showOverflowTooltip: true,
},
......@@ -86,7 +116,7 @@ let pageSize = ref(10);
const getStatementData = () => {
loading.value = true;
proxy.$post({
url: "/api/project/getJsqtzjcList",
url: "/api/project/getTzjhList",
data: {
page: currentPage.value,
pagesize: pageSize.value,
......@@ -136,7 +166,7 @@ const deleteStatement = (item) => {
})
.then(() => {
proxy.$post({
url: "/api/project/deleteJsqtzjc",
url: "/api/project/deleteTzjh",
data: {
id: item.id,
},
......
......@@ -106,7 +106,7 @@
</el-col>
<el-col :span="12">
<el-form-item label="项目建设期 (月)">
<el-input-number
<el-input
v-model="formData.xmjsqy"
:min="0"
placeholder="请输入项目建设期 (月)"
......@@ -115,39 +115,23 @@
</el-col>
<el-col :span="12">
<el-form-item label="项目流转状态(审批状态)">
<el-select
<CommonSelector
v-model="formData.projectLzType"
placeholder="请选择项目流转状态"
>
<el-option label="待立项" value="1" />
<el-option label="立项审批中" value="3" />
<el-option label="已立项" value="5" />
<el-option label="决策填报中" value="7" />
<el-option label="决策审批中" value="8" />
<el-option label="已决策" value="9" />
<el-option label="再决策填报中" value="11" />
<el-option label="再决策审批中" value="12" />
<el-option label="再决策审批通过" value="13" />
<el-option label="再决策审批不通过" value="15" />
</el-select>
dictName="xmlzzt"
></CommonSelector>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目阶段">
<el-select
<CommonSelector
v-model="formData.xmjd"
placeholder="请选择项目阶段"
>
<el-option label="筹备期" value="筹备期" />
<el-option label="建设期" value="建设期" />
<el-option label="运营期" value="运营期" />
<el-option label="收尾期" value="收尾期" />
</el-select>
dictName="project_phase"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="运营年限(年)">
<el-input-number
<el-input
v-model="formData.yynxn"
:min="0"
placeholder="请输入运营年限(年)"
......@@ -164,39 +148,6 @@
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="计划年份">
<el-input
v-model="formData.jhnf"
placeholder="请输入计划年份(如2026)"
maxlength="4"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="新建/续建">
<el-select v-model="formData.xjXj" placeholder="请选择">
<el-option label="新建" value="新建" />
<el-option label="续建" value="续建" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="年度投资目标">
<el-input
v-model="formData.ndTzMb"
placeholder="请输入年度投资目标"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目预计工期">
<el-input
v-model="formData.xmYjGq"
placeholder="请输入项目预计工期"
/>
</el-form-item>
</el-col>
</el-row>
</el-collapse-item>
......@@ -454,7 +405,7 @@
</el-row>
</el-collapse-item>
<!-- 可研/决策信息(单位:万元):保留原有功能,提交字段改为kyjcxx -->
<!-- 可研/决策信息(单位:万元):独立表格,专属方法/数据源 -->
<el-collapse-item
title="可研/决策信息(单位:万元)"
name="可研/决策信息(单位:万元)"
......@@ -464,6 +415,7 @@
style="width: 100%"
border
:cell-style="tableCellStyle"
row-key="serialNumber"
>
<!-- 序号列 -->
<el-table-column
......@@ -503,12 +455,12 @@
align="center"
>
<template #default="{ row }">
<el-input-number
<el-input
v-model="row[time]"
:min="0"
:precision="2"
controls-position="right"
@change="handleDataChange(row)"
@change="handleFinancialChange(row)"
:disabled="isPreview"
style="width: 100%"
/>
......@@ -517,11 +469,305 @@
</el-table>
</el-collapse-item>
<!-- 新增:项目年度计划表格(单位:万元) -->
<!-- 年度投资计划:基础信息 -->
<el-collapse-item title="年度投资计划" name="年度投资计划">
<el-row :gutter="20">
<!-- 基础短字段:span12分栏 -->
<el-col :span="12">
<el-form-item label="计划年份">
<el-date-picker
v-model="formData.jhnf"
type="year"
format="YYYY"
value-format="YYYY"
placeholder="请选择计划年份"
picker-options="{ disabledDate: (date) => date > new Date() }"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="新建/续建">
<CommonSelector v-model="formData.xjXj" dictName="sf" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="年度投资目标">
<el-input
v-model="formData.ndTzMb"
placeholder="请输入年度投资目标"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目预计工期">
<el-input
v-model="formData.xmYjGq"
placeholder="请输入项目预计工期(如12个月)"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目融资落地时间或预计落地时间">
<el-date-picker
v-model="formData.xmRzLdSjHyyjLdSj"
type="date"
placeholder="请选择时间"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="预计实现资本金内部收益率(%)">
<el-input
v-model="formData.yjSxZbjNbsyl"
:min="0"
:precision="2"
:max="999.99"
controls-position="right"
placeholder="请输入收益率"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目计划总投资类型">
<el-input
v-model="formData.xmTzZeXmJhZtzLx"
placeholder="请输入项目计划总投资类型"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="计划类型">
<el-input
v-model="formData.jhLx"
placeholder="请输入计划类型"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="当前进展阶段">
<el-input
v-model="formData.dqJzJd"
placeholder="请输入当前进展阶段"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目融资是否已经落地">
<CommonSelector
v-model="formData.xmRzSfYjLd"
dictName="sf"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="当前进展情况">
<el-input
v-model="formData.dqJzQk"
placeholder="请输入当前进展情况"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目实际开工时间">
<el-date-picker
v-model="formData.xmSjKgSj"
type="date"
placeholder="请选择时间"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<!-- 长文本字段:span24通栏,用文本域 -->
<el-col :span="24">
<el-form-item label="预期实现效果">
<el-input
v-model="formData.yqSxXg"
type="textarea"
:rows="3"
placeholder="请输入预期实现效果详细说明"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item
label="2025年投资金额(全口径)目标(决算)与计划差异说明"
>
<el-input
v-model="formData.nTzJqMbjyYjhCySm"
type="textarea"
:rows="3"
placeholder="请输入差异说明"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item
label="2025年计划投资目标(决算)与计划差异说明"
>
<el-input
v-model="formData.nJhTzMbjyYjhCySm"
type="textarea"
:rows="3"
placeholder="请输入差异说明"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="项目主要风险及主要风险应对举措">
<el-input
v-model="formData.xmZyFxJzyFxYdcj"
type="textarea"
:rows="4"
placeholder="请输入主要风险及对应的应对举措"
/>
</el-form-item>
</el-col>
</el-row>
</el-collapse-item>
<!-- 项目年度计划表格(单位:万元):独立子组件表格,专属方法/数据源 -->
<el-collapse-item
title="项目年度计划表格(单位:万元)"
name="项目年度计划表格(单位:万元)"
>
<div class="annualPlans">
<annualPlan
v-model="formData.xmndjh"
:dynamic-time-list="dynamicTimeList"
:is-preview="isPreview"
@handleAnnualPlanChange="handleAnnualPlanTableChange"
></annualPlan>
</div>
</el-collapse-item>
<!-- 项目年度计划(资金支付口径):金额类字段统一用数字输入框,精度2,单位万元 -->
<el-collapse-item
title="项目年度计划(资金支付口径)"
name="项目年度计划(资金支付口径)"
>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="资金支付比例(%)">
<el-input
v-model="formData.zjfzBl"
:min="0"
:max="100"
:precision="2"
controls-position="right"
placeholder="0.00"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="债权融资(万元)">
<el-input
v-model="formData.zqRz"
:min="0"
:precision="2"
controls-position="right"
placeholder="0.00"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="年度计划总额(万元)">
<el-input
v-model="formData.ndJhZe"
:min="0"
:precision="2"
controls-position="right"
placeholder="0.00"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="回款再投入(万元)">
<el-input
v-model="formData.hkZtr"
:min="0"
:precision="2"
controls-position="right"
placeholder="0.00"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="资本金-配套方(万元)">
<el-input
v-model="formData.zbjPtF"
:min="0"
:precision="2"
controls-position="right"
placeholder="0.00"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="其他(万元)">
<el-input
v-model="formData.qt"
:min="0"
:precision="2"
controls-position="right"
placeholder="0.00"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="资本金-外部股东(万元)">
<el-input
v-model="formData.zbjWbGd"
:min="0"
:precision="2"
controls-position="right"
placeholder="0.00"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="我方仅指本单位出资(万元)">
<el-input
v-model="formData.wfJzBdwCz"
:min="0"
:precision="2"
controls-position="right"
placeholder="0.00"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
</el-collapse-item>
<!-- 2026年参股单位出资情况修正 -->
<el-collapse-item
title="2026年参股单位出资情况修正(单位:万元)"
name="2026年参股单位出资情况修正(单位:万元)"
>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="参股单位出资情况修正">
<DynamicTable
:columns="transferColumns"
:disabled="isPreview"
v-model="formData.cgdwczqkxz"
:default-row="{
mc: '',
fj: '',
bz: '',
}"
/>
</el-form-item>
</el-col>
</el-row>
</el-collapse-item>
</el-collapse>
</el-form>
......@@ -536,14 +782,46 @@
import { reactive, ref, onMounted, getCurrentInstance } from "vue";
import { useRouter, useRoute } from "vue-router";
import { ElMessage } from "element-plus";
// 初始化全局变量
import annualPlan from "./annualPlan.vue";
import DynamicTable from "@/components/FormDynamicTable/index.vue";
// ========== 动态表格列配置(参股单位出资修正) ==========
const transferColumns = ref([
{
prop: "mc",
label: "单位名称",
type: "input",
placeholder: "请填写单位名称",
},
{
prop: "fj",
label: "我方出资额(万元)",
type: "input",
placeholder: "请输入我方出资额(万元)",
min: 0,
precision: 2,
},
{
prop: "bz",
label: "差异说明",
type: "textarea",
rows: 2,
placeholder: "请输入差异说明",
},
]);
const cgdwczqkxz = ref([]);
// ========== 路由/实例/全局方法 ==========
const router = useRouter();
const route = useRoute();
const { proxy } = getCurrentInstance();
// ========== 基础配置 ==========
// ========== 基础配置:两个表格共用(仅时间列表,无其他关联) ==========
const activeCollapse = ref([
"基本信息",
"年度投资计划",
"项目年度计划(资金支付口径)",
"2026年参股单位出资情况修正(单位:万元)",
"可研/决策信息(单位:万元)",
"项目年度计划表格(单位:万元)",
"战略类A",
......@@ -557,11 +835,14 @@ const isPreview = ref(!!route.query.isPreview);
const projectList = ref([]);
const rcCgqyglId = ref(route.query.id || "");
const timeHeaderType = ref("year"); // year/ month 切换时间表头类型
const dynamicTimeList = ref([]); // 动态时间列表(表头)
const financialIndicators = ref([]); // 财务表格渲染数据
const annualPlanIndicators = ref([]); // 年度计划表格渲染数据
const dynamicTimeList = ref([]); // 动态时间列表(两个表格共用表头,无其他数据关联)
// ========== 数据源完全隔离:可研表格 & 年度计划表格 各自独立 ==========
// 1. 可研/决策信息表格:专属数据源(独立)
const financialIndicators = ref([]); // 可研表格渲染数据源
// 2. 项目年度计划表格:仅用子组件双向绑定的formData.xmndjh,移除冗余的annualPlanIndicators(彻底解耦)
// ========== 财务指标原始配置(不可修改,用于回填匹配) ==========
// ========== 可研/决策信息表格:原始配置+专属初始化/方法(完全独立) ==========
const financialOriginConfig = ref([
{
serialNumber: "1",
......@@ -774,72 +1055,7 @@ const financialOriginConfig = ref([
},
]);
// ========== 年度计划表格原始配置(完全还原截图结构) ==========
const annualPlanOriginConfig = ref([
{ level: 0, indicatorName: "年度投资计划", isTotal: false },
{ level: 1, indicatorName: "计划投资完成及资金来源", isTotal: false },
{ level: 2, indicatorName: "计划资金来源(全口径)", isTotal: false },
{
level: 3,
indicatorName: "合计",
isTotal: true,
parentCode: ["资本金", "债权融资", "回款再投入", "其他"],
},
{ level: 3, indicatorName: "资本金", isTotal: false },
{
level: 4,
indicatorName: "小计",
isTotal: true,
parentCode: ["能建方", "外部股东"],
},
{ level: 4, indicatorName: "能建方", isTotal: false },
{ level: 4, indicatorName: "外部股东", isTotal: false },
{ level: 3, indicatorName: "债权融资", isTotal: false },
{
level: 4,
indicatorName: "小计",
isTotal: true,
parentCode: ["其中:非并表项目我方贷款/担保额"],
},
{ level: 4, indicatorName: "其中:非并表项目我方贷款/担保额", isTotal: false },
{ level: 3, indicatorName: "回款再投入", isTotal: false },
{ level: 3, indicatorName: "其他", isTotal: false },
{
level: 4,
indicatorName: "小计",
isTotal: true,
parentCode: ["其中:我方其他出资"],
},
{ level: 4, indicatorName: "其中:我方其他出资", isTotal: false },
{ level: 2, indicatorName: "资金来源(新口径)", isTotal: false },
{
level: 3,
indicatorName: "合计",
isTotal: true,
parentCode: ["资本金", "债权融资", "回款再投入", "其他"],
},
{ level: 3, indicatorName: "资本金", isTotal: false },
{
level: 4,
indicatorName: "小计",
isTotal: true,
parentCode: ["能建方", "外部股东"],
},
{ level: 4, indicatorName: "能建方", isTotal: false },
{ level: 4, indicatorName: "外部股东", isTotal: false },
{ level: 3, indicatorName: "债权融资", isTotal: false },
{ level: 3, indicatorName: "回款再投入", isTotal: false },
{ level: 3, indicatorName: "其他", isTotal: false },
{
level: 1,
indicatorName: "合计",
isTotal: true,
parentCode: ["计划投资完成及资金来源"],
},
{ level: 1, indicatorName: "政府付费", isTotal: false },
]);
// ========== 表单数据:完全对齐数据库字段,无冗余! ==========
// ========== 表单数据:完全对齐数据库,两个表格的JSON字段完全独立 ==========
const formData = reactive({
// 基础字段
projectId: "",
......@@ -895,18 +1111,17 @@ const formData = reactive({
// 最终分类
zzflqk: "",
// 可研/决策信息(JSON,与财务表格绑定)
kyjcxx: [],
// 年度计划信息(JSON,与新增表格绑定)
xmndjh: [],
// 两个表格的JSON字段:完全独立,无任何关联
kyjcxx: [], // 可研/决策信息表格:专属提交字段
xmndjh: [], // 项目年度计划表格:专属提交字段(子组件双向绑定)
// 其他扩展字段
// 年度投资计划基础信息
jhnf: "",
xjXj: "",
ndTzMb: "",
xmYjGq: "",
xmRzLdSjHyyjLdSj: "",
yjSxZbjNbsyl: 0.0,
yjSxZbjNbsyl: "",
xmTzZeXmJhZtzLx: "",
jhLx: "",
dqJzJd: "",
......@@ -918,7 +1133,7 @@ const formData = reactive({
nJhTzMbjyYjhCySm: "",
xmZyFxJzyFxYdcj: "",
// 金额类JSON字段
// 项目年度计划(资金支付口径) 金额类字段
zjfzBl: 0.0,
zqRz: 0.0,
ndJhZe: 0.0,
......@@ -927,72 +1142,47 @@ const formData = reactive({
qt: 0.0,
zbjWbGd: 0.0,
wfJzBdwCz: 0.0,
cgdwczqkxz: {},
});
// ========== 核心方法:生成动态时间表头 ==========
const generateDynamicTime = () => {
const now = new Date();
const currentYear = now.getFullYear();
const currentMonth = now.getMonth() + 1;
const timeList = [];
for (let i = 0; i <= 15; i++) {
if (timeHeaderType.value === "year") {
timeList.push(String(currentYear + i));
} else {
const calcYear = currentYear + Math.floor((currentMonth + i - 1) / 12);
const calcMonth = ((currentMonth + i - 1) % 12) + 1;
timeList.push(`${calcYear}-${calcMonth.toString().padStart(2, "0")}`);
}
}
dynamicTimeList.value = timeList;
initFinancialTable(); // 生成表头后初始化财务表格
initAnnualPlanTable(); // 生成表头后初始化年度计划表格
};
// 参股单位出资修正
cgdwczqkxz: [],
});
// ========== 初始化财务表格数据 ==========
// ========== 【专属方法】可研/决策信息表格:初始化(独立) ==========
const initFinancialTable = () => {
if (dynamicTimeList.value.length === 0) return;
const tableData = financialOriginConfig.value.map((item) => {
const row = { ...item, total: 0 };
// 仅为可研表格的行赋值时间字段,与年度计划无关
dynamicTimeList.value.forEach((time) => (row[time] = 0));
return row;
});
financialIndicators.value = tableData;
// 初始化可研表格的合计
initFinancialRowTotal();
};
// ========== 初始化年度计划表格数据 ==========
// ========== 【专属方法】项目年度计划表格:初始化(独立,子组件专属17行) ==========
const initAnnualPlanTable = () => {
if (dynamicTimeList.value.length === 0) return;
const tableData = annualPlanOriginConfig.value.map((item) => {
const row = { ...item, total: 0 };
dynamicTimeList.value.forEach((time) => (row[time] = 0));
return row;
});
annualPlanIndicators.value = tableData;
};
// ========== 财务表格数据变更:更新行合计+父项合计 ==========
const handleDataChange = (currentRow) => {
updateRowTotal(currentRow);
updateAllTotalRow();
};
// ========== 年度计划表格数据变更:更新行合计+父项合计 ==========
const handleAnnualPlanChange = (currentRow) => {
updateRowTotal(currentRow);
updateAnnualPlanTotalRow();
// 生成17行带唯一id的独立数据(子组件专属,与可研表格无任何关联)
const tableData = Array.from({ length: 17 }, (_, index) => ({
id: index + 1, // 子组件row-key="id" 专属,唯一标识
total: 0,
...dynamicTimeList.value.reduce((obj, time) => ({ ...obj, [time]: 0 }), {}),
}));
// 仅赋值给年度计划表格的绑定字段,与可研表格无任何引用
formData.xmndjh = tableData;
};
// ========== 更新单行合计 ==========
const updateRowTotal = (row) => {
// ========== 【专属方法】可研/决策信息表格:单行合计计算(独立) ==========
const updateFinancialRowTotal = (row) => {
row.total = dynamicTimeList.value.reduce((sum, time) => {
return sum + (Number(row[time]) || 0);
}, 0);
};
// ========== 更新财务表格所有父项合计行 ==========
const updateAllTotalRow = () => {
// ========== 【专属方法】可研/决策信息表格:全量合计行更新(独立) ==========
const updateAllFinancialTotalRow = () => {
financialIndicators.value.forEach((totalRow) => {
if (!totalRow.isTotal || !totalRow.parentCode) return;
totalRow.total = 0;
......@@ -1011,40 +1201,59 @@ const updateAllTotalRow = () => {
});
};
// ========== 更新年度计划表格所有父项合计行 ==========
const updateAnnualPlanTotalRow = () => {
annualPlanIndicators.value.forEach((totalRow) => {
if (!totalRow.isTotal || !totalRow.parentCode) return;
totalRow.total = 0;
dynamicTimeList.value.forEach((time) => (totalRow[time] = 0));
totalRow.parentCode.forEach((code) => {
const childRow = annualPlanIndicators.value.find(
(item) => item.indicatorName === code,
// ========== 【专属方法】可研/决策信息表格:批量初始化合计(独立) ==========
const initFinancialRowTotal = () => {
// 仅计算可研表格的非合计行
financialIndicators.value.forEach(
(row) => !row.isTotal && updateFinancialRowTotal(row),
);
if (childRow) {
totalRow.total += Number(childRow.total) || 0;
dynamicTimeList.value.forEach((time) => {
totalRow[time] += Number(childRow[time]) || 0;
});
}
});
});
// 仅更新可研表格的合计行
updateAllFinancialTotalRow();
// 同步到可研的提交字段
formData.kyjcxx = [...financialIndicators.value];
};
// ========== 批量初始化所有行合计 ==========
const initAllRowTotal = () => {
financialIndicators.value.forEach(
(row) => !row.isTotal && updateRowTotal(row),
);
updateAllTotalRow();
// ========== 【专属方法】可研/决策信息表格:数据变更回调(独立,仅影响自身) ==========
const handleFinancialChange = (currentRow) => {
if (isPreview.value) return;
// 仅更新可研表格的当前行合计
updateFinancialRowTotal(currentRow);
// 仅更新可研表格的全量合计行
updateAllFinancialTotalRow();
// 仅同步可研表格的提交字段
formData.kyjcxx = [...financialIndicators.value];
};
annualPlanIndicators.value.forEach(
(row) => !row.isTotal && updateRowTotal(row),
);
updateAnnualPlanTotalRow();
// ========== 【专属方法】项目年度计划表格:子组件回调(独立,仅影响自身) ==========
const handleAnnualPlanTableChange = (newData) => {
if (isPreview.value) return;
// 仅接收子组件的年度计划数据,与可研表格无任何交互
formData.xmndjh = [...newData];
};
// ========== 回填财务表格数据(按序号+指标名双匹配,避免错位) ==========
// ========== 动态时间生成:仅生成一次,两个表格共用表头(无数据关联) ==========
const generateDynamicTime = () => {
const now = new Date();
const currentYear = now.getFullYear();
const currentMonth = now.getMonth() + 1;
const timeList = [];
for (let i = 0; i <= 15; i++) {
if (timeHeaderType.value === "year") {
timeList.push(String(currentYear + i));
} else {
const calcYear = currentYear + Math.floor((currentMonth + i - 1) / 12);
const calcMonth = ((currentMonth + i - 1) % 12) + 1;
timeList.push(`${calcYear}-${calcMonth.toString().padStart(2, "0")}`);
}
}
dynamicTimeList.value = timeList;
// 两个表格分别独立初始化,无互相调用
initFinancialTable();
initAnnualPlanTable();
};
// ========== 回填逻辑:完全隔离,各自处理自身表格 ==========
// 1. 可研/决策信息表格:专属回填(独立)
const fillFinancialTable = (backfillData) => {
if (!Array.isArray(backfillData) || backfillData.length === 0) return;
financialIndicators.value.forEach((frontRow) => {
......@@ -1059,26 +1268,28 @@ const fillFinancialTable = (backfillData) => {
frontRow[time] = Number(backfillRow[time]) || 0;
});
});
initAllRowTotal();
// 仅初始化可研表格的合计
initFinancialRowTotal();
};
// ========== 回填年度计划表格数据(按指标名匹配,避免错位) ==========
// 2. 项目年度计划表格:专属回填(独立,按id匹配,精准无错位)
const fillAnnualPlanTable = (backfillData) => {
if (!Array.isArray(backfillData) || backfillData.length === 0) return;
annualPlanIndicators.value.forEach((frontRow) => {
const backfillRow = backfillData.find(
(item) => item.indicatorName === frontRow.indicatorName,
);
// 先初始化年度计划表格的空数据
initAnnualPlanTable();
// 按唯一id匹配回填,避免indicatorName重复导致的错位
formData.xmndjh.forEach((frontRow) => {
const backfillRow = backfillData.find((item) => item.id === frontRow.id);
if (!backfillRow) return;
frontRow.total = Number(backfillRow.total) || 0;
dynamicTimeList.value.forEach((time) => {
frontRow[time] = Number(backfillRow[time]) || 0;
frontRow[time] =
backfillRow[time] !== undefined ? Number(backfillRow[time]) || 0 : 0;
});
});
initAllRowTotal();
};
// ========== 业务方法:获取项目列表 ==========
// ========== 业务方法:获取项目列表(通用) ==========
const getProjectData = () => {
proxy.$post({
url: "/api/project/listProject",
......@@ -1090,80 +1301,86 @@ const getProjectData = () => {
});
};
// ========== 选择项目同步名称 ==========
// ========== 选择项目同步名称(通用) ==========
const changeProject = (val) => {
proxy.$post({
url: "/api/project/getProjectFinalInfo",
data: { id: val },
callback: (data) => {
loading.value = false;
if (!data) return ElMessage.error("未查询到数据");
console.log(data, "项目详情数据");
console.log(data, "data");
formData.projectName = data.projectName || "";
formData.sbdw = data.sbdw || "";
formData.xmgsmc = data.xmgsmc || "";
formData.xmkgsjyj = data.xmkgsjyj || "";
formData.xmjgsjyj = data.xmjgsjyj || "";
formData.xmjd = data.xmjd || "";
formData.yynxn = data.yynxn || "";
formData.xmjsqy = data.xmjsqy || "";
},
error: () => {
loading.value = false;
ElMessage.error("获取数据失败");
ElMessage.error("获取项目详情失败");
},
});
const selectItem = projectList.value.find((item) => item.id === val);
if (selectItem) formData.projectName = selectItem.projectName;
};
// ========== 核心修复:获取详情(编辑/预览),解决timeList未定义BUG ==========
// ========== 获取详情(编辑/预览):两个表格分别独立回填 ==========
const getJsqtzjcDetail = () => {
if (!rcCgqyglId.value) return;
loading.value = true;
proxy.$post({
url: "/api/project/getJsqtzjcInfo",
url: "/api/project/getTzjh",
data: { id: rcCgqyglId.value },
callback: (data) => {
loading.value = false;
if (!data) return ElMessage.error("未查询到数据");
Object.assign(formData, data); // 回填所有表单数据
console.log(data, "data");
// 核心修复:从后端kyjcxx提取时间列表,无则生成默认时间
Object.assign(formData, data); // 回填基础字段
// 步骤1:提取/生成时间列表(共用表头)
if (data.kyjcxx && Array.isArray(data.kyjcxx) && data.kyjcxx.length > 0) {
// 提取后端已有的时间字段(过滤非时间字段)
const firstRow = data.kyjcxx[0];
dynamicTimeList.value = Object.keys(firstRow).filter((key) =>
dynamicTimeList.value = Object.keys(data.kyjcxx[0]).filter((key) =>
/^\d{4}(-\d{2})?$/.test(key),
);
} else {
generateDynamicTime(); // 无数据则生成默认时间
generateDynamicTime();
}
// 初始化表格后再回填数据
if (dynamicTimeList.value.length === 0) generateDynamicTime();
else {
// 步骤2:两个表格分别独立初始化
if (dynamicTimeList.value.length > 0) {
initFinancialTable();
initAnnualPlanTable();
}
// 回填财务数据
// 步骤3:两个表格分别独立回填(完全隔离)
if (data.kyjcxx) fillFinancialTable(data.kyjcxx);
// 回填年度计划数据
if (data.xmndjh) fillAnnualPlanTable(data.xmndjh);
},
error: () => {
loading.value = false;
ElMessage.error("获取数据失败");
ElMessage.error("获取投资计划详情失败");
},
});
};
// ========== 核心修改:保存表单,提交字段改为kyjcxx和xmndjh ==========
// ========== 保存表单:两个表格的提交数据完全独立拆分 ==========
const saveClick = () => {
if (!formData.projectId) return ElMessage.warning("请选择项目信息");
loading.value = true;
const url = rcCgqyglId.value
? "/api/project/updateJsqtzjc"
: "/api/project/createJsqtzjc";
? "/api/project/updateTzjh"
: "/api/project/createTzjh";
// 组装提交数据:财务数据赋值给kyjcxx,年度计划数据赋值给xmndjh
// 组装提交数据:两个表格的字段完全独立拆分,无任何混叠
const submitData = {
...formData,
projectId: String(formData.projectId),
// 1. 可研/决策信息表格:专属提交数据(仅自身字段)
kyjcxx: financialIndicators.value.map((row) => {
const filterRow = {
serialNumber: row.serialNumber,
......@@ -1174,12 +1391,9 @@ const saveClick = () => {
dynamicTimeList.value.forEach((time) => (filterRow[time] = row[time]));
return filterRow;
}),
xmndjh: annualPlanIndicators.value.map((row) => {
const filterRow = {
indicatorName: row.indicatorName,
level: row.level,
total: row.total,
};
// 2. 项目年度计划表格:专属提交数据(仅自身字段,子组件绑定的xmndjh)
xmndjh: formData.xmndjh.map((row) => {
const filterRow = { id: row.id, total: row.total };
dynamicTimeList.value.forEach((time) => (filterRow[time] = row[time]));
return filterRow;
}),
......@@ -1200,18 +1414,19 @@ const saveClick = () => {
});
};
// ========== 辅助方法 ==========
// ========== 辅助方法(通用) ==========
const backClick = () => router.back(-1);
const tableCellStyle = ({ row }) =>
row.isTotal ? { background: "#f5f7fa", fontWeight: "bold" } : {};
// ========== 页面初始化 ==========
// ========== 页面初始化:两个表格分别独立处理 ==========
onMounted(() => {
getProjectData();
if (rcCgqyglId.value) {
setTimeout(() => getJsqtzjcDetail(), 100);
} else {
generateDynamicTime(); // 新增模式直接生成时间
// 新增模式:仅生成一次时间,两个表格分别独立初始化
generateDynamicTime();
}
});
</script>
......@@ -1239,7 +1454,7 @@ onMounted(() => {
border-radius: 4px;
}
// 表格样式优化
// 表格样式优化:两个表格统一样式,逻辑独立
:deep(.el-table-cell) {
padding-left: 8px !important;
box-sizing: border-box;
......@@ -1258,4 +1473,13 @@ onMounted(() => {
:deep(.el-textarea__inner) {
width: 100% !important;
}
// 年度计划表格子组件容器
.annualPlans {
width: 100%;
overflow-x: auto;
}
// 修复数字输入框样式
:deep(.el-input-number) {
width: 100%;
}
</style>
<template>
<!-- 外层左右布局flex容器 -->
<div class="annual-plan-wrap">
<!-- 左侧:数据驱动的原生表格(绑定右侧行合计值) -->
<div class="left-table">
<table class="investment-table">
<tbody>
<!-- 计划资金来源(全口径)模块 -->
<tr>
<td rowspan="18" class="first-col">年度投资计划</td>
<td rowspan="16" class="second-col">计划投资完成及资金来源</td>
<td rowspan="9" class="third-col">计划资金来源(全口径)</td>
<td style="height: 48px"></td>
<td style="height: 48px"></td>
<td style="height: 48px">合计</td>
</tr>
<tr>
<td rowspan="3">资本金</td>
<td>小计</td>
<td>{{ getRowTotal(1) }}万元</td>
</tr>
<tr>
<td>能建方*</td>
<td>{{ getRowTotal(2) }}万元</td>
</tr>
<tr>
<td>外部股东*</td>
<td>{{ getRowTotal(3) }}万元</td>
</tr>
<tr>
<td rowspan="2">债权融资*</td>
<td>小计*</td>
<td>{{ getRowTotal(4) }}万元</td>
</tr>
<tr>
<td>其中:非并表项目我方贷款/担保额*</td>
<td>{{ getRowTotal(5) }}万元</td>
</tr>
<tr>
<td>回款再投入*</td>
<td></td>
<td>{{ getRowTotal(6) }}万元</td>
</tr>
<tr>
<td rowspan="2">其他*</td>
<td>小计*</td>
<td>{{ getRowTotal(7) }}万元</td>
</tr>
<tr>
<td>其中:我方其他出资*</td>
<td>{{ getRowTotal(8) }}万元</td>
</tr>
<!-- 资金来源(新口径)模块 -->
<tr>
<td rowspan="6" class="third-col">资金来源(新口径)</td>
<td>合计</td>
<td></td>
<td>{{ getRowTotal(9) }}万元</td>
</tr>
<tr>
<td rowspan="3">资本金</td>
<td>小计</td>
<td>{{ getRowTotal(10) }}万元</td>
</tr>
<tr>
<td>能建方</td>
<td>{{ getRowTotal(11) }}万元</td>
</tr>
<tr>
<td>外部股东</td>
<td>{{ getRowTotal(12) }}万元</td>
</tr>
<tr>
<td>债权融资</td>
<td></td>
<td>{{ getRowTotal(13) }}万元</td>
</tr>
<tr>
<td>回款再投入</td>
<td></td>
<td>{{ getRowTotal(14) }}万元</td>
</tr>
<tr>
<td>其他</td>
<td></td>
<td></td>
<td>{{ getRowTotal(15) }}万元</td>
</tr>
<!-- 合计与政府付费模块 -->
<tr>
<td>合计</td>
<td></td>
<td></td>
<td></td>
<td>{{ getRowTotal(16) }}万元</td>
</tr>
<tr>
<td>政府付费*</td>
<td></td>
<td></td>
<td></td>
<td>{{ getRowTotal(17) }}万元</td>
</tr>
</tbody>
</table>
</div>
<!-- 右侧:Element 时间输入表格(绑定父组件传递的数据源+动态时间) -->
<div class="right-table">
<el-table
:data="modelValue"
style="width: 100%"
border
:cell-style="tableCellStyle"
:row-style="{ height: '48px' }"
:header-row-style="{ height: '48px' }"
>
<el-table-column
v-for="time in dynamicTimeList"
:key="time"
:label="time"
width="160"
align="center"
>
<template #default="{ row }">
<el-input-number
v-model="row[time]"
:min="0"
:precision="2"
controls-position="right"
@change="handleChange(row)"
:disabled="isPreview"
style="width: 100%"
/>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script setup>
import { defineProps, defineEmits, watch, onMounted } from "vue";
// 🔥 核心:接收父组件参数,v-model双向绑定核心(modelValue对应父组件v-model值)
const props = defineProps({
// 双向绑定的表格数据源(父组件formData.xmndjh)
modelValue: {
type: Array,
default: () => [],
},
// 父组件传递的动态时间列表(替代子组件内部定义)
dynamicTimeList: {
type: Array,
default: () => [],
},
// 预览状态(禁用输入)
isPreview: {
type: Boolean,
default: false,
},
});
// 🔥 核心:触发双向绑定更新+原有事件兼容
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 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 }) =>
row.isTotal ? { background: "#f5f7fa", fontWeight: "bold" } : {};
</script>
<style scoped lang="scss">
// 核心:左右布局样式
.annual-plan-wrap {
display: flex;
align-items: flex-start;
gap: 0; // 左右无缝衔接,视觉成一个整体
width: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
overflow-x: auto; // 整体横向滚动,避免左右分离滚动
}
// 左侧原生表格容器:固定宽度不挤压
.left-table {
flex-shrink: 0;
}
// 左侧原生表格样式(160px列宽 + 48px行高 + 无缝边框)
.investment-table {
width: fit-content;
border-collapse: collapse;
border: 1px solid #ebeef5; // 和父组件表格边框色统一
border-right: 0; // 取消右侧边框,和右侧EL表格无缝衔接
}
.investment-table td {
width: 160px; // 和右侧EL表格列宽完全统一
height: 49px; // 匹配原有行高,避免错位
border: 1px solid #ebeef5; // 和父组件表格边框色统一
padding: 0 !important; // 取消内边距,文字居中更整齐
text-align: center;
vertical-align: middle;
word-break: break-all;
box-sizing: border-box;
}
// 左侧表格层级背景色(保留原有样式)
.first-col {
font-weight: bold;
background-color: #f5f7fa; // 和父组件合计行背景统一
}
.second-col {
background-color: #f5f7fa;
}
.third-col {
font-weight: 500;
background-color: #f5f7fa;
}
// 右侧EL表格样式优化(和父组件完全统一:行高/边框/内边距)
:deep(.el-table) {
--el-table-border-color: #ebeef5; // 边框色和父组件表格一致
--el-table-row-height: 48px !important; // 行高和左侧强制统一
border-left: 0; // 取消左侧边框,和左侧无缝衔接
}
// 表头行高也强制48px,避免表头错位
:deep(.el-table__header tr),
:deep(.el-table__body tr) {
height: 48px !important;
}
:deep(.el-table-cell) {
padding: 0 !important; // 内边距和左侧一致
text-align: center;
vertical-align: middle;
box-sizing: border-box;
}
// 输入框适配行高,居中显示
:deep(.el-input-number) {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
:deep(.el-input-number__input) {
text-align: right;
padding-right: 25px;
width: 90%; // 微调宽度,避免输入框溢出
}
// 去除多余样式
.flex {
display: flex;
}
</style>
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