明树Git Lab

Commit bc1354b0 authored by zhanghan's avatar zhanghan

1

parent 5c1a28a5
Pipeline #108823 passed with stage
in 21 seconds
{
"permissions": {
"allow": [
"mcp__zai-mcp-server__analyze_image"
"mcp__zai-mcp-server__analyze_image",
"mcp__zai-mcp-server__extract_text_from_screenshot"
]
}
}
......@@ -69,22 +69,29 @@
</el-table-column>
<!-- 多级时间列表头渲染逻辑 -->
<template v-if="hasTimeHeaderGroup">
<template v-for="group in timeColumnGroups" :key="`time-group-${group.key}`">
<!-- 有一级分组标题 -->
<el-table-column
v-for="group in timeColumnGroups"
:key="`time-group-${group.key}`"
v-if="!group.isSingle && group.label"
:label="group.label"
align="right"
>
<!-- 遍历一级分组的子项 -->
<template v-for="child in group.children" :key="`child-${child.key || child.prop}`">
<!-- 如果是二级分组 -->
<el-table-column
v-for="timeItem in group.children"
v-if="child.isSubGroup"
:label="child.label"
align="center"
>
<el-table-column
v-for="timeItem in child.children"
:key="`time-col-${timeItem.prop}`"
:label="timeItem.label"
min-width="140"
align="center"
>
<template #default="{ row }">
<!-- 🌟 核心修改:优先使用列级isTextRow配置,其次行级 -->
<el-input
v-if="timeItem.isTextRow || row.isTextRow"
v-model="row[timeItem.prop]"
......@@ -95,7 +102,6 @@
style="width: 100%"
@input="() => handleTextInput(row)"
/>
<!-- 数字行:金额输入框 -->
<el-input
v-else
v-model="row[timeItem.prop]"
......@@ -109,22 +115,52 @@
</template>
</el-table-column>
</el-table-column>
<!-- 如果是直接列(没有二级分组) -->
<el-table-column
v-else
:key="`time-col-${child.prop}`"
:label="child.label"
min-width="140"
align="center"
>
<template #default="{ row }">
<el-input
v-if="child.isTextRow || row.isTextRow"
v-model="row[child.prop]"
type="textarea"
:rows="2"
:disabled="isPreview"
placeholder="请输入说明"
style="width: 100%"
@input="() => handleTextInput(row)"
/>
<el-input
v-else
v-model="row[child.prop]"
:precision="2"
controls-position="right"
:disabled="isPreview"
placeholder="请输入金额"
style="width: 100%"
@change="() => handleFinancialChange(row)"
/>
</template>
<!-- 无时间列分组 → 单级表头逻辑 -->
</el-table-column>
</template>
</el-table-column>
<!-- 单列(没有分组标题,label 为空) -->
<el-table-column
v-else
v-for="time in validDynamicTimeConfig"
:key="`time-col-${time.prop || time}`"
:label="time.label || time"
v-for="timeItem in group.children"
:key="`time-col-${timeItem.prop}`"
:label="timeItem.label"
min-width="140"
align="center"
>
<template #default="{ row }">
<!-- 🌟 核心修改:优先使用列级isTextRow配置,其次行级 -->
<el-input
v-if="time.isTextRow || row.isTextRow"
v-model="row[time.prop || time]"
v-if="timeItem.isTextRow || row.isTextRow"
v-model="row[timeItem.prop]"
type="textarea"
:rows="2"
:disabled="isPreview"
......@@ -132,10 +168,9 @@
style="width: 100%"
@input="() => handleTextInput(row)"
/>
<!-- 数字行:金额输入框 -->
<el-input
v-else
v-model="row[time.prop || time]"
v-model="row[timeItem.prop]"
:precision="2"
controls-position="right"
:disabled="isPreview"
......@@ -145,6 +180,7 @@
/>
</template>
</el-table-column>
</template>
</el-table>
<div v-else style="text-align: center; padding: 20px; color: #909399">
请传入合法的表格配置(包含indicatorList、dynamicTimeList)
......@@ -213,6 +249,7 @@ const validDynamicTimeConfig = computed(() => {
label: item.trim(),
prop: item.trim(),
headerGroup: "",
subGroup: "", // 支持三级分组
isTextRow: false, // 字符串格式默认数字列
};
}
......@@ -220,42 +257,88 @@ const validDynamicTimeConfig = computed(() => {
label: item.label?.trim() || "",
prop: item.prop?.trim() || item.label?.trim() || "",
headerGroup: item.headerGroup?.trim() || "",
subGroup: item.subGroup?.trim() || "", // 支持三级分组
isTextRow: item.isTextRow === true, // 显式转换为布尔值,默认false
};
})
.filter((item) => !!item.prop);
});
// 判断是否有时间列分组
const hasTimeHeaderGroup = computed(() => {
return validDynamicTimeConfig.value.some((item) => !!item.headerGroup);
});
// 时间列分组计算
// 时间列分组计算(支持三级分组:headerGroup -> subGroup -> 列)
const timeColumnGroups = computed(() => {
const timeConfig = validDynamicTimeConfig.value;
if (!timeConfig.length) return [];
const groupMap = {};
const groupMap = {}; // 一级分组:headerGroup
const singleColumns = []; // 没有分组的单列
timeConfig.forEach((item) => {
const groupKey = item.headerGroup || `single_${item.prop}`;
if (!groupMap[groupKey]) {
groupMap[groupKey] = {
label: item.headerGroup || item.label,
key: groupKey,
const headerGroup = item.headerGroup?.trim();
const subGroup = item.subGroup?.trim();
// 如果有有效的 headerGroup,则创建分组
if (headerGroup && headerGroup.length > 0) {
if (!groupMap[headerGroup]) {
groupMap[headerGroup] = {
label: headerGroup,
key: headerGroup,
children: [],
};
}
groupMap[groupKey].children.push(item);
// 如果有 subGroup,在一级分组下创建二级分组
if (subGroup && subGroup.length > 0) {
let subGroupItem = groupMap[headerGroup].children.find(
(child) => child.label === subGroup
);
if (!subGroupItem) {
subGroupItem = {
label: subGroup,
key: `${headerGroup}_${subGroup}`,
isSubGroup: true, // 标记为二级分组
children: [],
};
groupMap[headerGroup].children.push(subGroupItem);
}
subGroupItem.children.push(item);
} else {
// 没有 subGroup,直接挂在一级分组下
groupMap[headerGroup].children.push(item);
}
} else {
// 没有 headerGroup 的作为单列处理(不显示一级标题)
singleColumns.push({
label: "", // 空标题,不显示一级分组
key: `single_${item.prop}`,
isSingle: true, // 标记为单列
children: [item],
});
}
});
// 合并分组和单列,保持原始顺序
const result = [];
const addedKeys = new Set();
timeConfig.forEach((item) => {
const groupKey = item.headerGroup || `single_${item.prop}`;
if (!addedKeys.has(groupKey)) {
result.push(groupMap[groupKey]);
addedKeys.add(groupKey);
const headerGroup = item.headerGroup?.trim();
// 有分组的
if (headerGroup && headerGroup.length > 0) {
if (!addedKeys.has(headerGroup)) {
result.push(groupMap[headerGroup]);
addedKeys.add(headerGroup);
}
} else {
// 单列
const singleKey = `single_${item.prop}`;
if (!addedKeys.has(singleKey)) {
const singleColumn = singleColumns.find((sc) => sc.key === singleKey);
if (singleColumn) {
result.push(singleColumn);
addedKeys.add(singleKey);
}
}
}
});
......@@ -414,6 +497,7 @@ const tableCellStyle = ({ row }) => {
line-height: 48px;
background: #f5f7fa;
text-align: center;
margin-bottom: 0;
}
:deep(.el-input-number) {
......
<template>
<div class="investment-recovery-table-wrap">
<!-- 总标题 -->
<div v-if="tableTitle" class="table-main-title">
{{ tableTitle }}
</div>
<el-table
:data="tableDataRef"
style="width: 100%"
border
:cell-style="tableCellStyle"
row-key="serialNumber"
>
<!-- 序号列 -->
<el-table-column
prop="serialNumber"
label="序号"
width="80"
fixed="left"
align="center"
/>
<!-- 指标名称列 -->
<el-table-column
prop="indicatorName"
label="指标名称"
fixed="left"
align="left"
min-width="180"
/>
<!-- 年度更新表格的列 -->
<template v-if="tableType === 'year'">
<el-table-column
v-for="col in yearColumns"
:key="col.prop"
:label="col.label"
min-width="180"
align="center"
>
<template #default="{ row }">
<!-- 文本输入 -->
<el-input
v-if="col.isText"
v-model="row[col.prop]"
type="textarea"
:rows="2"
:disabled="isPreview || row.noEdit"
placeholder="请输入"
style="width: 100%"
@input="handleTextInput"
/>
<!-- 数字输入 -->
<el-input
v-else-if="!col.isCalculated"
v-model="row[col.prop]"
type="number"
:disabled="isPreview || row.noEdit"
placeholder="请输入"
@input="handleNumberInput(row)"
/>
<!-- 计算字段 -->
<span v-else class="calculated-value">
{{ formatCalculatedValue(row[col.prop]) }}
</span>
</template>
</el-table-column>
</template>
<!-- 月度更新表格的列 -->
<template v-else>
<!-- 1-12月 -->
<el-table-column
v-for="month in 12"
:key="`month-${month}`"
:label="`${month}月`"
:prop="`m${month}`"
min-width="180"
align="center"
>
<template #default="{ row }">
<el-input
v-model="row[`m${month}`]"
type="number"
:disabled="isPreview || row.noEdit"
placeholder="请输入"
@input="handleMonthInput(row)"
/>
</template>
</el-table-column>
<!-- 本年累计 -->
<el-table-column
label="本年累计"
prop="yearTotal"
min-width="180"
align="center"
>
<template #default="{ row }">
<span v-if="row.isTotalRow" class="calculated-value">
{{ formatCalculatedValue(row.yearTotal) }}
</span>
<el-input
v-else
v-model="row.yearTotal"
type="number"
:disabled="isPreview || row.noEdit || row.autoCalcYearTotal"
placeholder="自动计算"
@input="handleYearTotalInput(row)"
/>
</template>
</el-table-column>
<!-- 本年完成率(分组) -->
<el-table-column label="本年完成率" align="center">
<el-table-column
label="本年决策"
prop="yearDecision"
min-width="180"
align="center"
>
<template #default="{ row }">
<span v-if="!row.isTotalRow" class="calculated-value">
{{ formatCalculatedValue(row.yearDecision) }}
</span>
<span v-else class="calculated-value">
{{ formatCalculatedValue(row.yearDecision) }}
</span>
</template>
</el-table-column>
<el-table-column
label="本年计划"
prop="yearPlan"
align="center"
width="180"
>
<template #default="{ row }">
<span v-if="!row.isTotalRow" class="calculated-value">
{{ formatCalculatedValue(row.yearPlan) }}
</span>
<span v-else class="calculated-value">
{{ formatCalculatedValue(row.yearPlan) }}
</span>
</template>
</el-table-column>
<el-table-column
label="本年累计/本年决策"
prop="yearCompletionRateDecision"
min-width="180"
align="center"
>
<template #default="{ row }">
<span class="calculated-value">
{{ formatCalculatedValue(row.yearCompletionRateDecision, "%") }}
</span>
</template>
</el-table-column>
<el-table-column
label="本年累计/本年计划"
prop="yearCompletionRatePlan"
min-width="180"
align="center"
>
<template #default="{ row }">
<span class="calculated-value">
{{ formatCalculatedValue(row.yearCompletionRatePlan, "%") }}
</span>
</template>
</el-table-column>
</el-table-column>
<!-- 截至本年完成(开累) -->
<el-table-column
label="截至本年完成(开累)"
prop="invoiceCompleted"
min-width="180"
align="center"
>
<template #default="{ row }">
<el-input
v-model="row.invoiceCompleted"
type="number"
:disabled="isPreview || row.noEdit"
placeholder="请输入"
@input="handleInvoiceInput(row)"
/>
</template>
</el-table-column>
<!-- 开累完成率(分组) -->
<el-table-column label="开累完成率" min-width="180" align="center">
<el-table-column
min-width="180"
label="开累决策"
prop="invoiceDecision"
align="center"
>
<template #default="{ row }">
<span v-if="!row.isTotalRow" class="calculated-value">
{{ formatCalculatedValue(row.invoiceDecision) }}
</span>
<span v-else class="calculated-value">
{{ formatCalculatedValue(row.invoiceDecision) }}
</span>
</template>
</el-table-column>
<el-table-column
label="开累计划"
min-width="180"
prop="invoicePlan"
align="center"
>
<template #default="{ row }">
<span v-if="!row.isTotalRow" class="calculated-value">
{{ formatCalculatedValue(row.invoicePlan) }}
</span>
<span v-else class="calculated-value">
{{ formatCalculatedValue(row.invoicePlan) }}
</span>
</template>
</el-table-column>
<el-table-column
label="开累完成/开累决策"
prop="invoiceCompletionRateDecision"
min-width="180"
align="center"
>
<template #default="{ row }">
<span class="calculated-value">
{{
formatCalculatedValue(row.invoiceCompletionRateDecision, "%")
}}
</span>
</template>
</el-table-column>
<el-table-column
label="开累完成/开累计划"
prop="invoiceCompletionRatePlan"
min-width="180"
align="center"
>
<template #default="{ row }">
<span class="calculated-value">
{{ formatCalculatedValue(row.invoiceCompletionRatePlan, "%") }}
</span>
</template>
</el-table-column>
</el-table-column>
<!-- 欠收金额 -->
<el-table-column
label="欠收金额"
prop="shortfallAmount"
min-width="180"
align="center"
>
<template #default="{ row }">
<span class="calculated-value">
{{ formatCalculatedValue(row.shortfallAmount) }}
</span>
</template>
</el-table-column>
<!-- 合同总金额 -->
<el-table-column
label="合同总金额"
prop="contractTotalAmount"
min-width="120"
align="center"
>
<template #default="{ row }">
<el-input
v-model="row.contractTotalAmount"
type="number"
:disabled="isPreview || row.noEdit"
placeholder="请输入"
@input="handleContractAmountInput(row)"
/>
</template>
</el-table-column>
<!-- 计划差额 -->
<el-table-column
label="计划差额"
prop="planDifference"
min-width="120"
align="center"
>
<template #default="{ row }">
<span class="calculated-value">
{{ formatCalculatedValue(row.planDifference) }}
</span>
</template>
</el-table-column>
</template>
</el-table>
</div>
</template>
<script setup>
import { ref, computed, watch, nextTick } from "vue";
const props = defineProps({
modelValue: {
type: Object,
default: () => ({ tableData: [] }),
},
isPreview: {
type: Boolean,
default: false,
},
tableType: {
type: String,
default: "year", // 'year' 或 'month'
validator: (val) => ["year", "month"].includes(val),
},
tableTitle: {
type: String,
default: "",
},
// 投资回收计划数据(用于自动填充本年计划、开累计划)
planData: {
type: Object,
default: () => ({}),
},
// 投资回收决策数据(用于自动填充本年决策、开累决策)
decisionData: {
type: Object,
default: () => ({}),
},
// 去年投资回收计划数据(用于获取前年数据)
lastYearPlanData: {
type: Object,
default: () => ({}),
},
// 当前年份
currentYear: {
type: [String, Number],
default: "",
},
});
const emit = defineEmits(["update:modelValue"]);
const tableDataRef = ref([]);
// 年度表格列配置
const yearColumns = computed(() => [
{
prop: "decisionReceivable",
label: "截至年底应收决策应收",
width: 160,
isText: false,
isCalculated: false,
},
{
prop: "actualReceived",
label: "截至年底实收",
width: 140,
isText: false,
isCalculated: false,
},
{
prop: "shortfall",
label: "欠收(决策应收-实收)",
width: 180,
isText: false,
isCalculated: true,
},
{
prop: "completionRate",
label: "完成率",
width: 120,
isText: false,
isCalculated: true,
},
{
prop: "problems",
label: "存在主要问题",
width: 200,
isText: true,
isCalculated: false,
},
{
prop: "measures",
label: "采取措施情况",
width: 200,
isText: true,
isCalculated: false,
},
]);
// 格式化计算值
const formatCalculatedValue = (value, suffix = "") => {
if (value === null || value === undefined || isNaN(value)) return "-";
if (suffix === "%") {
return `${Number(value).toFixed(2)}%`;
}
return Number(value).toFixed(2);
};
// 初始化行数据
const initRowData = (indicator, index, sourceRow = {}) => {
const baseRow = {
serialNumber: index + 1,
indicatorName: indicator.name,
noEdit: indicator.noEdit || false,
isTotalRow: indicator.isTotalRow || false,
};
if (props.tableType === "year") {
return {
...baseRow,
decisionReceivable: sourceRow.decisionReceivable || null,
actualReceived: sourceRow.actualReceived || null,
shortfall: sourceRow.shortfall || null,
completionRate: sourceRow.completionRate || null,
problems: sourceRow.problems || "",
measures: sourceRow.measures || "",
};
} else {
// 月度表格
const monthData = {};
for (let i = 1; i <= 12; i++) {
monthData[`m${i}`] = sourceRow[`m${i}`] || null;
}
return {
...baseRow,
...monthData,
yearTotal: sourceRow.yearTotal || null,
yearCompletionRateDecision: sourceRow.yearCompletionRateDecision || null,
yearCompletionRatePlan: sourceRow.yearCompletionRatePlan || null,
invoiceCompleted: sourceRow.invoiceCompleted || null,
invoiceCompletionRateDecision:
sourceRow.invoiceCompletionRateDecision || null,
invoiceCompletionRatePlan: sourceRow.invoiceCompletionRatePlan || null,
shortfallAmount: sourceRow.shortfallAmount || null,
contractTotalAmount: sourceRow.contractTotalAmount || null,
planDifference: sourceRow.planDifference || null,
yearDecision: sourceRow.yearDecision || null, // 本年决策目标
yearPlan: sourceRow.yearPlan || null, // 本年计划目标
invoiceDecision: sourceRow.invoiceDecision || null, // 开累决策目标
invoicePlan: sourceRow.invoicePlan || null, // 开累计划目标
autoCalcYearTotal: indicator.autoCalcYearTotal !== false, // 默认自动计算本年累计
};
}
};
// 指标列表
const indicators = [
{ name: "政府付费", noEdit: false },
{ name: "建设期", noEdit: false },
{ name: "运营期", noEdit: false },
{ name: "使用者付费", noEdit: false },
{ name: "投资价款", noEdit: false },
{ name: "参股项目投资回收", noEdit: false },
{ name: "代建工程款", noEdit: false },
{ name: "其他", noEdit: false },
{ name: "合计", isTotalRow: true, noEdit: true },
];
// 从计划数据和决策数据中获取对应指标的值
const getIndicatorValue = (indicatorName, dataSource, type) => {
if (!dataSource || !dataSource.tableData) return 0;
const row = dataSource.tableData.find(
(r) => r.indicatorName === indicatorName
);
if (!row) return 0;
// 根据类型返回对应字段的值
if (type === "yearDecision") {
// 本年决策:从决策数据中获取当前年度的合计
// 决策数据的时间列表是 ["一季度", "二季度", "三季度", "四季度"]
// 合计 = 一季度 + 二季度 + 三季度 + 四季度
const q1 = parseFloat(row["一季度"]) || 0;
const q2 = parseFloat(row["二季度"]) || 0;
const q3 = parseFloat(row["三季度"]) || 0;
const q4 = parseFloat(row["四季度"]) || 0;
return q1 + q2 + q3 + q4;
} else if (type === "yearPlan") {
// 本年计划:从去年投资回收计划数据中获取合计值
return parseFloat(row.a1) || 0;
} else if (type === "invoiceDecision") {
// 开累决策:从决策数据中获取开累值
// 这里可能是累计值,需要根据实际业务逻辑确定
const q1 = parseFloat(row["一季度"]) || 0;
const q2 = parseFloat(row["二季度"]) || 0;
const q3 = parseFloat(row["三季度"]) || 0;
const q4 = parseFloat(row["四季度"]) || 0;
return q1 + q2 + q3 + q4; // 暂时用年度合计
} else if (type === "invoicePlan") {
// 开累计划:从计划数据中获取开累值
return parseFloat(row.a1) || 0; // 暂时用年度合计
}
return 0;
};
// 自动填充决策和计划数据
const autoFillDecisionAndPlan = () => {
if (props.tableType !== "month") return;
tableDataRef.value.forEach((row) => {
if (row.isTotalRow || row.noEdit) return;
// 自动填充本年决策(从决策数据获取)
if (props.decisionData && Object.keys(props.decisionData).length > 0) {
row.yearDecision = getIndicatorValue(
row.indicatorName,
props.decisionData,
"yearDecision"
);
}
// 自动填充本年计划(从去年投资回收计划数据获取)
if (props.lastYearPlanData && Object.keys(props.lastYearPlanData).length > 0) {
row.yearPlan = getIndicatorValue(
row.indicatorName,
props.lastYearPlanData,
"yearPlan"
);
} else if (props.planData && Object.keys(props.planData).length > 0) {
// 如果没有去年数据,则使用当前年计划数据作为后备
row.yearPlan = getIndicatorValue(
row.indicatorName,
props.planData,
"yearPlan"
);
}
// 自动填充开累决策(从决策数据获取)
if (props.decisionData && Object.keys(props.decisionData).length > 0) {
row.invoiceDecision = getIndicatorValue(
row.indicatorName,
props.decisionData,
"invoiceDecision"
);
}
// 自动填充开累计划(从计划数据获取)
if (props.planData && Object.keys(props.planData).length > 0) {
row.invoicePlan = getIndicatorValue(
row.indicatorName,
props.planData,
"invoicePlan"
);
}
});
// 重新计算完成率
tableDataRef.value.forEach((row) => {
calculateMonthRow(row);
});
};
// 年度表格计算
const calculateYearRow = (row) => {
if (row.noEdit || row.isTotalRow) return;
// 欠收 = 决策应收 - 实收
const decision = parseFloat(row.decisionReceivable) || 0;
const actual = parseFloat(row.actualReceived) || 0;
row.shortfall = decision - actual;
// 完成率 = 实收 / 决策应收 * 100
if (decision > 0) {
row.completionRate = (actual / decision) * 100;
} else {
row.completionRate = 0;
}
};
// 月度表格计算
const calculateMonthRow = (row) => {
if (row.noEdit || row.isTotalRow) return;
// 本年累计 = 1-12月之和
if (row.autoCalcYearTotal) {
let monthSum = 0;
for (let i = 1; i <= 12; i++) {
monthSum += parseFloat(row[`m${i}`]) || 0;
}
row.yearTotal = monthSum;
}
// 本年完成率/本年决策 = 本年累计 / 本年决策 * 100
const yearDecision = parseFloat(row.yearDecision) || 0;
const yearTotal = parseFloat(row.yearTotal) || 0;
if (yearDecision > 0) {
row.yearCompletionRateDecision = (yearTotal / yearDecision) * 100;
} else {
row.yearCompletionRateDecision = 0;
}
// 本年完成率/本年计划 = 本年累计 / 本年计划 * 100
const yearPlan = parseFloat(row.yearPlan) || 0;
if (yearPlan > 0) {
row.yearCompletionRatePlan = (yearTotal / yearPlan) * 100;
} else {
row.yearCompletionRatePlan = 0;
}
// 开累完成率/开累决策 = 开累完成 / 开累决策 * 100
const invoiceDecision = parseFloat(row.invoiceDecision) || 0;
const invoiceCompleted = parseFloat(row.invoiceCompleted) || 0;
if (invoiceDecision > 0) {
row.invoiceCompletionRateDecision =
(invoiceCompleted / invoiceDecision) * 100;
} else {
row.invoiceCompletionRateDecision = 0;
}
// 开累完成率/本年计划 = 开累完成 / 开累计划 * 100
const invoicePlan = parseFloat(row.invoicePlan) || 0;
if (invoicePlan > 0) {
row.invoiceCompletionRatePlan = (invoiceCompleted / invoicePlan) * 100;
} else {
row.invoiceCompletionRatePlan = 0;
}
// 欠收金额 = 本年决策 - 本年累计
row.shortfallAmount = yearDecision - yearTotal;
// 计划差额 = 合同总金额 - 本年计划
const contractTotal = parseFloat(row.contractTotalAmount) || 0;
row.planDifference = contractTotal - yearPlan;
};
// 计算合计行
const calculateTotalRow = () => {
const totalRow = tableDataRef.value.find((row) => row.isTotalRow);
if (!totalRow) return;
if (props.tableType === "year") {
// 年度表格合计
let totalDecision = 0;
let totalActual = 0;
tableDataRef.value.forEach((row) => {
if (!row.isTotalRow && !row.noEdit) {
totalDecision += parseFloat(row.decisionReceivable) || 0;
totalActual += parseFloat(row.actualReceived) || 0;
}
});
totalRow.decisionReceivable = totalDecision;
totalRow.actualReceived = totalActual;
totalRow.shortfall = totalDecision - totalActual;
if (totalDecision > 0) {
totalRow.completionRate = (totalActual / totalDecision) * 100;
} else {
totalRow.completionRate = 0;
}
} else {
// 月度表格合计
for (let i = 1; i <= 12; i++) {
let monthSum = 0;
tableDataRef.value.forEach((row) => {
if (!row.isTotalRow && !row.noEdit) {
monthSum += parseFloat(row[`m${i}`]) || 0;
}
});
totalRow[`m${i}`] = monthSum;
}
// 本年累计
let yearTotalSum = 0;
for (let i = 1; i <= 12; i++) {
yearTotalSum += parseFloat(totalRow[`m${i}`]) || 0;
}
totalRow.yearTotal = yearTotalSum;
}
};
// 处理输入事件
const handleNumberInput = (row) => {
calculateYearRow(row);
calculateTotalRow();
emitChange();
};
const handleTextInput = () => {
emitChange();
};
const handleMonthInput = (row) => {
calculateMonthRow(row);
calculateTotalRow();
emitChange();
};
const handleYearTotalInput = (row) => {
calculateMonthRow(row);
calculateTotalRow();
emitChange();
};
const handleInvoiceInput = (row) => {
calculateMonthRow(row);
calculateTotalRow();
emitChange();
};
const handleContractAmountInput = (row) => {
calculateMonthRow(row);
calculateTotalRow();
emitChange();
};
const emitChange = () => {
if (props.isPreview) return;
const newModelValue = {
...props.modelValue,
tableData: JSON.parse(JSON.stringify(tableDataRef.value)),
};
emit("update:modelValue", newModelValue);
};
// 表格单元格样式
const tableCellStyle = ({ row }) => {
if (row.isTotalRow) {
return { background: "#f5f7fa", fontWeight: "bold", textAlign: "center" };
}
return { textAlign: "center" };
};
// 监听数据变化
watch(
() => props.modelValue,
(newVal) => {
nextTick(() => {
const sourceData = newVal.tableData || [];
const newData = indicators.map((indicator, index) => {
const sourceRow =
sourceData.find((r) => r.indicatorName === indicator.name) || {};
return initRowData(indicator, index, sourceRow);
});
tableDataRef.value = newData;
// 自动填充决策和计划数据
autoFillDecisionAndPlan();
});
},
{ deep: true, immediate: true },
);
// 监听计划数据和决策数据变化
watch(
[() => props.planData, () => props.decisionData, () => props.lastYearPlanData],
() => {
nextTick(() => {
autoFillDecisionAndPlan();
});
},
{ deep: true },
);
</script>
<style scoped lang="scss">
.investment-recovery-table-wrap {
width: 100%;
box-sizing: border-box;
overflow-x: auto;
}
.table-main-title {
font-size: 16px;
font-weight: 600;
color: #1f2937;
border-bottom: 1px solid #ebeef5;
height: 48px;
line-height: 48px;
background: #f5f7fa;
text-align: center;
}
.calculated-value {
color: #409eff;
font-weight: 500;
}
:deep(.el-input__wrapper) {
width: 100%;
}
:deep(.el-input__inner) {
text-align: center;
}
:deep(.el-textarea__inner) {
text-align: left;
min-height: 40px !important;
}
:deep(.el-table__cell) {
padding: 4px 8px !important;
height: 48px !important;
vertical-align: middle !important;
}
:deep(.el-table__row) {
height: 48px !important;
}
</style>
......@@ -116,6 +116,7 @@
color: var(--el-color-primary);
}
}
&-header {
border: none;
height: 64px;
......@@ -130,6 +131,7 @@
height: 0;
display: flex;
flex-direction: column;
.tabs-content {
flex: 1;
height: 0;
......
......@@ -9,6 +9,18 @@
<el-collapse v-model="activeCollapse">
<el-collapse-item title="项目基本信息" name="项目基本信息">
<el-row :gutter="24">
<el-col :span="6">
<el-form-item label="年度" required>
<el-date-picker
v-model="formData.nd"
type="year"
placeholder="请选择年度"
format="YYYY"
value-format="YYYY"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="项目名称" required>
<el-select
......@@ -172,8 +184,8 @@
</el-collapse-item>
<el-collapse-item
title="2025年投资回收累计完成情况-年度更新"
name="2025年投资回收累计完成情况-年度更新"
:title="dynamicTitles.yearlyUpdateTitle"
:name="dynamicTitles.yearlyUpdateTitle"
>
<el-row gutter="20">
<!-- 第一行:决策目标值相关数字项 -->
......@@ -342,11 +354,38 @@
v-model="formData.tzhsjh"
:showSerialColumn="false"
:is-preview="isPreview"
table-title="2026年投资回收计划明细"
:showNameColumn="false"
:showTotalColumn="false"
/>
</el-collapse-item>
<el-collapse-item
:title="dynamicTitles.yearlyCompletionTitle"
:name="dynamicTitles.yearlyCompletionTitle"
>
<InvestmentRecoveryTable
v-model="formData.tzhswcqkndgx"
:is-preview="isPreview"
table-type="year"
:table-title="dynamicTitles.yearlyCompletionTitle"
/>
</el-collapse-item>
<el-collapse-item
:title="dynamicTitles.monthlyCompletionTitle"
:name="dynamicTitles.monthlyCompletionTitle"
>
<InvestmentRecoveryTable
v-model="formData.tzhswcqkydgx"
:is-preview="isPreview"
table-type="month"
:table-title="dynamicTitles.monthlyCompletionTitle"
:plan-data="formData.tzhsjh"
:decision-data="formData.tzhsjc"
:last-year-plan-data="formData.lastyeartzhs"
:current-year="currentYear"
/>
</el-collapse-item>
</el-collapse>
</el-form>
</div>
......@@ -366,11 +405,20 @@
</template>
<script setup>
import { reactive, ref, onMounted, getCurrentInstance, h, computed } from "vue";
import {
reactive,
watch,
ref,
onMounted,
getCurrentInstance,
h,
computed,
} from "vue";
import { useRouter, useRoute } from "vue-router";
import { ElMessage } from "element-plus";
import DynamicTable from "@/components/FormDynamicTable/index.vue";
import FinancialTable from "@/components/FinancialTable.vue";
import InvestmentRecoveryTable from "@/components/InvestmentRecoveryTable.vue";
import routerBack from "@/components/common/routerBack.vue";
const transferColumns = ref([
{
......@@ -415,27 +463,10 @@ const router = useRouter();
const route = useRoute();
const { proxy } = getCurrentInstance();
// 折叠面板默认展开项
const activeCollapse = ref([
"项目基本信息",
"出资情况",
"合同约定权益获取",
"分红情况",
"投资额完成情况(万元)",
"投资回收(决策)",
"投资回收(计划)",
"2025年投资回收累计完成情况-年度更新",
"净现金流",
"截止12月末累计应收",
"截止12月末累计实收",
"资金流出",
"分红情况",
]);
// 表单数据
let initTableData = () => {
Object.assign(formData, {
jzymljys: {
// 使用 Object.assign 直接替换整个对象,而不是合并
formData.jzymljys = {
indicatorList: [
{ name: "一月", isTextRow: false, noTotal: false },
{ name: "二月", isTextRow: false, noTotal: false },
......@@ -474,8 +505,9 @@ let initTableData = () => {
// 表格数据(子组件自动初始化,无需手动赋值)
tableData: [],
},
jzymljsh: {
};
formData.jzymljsh = {
indicatorList: [
{ name: "一月", isTextRow: false, noTotal: false },
{ name: "二月", isTextRow: false, noTotal: false },
......@@ -518,8 +550,9 @@ let initTableData = () => {
// 表格数据(子组件自动初始化,无需手动赋值)
tableData: [],
},
tzhsjc: {
};
formData.tzhsjc = {
// 指标列表
indicatorList: [
{ name: "建设期政府付费", isTextRow: false, noTotal: false },
......@@ -548,8 +581,9 @@ let initTableData = () => {
dynamicTimeList: ["一季度", "二季度", "三季度", "四季度"],
// 表格数据(子组件会自动初始化,父组件可传初始值)
tableData: [],
},
tzhsjh: {
};
formData.tzhsjh = {
// 指标列表
indicatorList: [
{
......@@ -557,131 +591,311 @@ let initTableData = () => {
},
],
// 时间列表(月度)
dynamicTimeList: [
// 第一组:2026年
{ label: "合计", prop: "a1", headerGroup: "2026年" },
{ label: "政府付费", prop: "a2", headerGroup: "2026年" },
{ label: "政府补助", prop: "a3", headerGroup: "2026年" },
{ label: "使用者付费", prop: "a4", headerGroup: "2026年" },
{ label: "使用者欠付", prop: "a5", headerGroup: "2026年" },
{ label: "补贴收入", prop: "a6", headerGroup: "2026年" },
// 时间列表(月度)- 使用动态年度
get dynamicTimeList() {
const year = formData.nd;
const yearStr = `${year}年`;
const q1Str = `${year}年(一季度)`;
const q2Str = `${year}年(二季度)`;
const q3Str = `${year}年(三季度)`;
const q4Str = `${year}年(四季度)`;
return [
// 第一组:投资回收(决策) -> 动态年度
{
label: "合计",
prop: "a1",
headerGroup: "投资回收(决策)",
subGroup: yearStr,
},
{
label: "政府付费",
prop: "a2",
headerGroup: "投资回收(决策)",
subGroup: yearStr,
},
{
label: "政府补助",
prop: "a3",
headerGroup: "投资回收(决策)",
subGroup: yearStr,
},
{
label: "使用者付费",
prop: "a4",
headerGroup: "投资回收(决策)",
subGroup: yearStr,
},
{
label: "使用者欠付",
prop: "a5",
headerGroup: "投资回收(决策)",
subGroup: yearStr,
},
{
label: "补贴收入",
prop: "a6",
headerGroup: "投资回收(决策)",
subGroup: yearStr,
},
{
label: "销售回款(含房地产、水泥、新能源售电、其他生产制造收入等)",
prop: "a7",
headerGroup: "2026年",
headerGroup: "投资回收(决策)",
subGroup: yearStr,
},
{
label: "资产盘活",
prop: "a8",
headerGroup: "投资回收(决策)",
subGroup: yearStr,
},
{
label: "股权分红",
prop: "a9",
headerGroup: "投资回收(决策)",
subGroup: yearStr,
},
{
label: "其他",
prop: "a10",
headerGroup: "投资回收(决策)",
subGroup: yearStr,
},
{ label: "资产盘活", prop: "a8", headerGroup: "2026年" },
{ label: "股权分红", prop: "a9", headerGroup: "2026年" },
{ label: "其他", prop: "a10", headerGroup: "2026年" },
{
label: "目标(决策)与计划差异说明",
isTextRow: true,
prop: "a11",
headerGroup: "2026年",
headerGroup: "投资回收(决策)",
subGroup: yearStr,
},
// 第三组:2026年(一季度)
{ label: "合计", prop: "a23", headerGroup: "2026年(一季度)" },
{ label: "政府付费", prop: "a24", headerGroup: "2026年(一季度)" },
{ label: "政府补助", prop: "a25", headerGroup: "2026年(一季度)" },
{ label: "使用者付费", prop: "a26", headerGroup: "2026年(一季度)" },
{ label: "使用者欠付", prop: "a27", headerGroup: "2026年(一季度)" },
{ label: "补贴收入", prop: "a28", headerGroup: "2026年(一季度)" },
// 第二组:动态年度
{ label: "合计", prop: "a12", headerGroup: yearStr },
{ label: "政府付费", prop: "a13", headerGroup: yearStr },
{ label: "政府补助", prop: "a14", headerGroup: yearStr },
{ label: "使用者付费", prop: "a15", headerGroup: yearStr },
{ label: "使用者欠付", prop: "a16", headerGroup: yearStr },
{ label: "补贴收入", prop: "a17", headerGroup: yearStr },
{
label: "销售回款(含房地产、水泥、新能源售电、其他生产制造收入等)",
prop: "a18",
headerGroup: yearStr,
},
{ label: "资产盘活", prop: "a19", headerGroup: yearStr },
{ label: "股权分红", prop: "a20", headerGroup: yearStr },
{ label: "其他", prop: "a21", headerGroup: yearStr },
{
label: "目标(决策)与计划差异说明",
isTextRow: true,
prop: "a22",
headerGroup: yearStr,
},
// 第三组:动态年度(一季度)
{ label: "合计", prop: "a23", headerGroup: q1Str },
{ label: "政府付费", prop: "a24", headerGroup: q1Str },
{ label: "政府补助", prop: "a25", headerGroup: q1Str },
{ label: "使用者付费", prop: "a26", headerGroup: q1Str },
{ label: "使用者欠付", prop: "a27", headerGroup: q1Str },
{ label: "补贴收入", prop: "a28", headerGroup: q1Str },
{
label: "销售回款(含房地产、水泥、新能源售电、其他生产制造收入等)",
prop: "a29",
headerGroup: "2026年(一季度)",
headerGroup: q1Str,
},
{ label: "资产盘活", prop: "a30", headerGroup: "2026年(一季度)" },
{ label: "股权分红", prop: "a31", headerGroup: "2026年(一季度)" },
{ label: "其他", prop: "a32", headerGroup: "2026年(一季度)" },
{ label: "资产盘活", prop: "a30", headerGroup: q1Str },
{ label: "股权分红", prop: "a31", headerGroup: q1Str },
{ label: "其他", prop: "a32", headerGroup: q1Str },
{
label: "目标(决策)与计划差异说明",
isTextRow: true,
prop: "a33",
headerGroup: "2026年(一季度)",
headerGroup: q1Str,
},
// 第四组:投资回收(计划) -> 动态年度
{
label: "合计",
prop: "a34",
headerGroup: "投资回收(计划)",
subGroup: yearStr,
},
{
label: "政府付费",
prop: "a35",
headerGroup: "投资回收(计划)",
subGroup: yearStr,
},
{
label: "政府补助",
prop: "a36",
headerGroup: "投资回收(计划)",
subGroup: yearStr,
},
{
label: "使用者付费",
prop: "a37",
headerGroup: "投资回收(计划)",
subGroup: yearStr,
},
{
label: "使用者欠付",
prop: "a38",
headerGroup: "投资回收(计划)",
subGroup: yearStr,
},
{
label: "补贴收入",
prop: "a39",
headerGroup: "投资回收(计划)",
subGroup: yearStr,
},
// 第四组:2026年(二季度)
{ label: "合计", prop: "a34", headerGroup: "2026年(二季度)" },
{ label: "政府付费", prop: "a35", headerGroup: "2026年(二季度)" },
{ label: "政府补助", prop: "a36", headerGroup: "2026年(二季度)" },
{ label: "使用者付费", prop: "a37", headerGroup: "2026年(二季度)" },
{ label: "使用者欠付", prop: "a38", headerGroup: "2026年(二季度)" },
{ label: "补贴收入", prop: "a39", headerGroup: "2026年(二季度)" },
{
label: "销售回款(含房地产、水泥、新能源售电、其他生产制造收入等)",
prop: "a40",
headerGroup: "2026年(二季度)",
headerGroup: "投资回收(计划)",
subGroup: yearStr,
},
{
label: "资产盘活",
prop: "a41",
headerGroup: "投资回收(计划)",
subGroup: yearStr,
},
{
label: "股权分红",
prop: "a42",
headerGroup: "投资回收(计划)",
subGroup: yearStr,
},
{
label: "其他",
prop: "a43",
headerGroup: "投资回收(计划)",
subGroup: yearStr,
},
{ label: "资产盘活", prop: "a41", headerGroup: "2026年(二季度)" },
{ label: "股权分红", prop: "a42", headerGroup: "2026年(二季度)" },
{ label: "其他", prop: "a43", headerGroup: "2026年(二季度)" },
{
label: "目标(决策)与计划差异说明",
isTextRow: true,
prop: "a44",
headerGroup: "2026年(二季度)",
headerGroup: "投资回收(计划)",
subGroup: yearStr,
},
// 第五组:2026年(三季度)
{ label: "合计", prop: "a45", headerGroup: "2026年(三季度)" },
{ label: "政府付费", prop: "a46", headerGroup: "2026年(三季度)" },
{ label: "政府补助", prop: "a47", headerGroup: "2026年(三季度)" },
{ label: "使用者付费", prop: "a48", headerGroup: "2026年(三季度)" },
{ label: "使用者欠付", prop: "a49", headerGroup: "2026年(三季度)" },
{ label: "补贴收入", prop: "a50", headerGroup: "2026年(三季度)" },
// 第五组:动态年度(三季度)
{ label: "合计", prop: "a45", headerGroup: q3Str },
{ label: "政府付费", prop: "a46", headerGroup: q3Str },
{ label: "政府补助", prop: "a47", headerGroup: q3Str },
{ label: "使用者付费", prop: "a48", headerGroup: q3Str },
{ label: "使用者欠付", prop: "a49", headerGroup: q3Str },
{ label: "补贴收入", prop: "a50", headerGroup: q3Str },
{
label: "销售回款(含房地产、水泥、新能源售电、其他生产制造收入等)",
prop: "a51",
headerGroup: "2026年(三季度)",
headerGroup: q3Str,
},
{ label: "资产盘活", prop: "a52", headerGroup: "2026年(三季度)" },
{ label: "股权分红", prop: "a53", headerGroup: "2026年(三季度)" },
{ label: "其他", prop: "a54", headerGroup: "2026年(三季度)" },
{ label: "资产盘活", prop: "a52", headerGroup: q3Str },
{ label: "股权分红", prop: "a53", headerGroup: q3Str },
{ label: "其他", prop: "a54", headerGroup: q3Str },
{
label: "目标(决策)与计划差异说明",
isTextRow: true,
prop: "a55",
headerGroup: "2026年(三季度)",
headerGroup: q3Str,
},
// 第六组:2026年(四季度)
{ label: "合计", prop: "a56", headerGroup: "2026年(四季度)" },
{ label: "政府付费", prop: "a57", headerGroup: "2026年(四季度)" },
{ label: "政府补助", prop: "a58", headerGroup: "2026年(四季度)" },
{ label: "使用者付费", prop: "a59", headerGroup: "2026年(四季度)" },
{ label: "使用者欠付", prop: "a60", headerGroup: "2026年(四季度)" },
{ label: "补贴收入", prop: "a61", headerGroup: "2026年(四季度)" },
// 第六组:动态年度(四季度)
{ label: "合计", prop: "a56", headerGroup: q4Str },
{ label: "政府付费", prop: "a57", headerGroup: q4Str },
{ label: "政府补助", prop: "a58", headerGroup: q4Str },
{ label: "使用者付费", prop: "a59", headerGroup: q4Str },
{ label: "使用者欠付", prop: "a60", headerGroup: q4Str },
{ label: "补贴收入", prop: "a61", headerGroup: q4Str },
{
label: "销售回款(含房地产、水泥、新能源售电、其他生产制造收入等)",
prop: "a62",
headerGroup: "2026年(四季度)",
headerGroup: q4Str,
},
{ label: "资产盘活", prop: "a63", headerGroup: "2026年(四季度)" },
{ label: "股权分红", prop: "a64", headerGroup: "2026年(四季度)" },
{ label: "其他", prop: "a65", headerGroup: "2026年(四季度)" },
{ label: "资产盘活", prop: "a63", headerGroup: q4Str },
{ label: "股权分红", prop: "a64", headerGroup: q4Str },
{ label: "其他", prop: "a65", headerGroup: q4Str },
{
label: "目标(决策)与计划差异说明",
isTextRow: true,
prop: "a66",
headerGroup: "2026年(四季度)",
headerGroup: q4Str,
},
];
},
],
// 表格数据(子组件会自动初始化,父组件可传初始值)
tableData: [],
},
});
};
formData.tzhswcqkndgx = {
tableData: [],
};
formData.tzhswcqkydgx = {
tableData: [],
};
};
const formData = reactive({
nd: new Date().getFullYear().toString(), // 年度,默认当前年份
jzymljys: {},
jzymljsh: {},
tzhsjc: {},
tzhsjh: {},
tzhswcqkndgx: {},
tzhswcqkydgx: {},
lastyeartzhs: null, // 去年投资回收计划数据
projectGdxxs: [],
});
// 计算属性:动态生成年份相关的标题
const currentYear = computed(
() => formData.nd || new Date().getFullYear().toString(),
);
const lastYear = computed(() => {
const year = parseInt(currentYear.value);
return (year - 1).toString();
});
// 动态标题
const dynamicTitles = computed(() => ({
// 投资回收累计完成情况-年度更新
yearlyUpdateTitle: `${currentYear.value}年投资回收累计完成情况-年度更新`,
// 投资回收完成情况-年度更新
yearlyCompletionTitle: `${currentYear.value}年投资回收完成情况(年度更新)`,
// 投资回收完成情况-月度更新
monthlyCompletionTitle: `${currentYear.value}年投资回收完成情况(月度更新)`,
}));
// 折叠面板默认展开项(动态计算)
const getActiveCollapseItems = () => [
"项目基本信息",
"出资情况",
"合同约定权益获取",
"分红情况",
"投资额完成情况(万元)",
"投资回收(决策)",
"投资回收(计划)",
dynamicTitles.value.yearlyUpdateTitle,
"净现金流",
"截止12月末累计应收",
"截止12月末累计实收",
"资金流出",
"分红情况",
dynamicTitles.value.yearlyCompletionTitle,
dynamicTitles.value.monthlyCompletionTitle,
];
const activeCollapse = ref(getActiveCollapseItems());
// 监听年度变化,更新 activeCollapse
watch(
() => formData.nd,
() => {
activeCollapse.value = getActiveCollapseItems();
},
{ immediate: false },
);
let options = ref();
// ========== 选择项目同步名称(通用) ==========
......@@ -747,10 +961,51 @@ const getRcCgqyglDetail = () => {
data: { id: rcCgqyglId.value },
callback: (data) => {
loading.value = false;
console.log(data.jzymljys.tableData[0], "1111");
console.log(data.jzymljys, "jzymljys详情数据");
console.log(data.jzymljsh, "jzymljsh详情数据");
console.log(data.lastyeartzhs, "lastyeartzhs");
// 先初始化表格结构(确保 indicatorList 和 dynamicTimeList 存在)
initTableData();
// 然后合并数据(保留结构配置,只更新 tableData)
if (data.jzymljys && data.jzymljys.tableData) {
formData.jzymljys.tableData = data.jzymljys.tableData;
}
if (data.jzymljsh && data.jzymljsh.tableData) {
formData.jzymljsh.tableData = data.jzymljsh.tableData;
}
if (data.tzhsjc && data.tzhsjc.tableData) {
formData.tzhsjc.tableData = data.tzhsjc.tableData;
}
if (data.tzhsjh && data.tzhsjh.tableData) {
formData.tzhsjh.tableData = data.tzhsjh.tableData;
}
if (data.tzhswcqkndgx && data.tzhswcqkndgx.tableData) {
formData.tzhswcqkndgx.tableData = data.tzhswcqkndgx.tableData;
}
if (data.tzhswcqkydgx && data.tzhswcqkydgx.tableData) {
formData.tzhswcqkydgx.tableData = data.tzhswcqkydgx.tableData;
}
if (data.lastyeartzhs) {
formData.lastyeartzhs = data.lastyeartzhs;
}
// 合并其他非表格字段
Object.assign(formData, {
...data,
nd: data.nd || new Date().getFullYear().toString(),
projectId: data.projectId,
sshy: data.sshy,
xmgsmc: data.xmgsmc,
sbdw: data.sbdw,
ssejqy: data.ssejqy,
xmjd: data.xmjd,
xmlx: data.xmlx,
tzms: data.tzms,
tzhsfs: data.tzhsfs,
xnxmjd: data.xnxmjd,
cwbblx: data.cwbblx,
projectGdxxs: data.projectGdxxs || [],
});
},
});
......
......@@ -64,15 +64,6 @@
</td>
<!-- 1级指标建议值 -->
<td class="cell-item">
<!-- <el-input
type="textarea"
rows="3"
v-model="item.level1Target.value"
:placeholder="item.level1Target.placeholder"
size="small"
class="table-input"
:disabled="isPreview"
/> -->
{{ item.level1Target.placeholder }}
</td>
<td class="cell-item">
......
......@@ -50,6 +50,7 @@
type="month"
placeholder="请选择"
value-format="YYYY-MM"
:disabled-date="fyfxkzDisabledDate"
/>
</el-form-item>
</el-col>
......@@ -767,7 +768,7 @@
</el-table-column>
<el-table-column
prop="sffsaqzlhbwt"
label="是否发生安全质量环保问题被行政处罚"
label="是否发生较大及以上安全质量环保问题被行政处罚"
>
<template #default="scope">
<el-radio-group v-model="scope.row.sffsaqzlhbwt">
......@@ -790,7 +791,10 @@
</el-table>
</el-col>
<el-col :span="24" class="fileCla">
<el-form-item label="文件上传">
<el-form-item
label-width="320"
label="较大及以上安全质量环保问题行政处罚文件上传"
>
<FileUploader
v-model="formData.qtjsmbzdpc"
></FileUploader>
......@@ -820,6 +824,78 @@
label="财务评价主要边界条件变化对比表"
:label-width="230"
></el-form-item>
<el-row :gutter="20">
<template v-if="cwpjIsEdit">
<el-col :span="6">
<el-form-item label="起始时间" required>
<el-date-picker
v-model="cwpjFormData.bqsj"
type="month"
placeholder="请选择"
value-format="YYYY-MM"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="截止时间" required>
<el-date-picker
v-model="cwpjFormData.sqsj"
type="month"
placeholder="请选择"
value-format="YYYY-MM"
:disabled-date="cwpjDisabledDate"
/>
</el-form-item>
</el-col>
</template>
<el-col :span="6" :offset="cwpjIsEdit ? 6 : 18">
<div class="tab-handle">
<template v-if="cwpjIsEdit">
<el-button
type="default"
size="small"
@click="cancelCwpj"
>取消</el-button
>
<el-button
type="primary"
size="small"
@click="saveCwpj"
>确定</el-button
>
</template>
<template v-else>
<el-select
v-model="cwpjSelectRange"
placeholder="请选择"
no-data-text="暂无数据"
:style="{ width: '200px', marginRight: '20px' }"
@change="changeCwpjRange"
>
<el-option
v-for="item in cwpjRangeList"
:key="item.key"
:label="item.key"
:value="item.key"
></el-option>
</el-select>
<el-button
v-if="cwpjSelectRange"
type="primary"
size="small"
@click="editCwpj"
>编辑</el-button
>
<el-button
type="primary"
size="small"
@click="addCwpj"
>新增</el-button
>
</template>
</div>
</el-col>
</el-row>
<el-table
:data="cwpjData"
style="width: 100%"
......@@ -831,12 +907,19 @@
<el-table-column prop="bjtj" label="边界条件" />
<el-table-column prop="jc" label="决策">
<template #default="scope">
<el-input v-model="scope.row.jc" />
<el-input
v-model="scope.row.jc"
:disabled="!cwpjIsEdit"
/>
</template>
</el-table-column>
<el-table-column prop="xz" label="现状">
<template #default="scope">
<el-input v-model="scope.row.xz" type="textarea" />
<el-input
v-model="scope.row.xz"
type="textarea"
:disabled="!cwpjIsEdit"
/>
</template>
</el-table-column>
<el-table-column prop="zycysm" label="主要差异说明">
......@@ -844,6 +927,7 @@
<el-input
v-model="scope.row.zycysm"
type="textarea"
:disabled="!cwpjIsEdit"
/>
</template>
</el-table-column>
......@@ -913,6 +997,7 @@ const activeCollapse = reactive([
"投资额控制",
"费用分项控制、阶段对比表",
"决策条件落实情况",
"项目信息",
"其他建设目标重大偏差",
"投资收益",
]);
......@@ -1001,7 +1086,17 @@ const getControlInfo = () => {
Object.assign(pfyjlsqkData.value, res.tzkzjcpfyjs);
Object.assign(gqData.value, res.tzkzgqs);
Object.assign(aqzlhbData.value, res.tzkzaqzlhbs);
Object.assign(cwpjData.value, res.tzkzcwpjs);
if (res.tzkzcwpjs && res.tzkzcwpjs.length) {
cwpjRangeList.value = res.tzkzcwpjs.map((item) => {
return {
...item,
bqsj: proxy.moment(item.bqsj).format("YYYY-MM"),
sqsj: proxy.moment(item.sqsj).format("YYYY-MM"),
};
});
cwpjSelectRange.value = res.tzkzcwpjs[0].key;
cwpjData.value = res.tzkzcwpjs[0].tables;
}
Object.assign(tzsyData.value, res.tzkztzsys);
},
});
......@@ -1021,16 +1116,28 @@ onMounted(() => {
if (controlId) {
getControlInfo();
fyfxkzIsEdit.value = false;
cwpjIsEdit.value = false;
} else {
fyfxkzData.value = JSON.parse(JSON.stringify(fyfxkzDefaultData));
fyfxkzIsEdit.value = true;
cwpjData.value = JSON.parse(JSON.stringify(cwpjDefaultData));
cwpjIsEdit.value = true;
}
});
// 费用分项控制:截止时间禁用日期方法
const fyfxkzDisabledDate = (time) => {
if (!fyfxkzFormData.bqsj) return false;
const startTime = new Date(fyfxkzFormData.bqsj);
return time.getTime() < startTime.getTime();
};
// 新增费用分项控制、阶段对比表
const addFyfxkz = () => {
selectRange.value = "";
fyfxkzIsEdit.value = true;
fyfxkzData.value = JSON.parse(JSON.stringify(fyfxkzDefaultData));
fyfxkzFormData.bqsj = "";
fyfxkzFormData.sqsj = "";
};
const editFyfxkz = () => {
fyfxkzIsEdit.value = true;
......@@ -1042,14 +1149,29 @@ const editFyfxkz = () => {
};
const saveFyfxkz = () => {
if (fyfxkzFormData.bqsj && fyfxkzFormData.sqsj) {
// 验证截止时间不能小于起始时间
if (fyfxkzFormData.sqsj < fyfxkzFormData.bqsj) {
return ElMessage.warning("截止时间不能小于起始时间");
}
const newKey = fyfxkzFormData.bqsj + "至" + fyfxkzFormData.sqsj;
// 验证时间范围是否重复(排除当前编辑的记录)
const isDuplicate = rangeList.value.some(
(item) => item.key === newKey && item.key !== selectRange.value
);
if (isDuplicate) {
return ElMessage.warning("该时间范围已存在,请选择其他时间");
}
if (selectRange.value) {
rangeList.value = rangeList.value.map((item) => {
if (item.key === selectRange.value) {
selectRange.value = fyfxkzFormData.bqsj + "至" + fyfxkzFormData.sqsj;
selectRange.value = newKey;
return {
bqsj: fyfxkzFormData.bqsj,
sqsj: fyfxkzFormData.sqsj,
key: fyfxkzFormData.bqsj + "至" + fyfxkzFormData.sqsj,
key: newKey,
tables: fyfxkzData.value,
};
} else {
......@@ -1062,7 +1184,7 @@ const saveFyfxkz = () => {
rangeList.value.push({
bqsj: fyfxkzFormData.bqsj,
sqsj: fyfxkzFormData.sqsj,
key: fyfxkzFormData.bqsj + "至" + fyfxkzFormData.sqsj,
key: newKey,
tables: fyfxkzData.value,
});
}
......@@ -1211,8 +1333,16 @@ const downloadFile = (data) => {
a.remove();
};
// 财务评价:截止时间禁用日期方法
const cwpjDisabledDate = (time) => {
if (!cwpjFormData.bqsj) return false;
const startTime = new Date(cwpjFormData.bqsj);
return time.getTime() < startTime.getTime();
};
// 财务评价主要边界条件变化对比表
const cwpjData = ref([
const cwpjFormData = reactive({});
const cwpjDefaultData = [
{
bjtj: "项目总投资(万元)",
},
......@@ -1261,7 +1391,96 @@ const cwpjData = ref([
{
bjtj: "折旧摊销方式",
},
]);
];
let cwpjData = ref([]);
let cwpjIsEdit = ref(false);
let cwpjRangeList = ref([]);
const cwpjSelectRange = ref("");
// 新增财务评价主要边界条件变化对比表
const addCwpj = () => {
cwpjSelectRange.value = "";
cwpjIsEdit.value = true;
cwpjData.value = JSON.parse(JSON.stringify(cwpjDefaultData));
};
const editCwpj = () => {
cwpjIsEdit.value = true;
let selectData = cwpjRangeList.value.filter(
(item) => item.key === cwpjSelectRange.value,
);
cwpjFormData.bqsj = selectData[0].bqsj;
cwpjFormData.sqsj = selectData[0].sqsj;
};
const saveCwpj = () => {
if (cwpjFormData.bqsj && cwpjFormData.sqsj) {
// 验证截止时间不能小于起始时间
if (cwpjFormData.sqsj < cwpjFormData.bqsj) {
return ElMessage.warning("截止时间不能小于起始时间");
}
const newKey = cwpjFormData.bqsj + "至" + cwpjFormData.sqsj;
// 验证时间范围是否重复(排除当前编辑的记录)
const isDuplicate = cwpjRangeList.value.some(
(item) => item.key === newKey && item.key !== cwpjSelectRange.value
);
if (isDuplicate) {
return ElMessage.warning("该时间范围已存在,请选择其他时间");
}
if (cwpjSelectRange.value) {
cwpjRangeList.value = cwpjRangeList.value.map((item) => {
if (item.key === cwpjSelectRange.value) {
cwpjSelectRange.value = newKey;
return {
bqsj: cwpjFormData.bqsj,
sqsj: cwpjFormData.sqsj,
key: newKey,
tables: cwpjData.value,
};
} else {
return {
...item,
};
}
});
} else {
cwpjRangeList.value.push({
bqsj: cwpjFormData.bqsj,
sqsj: cwpjFormData.sqsj,
key: newKey,
tables: cwpjData.value,
});
}
cwpjIsEdit.value = false;
if (!cwpjSelectRange.value) {
cwpjSelectRange.value = cwpjRangeList.value[0].key;
cwpjData.value = cwpjRangeList.value[0].tables;
} else {
let selectData = cwpjRangeList.value.filter(
(item) => item.key === cwpjSelectRange.value,
);
cwpjData.value = selectData.length ? selectData[0].tables : [];
}
} else {
ElMessage.warning("请补充本期及上期时间");
}
};
const cancelCwpj = () => {
if (cwpjSelectRange.value) {
cwpjData.value = cwpjRangeList.value[0].tables;
} else if (formData.id) {
cwpjData.value = [];
} else {
cwpjData.value = JSON.parse(JSON.stringify(cwpjDefaultData));
}
cwpjIsEdit.value = false;
};
const changeCwpjRange = (val) => {
cwpjData.value = cwpjRangeList.value.filter(
(item) => item.key === val,
)[0].tables;
};
// 投资收益指标变化对比表
const tzsyData = ref([
{
......@@ -1309,6 +1528,11 @@ const saveClick = () => {
if (fyfxkzIsEdit.value) {
return ElMessage.warning("请先保存正在添加的费用分项控制、阶段对比表");
}
if (cwpjIsEdit.value) {
return ElMessage.warning(
"请先保存正在添加的财务评价主要边界条件变化对比表",
);
}
let url = formData.id ? "updateTzkz" : "createTzkz";
let project = projectList.value.filter(
(item) => item.id === formData.projectId,
......@@ -1322,7 +1546,7 @@ const saveClick = () => {
tzkzjcpfyjs: pfyjlsqkData.value,
tzkzgqs: gqData.value,
tzkzaqzlhbs: aqzlhbData.value,
tzkzcwpjs: cwpjData.value,
tzkzcwpjs: cwpjRangeList.value,
tzkztzsys: tzsyData.value,
},
callback: (data) => {
......
......@@ -4,9 +4,7 @@
<search-form @search="handleSearch" />
<div class="manage-header">
<div class="header-left"></div>
<div class="header-right">
<el-button type="primary" @click="annualAdd">新增</el-button>
</div>
<div class="header-right"></div>
</div>
<div class="manage-content" v-loading="loading">
<common-table
......@@ -29,22 +27,11 @@
link
type="primary"
size="small"
@click="previewStatement(row)"
>查看</el-button
>
<el-button
link
type="primary"
size="small"
@click="editStatement(row)"
>编辑</el-button
>
<el-button
link
type="danger"
size="small"
@click="deleteStatement(row)"
>删除</el-button
:loading="row.loading"
@click="previewProject(row)"
>{{
canAudit && row.projectLzType == 8 ? "审批" : "查看"
}}</el-button
>
</template>
</common-table>
......@@ -54,9 +41,10 @@
</template>
<script setup>
import { ref, onMounted, getCurrentInstance } from "vue";
import { reactive, ref, onMounted, computed, getCurrentInstance } from "vue";
import { useRouter } from "vue-router";
import { ElMessage, ElMessageBox } from "element-plus";
import { useUserStore } from "@/stores/user.js";
import CommonTable from "@/components/common/commonTable.vue";
import SearchForm from "@/components/common/SearchForm.vue";
......@@ -67,6 +55,19 @@ const handleSearch = (formData) => {
};
const router = useRouter();
const { proxy } = getCurrentInstance();
// 是否是审核角色
const userStore = useUserStore();
const userInfo =
userStore.userInfo ||
(sessionStorage.getItem("userInfo") &&
JSON.parse(sessionStorage.getItem("userInfo"))) ||
{};
let canAudit = ref(false);
userInfo.roles.map((item) => {
if (["xmlx_sp", "xmjc_sp"].includes(item.key)) {
canAudit.value = true;
}
});
let tableData = ref([]);
let tableColumns = ref([
......@@ -76,45 +77,41 @@ let tableColumns = ref([
showOverflowTooltip: true,
},
{
prop: "projectForeignName",
label: "项目外文名称",
prop: "projectCode",
label: "项目编号",
showOverflowTooltip: true,
},
{
prop: "sbdw",
label: "申报单位",
showOverflowTooltip: true,
},
{
prop: "ssejqy",
label: "所属二级企业",
showOverflowTooltip: true,
},
{
prop: "xmgsmc",
label: "项目公司名称",
showOverflowTooltip: true,
},
{
prop: "xmkgsjyj",
label: "项目预计起始时间",
showOverflowTooltip: true,
prop: "projectLzType",
label: "状态",
width: 120,
align: "center",
formatter: (data) => {
return data.projectLzType === "1"
? "待立项"
: data.projectLzType === "3"
? "立项审批中"
: data.projectLzType === "5"
? "已立项"
: data.projectLzType === "7"
? "决策填报中"
: data.projectLzType === "8"
? "决策审批中"
: data.projectLzType === "9"
? "已决策"
: "待立项";
},
{
prop: "xmjgsjyj",
label: "目预计完成时间",
showOverflowTooltip: true,
},
{
prop: "operations",
label: "操作",
width: 170,
width: 160,
slot: "operations",
fixed: "right",
align: "center",
},
]);
let loading = ref(false);
let total = ref(0);
let currentPage = ref(1);
......@@ -123,10 +120,12 @@ let pageSize = ref(10);
const getProjectData = (params = {}) => {
loading.value = true;
proxy.$post({
url: "/api/project/getTzjhList",
url: "/api/project/listProject",
data: {
page: currentPage.value,
pagesize: pageSize.value,
attributes: [],
menuType: "xmjc",
...params,
},
callback: (data) => {
......@@ -136,6 +135,9 @@ const getProjectData = (params = {}) => {
},
});
};
onMounted(() => {
getProjectData();
});
// 分页
const handleSizeChange = (size) => {
pageSize.value = size;
......@@ -146,49 +148,15 @@ const handleCurrentPageChange = (page) => {
currentPage.value = page;
getProjectData();
};
const annualAdd = () => {
router.push("/decisionAdd");
};
const editStatement = (item) => {
router.push({
name: "decisionAdd",
query: {
id: item.id,
},
});
};
const previewStatement = (item) => {
const previewProject = (item) => {
router.push({
name: "decisionAdd",
name: "addProject",
query: {
isPreview: true,
id: item.id,
projectId: item.id,
},
});
};
const deleteStatement = (item) => {
ElMessageBox.confirm("确认删除该项?", "提示", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
proxy.$post({
url: "/api/project/deleteTzjh",
data: {
id: item.id,
},
callback: (data) => {
ElMessage.success("删除成功");
getProjectData();
},
});
})
.catch(() => {});
};
onMounted(() => {
getProjectData();
});
</script>
<style scoped lang="less"></style>
This source diff could not be displayed because it is too large. You can view the blob instead.
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