明树Git Lab

Commit 02499d44 authored by zhanghan's avatar zhanghan

111

parent c3c6bcd6
Pipeline #111969 passed with stage
in 18 seconds
......@@ -2,7 +2,11 @@
"permissions": {
"allow": [
"mcp__zai-mcp-server__analyze_image",
"mcp__zai-mcp-server__extract_text_from_screenshot"
"mcp__zai-mcp-server__extract_text_from_screenshot",
"Read(//Users/**)",
"Bash(python3 -c ' *)",
"Bash(where python *)",
"Bash(where node *)"
]
}
}
......@@ -32,29 +32,81 @@
<!-- 年度更新表格的列 -->
<template v-if="tableType === 'year'">
<!-- 截至年底应收 -->
<el-table-column label="截至年底应收" align="center">
<el-table-column
v-for="col in yearColumns"
:key="col.prop"
:label="col.label"
min-width="180"
label="决策应收"
prop="decisionReceivable"
min-width="150"
align="center"
>
<template #default="{ row }">
<!-- 文本输入 -->
<span
v-if="row.isTotalRow || row.isParentRow"
class="calculated-value"
>
{{ formatCalculatedValue(row.decisionReceivable) }}
</span>
<el-input
v-if="col.isText"
v-model="row[col.prop]"
type="textarea"
:rows="2"
v-else
v-model="row.decisionReceivable"
type="number"
:disabled="isPreview || row.noEdit"
placeholder="请输入"
style="width: 100%"
@input="handleTextInput"
/>
<!-- 数字输入 -->
@input="handleNumberInput(row)"
>
<template #suffix>
<span>{{ moneyUnit }}</span>
</template>
</el-input>
</template>
</el-table-column>
<el-table-column
label="计划应收"
prop="actualReceived"
min-width="150"
align="center"
>
<template #default="{ row }">
<span
v-if="row.isTotalRow || row.isParentRow"
class="calculated-value"
>
{{ formatCalculatedValue(row.actualReceived) }}
</span>
<el-input
v-else
v-model="row.actualReceived"
type="number"
:disabled="isPreview || row.noEdit"
placeholder="请输入"
@input="handleNumberInput(row)"
>
<template #suffix>
<span>{{ moneyUnit }}</span>
</template>
</el-input>
</template>
</el-table-column>
</el-table-column>
<!-- 截至年底实收 -->
<el-table-column
label="截至年底实收"
prop="jzss"
min-width="150"
align="center"
>
<template #default="{ row }">
<span
v-if="row.isTotalRow || row.isParentRow"
class="calculated-value"
>
{{ formatCalculatedValue(row.jzss) }}
</span>
<el-input
v-else-if="!col.isCalculated"
v-model="row[col.prop]"
v-else
v-model="row.jzss"
type="number"
:disabled="isPreview || row.noEdit"
placeholder="请输入"
......@@ -64,12 +116,104 @@
<span>{{ moneyUnit }}</span>
</template>
</el-input>
<!-- 计算字段 -->
<span v-else class="calculated-value">
{{ formatCalculatedValue(row[col.prop], rateUnit) }}
</template>
</el-table-column>
<!-- 截至年底欠收 -->
<el-table-column label="截至年底欠收" align="center">
<el-table-column
label="决策应收-实收"
prop="shortfallDecision"
min-width="150"
align="center"
>
<template #default="{ row }">
<span class="calculated-value">
{{ formatCalculatedValue(row.shortfallDecision) }}
</span>
</template>
</el-table-column>
<el-table-column
label="计划应收-实收"
prop="shortfallPlan"
min-width="150"
align="center"
>
<template #default="{ row }">
<span class="calculated-value">
{{ formatCalculatedValue(row.shortfallPlan) }}
</span>
</template>
</el-table-column>
</el-table-column>
<!-- 完成率 -->
<el-table-column label="完成率" align="center">
<el-table-column
label="实收/决策应收"
prop="completionRateDecision"
min-width="150"
align="center"
>
<template #default="{ row }">
<span class="calculated-value">
{{
formatCalculatedValue(row.completionRateDecision, rateUnit)
}}
</span>
</template>
</el-table-column>
<el-table-column
label="实收/计划应收"
prop="completionRatePlan"
min-width="150"
align="center"
>
<template #default="{ row }">
<span class="calculated-value">
{{ formatCalculatedValue(row.completionRatePlan, rateUnit) }}
</span>
</template>
</el-table-column>
</el-table-column>
<!-- 存在主要问题 -->
<el-table-column
label="存在主要问题"
prop="problems"
min-width="200"
align="center"
>
<template #default="{ row }">
<el-input
v-model="row.problems"
type="textarea"
:rows="2"
:disabled="isPreview || row.noEdit"
placeholder="请输入"
@input="handleTextInput"
/>
</template>
</el-table-column>
<!-- 采取措施情况 -->
<el-table-column
label="采取措施情况"
prop="measures"
min-width="200"
align="center"
>
<template #default="{ row }">
<el-input
v-model="row.measures"
type="textarea"
:rows="2"
:disabled="isPreview || row.noEdit"
placeholder="请输入"
@input="handleTextInput"
/>
</template>
</el-table-column>
</template>
<!-- 月度更新表格的列 -->
......@@ -81,16 +225,23 @@
:label="`${month}月`"
align="center"
>
<!-- 收 -->
<!-- 收 -->
<el-table-column
:label="`收`"
:prop="`m${month}Receivable`"
:label="`收`"
:prop="`m${month}Received`"
min-width="150"
align="center"
>
<template #default="{ row }">
<span
v-if="row.isTotalRow || row.isParentRow"
class="calculated-value"
>
{{ formatCalculatedValue(row[`m${month}Received`]) }}
</span>
<el-input
v-model="row[`m${month}Receivable`]"
v-else
v-model="row[`m${month}Received`]"
type="number"
:disabled="isPreview || row.noEdit"
placeholder="请输入"
......@@ -102,16 +253,23 @@
</el-input>
</template>
</el-table-column>
<!-- 收 -->
<!-- -->
<el-table-column
:label="`收`"
:prop="`m${month}Received`"
:label="`收`"
:prop="`m${month}Receivable`"
min-width="150"
align="center"
>
<template #default="{ row }">
<span
v-if="row.isTotalRow || row.isParentRow"
class="calculated-value"
>
{{ formatCalculatedValue(row[`m${month}Receivable`]) }}
</span>
<el-input
v-model="row[`m${month}Received`]"
v-else
v-model="row[`m${month}Receivable`]"
type="number"
:disabled="isPreview || row.noEdit"
placeholder="请输入"
......@@ -126,36 +284,38 @@
</el-table-column>
<!-- 本年累计 -->
<el-table-column label="本年累计" align="center">
<el-table-column
label="本年累计(实收)"
label="实收"
prop="yearTotal"
min-width="180"
min-width="150"
align="center"
>
<template #default="{ row }">
<span v-if="row.isTotalRow" class="calculated-value">
<span 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 #suffix>
<span>{{ moneyUnit }}</span>
</template>
</el-input>
</el-table-column>
<el-table-column
label="应收"
prop="yearTotalReceivable"
min-width="150"
align="center"
>
<template #default="{ row }">
<span class="calculated-value">
{{ formatCalculatedValue(row.yearTotalReceivable) }}
</span>
</template>
</el-table-column>
</el-table-column>
<!-- 本年完成率(分组) -->
<!-- 本年完成率 -->
<el-table-column label="本年完成率" align="center">
<el-table-column
label="本年累计/本年决策"
prop="yearCompletionRateDecision"
label="累计实收/累计应收"
prop="yearCompletionRateReceivable"
min-width="180"
align="center"
>
......@@ -163,7 +323,7 @@
<span class="calculated-value">
{{
formatCalculatedValue(
row.yearCompletionRateDecision,
row.yearCompletionRateReceivable,
rateUnit,
)
}}
......@@ -171,7 +331,7 @@
</template>
</el-table-column>
<el-table-column
label="本年累计/本年计划"
label="累计实收/本年计划"
prop="yearCompletionRatePlan"
min-width="180"
align="center"
......@@ -194,69 +354,37 @@
align="center"
>
<template #default="{ row }">
<span v-if="row.isTotalRow" class="calculated-value">
{{ formatCalculatedValue(row.invoiceCompleted) }}
</span>
<span v-else class="calculated-value">
<span class="calculated-value">
{{ formatCalculatedValue(row.invoiceCompleted) }}
</span>
</template>
</el-table-column>
<!-- 开累完成率(分组) -->
<el-table-column label="开累完成率" min-width="180" align="center">
<!-- 开累完成率 -->
<el-table-column
label="开累完成/开累决策"
prop="invoiceCompletionRateDecision"
label="开累完成率"
prop="invoiceCompletionRate"
min-width="180"
align="center"
>
<template #default="{ row }">
<span class="calculated-value">
{{
formatCalculatedValue(
row.invoiceCompletionRateDecision,
rateUnit,
)
}}
{{ formatCalculatedValue(row.invoiceCompletionRate, rateUnit) }}
</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, rateUnit)
}}
</span>
</template>
</el-table-column>
</el-table-column>
<!-- 欠收金额 -->
<el-table-column
label="欠收金额"
prop="shortfallAmount"
min-width="180"
min-width="150"
align="center"
>
<template #default="{ row }">
<el-input
v-model="row.shortfallAmount"
type="number"
:disabled="isPreview || row.noEdit"
placeholder="请输入"
@input="handleShortfallAmountInput(row)"
>
<template #suffix>
<span>{{ moneyUnit }}</span>
</template>
</el-input>
<span class="calculated-value">
{{ formatCalculatedValue(row.shortfallAmount) }}
</span>
</template>
</el-table-column>
......@@ -268,7 +396,14 @@
align="center"
>
<template #default="{ row }">
<span
v-if="row.isTotalRow || row.isParentRow"
class="calculated-value"
>
{{ formatCalculatedValue(row.contractTotalAmount) }}
</span>
<el-input
v-else
v-model="row.contractTotalAmount"
type="number"
:disabled="isPreview || row.noEdit"
......@@ -319,7 +454,14 @@
align="center"
>
<template #default="{ row }">
<span
v-if="row.isTotalRow || row.isParentRow"
class="calculated-value"
>
{{ formatCalculatedValue(row[`m${month}Decision`]) }}
</span>
<el-input
v-else
v-model="row[`m${month}Decision`]"
type="number"
:disabled="isPreview || row.noEdit"
......@@ -340,7 +482,14 @@
align="center"
>
<template #default="{ row }">
<span
v-if="row.isTotalRow || row.isParentRow"
class="calculated-value"
>
{{ formatCalculatedValue(row[`m${month}Plan`]) }}
</span>
<el-input
v-else
v-model="row[`m${month}Plan`]"
type="number"
:disabled="isPreview || row.noEdit"
......@@ -422,49 +571,41 @@ const props = defineProps({
},
tableType: {
type: String,
default: "year", // 'year' 或 'month' 或 'plan'
default: "year",
validator: (val) => ["year", "month", "plan"].includes(val),
},
tableTitle: {
type: String,
default: "",
},
// 投资回收计划数据(用于自动填充本年计划、开累计划)
planData: {
type: Object,
default: () => ({}),
},
// 投资回收决策数据(用于自动填充本年决策、开累决策)
decisionData: {
type: Object,
default: () => ({}),
},
// 去年投资回收计划数据(用于获取前年数据)
lastYearPlanData: {
type: Object,
default: () => ({}),
},
// 当前年份
currentYear: {
type: [String, Number],
default: "",
},
// 投资回收完成情况年度计划数据(用于获取年度计划的合计决策值)
yearlyPlanData: {
type: Object,
default: () => ({}),
},
// 投资回收完成情况年度更新数据(用于获取截至年底实收值)
yearlyCompletionData: {
type: Object,
default: () => ({}),
},
// 金额单位(默认万元)
moneyUnit: {
type: String,
default: "万元",
},
// 利率单位(默认%)
rateUnit: {
type: String,
default: "%",
......@@ -475,72 +616,19 @@ 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: "jzss",
label: "截至(具体年份)年底实收",
width: 180,
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 "-";
return `${Number(value).toFixed(2)}${suffix}`;
};
// 初始化行数据
const initRowData = (indicator, index, sourceRow = {}) => {
const baseRow = {
serialNumber: indicator.xh || index + 1,
indicatorName: indicator.name,
noEdit: indicator.noEdit || false,
isTotalRow: indicator.isTotalRow || false,
isParentRow: indicator.isParentRow || false,
isChild: indicator.isChild || false,
};
if (props.tableType === "year") {
......@@ -548,14 +636,18 @@ const initRowData = (indicator, index, sourceRow = {}) => {
...baseRow,
decisionReceivable: sourceRow.decisionReceivable || null,
actualReceived: sourceRow.actualReceived || null,
jzss: sourceRow.jzss || null,
shortfallDecision: sourceRow.shortfallDecision || null,
shortfallPlan: sourceRow.shortfallPlan || null,
completionRateDecision: sourceRow.completionRateDecision || null,
completionRatePlan: sourceRow.completionRatePlan || null,
// backward compat
shortfall: sourceRow.shortfall || null,
completionRate: sourceRow.completionRate || null,
problems: sourceRow.problems || "",
measures: sourceRow.measures || "",
jzss: sourceRow.jzss || null,
};
} else if (props.tableType === "plan") {
// 计划表格
const monthData = {};
for (let i = 1; i <= 12; i++) {
monthData[`m${i}Decision`] = sourceRow[`m${i}Decision`] || null;
......@@ -569,7 +661,6 @@ const initRowData = (indicator, index, sourceRow = {}) => {
differenceExplanation: sourceRow.differenceExplanation || "",
};
} else {
// 月度表格
const monthData = {};
for (let i = 1; i <= 12; i++) {
monthData[`m${i}Receivable`] = sourceRow[`m${i}Receivable`] || null;
......@@ -579,176 +670,165 @@ const initRowData = (indicator, index, sourceRow = {}) => {
...baseRow,
...monthData,
yearTotal: sourceRow.yearTotal || null,
yearCompletionRateDecision: sourceRow.yearCompletionRateDecision || null,
yearTotalReceivable: sourceRow.yearTotalReceivable || null,
yearCompletionRateReceivable:
sourceRow.yearCompletionRateReceivable || null,
yearCompletionRatePlan: sourceRow.yearCompletionRatePlan || null,
invoiceCompleted: sourceRow.invoiceCompleted || null,
invoiceCompletionRateDecision:
sourceRow.invoiceCompletionRateDecision || null,
invoiceCompletionRatePlan: sourceRow.invoiceCompletionRatePlan || null,
invoiceCompletionRate: sourceRow.invoiceCompletionRate || null,
shortfallAmount: sourceRow.shortfallAmount || null,
contractTotalAmount: sourceRow.contractTotalAmount || null,
planDifference: sourceRow.planDifference || "",
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, xh: "1" },
{ name: "建设期", noEdit: false, xh: "1.1" },
{ name: "运营期", noEdit: false, xh: "1.2" },
{
name: "政府付费",
noEdit: false,
xh: "1",
isParentRow: true,
childNames: ["建设期", "运营期"],
},
{ name: "建设期", noEdit: false, xh: "1.1", isChild: true },
{ name: "运营期", noEdit: false, xh: "1.2", isChild: true },
{ name: "使用者付费", noEdit: false, xh: "2" },
{ name: "投资价", noEdit: false, xh: "3" },
{ name: "投资价", noEdit: false, xh: "3" },
{ name: "参股项目投资回收", noEdit: false, xh: "4" },
{ name: "代建工程款", noEdit: false, xh: "5" },
{ name: "其他", noEdit: false, xh: "6" },
{ name: "合计", isTotalRow: true, noEdit: true, xh: "7" },
];
// 从计划数据和决策数据中获取对应指标的值
const getIndicatorValue = (indicatorName, dataSource, type) => {
if (!dataSource || !dataSource.tableData) return 0;
const row = dataSource.tableData.find(
(r) => r.indicatorName === indicatorName,
// 计算父行(政府付费 = 建设期 + 运营期)
const calculateParentRows = () => {
const parentRows = tableDataRef.value.filter((row) => row.isParentRow);
parentRows.forEach((parentRow) => {
const indicator = indicators.find(
(i) => i.name === parentRow.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;
};
if (!indicator || !indicator.childNames) return;
// 自动填充决策和计划数据
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",
const childRows = tableDataRef.value.filter((r) =>
indicator.childNames.includes(r.indicatorName),
);
}
// 自动填充本年计划(从去年投资回收计划数据获取)
if (
props.lastYearPlanData &&
Object.keys(props.lastYearPlanData).length > 0
) {
row.yearPlan = getIndicatorValue(
row.indicatorName,
props.lastYearPlanData,
"yearPlan",
if (props.tableType === "year") {
parentRow.decisionReceivable = childRows.reduce(
(sum, r) => sum + (parseFloat(r.decisionReceivable) || 0),
0,
);
parentRow.actualReceived = childRows.reduce(
(sum, r) => sum + (parseFloat(r.actualReceived) || 0),
0,
);
parentRow.jzss = childRows.reduce(
(sum, r) => sum + (parseFloat(r.jzss) || 0),
0,
);
const dr = parentRow.decisionReceivable;
const pr = parentRow.actualReceived;
const jz = parentRow.jzss;
parentRow.shortfallDecision = dr - jz;
parentRow.shortfallPlan = pr - jz;
parentRow.completionRateDecision = dr > 0 ? (jz / dr) * 100 : 0;
parentRow.completionRatePlan = pr > 0 ? (jz / pr) * 100 : 0;
} else if (props.tableType === "plan") {
for (let i = 1; i <= 12; i++) {
parentRow[`m${i}Decision`] = childRows.reduce(
(sum, r) => sum + (parseFloat(r[`m${i}Decision`]) || 0),
0,
);
} else if (props.planData && Object.keys(props.planData).length > 0) {
// 如果没有去年数据,则使用当前年计划数据作为后备
row.yearPlan = getIndicatorValue(
row.indicatorName,
props.planData,
"yearPlan",
parentRow[`m${i}Plan`] = childRows.reduce(
(sum, r) => sum + (parseFloat(r[`m${i}Plan`]) || 0),
0,
);
}
// 自动填充开累决策(从决策数据获取)
if (props.decisionData && Object.keys(props.decisionData).length > 0) {
row.invoiceDecision = getIndicatorValue(
row.indicatorName,
props.decisionData,
"invoiceDecision",
let totalDecision = 0,
totalPlan = 0;
for (let i = 1; i <= 12; i++) {
totalDecision += parseFloat(parentRow[`m${i}Decision`]) || 0;
totalPlan += parseFloat(parentRow[`m${i}Plan`]) || 0;
}
parentRow.totalDecision = totalDecision;
parentRow.totalPlan = totalPlan;
} else if (props.tableType === "month") {
for (let i = 1; i <= 12; i++) {
parentRow[`m${i}Received`] = childRows.reduce(
(sum, r) => sum + (parseFloat(r[`m${i}Received`]) || 0),
0,
);
parentRow[`m${i}Receivable`] = childRows.reduce(
(sum, r) => sum + (parseFloat(r[`m${i}Receivable`]) || 0),
0,
);
}
// 自动填充开累计划(从计划数据获取)
if (props.planData && Object.keys(props.planData).length > 0) {
row.invoicePlan = getIndicatorValue(
row.indicatorName,
props.planData,
"invoicePlan",
parentRow.contractTotalAmount = childRows.reduce(
(sum, r) => sum + (parseFloat(r.contractTotalAmount) || 0),
0,
);
calculateMonthRowInternal(parentRow);
}
});
// 重新计算完成率(不触发 emitChange,避免循环)
tableDataRef.value.forEach((row) => {
calculateMonthRow(row);
});
// 计算合计行(不触发 emitChange,避免循环)
calculateTotalRow();
};
// 年度表格计算
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;
}
if (row.noEdit || row.isTotalRow || row.isParentRow) return;
const decisionReceivable = parseFloat(row.decisionReceivable) || 0;
const planReceivable = parseFloat(row.actualReceived) || 0;
const jzss = parseFloat(row.jzss) || 0;
// 欠收(决策应收-实收)
row.shortfallDecision = decisionReceivable - jzss;
// 欠收(计划应收-实收)
row.shortfallPlan = planReceivable - jzss;
// 完成率(实收/决策应收)
row.completionRateDecision =
decisionReceivable > 0 ? (jzss / decisionReceivable) * 100 : 0;
// 完成率(实收/计划应收)
row.completionRatePlan =
planReceivable > 0 ? (jzss / planReceivable) * 100 : 0;
};
// 月度表格计算
const calculateMonthRow = (row) => {
if (row.noEdit || row.isTotalRow) return;
// 本年累计 = 1-12月实收之和
if (row.autoCalcYearTotal) {
let monthSum = 0;
// 月度表格计算(内部,不检查 isParentRow)
const calculateMonthRowInternal = (row) => {
// 本年累计实收
let monthReceivedSum = 0;
for (let i = 1; i <= 12; i++) {
monthSum += parseFloat(row[`m${i}Received`]) || 0;
monthReceivedSum += parseFloat(row[`m${i}Received`]) || 0;
}
row.yearTotal = monthSum;
row.yearTotal = monthReceivedSum;
// 本年累计应收
let monthReceivableSum = 0;
for (let i = 1; i <= 12; i++) {
monthReceivableSum += parseFloat(row[`m${i}Receivable`]) || 0;
}
row.yearTotalReceivable = monthReceivableSum;
const yearTotal = row.yearTotal || 0;
const yearTotalReceivable = row.yearTotalReceivable || 0;
// 本年完成率: 累计实收/累计应收
row.yearCompletionRateReceivable =
yearTotalReceivable > 0 ? (yearTotal / yearTotalReceivable) * 100 : 0;
// 从年度计划数据中获取合计计划值
const getYearlyPlanTotalPlan = () => {
// 本年完成率: 累计实收/本年计划(从计划表获取该行的计划合计)
const getPlanTotalPlan = () => {
if (!props.yearlyPlanData || !props.yearlyPlanData.tableData) return 0;
const totalRow = props.yearlyPlanData.tableData.find((r) => r.isTotalRow);
return totalRow ? parseFloat(totalRow.totalPlan) || 0 : 0;
const planRow = props.yearlyPlanData.tableData.find(
(r) => r.indicatorName === row.indicatorName,
);
return planRow ? parseFloat(planRow.totalPlan) || 0 : 0;
};
const planTotalPlan = getPlanTotalPlan();
row.yearCompletionRatePlan =
planTotalPlan > 0 ? (yearTotal / planTotalPlan) * 100 : 0;
// 从年度更新数据中获取截至年底实收值
// 截至本年完成(开累)= 本年累计实收 + 年度更新的实收
const getYearlyJzss = () => {
if (!props.yearlyCompletionData || !props.yearlyCompletionData.tableData)
return 0;
......@@ -757,114 +837,85 @@ const calculateMonthRow = (row) => {
);
return targetRow ? parseFloat(targetRow.jzss) || 0 : 0;
};
// 本年完成率/本年决策 = 本年累计 / 年度计划的合计计划值 * 100
const yearlyPlanTotalPlan = getYearlyPlanTotalPlan();
const yearTotal = parseFloat(row.yearTotal) || 0;
if (yearlyPlanTotalPlan > 0) {
row.yearCompletionRateDecision = (yearTotal / yearlyPlanTotalPlan) * 100;
} else {
row.yearCompletionRateDecision = 0;
}
// 自动计算截至本年完成(开累)= 本年累计 + 年度更新的截至年底实收
const yearlyJzss = getYearlyJzss();
row.invoiceCompleted = yearTotal + yearlyJzss;
// 从年度更新数据中获取决策应收和计划应收值
const getYearlyDecisionReceivable = () => {
if (!props.yearlyCompletionData || !props.yearlyCompletionData.tableData)
return 0;
const targetRow = props.yearlyCompletionData.tableData.find(
(r) => r.indicatorName === row.indicatorName,
);
return targetRow ? parseFloat(targetRow.decisionReceivable) || 0 : 0;
};
const getYearlyActualReceived = () => {
if (!props.yearlyCompletionData || !props.yearlyCompletionData.tableData)
return 0;
const targetRow = props.yearlyCompletionData.tableData.find(
(r) => r.indicatorName === row.indicatorName,
);
return targetRow ? parseFloat(targetRow.actualReceived) || 0 : 0;
};
// 本年完成率/本年计划 = 本年累计 / 年度更新的计划应收值 * 100
const yearlyActualReceived = getYearlyActualReceived();
if (yearlyActualReceived > 0) {
row.yearCompletionRatePlan = (yearTotal / yearlyActualReceived) * 100;
} else {
row.yearCompletionRatePlan = 0;
}
// 开累完成率/开累决策 = 截至本年完成(开累) / (year的决策应收 + plan的合计决策值) * 100
const yearlyDecisionReceivable = getYearlyDecisionReceivable();
const invoiceCompleted = parseFloat(row.invoiceCompleted) || 0;
const totalInvoiceDecision = yearlyDecisionReceivable + yearlyPlanTotalPlan;
if (totalInvoiceDecision > 0) {
row.invoiceCompletionRateDecision =
(invoiceCompleted / totalInvoiceDecision) * 100;
} else {
row.invoiceCompletionRateDecision = 0;
}
// 开累完成率 = 截至本年完成 / 合同总金额
const contractTotal = parseFloat(row.contractTotalAmount) || 0;
row.invoiceCompletionRate =
contractTotal > 0 ? (row.invoiceCompleted / contractTotal) * 100 : 0;
// 开累完成率/开累计划 = 截至本年完成(开累) / (year的计划应收 + plan的合计计划值) * 100
const totalInvoicePlan = yearlyActualReceived + yearlyPlanTotalPlan;
if (totalInvoicePlan > 0) {
row.invoiceCompletionRatePlan = (invoiceCompleted / totalInvoicePlan) * 100;
} else {
row.invoiceCompletionRatePlan = 0;
}
// 欠收金额 = 累计应收 - 累计实收
row.shortfallAmount = yearTotalReceivable - yearTotal;
};
// 欠收金额不再自动计算,由用户手动输入
// row.shortfallAmount = yearDecision - yearTotal;
// 月度表格计算(对外,检查 isParentRow)
const calculateMonthRow = (row) => {
if (row.noEdit || row.isTotalRow || row.isParentRow) return;
calculateMonthRowInternal(row);
};
// 计算合计行
// 计算合计行(仅汇总非子行)
const calculateTotalRow = () => {
const totalRow = tableDataRef.value.find((row) => row.isTotalRow);
if (!totalRow) return;
// 获取参与合计的行(排除子行、合计行、noEdit 行)
const sumRows = tableDataRef.value.filter(
(row) => !row.isTotalRow && !row.noEdit && !row.isChild,
);
if (props.tableType === "year") {
// 年度表格合计
let totalDecision = 0;
let totalActual = 0;
let totalDecisionReceivable = 0;
let totalPlanReceivable = 0;
let totalJzss = 0;
tableDataRef.value.forEach((row) => {
if (!row.isTotalRow && !row.noEdit) {
totalDecision += parseFloat(row.decisionReceivable) || 0;
totalActual += parseFloat(row.actualReceived) || 0;
sumRows.forEach((row) => {
totalDecisionReceivable += parseFloat(row.decisionReceivable) || 0;
totalPlanReceivable += parseFloat(row.actualReceived) || 0;
totalJzss += parseFloat(row.jzss) || 0;
}
});
totalRow.decisionReceivable = totalDecision;
totalRow.actualReceived = totalActual;
totalRow.decisionReceivable = totalDecisionReceivable;
totalRow.actualReceived = totalPlanReceivable;
totalRow.jzss = totalJzss;
totalRow.shortfall = totalDecision - totalActual;
if (totalDecision > 0) {
totalRow.completionRate = (totalActual / totalDecision) * 100;
} else {
totalRow.completionRate = 0;
}
totalRow.shortfallDecision = totalDecisionReceivable - totalJzss;
totalRow.shortfallPlan = totalPlanReceivable - totalJzss;
totalRow.completionRateDecision =
totalDecisionReceivable > 0
? (totalJzss / totalDecisionReceivable) * 100
: 0;
totalRow.completionRatePlan =
totalPlanReceivable > 0
? (totalJzss / totalPlanReceivable) * 100
: 0;
} else if (props.tableType === "plan") {
// 计划表格合计
for (let i = 1; i <= 12; i++) {
// 每行先算自己的合计
tableDataRef.value.forEach((row) => {
if (!row.isTotalRow && !row.noEdit && !row.isParentRow) {
let decisionSum = 0;
let planSum = 0;
tableDataRef.value.forEach((row) => {
if (!row.isTotalRow && !row.noEdit) {
for (let i = 1; i <= 12; i++) {
decisionSum += parseFloat(row[`m${i}Decision`]) || 0;
planSum += parseFloat(row[`m${i}Plan`]) || 0;
}
row.totalDecision = decisionSum;
row.totalPlan = planSum;
}
});
// 合计行汇总
for (let i = 1; i <= 12; i++) {
let decisionSum = 0;
let planSum = 0;
sumRows.forEach((row) => {
decisionSum += parseFloat(row[`m${i}Decision`]) || 0;
planSum += parseFloat(row[`m${i}Plan`]) || 0;
});
totalRow[`m${i}Decision`] = decisionSum;
totalRow[`m${i}Plan`] = planSum;
}
// 计算合计列
let totalDecisionSum = 0;
let totalPlanSum = 0;
for (let i = 1; i <= 12; i++) {
......@@ -873,67 +924,52 @@ const calculateTotalRow = () => {
}
totalRow.totalDecision = totalDecisionSum;
totalRow.totalPlan = totalPlanSum;
// 计算每行的合计值(1-12月总和)
tableDataRef.value.forEach((row) => {
if (!row.isTotalRow && !row.noEdit) {
let decisionSum = 0;
let planSum = 0;
for (let i = 1; i <= 12; i++) {
decisionSum += parseFloat(row[`m${i}Decision`]) || 0;
planSum += parseFloat(row[`m${i}Plan`]) || 0;
}
row.totalDecision = decisionSum;
row.totalPlan = planSum;
}
});
} else {
// 月度表格合计
for (let i = 1; i <= 12; i++) {
let receivableSum = 0;
let receivedSum = 0;
tableDataRef.value.forEach((row) => {
if (!row.isTotalRow && !row.noEdit) {
receivableSum += parseFloat(row[`m${i}Receivable`]) || 0;
let receivableSum = 0;
sumRows.forEach((row) => {
receivedSum += parseFloat(row[`m${i}Received`]) || 0;
}
receivableSum += parseFloat(row[`m${i}Receivable`]) || 0;
});
totalRow[`m${i}Receivable`] = receivableSum;
totalRow[`m${i}Received`] = receivedSum;
totalRow[`m${i}Receivable`] = receivableSum;
}
// 本年累计(实收合计)
// 本年累计实收
let yearTotalSum = 0;
for (let i = 1; i <= 12; i++) {
yearTotalSum += parseFloat(totalRow[`m${i}Received`]) || 0;
}
totalRow.yearTotal = yearTotalSum;
// 计算合计行的完成率
// 从年度计划数据中获取合计计划值
const getYearlyPlanTotalPlan = () => {
// 本年累计应收
let yearTotalReceivableSum = 0;
for (let i = 1; i <= 12; i++) {
yearTotalReceivableSum += parseFloat(totalRow[`m${i}Receivable`]) || 0;
}
totalRow.yearTotalReceivable = yearTotalReceivableSum;
// 本年完成率: 累计实收/累计应收
totalRow.yearCompletionRateReceivable =
yearTotalReceivableSum > 0
? (yearTotalSum / yearTotalReceivableSum) * 100
: 0;
// 本年完成率: 累计实收/本年计划
const getPlanTotalPlan = () => {
if (!props.yearlyPlanData || !props.yearlyPlanData.tableData) return 0;
const yearlyTotalRow = props.yearlyPlanData.tableData.find(
(r) => r.isTotalRow,
);
return yearlyTotalRow ? parseFloat(yearlyTotalRow.totalPlan) || 0 : 0;
};
const planTotalPlan = getPlanTotalPlan();
totalRow.yearCompletionRatePlan =
planTotalPlan > 0 ? (yearTotalSum / planTotalPlan) * 100 : 0;
const yearlyPlanTotalPlan = getYearlyPlanTotalPlan();
// 本年完成率/本年决策 = 本年累计 / 年度计划的合计计划值 * 100
if (yearlyPlanTotalPlan > 0) {
totalRow.yearCompletionRateDecision =
(yearTotalSum / yearlyPlanTotalPlan) * 100;
} else {
totalRow.yearCompletionRateDecision = 0;
}
// 本年完成率/本年计划 = 本年累计 / 本年计划 * 100(合计行暂不计算此值)
totalRow.yearCompletionRatePlan = 0;
// 计算合计行的截至本年完成(开累)
// 从年度更新数据中获取截至年底实收值
// 截至本年完成(开累)
const getYearlyJzss = () => {
if (!props.yearlyCompletionData || !props.yearlyCompletionData.tableData)
return 0;
......@@ -942,79 +978,43 @@ const calculateTotalRow = () => {
);
return yearlyTotalRow ? parseFloat(yearlyTotalRow.jzss) || 0 : 0;
};
const yearlyJzss = getYearlyJzss();
totalRow.invoiceCompleted = yearTotalSum + yearlyJzss;
// 计算合计行的欠收金额
let shortfallAmountSum = 0;
tableDataRef.value.forEach((row) => {
if (!row.isTotalRow && !row.noEdit) {
shortfallAmountSum += parseFloat(row.shortfallAmount) || 0;
}
});
totalRow.shortfallAmount = shortfallAmountSum;
// 计算合计行的合同总金额
// 合同总金额合计
let contractTotalAmountSum = 0;
tableDataRef.value.forEach((row) => {
if (!row.isTotalRow && !row.noEdit) {
sumRows.forEach((row) => {
contractTotalAmountSum += parseFloat(row.contractTotalAmount) || 0;
}
});
totalRow.contractTotalAmount = contractTotalAmountSum;
// 计算合计行的开累完成率
// 从年度更新数据中获取合计的决策应收和计划应收值
const getYearlyDecisionReceivable = () => {
if (!props.yearlyCompletionData || !props.yearlyCompletionData.tableData)
return 0;
const yearlyTotalRow = props.yearlyCompletionData.tableData.find(
(r) => r.isTotalRow,
);
return yearlyTotalRow
? parseFloat(yearlyTotalRow.decisionReceivable) || 0
// 开累完成率 = 截至本年完成 / 合同总金额
totalRow.invoiceCompletionRate =
contractTotalAmountSum > 0
? (totalRow.invoiceCompleted / contractTotalAmountSum) * 100
: 0;
};
const getYearlyActualReceived = () => {
if (!props.yearlyCompletionData || !props.yearlyCompletionData.tableData)
return 0;
const yearlyTotalRow = props.yearlyCompletionData.tableData.find(
(r) => r.isTotalRow,
);
return yearlyTotalRow
? parseFloat(yearlyTotalRow.actualReceived) || 0
: 0;
};
// 欠收金额 = 累计应收 - 累计实收
totalRow.shortfallAmount = yearTotalReceivableSum - yearTotalSum;
}
};
const yearlyDecisionReceivable = getYearlyDecisionReceivable();
const yearlyActualReceived = getYearlyActualReceived();
const invoiceCompleted = parseFloat(totalRow.invoiceCompleted) || 0;
const totalInvoiceDecision = yearlyDecisionReceivable + yearlyPlanTotalPlan;
const totalInvoicePlan = yearlyActualReceived + yearlyPlanTotalPlan;
// 自动填充决策和计划数据
const autoFillDecisionAndPlan = () => {
if (props.tableType !== "month") return;
// 开累完成率/开累决策
if (totalInvoiceDecision > 0) {
totalRow.invoiceCompletionRateDecision =
(invoiceCompleted / totalInvoiceDecision) * 100;
} else {
totalRow.invoiceCompletionRateDecision = 0;
}
tableDataRef.value.forEach((row) => {
calculateMonthRow(row);
});
// 开累完成率/开累计划
if (totalInvoicePlan > 0) {
totalRow.invoiceCompletionRatePlan =
(invoiceCompleted / totalInvoicePlan) * 100;
} else {
totalRow.invoiceCompletionRatePlan = 0;
}
}
calculateParentRows();
calculateTotalRow();
};
// 处理输入事件
const handleNumberInput = (row) => {
calculateYearRow(row);
calculateParentRows();
calculateTotalRow();
emitChange();
};
......@@ -1025,24 +1025,21 @@ const handleTextInput = () => {
const handleMonthInput = (row) => {
calculateMonthRow(row);
calculateTotalRow();
emitChange();
};
const handleYearTotalInput = (row) => {
calculateMonthRow(row);
calculateParentRows();
calculateTotalRow();
emitChange();
};
const handleContractAmountInput = (row) => {
calculateMonthRow(row);
calculateParentRows();
calculateTotalRow();
emitChange();
};
const handleShortfallAmountInput = (row) => {
calculateMonthRow(row);
calculateParentRows();
calculateTotalRow();
emitChange();
};
......@@ -1060,11 +1057,13 @@ const emitChange = () => {
emit("update:modelValue", newModelValue);
};
// 表格单元格样式
const tableCellStyle = ({ row }) => {
if (row.isTotalRow) {
return { background: "#f5f7fa", fontWeight: "bold", textAlign: "center" };
}
if (row.isParentRow) {
return { background: "#fafafa", fontWeight: "500", textAlign: "center" };
}
return { textAlign: "center" };
};
......@@ -1080,7 +1079,18 @@ watch(
return initRowData(indicator, index, sourceRow);
});
tableDataRef.value = newData;
// 自动填充决策和计划数据
// 重新计算
tableDataRef.value.forEach((row) => {
if (props.tableType === "year") {
calculateYearRow(row);
} else if (props.tableType === "month") {
calculateMonthRow(row);
}
});
calculateParentRows();
calculateTotalRow();
autoFillDecisionAndPlan();
});
},
......@@ -1111,8 +1121,8 @@ watch(
tableDataRef.value.forEach((row) => {
calculateMonthRow(row);
});
calculateParentRows();
calculateTotalRow();
// 不调用 emitChange,避免无限循环
}
});
},
......@@ -1128,8 +1138,8 @@ watch(
tableDataRef.value.forEach((row) => {
calculateMonthRow(row);
});
calculateParentRows();
calculateTotalRow();
// 不调用 emitChange,避免无限循环
}
});
},
......
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