✨ 评分减分累计与最高扣分、用户部门标签联动树,并修复对账导入与结算调整等

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-23 04:28:22 +08:00
parent 8d9fe998c0
commit 2a5eca8393
17 changed files with 691 additions and 307 deletions
+107 -166
View File
@@ -244,7 +244,7 @@
<el-table-column prop="paymentRatio" label="付款比例(%)" min-width="160">
<template #default="{ row }">
<el-input-number
v-if="multipleSettlementRows && !readonly"
v-if="!readonly"
:model-value="row.paymentRatio"
:min="0"
:max="rowPaymentRatioMax(row)"
@@ -262,7 +262,7 @@
<el-table-column prop="appliedAmount" label="付款金额" min-width="160">
<template #default="{ row }">
<el-input-number
v-if="multipleSettlementRows && !readonly"
v-if="!readonly"
:model-value="row.appliedAmount"
:min="0"
:max="Number(row.payableAmount || 0)"
@@ -1014,62 +1014,37 @@ export default {
referenceSettlementCount() {
return this.referenceSettlementNos.length;
},
multipleSettlementRows() {
return this.referenceRows.length > 1;
},
// 结算单号展示:单张显示单号;多张仅提示数量,明细在下方「结算信息」表,悬停可看全部单号
referenceDisplayText() {
if (this.referenceSettlementCount > 1) return `已选 ${this.referenceSettlementCount} 张结算单`;
return this.referenceLabel;
},
settlementRows() {
if (this.referenceRows.length > 1) {
// 多结算单:行内比例/金额独立维护,不回落到表头比例(表头与结算信息不关联)
return this.referenceRows.map(item => {
const paymentRatio =
item.paymentRatio === undefined || item.paymentRatio === null || item.paymentRatio === ''
? 0
: Number(item.paymentRatio);
const appliedAmount = Number(item.appliedAmount || 0);
return {
...item,
paymentRatio,
appliedAmount,
...this.buildSettlementRatioHint({ ...item, paymentRatio }),
};
});
}
if (this.referenceRows.length) {
return this.referenceRows.map(item => {
const appliedAmount =
item.fixedAppliedAmount === true
? Number(item.appliedAmount || 0)
: Number(
(
(Number(item.settlementAmount || 0) * Number(this.form.paymentRatio || 0)) /
100
).toFixed(2)
);
return {
...item,
paymentRatio: this.form.paymentRatio,
appliedAmount,
...this.buildSettlementRatioHint(item),
};
});
}
if (!this.referenceLabel) return [];
return [
{
settlementNo: this.referenceLabel,
settlementAmount: this.form.settlementAmount,
payableAmount: this.form.payableAmount,
paymentRatio: this.form.paymentRatio,
appliedAmount: this.form.appliedAmount,
paymentSequence: 1,
...this.buildSettlementRatioHint({ paymentSequence: 1 }),
},
];
const source = this.referenceRows.length
? this.referenceRows
: this.referenceLabel
? [
{
settlementNo: this.referenceLabel,
settlementAmount: this.form.settlementAmount,
payableAmount: this.form.payableAmount,
paymentRatio: null,
appliedAmount: null,
paymentSequence: 1,
},
]
: [];
// 结算行比例/金额只取行内数据,不回落到付款基本信息
return source.map(item => {
const paymentRatio = this.nullableNumber(item.paymentRatio);
const appliedAmount = this.nullableNumber(item.appliedAmount);
return {
...item,
paymentRatio,
appliedAmount,
...this.buildSettlementRatioHint({ ...item, paymentRatio }),
};
});
},
highRiskPayee() {
const level = String(this.payeeCustomerLevel || '')
@@ -1127,11 +1102,12 @@ export default {
if (
this.amountSyncing ||
this.form.paymentType === 'project_advance' ||
!this.paymentAmountBase
!this.paymentAmountBase ||
this.nullableNumber(this.form.paymentRatio) === null
) {
return;
}
// 结算金额变化时按上方比例重算申请付款金额,仍不联动结算信息明细
// 结算金额变化时只按上方比例重算申请付款金额,不改结算信息明细
this.amountSyncing = true;
this.form.appliedAmount = Number(
((this.paymentAmountBase * Number(this.form.paymentRatio || 0)) / 100).toFixed(2)
@@ -1532,8 +1508,6 @@ export default {
paymentSequence: amount.paymentSequence,
}),
settlementNo: item.formalSettlementNo || item.settlementNo || '',
appliedAmount: Number(item.appliedAmount || 0),
fixedAppliedAmount: true,
};
});
this.seedMultipleRowPayments(referenceRows);
@@ -1545,27 +1519,6 @@ export default {
(total, item) => total + Number(item.payableAmount || 0),
0
);
const multiple = referenceRows.length > 1;
// 多结算单:表头比例/申请金额独立;单结算单:沿用行金额或按比例计算
const paymentRatio = Number(this.form.paymentRatio || 0);
const rowsAppliedTotal = referenceRows.reduce(
(total, item) => total + Number(item.appliedAmount || 0),
0
);
const headerAppliedAmount = multiple
? Number(((settlementAmount * paymentRatio) / 100).toFixed(2))
: Number(
(
rowsAppliedTotal > 0
? rowsAppliedTotal
: (payableAmount * paymentRatio) / 100
).toFixed(2)
);
const headerPaymentRatio = multiple
? paymentRatio
: settlementAmount
? Number(((headerAppliedAmount / settlementAmount) * 100).toFixed(2))
: paymentRatio;
const settlementIds = rows.map(item => item.settlementId || item.id).filter(Boolean);
this.amountSyncing = true;
this.referenceRows = referenceRows;
@@ -1592,8 +1545,7 @@ export default {
settlementAmount: Number(settlementAmount.toFixed(2)),
payableAmount: Number(payableAmount.toFixed(2)),
billType: '',
paymentRatio: headerPaymentRatio,
appliedAmount: headerAppliedAmount,
appliedAmount: this.headerAppliedAmountByRatio(settlementAmount),
invoices: rows.flatMap(item =>
this.createInvoiceRows(item.invoices || [], item.formalSettlementNo || item.settlementNo)
),
@@ -1649,7 +1601,7 @@ export default {
referenceType: isPre ? 'pre' : 'formal',
settlementAmount: Number(Number(item.settlementAmount || 0).toFixed(2)),
payableAmount: Number(Number(item.payableAmount ?? item.settlementAmount ?? 0).toFixed(2)),
appliedAmount: Number(Number(item.appliedAmount || 0).toFixed(2)),
appliedAmount: this.nullableNumber(item.appliedAmount),
paymentRatio: Number(item.settlementAmount || 0)
? Number(
(
@@ -1658,11 +1610,10 @@ export default {
).toFixed(2)
)
: null,
fixedAppliedAmount: this.readonly,
};
})
.filter(Boolean);
if (rows.length > 1) {
if (rows.length) {
this.form.preSettlementIds = rows.filter(item => item.referenceType === 'pre').map(item => item.id);
this.form.settlementIds = rows.filter(item => item.referenceType === 'formal').map(item => item.id);
if (this.form.preSettlementIds.length) this.form.preSettlementId = this.form.preSettlementIds[0];
@@ -1696,18 +1647,27 @@ export default {
settlementAmount: Number(Number(settlementAmount || 0).toFixed(2)),
payableAmount: Number(Math.max(0, payableAmount).toFixed(2)),
paymentSequence: Number.isFinite(paymentSequence) && paymentSequence > 0 ? paymentSequence : 1,
paymentRatio: null,
appliedAmount: null,
};
},
nullableNumber(value) {
if (value === undefined || value === null || value === '') return null;
const amount = Number(value);
return Number.isFinite(amount) ? amount : null;
},
/** 付款基本信息内部:已填写比例时,按结算金额重算申请付款金额;不读取结算行付款金额。 */
headerAppliedAmountByRatio(settlementAmount) {
if (this.nullableNumber(this.form.paymentRatio) === null) return this.form.appliedAmount;
return Number(((Number(settlementAmount || 0) * Number(this.form.paymentRatio)) / 100).toFixed(2));
},
/**
* 按合同付款比例配置,判断当前付款次序是否超上限。
* @param {Object} row 结算信息行(含 paymentSequence)
*/
buildSettlementRatioHint(row = {}) {
const sequence = Number(row.paymentSequence || 1);
const rawRatio =
row.paymentRatio !== undefined && row.paymentRatio !== null && row.paymentRatio !== ''
? row.paymentRatio
: this.form.paymentRatio;
const rawRatio = row.paymentRatio;
const paymentRatio = Number(rawRatio);
const ratioConfig = this.contractPaymentRatioRows[sequence - 1];
const ratioLimitValue = ratioConfig?.ratioLimit;
@@ -1850,21 +1810,6 @@ export default {
(total, item) => total + Number(item.payableAmount || 0),
0
);
const multiple = this.referenceRows.length > 1;
const paymentRatio = Number(this.form.paymentRatio || 0);
const rowsAppliedTotal = this.referenceRows.reduce(
(total, item) => total + Number(item.appliedAmount || 0),
0
);
// 多结算单:表头申请金额只由上方比例计算;单结算单仍按可付款金额×比例
const headerAppliedAmount = multiple
? Number(((settlementAmount * paymentRatio) / 100).toFixed(2))
: Number(((payableAmount * paymentRatio) / 100).toFixed(2));
const headerPaymentRatio = multiple
? paymentRatio
: settlementAmount && rowsAppliedTotal > 0
? Number(((rowsAppliedTotal / settlementAmount) * 100).toFixed(2))
: paymentRatio;
// 尾款付款场景下选择预付结算单时保留尾款付款类型,不强制改为进度预付
this.amountSyncing = true;
Object.assign(this.form, {
@@ -1890,8 +1835,7 @@ export default {
settlementAmount: Number(settlementAmount.toFixed(2)),
payableAmount: Number(payableAmount.toFixed(2)),
billType: '',
paymentRatio: headerPaymentRatio,
appliedAmount: headerAppliedAmount,
appliedAmount: this.headerAppliedAmountByRatio(settlementAmount),
});
this.rememberSettlementParties(first.payerName, first.payeeName);
this.paymentRecords = [];
@@ -3080,22 +3024,54 @@ export default {
return Number(Math.min(100, (payableAmount / settlementAmount) * 100).toFixed(2));
},
/**
* 多结算单行金额规范化:仅在行内反算比例,不使用表头比例灌入(表头与结算信息不关联)。
* 结算行金额规范化:只在行内反算比例,不使用付款基本信息的比例和申请金额。
*/
seedMultipleRowPayments(rows = []) {
if (rows.length <= 1) return;
rows.forEach(item => {
const settlementAmount = Number(item.settlementAmount || 0);
const payableAmount = Number(item.payableAmount || 0);
let applied = Number(item.appliedAmount || 0);
if (!Number.isFinite(applied) || applied < 0) applied = 0;
if (applied > payableAmount) applied = Number(payableAmount.toFixed(2));
item.appliedAmount = Number(applied.toFixed(2));
item.paymentRatio = settlementAmount
? Number(((item.appliedAmount / settlementAmount) * 100).toFixed(2))
: 0;
if (this.nullableNumber(item.appliedAmount) === null && this.nullableNumber(item.paymentRatio) === null) {
item.appliedAmount = null;
item.paymentRatio = null;
return;
}
this.syncRowPayment(item, 'amount');
});
},
/**
* 结算行付款比例与付款金额联动。比例 = 付款金额 / 结算总金额。
* @param {'ratio'|'amount'} source 以哪一侧为准
*/
syncRowPayment(row, source) {
const settlementAmount = Number(row.settlementAmount || 0);
const payableAmount = Number(row.payableAmount || 0);
if (source === 'ratio') {
const ratio = this.nullableNumber(row.paymentRatio);
if (ratio === null) {
row.paymentRatio = null;
row.appliedAmount = null;
return;
}
let applied = Number(((settlementAmount * ratio) / 100).toFixed(2));
if (applied > payableAmount) applied = Number(payableAmount.toFixed(2));
row.appliedAmount = applied;
row.paymentRatio = settlementAmount
? Number(((applied / settlementAmount) * 100).toFixed(2))
: Number(ratio.toFixed(2));
return;
}
const appliedValue = this.nullableNumber(row.appliedAmount);
if (appliedValue === null) {
row.appliedAmount = null;
row.paymentRatio = null;
return;
}
let applied = appliedValue;
if (applied < 0) applied = 0;
if (applied > payableAmount) applied = payableAmount;
row.appliedAmount = Number(applied.toFixed(2));
row.paymentRatio = settlementAmount
? Number(((row.appliedAmount / settlementAmount) * 100).toFixed(2))
: 0;
},
findReferenceRow(row) {
return this.referenceRows.find(
item =>
@@ -3104,55 +3080,19 @@ export default {
item.settlementNo === row.settlementNo
);
},
/** 结算信息付款金额合计 */
settlementRowsAppliedTotal(rows = this.referenceRows) {
return (rows || []).reduce((total, item) => total + Number(item.appliedAmount || 0), 0);
},
/** 结算信息合计不得超过上方申请付款金额 */
ensureSettlementAppliedWithinHeader(showWarning = true) {
if (!this.multipleSettlementRows) return true;
const headerApplied = Number(this.form.appliedAmount || 0);
const rowsTotal = Number(this.settlementRowsAppliedTotal().toFixed(2));
if (rowsTotal - headerApplied > 1e-8) {
if (showWarning) {
this.$message.warning(
`结算信息付款金额合计(${rowsTotal.toFixed(2)})不能大于申请付款金额(${headerApplied.toFixed(2)})`
);
}
return false;
}
return true;
},
handleRowPaymentRatioChange(row, value) {
const target = this.findReferenceRow(row);
if (!target) return;
const settlementAmount = Number(target.settlementAmount || 0);
const payableAmount = Number(target.payableAmount || 0);
const ratio = value === null || value === undefined || value === '' ? 0 : Number(value);
let applied = Number(((settlementAmount * ratio) / 100).toFixed(2));
if (applied > payableAmount) applied = Number(payableAmount.toFixed(2));
target.appliedAmount = applied;
target.paymentRatio = settlementAmount
? Number(((applied / settlementAmount) * 100).toFixed(2))
: Number(ratio.toFixed(2));
// 仅刷新结算金额/可付款金额合计,不回写表头申请金额与比例
target.paymentRatio = value;
this.syncRowPayment(target, 'ratio');
this.syncHeaderSettlementTotalsFromRows();
this.ensureSettlementAppliedWithinHeader(true);
},
handleRowAppliedAmountChange(row, value) {
const target = this.findReferenceRow(row);
if (!target) return;
const settlementAmount = Number(target.settlementAmount || 0);
const payableAmount = Number(target.payableAmount || 0);
let applied = value === null || value === undefined || value === '' ? 0 : Number(value);
if (!Number.isFinite(applied) || applied < 0) applied = 0;
if (applied > payableAmount) applied = payableAmount;
target.appliedAmount = Number(applied.toFixed(2));
target.paymentRatio = settlementAmount
? Number(((target.appliedAmount / settlementAmount) * 100).toFixed(2))
: 0;
target.appliedAmount = value;
this.syncRowPayment(target, 'amount');
this.syncHeaderSettlementTotalsFromRows();
this.ensureSettlementAppliedWithinHeader(true);
},
/** 多结算单时仅同步结算金额/可付款金额合计,申请付款金额与比例由上方独立维护 */
syncHeaderSettlementTotalsFromRows() {
@@ -3261,8 +3201,8 @@ export default {
settlementLines: (this.referenceRows || []).map(item => ({
formalSettlementId: item.referenceType === 'formal' ? item.id : null,
preSettlementId: item.referenceType === 'pre' ? item.id : null,
paymentRatio: item.paymentRatio,
appliedAmount: item.appliedAmount,
paymentRatio: this.nullableNumber(item.paymentRatio),
appliedAmount: this.nullableNumber(item.appliedAmount),
})),
paymentMethod,
billLedgerId,
@@ -3323,21 +3263,22 @@ export default {
ElMessage.warning('申请付款金额必填且必须大于0');
return false;
}
if (this.multipleSettlementRows) {
const invalidRow = this.referenceRows.find(
item => Number(item.appliedAmount || 0) > Number(item.payableAmount || 0)
);
if (this.isSettlementRelatedType && this.referenceRows.length) {
const invalidRow = this.referenceRows.find(item => {
const rowApplied = Number(item.appliedAmount || 0);
return !Number.isFinite(rowApplied) || rowApplied <= 0;
});
if (invalidRow) {
this.clearMessages();
ElMessage.warning(`${invalidRow.settlementNo || '结算单'}付款金额不能超过可付款金额`);
ElMessage.warning(`${invalidRow.settlementNo || '结算单'}请填写付款金额,且必须大于0`);
return false;
}
const rowsTotal = Number(this.settlementRowsAppliedTotal().toFixed(2));
if (rowsTotal - appliedAmount > 1e-8) {
const exceededRow = this.referenceRows.find(
item => Number(item.appliedAmount || 0) > Number(item.payableAmount || 0)
);
if (exceededRow) {
this.clearMessages();
ElMessage.warning(
`结算信息付款金额合计(${rowsTotal.toFixed(2)})不能大于申请付款金额(${appliedAmount.toFixed(2)})`
);
ElMessage.warning(`${exceededRow.settlementNo || '结算单'}付款金额不能超过可付款金额`);
return false;
}
}