From 65967226019d64b9998f5d49d2be90bd674cff16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com> Date: Sun, 18 Oct 2026 14:32:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=AD=A3=E5=BC=8F=E7=BB=93=E7=AE=97?= =?UTF-8?q?=E5=8D=95=E6=96=B0=E5=A2=9E=E9=A2=84=E4=BB=98=E6=B1=87=E6=80=BB?= =?UTF-8?q?=E6=A0=8F=E4=B8=8E=E4=BB=98=E6=AC=BE=E8=AE=B0=E5=BD=95=E5=90=88?= =?UTF-8?q?=E5=B9=B6=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 结算合计下新增汇总栏(结算合计原金额-已付金额含预付核销-剩余未付);只读付款信息合并展示进度预付-项目预付核销-结算尾款三类记录;预结算转正式自动预填来源预结算核销行;单据状态名提取公共 settlementStatus 工具与预结算编辑器共用 --- src/utils/settlementStatus.js | 23 ++++ .../components/formal-settlement-editor.vue | 116 ++++++++++++++++-- .../components/pre-settlement-editor.vue | 11 +- 3 files changed, 128 insertions(+), 22 deletions(-) create mode 100644 src/utils/settlementStatus.js diff --git a/src/utils/settlementStatus.js b/src/utils/settlementStatus.js new file mode 100644 index 0000000..4fdf2ec --- /dev/null +++ b/src/utils/settlementStatus.js @@ -0,0 +1,23 @@ +/** + * 结算单预付记录单据状态名(预结算编辑器与正式结算编辑器共用,避免两页状态名不一致) + */ +export function settlementStatusName(status, displayValue) { + const labels = { + reviewing: '审批中', + approved: '审批通过', + paid: '已付款', + returned: '已驳回', + voided: '已作废', + draft: '草稿', + }; + const name = labels[status]; + if (name) return name; + return typeof displayValue === 'function' ? displayValue(status) : status || '-'; +} + +/** + * 统一预付付款记录类型名 + */ +export function settlementRecordTypeName(recordType) { + return { progress_advance: '进度预付', project_advance: '项目预付核销' }[recordType] || ''; +} diff --git a/src/views/settlement/components/formal-settlement-editor.vue b/src/views/settlement/components/formal-settlement-editor.vue index 3807f0c..acb8277 100644 --- a/src/views/settlement/components/formal-settlement-editor.vue +++ b/src/views/settlement/components/formal-settlement-editor.vue @@ -177,6 +177,22 @@ + + + {{ formatMoney(form.settlementAmount) }} + + + {{ formatMoney(settlePaidTotal) }} + + + {{ formatMoney(settleRemainTotal) }} + + @@ -577,28 +593,27 @@ - + - - + + - + - + - - + + @@ -1054,6 +1069,10 @@ import { InfoFilled } from '@element-plus/icons-vue'; import { mapGetters } from 'vuex'; import { formatContractOptionLabel } from '@/utils/contract'; import { normalizeCurrencyCode } from '@/utils/currency'; +import { + settlementRecordTypeName, + settlementStatusName, +} from '@/utils/settlementStatus'; import { downloadFileByUrl } from '@/utils/util'; import { downloadKingdeeInvoiceFile, @@ -1097,6 +1116,7 @@ export default { details: [], summaryFees: [], paymentApplications: [], + advancePayments: [], receiptClaims: [], adjustments: [], changeRecords: [], @@ -1270,6 +1290,34 @@ export default { summaryTotal() { return this.summaryFees.reduce((total, row) => total + Number(row.settlementAmount || 0), 0); }, + // 已付金额(含预付、核销):编辑态取来源预结算预付快照 + 当前核销行实时合计(草稿态无尾款申请);只读态用落库值 + settlePaidTotal() { + if (!this.editable) return Number(this.form.paidAmount || 0); + const advancePaid = this.sources.reduce( + (sum, item) => sum + Number(item.advancePaidAmount || 0), + 0 + ); + const writeOff = this.projectAdvanceWriteOffs.reduce( + (sum, row) => sum + Number(row.writeOffAmount || 0), + 0 + ); + return advancePaid + writeOff; + }, + settleRemainTotal() { + return Math.max(Number(this.form.settlementAmount || 0) - Number(this.settlePaidTotal || 0), 0); + }, + // 付款记录合并表:预付付款记录(进度预付/项目预付核销)+ 尾款付款申请 + paymentRecords() { + const advances = (this.advancePayments || []).map(row => ({ + ...row, + typeName: settlementRecordTypeName(row.recordType), + })); + const tails = (this.paymentApplications || []).map(row => ({ + ...row, + typeName: row.paymentTypeName, + })); + return [...advances, ...tails]; + }, filteredDetails() { return this.details.filter(row => Object.entries(this.appliedDetailQuery).every(([field, keyword]) => { @@ -1365,6 +1413,7 @@ export default { this.detailFeeSnapshots = {}; this.pendingAdjustments = {}; this.paymentApplications = data.paymentApplications || []; + this.advancePayments = data.advancePayments || []; if (this.readonly && this.form.settlementType === 'receivable' && !this.publicMode) { this.receiptClaims = this.unwrapData(await getReceiptClaims(id)) || []; } @@ -1408,6 +1457,7 @@ export default { this.adjustments = []; this.changeRecords = []; this.projectAdvanceWriteOffs = []; + this.advancePayments = []; this.attachments = []; this.selectedAttachmentRows = []; this.invoices = []; @@ -1556,6 +1606,19 @@ export default { this.sources.map(item => getPreSettlementDetail(item.preSettlementId)) ); const settlements = responses.map(response => this.unwrapData(response) || {}); + // 预结算转正式:带出来源预结算已保存的项目预付核销行(后端保存时转移源行) + const writeOffRows = settlements.flatMap(settlement => settlement.projectAdvanceWriteOffs || []); + const existingWriteOffIds = new Set( + this.projectAdvanceWriteOffs.map(row => String(row.paymentApplicationId)) + ); + writeOffRows.forEach(row => { + if (existingWriteOffIds.has(String(row.paymentApplicationId))) return; + existingWriteOffIds.add(String(row.paymentApplicationId)); + this.projectAdvanceWriteOffs.push({ + ...row, + writeOffAmount: Number(row.writeOffAmount || 0), + }); + }); this.details = settlements.flatMap(settlement => (settlement.details || []).map(detail => ({ ...detail, @@ -1726,7 +1789,7 @@ export default { this.candidate.page.current = 1; this.loadCandidates(); }, - confirmCandidates() { + async confirmCandidates() { const merged = [...this.sources, ...this.candidate.selected]; const map = new Map( merged.map(item => [ @@ -1734,6 +1797,7 @@ export default { { ...item, preSettlementId: item.preSettlementId || item.id }, ]) ); + const previousIds = new Set(this.sources.map(item => String(item.preSettlementId || item.id))); this.sources = [...map.values()]; this.form.sourcePreSettlementIds = this.sources.map(item => item.preSettlementId); if (this.sources.length) { @@ -1750,6 +1814,26 @@ export default { this.form.localSettlementAmount = this.form.settlementAmount * Number(this.form.exchangeRate || 1); this.candidate.visible = false; + // 预结算转正式:带出来源预结算已保存的项目预付核销行(后端保存时转移源行,额度单份占用) + const newIds = this.sources + .map(item => item.preSettlementId) + .filter(id => !previousIds.has(String(id))); + if (!newIds.length) return; + const responses = await Promise.all(newIds.map(id => getPreSettlementDetail(id))); + const rows = responses.flatMap(response => + this.unwrapData(response)?.projectAdvanceWriteOffs || [] + ); + const existing = new Set( + this.projectAdvanceWriteOffs.map(row => String(row.paymentApplicationId)) + ); + rows.forEach(row => { + if (existing.has(String(row.paymentApplicationId))) return; + existing.add(String(row.paymentApplicationId)); + this.projectAdvanceWriteOffs.push({ + ...row, + writeOffAmount: Number(row.writeOffAmount || 0), + }); + }); }, removeSource(index) { this.sources.splice(index, 1); @@ -2736,6 +2820,9 @@ export default { }; return this.displayValue(labels[value] || value); }, + settlementStatusName(status) { + return settlementStatusName(status, this.displayValue); + }, unwrapData(response) { const body = response?.data || response || {}; return body?.data || body; @@ -3132,6 +3219,9 @@ export default { .formal-editor__negative-amount { color: var(--el-color-danger); } +.formal-editor__settle-summary { + margin-top: 8px; +} .formal-editor :deep(.formal-editor__negative-amount .el-input__inner) { color: var(--el-color-danger); } diff --git a/src/views/settlement/components/pre-settlement-editor.vue b/src/views/settlement/components/pre-settlement-editor.vue index 5d5ad8e..22f4832 100644 --- a/src/views/settlement/components/pre-settlement-editor.vue +++ b/src/views/settlement/components/pre-settlement-editor.vue @@ -990,6 +990,7 @@ import { } from '@/option/settlement/preSettlementTable'; import { formatContractOptionLabel } from '@/utils/contract'; import { normalizeCurrencyCode } from '@/utils/currency'; +import { settlementStatusName } from '@/utils/settlementStatus'; import { downloadFileByUrl } from '@/utils/util'; import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval'; import * as XLSX from 'xlsx'; @@ -2877,15 +2878,7 @@ export default { return value === undefined || value === null || value === '' ? '-' : value; }, advanceStatusName(status) { - return ( - { - reviewing: '审批中', - approved: '审批通过', - paid: '已付款', - returned: '已驳回', - voided: '已作废', - }[status] || this.displayValue(status) - ); + return settlementStatusName(status, this.displayValue); }, advanceCreateUserName(row) { return this.displayValue(