From 31794bf26a6eff30d43661bd85520a43d6b8a906 Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Mon, 19 Oct 2026 23:28:51 +0800 Subject: [PATCH 1/3] =?UTF-8?q?:bug:=20=E7=BB=93=E7=AE=97=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E5=8E=BB=E6=8E=89=E6=A8=A1=E6=9D=BF=E7=BB=93=E7=AE=97?= =?UTF-8?q?=E9=87=91=E9=A2=9D=E4=BE=9D=E8=B5=96=E5=B9=B6=E5=8F=96=E6=B6=88?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=9B=9E=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 导入后按运输费与费用项自动合计结算金额,编辑费用项时不再覆盖结算金额。 Co-authored-by: Cursor --- .../components/settlement-import-page.vue | 107 ++++++++++++------ 1 file changed, 72 insertions(+), 35 deletions(-) diff --git a/src/views/settlement/components/settlement-import-page.vue b/src/views/settlement/components/settlement-import-page.vue index 7c3d6a4..58c65d9 100644 --- a/src/views/settlement/components/settlement-import-page.vue +++ b/src/views/settlement/components/settlement-import-page.vue @@ -604,6 +604,38 @@ const dedupeColumns = columns => { }); }; +const settlementAmountColumn = { + prop: 'settlementAmount', + label: '结算金额', + editor: 'number', + minWidth: 120, +}; + +/** 模板已去掉结算金额;明细仍展示,并插在费用项之后、日期列之前 */ +const ensureSettlementAmountColumn = columns => { + if (columns.some(column => column.prop === 'settlementAmount')) { + return columns; + } + const result = [...columns]; + let insertAt = result.findIndex(column => + ['actualStartDate', 'actualEndDate', 'planStartDate', 'planEndDate', 'remark', 'waybillIdentifier'].includes( + column.prop + ) + ); + if (insertAt < 0) { + const lastFeeIndex = [...result].map((column, index) => (column.isFeeItem ? index : -1)).filter(index => index >= 0).pop(); + const transportFeeIndex = result.findIndex(column => column.prop === 'transportFee'); + insertAt = + lastFeeIndex !== undefined + ? lastFeeIndex + 1 + : transportFeeIndex >= 0 + ? transportFeeIndex + 1 + : result.length; + } + result.splice(insertAt, 0, { ...settlementAmountColumn }); + return result; +}; + const detailColumns = computed(() => { const historyColumns = form.waybillMode === 'history' @@ -619,16 +651,13 @@ const detailColumns = computed(() => { const fallback = [ ...columnsBeforeFee, ...feeItemNames.value.map(name => feeColumn(name)), - { - prop: 'settlementAmount', - label: '结算金额', - editor: 'number', - minWidth: 120, - }, + { ...settlementAmountColumn }, ...columnsAfterFee, ]; const uploaded = parsedHeaders.value.map(columnFromHeader).filter(Boolean); - const columns = dedupeColumns(uploaded.length ? uploaded : fallback); + const columns = ensureSettlementAmountColumn( + dedupeColumns(uploaded.length ? uploaded : fallback) + ); return [...historyColumns, ...columns]; }); @@ -717,6 +746,7 @@ const formatCell = (row, column) => { return value ?? ''; }; +/** 导入解析后自动合计结算金额;编辑运输费/费用项时不再回算 */ const recalcSettlementAmount = row => { const transportFee = toNumber(row.transportFee ?? row.freight); row.transportFee = transportFee; @@ -735,6 +765,18 @@ const recalcSettlementAmount = row => { row.settlementAmount = settlementAmount; row.otherFeeTotal = Number(feeSum.toFixed(2)); row.freightTotal = settlementAmount; + row['运费合计'] = settlementAmount; +}; + +const syncOtherFeeTotal = row => { + if (!row.feeItems || typeof row.feeItems !== 'object') { + row.feeItems = {}; + } + let feeSum = 0; + feeItemNames.value.forEach(name => { + feeSum += toNumber(row.feeItems[name] ?? row[name]); + }); + row.otherFeeTotal = Number(feeSum.toFixed(2)); }; const updateNumberCell = (row, column, value) => { @@ -744,17 +786,16 @@ const updateNumberCell = (row, column, value) => { if (!row.feeItems) row.feeItems = {}; row.feeItems[column.prop] = ''; row[column.prop] = ''; - } else { - row[column.prop] = column.editor === 'optionalNumber' ? '' : ''; - if (column.prop === 'transportFee') row.freight = ''; - if (column.prop === 'unitPrice') row.transportUnitPrice = ''; - } - if (column.prop === 'settlementAmount') { - row.settlementAmount = ''; - row.freightTotal = ''; + syncOtherFeeTotal(row); return; } - recalcSettlementAmount(row); + row[column.prop] = ''; + if (column.prop === 'transportFee') row.freight = ''; + if (column.prop === 'unitPrice') row.transportUnitPrice = ''; + if (column.prop === 'settlementAmount') { + row.freightTotal = ''; + row['运费合计'] = ''; + } return; } const [integer = '', decimal = ''] = raw.replace(/[^\d.]/g, '').split('.'); @@ -764,21 +805,20 @@ const updateNumberCell = (row, column, value) => { if (!row.feeItems) row.feeItems = {}; row.feeItems[column.prop] = normalized; row[column.prop] = normalized; - } else { - row[column.prop] = normalized; - if (column.prop === 'transportFee') { - row.freight = normalized; - } - if (column.prop === 'unitPrice') { - row.transportUnitPrice = normalized; - } - if (column.prop === 'settlementAmount') { - row.freightTotal = normalized; - row['运费合计'] = normalized; - return; - } + syncOtherFeeTotal(row); + return; + } + row[column.prop] = normalized; + if (column.prop === 'transportFee') { + row.freight = normalized; + } + if (column.prop === 'unitPrice') { + row.transportUnitPrice = normalized; + } + if (column.prop === 'settlementAmount') { + row.freightTotal = normalized; + row['运费合计'] = normalized; } - recalcSettlementAmount(row); }; const importFieldText = value => String(value ?? '').trim(); @@ -854,11 +894,8 @@ const normalizeParsedRow = (item, index) => { applyVehicleNo(row, vehicleNo); row.transportUnitPrice = row.unitPrice; row.freight = row.transportFee; - if (row.settlementAmount === '' || row.settlementAmount === null || row.settlementAmount === undefined) { - recalcSettlementAmount(row); - } else { - row.freightTotal = row.settlementAmount; - } + // 模板无结算金额列:导入后按运输费+费用项自动合计 + recalcSettlementAmount(row); return row; }; From 4c9bbf1eb712a3a5387bbe8421ef62198a4e1705 Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Tue, 20 Oct 2026 01:50:54 +0800 Subject: [PATCH 2/3] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=E9=A2=84?= =?UTF-8?q?=E4=BB=98=E9=A9=B3=E5=9B=9E=E9=87=8D=E6=8F=90=E3=80=81=E9=A2=84?= =?UTF-8?q?=E7=BB=93=E7=AE=97=E5=8F=98=E6=9B=B4=E8=AE=B0=E5=BD=95=E4=B8=8E?= =?UTF-8?q?=E6=94=B6=E7=A5=A8=E5=A4=9A=E9=80=89=E7=BB=93=E7=AE=97=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- src/api/payment/billLedger.js | 8 +- .../settlement/formalSettlementTable.js | 1 - src/views/payment/bill-ledger-form.vue | 160 +++++++++++++----- src/views/payment/invoice-receipt-form.vue | 34 +++- .../payment/payment-application-form.vue | 113 ++++++++++--- .../components/formal-settlement-editor.vue | 4 +- .../components/pre-settlement-editor.vue | 143 +++++++++++----- .../settlement/receivable-payable-detail.vue | 68 ++++++-- 8 files changed, 397 insertions(+), 134 deletions(-) diff --git a/src/api/payment/billLedger.js b/src/api/payment/billLedger.js index d268f2b..cace3bb 100644 --- a/src/api/payment/billLedger.js +++ b/src/api/payment/billLedger.js @@ -6,17 +6,17 @@ export const getList = (current, size, params) => request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } }); export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } }); export const getExpiryCounts = () => request({ url: `${baseUrl}/expiry-counts`, method: 'get' }); -export const getAvailableOptions = (keyword, deptId, selectedId) => +export const getAvailableOptions = (keyword, deptId, selectedId, payerName, payeeName) => request({ url: `${baseUrl}/available-options`, method: 'get', - params: { keyword, deptId, selectedId }, + params: { keyword, deptId, selectedId, payerName, payeeName }, }); -export const getAvailablePage = (current, size, keyword, deptId, selectedId) => +export const getAvailablePage = (current, size, keyword, deptId, selectedId, payerName, payeeName) => request({ url: `${baseUrl}/available-page`, method: 'get', - params: { current, size, keyword, deptId, selectedId }, + params: { current, size, keyword, deptId, selectedId, payerName, payeeName }, }); export const submit = data => request({ url: `${baseUrl}/submit`, method: 'post', data }); export const remove = id => request({ url: `${baseUrl}/remove`, method: 'post', params: { id } }); diff --git a/src/option/settlement/formalSettlementTable.js b/src/option/settlement/formalSettlementTable.js index 92713ca..f53ee46 100644 --- a/src/option/settlement/formalSettlementTable.js +++ b/src/option/settlement/formalSettlementTable.js @@ -55,7 +55,6 @@ export const detailColumns = [ { label: '运费', prop: 'freightAmount', minWidth: 120, money: true }, { label: '调整金额', prop: 'adjustAmount', minWidth: 120, money: true }, { label: '结算金额(含税)', prop: 'settlementAmountTax', minWidth: 155, money: true }, - { label: '结算金额(不含税)', prop: 'settlementAmountNoTax', minWidth: 170, money: true }, { label: '备注', prop: 'remark', minWidth: 180 }, ]; diff --git a/src/views/payment/bill-ledger-form.vue b/src/views/payment/bill-ledger-form.vue index 2c511d4..ccc8364 100644 --- a/src/views/payment/bill-ledger-form.vue +++ b/src/views/payment/bill-ledger-form.vue @@ -44,7 +44,8 @@ reserve-keyword :remote-method="searchCustomers" :loading="customerLoading" - placeholder="请选择客商基础档案" + placeholder="请选择" + @change="syncFeeBearer" > - + - @@ -140,17 +140,9 @@ - + ({ billNo: '', issuerId: null, receiverName: '', - billType: 'issued', + billType: '', faceAmount: null, availableBalance: 0, issueDate: '', maturityDate: '', - availableDeptIds: ['all'], - availableDeptNames: '全部', + availableDeptIds: [], + availableDeptNames: '', feeBearerId: null, confirmedDiscountRate: null, issuingBank: '', @@ -272,9 +264,17 @@ export default { receiverOptions: [], customerLoading: false, saving: false, - deptTree: [], + businessDeptTreeOptions: [], deptMap: new Map(), - deptProps: { label: 'title', value: 'id', children: 'children' }, + deptCascaderProps: { + label: 'label', + value: 'value', + children: 'children', + multiple: true, + checkStrictly: true, + emitPath: true, + expandTrigger: 'click', + }, bankOptions: [ '中国工商银行', '中国农业银行', @@ -309,7 +309,10 @@ export default { issuerId: [{ required: true, message: '请选择出票单位', trigger: 'change' }], receiverName: [{ required: true, message: '请选择或输入收票单位', trigger: 'change' }], billType: [{ required: true, message: '请选择汇票类型', trigger: 'change' }], - faceAmount: [{ validator: this.validateFaceAmount, trigger: 'change' }], + faceAmount: [ + { required: true, message: '请输入票面金额', trigger: 'change' }, + { validator: this.validateFaceAmount, trigger: 'change' }, + ], issueDate: [{ required: true, message: '请选择出票日期', trigger: 'change' }], maturityDate: [{ required: true, validator: this.validateMaturityDate, trigger: 'change' }], availableDeptIds: [{ validator: this.validateDepartments, trigger: 'change' }], @@ -340,6 +343,42 @@ export default { (Number(this.form.faceAmount || 0) * Number(this.form.confirmedDiscountRate || 0)) / 100 ); }, + // 可用部门:表单存部门 id,级联多选回显用根到节点的路径 + availableDeptCascaderValue: { + get() { + return (this.form.availableDeptIds || []) + .filter(id => id !== undefined && id !== null && String(id) !== 'all' && String(id) !== '') + .map(id => this.findDeptPath(id, this.businessDeptTreeOptions) || [String(id)]); + }, + set(paths) { + const ids = [ + ...new Set( + (paths || []) + .map(path => { + const nodes = Array.isArray(path) ? path.filter(item => item !== undefined && item !== null && item !== '') : []; + return nodes.length ? String(nodes.at(-1)) : ''; + }) + .filter(Boolean) + ), + ]; + this.form.availableDeptIds = ids; + this.form.availableDeptNames = ids + .map(id => this.deptMap.get(String(id))) + .filter(Boolean) + .join('、'); + }, + }, + feeBearerOptions() { + const issuer = this.customerOptions.find(item => String(item.id) === String(this.form.issuerId)); + const receiverName = String(this.form.receiverName || '').trim(); + const receiver = receiverName + ? this.customerOptions.find(item => this.customerName(item) === receiverName) + : null; + return [issuer, receiver].filter( + (item, index, rows) => + item && rows.findIndex(row => row && String(row.id) === String(item.id)) === index + ); + }, }, created() { this.initialize(); @@ -352,7 +391,6 @@ export default { async initialize() { await Promise.all([this.loadDepartments(), this.searchCustomers('')]); if (!this.recordId) { - this.form.issueDate = this.$dayjs().format('YYYY-MM-DD'); return; } const data = this.unwrapData(await api.getDetail(this.recordId)); @@ -361,14 +399,20 @@ export default { ...data, confirmedDiscountRate: this.normalizeDiscountRate(data.confirmedDiscountRate), bankDiscountReferenceRate: this.normalizeDiscountRate(data.bankDiscountReferenceRate), - availableDeptIds: this.parse(data.availableDeptIdsJson), + availableDeptIds: this.parse(data.availableDeptIdsJson) + .filter(id => String(id) !== 'all') + .map(id => String(id)), attachments: this.parse(data.attachmentsJson), }; this.usageRecords = data.usageRecords || []; this.receiverOptions = data.receiverName ? [data.receiverName] : []; this.ensureCustomerOption(data.issuerId, data.issuerName); this.ensureCustomerOption(data.feeBearerId, data.feeBearerName); - this.handleDeptChange(this.form.availableDeptIds); + this.form.availableDeptNames = (this.form.availableDeptIds || []) + .map(id => this.deptMap.get(String(id))) + .filter(Boolean) + .join('、'); + this.syncFeeBearer(); }, parse(value) { if (!value) return []; @@ -381,22 +425,52 @@ export default { }, async loadDepartments() { const response = await getDeptTree(this.userInfo?.tenantId); - const rows = this.unwrapData(response) || []; - this.deptTree = [{ id: 'all', title: '全部', children: rows }]; - this.deptMap = new Map([['all', '全部']]); - this.flattenDepartments(rows).forEach(item => this.deptMap.set(String(item.id), item.title)); + const rows = this.excludeExternalOrganization(this.unwrapData(response) || []); + this.businessDeptTreeOptions = this.toDeptCascaderOptions(rows); + this.deptMap = new Map(); + this.flattenDepartments(rows).forEach(item => + this.deptMap.set(String(item.id), item.title || item.deptName || item.name) + ); + }, + excludeExternalOrganization(list = []) { + return list.reduce((result, item) => { + const deptName = item.title || item.deptName || item.name; + if (String(deptName || '').trim() === '外部组织') return result; + result.push({ + ...item, + children: item.children?.length + ? this.excludeExternalOrganization(item.children) + : item.children, + }); + return result; + }, []); + }, + toDeptCascaderOptions(list = []) { + return list.map(item => ({ + label: item.title || item.deptName || item.name, + value: item.id == null || item.id === '' ? '' : String(item.id), + children: item.children?.length ? this.toDeptCascaderOptions(item.children) : undefined, + })); }, flattenDepartments(rows) { return (rows || []).flatMap(item => [item, ...this.flattenDepartments(item.children || [])]); }, - handleDeptChange(values) { - const ids = values || []; - this.form.availableDeptNames = ids.some(item => String(item) === 'all') - ? '全部' - : ids - .map(item => this.deptMap.get(String(item))) - .filter(Boolean) - .join('、'); + findDeptPath(id, tree = [], parents = []) { + const target = Array.isArray(id) ? id.at(-1) : id; + if (target === undefined || target === null || target === '') return null; + for (const node of tree || []) { + const path = [...parents, node.value]; + if (String(node.value) === String(target)) return path; + const matched = this.findDeptPath(target, node.children, path); + if (matched) return matched; + } + return null; + }, + syncFeeBearer() { + const allowed = this.feeBearerOptions.map(item => String(item.id)); + if (this.form.feeBearerId && !allowed.includes(String(this.form.feeBearerId))) { + this.form.feeBearerId = null; + } }, async searchCustomers(keyword) { this.customerLoading = true; @@ -545,7 +619,7 @@ export default { .bill-ledger-form-page__form :deep(.el-select), .bill-ledger-form-page__form :deep(.el-input-number), .bill-ledger-form-page__form :deep(.el-date-editor), -.bill-ledger-form-page__form :deep(.el-tree-select) { +.bill-ledger-form-page__form :deep(.el-cascader) { width: 100%; } .bill-ledger-form-page__form :deep(.el-input-number .el-input__inner) { diff --git a/src/views/payment/invoice-receipt-form.vue b/src/views/payment/invoice-receipt-form.vue index 200e758..2a6b5d0 100644 --- a/src/views/payment/invoice-receipt-form.vue +++ b/src/views/payment/invoice-receipt-form.vue @@ -173,7 +173,7 @@ 查询 @@ -182,10 +182,11 @@ ref="settlementTable" v-loading="settlementDialog.loading" :data="settlementDialog.rows" + row-key="id" border @selection-change="settlementDialog.selection = $event" > - + @@ -206,7 +207,9 @@ @@ -437,7 +440,15 @@ export default { }, async openSettlementDialog() { this.settlementDialog.visible = true; + this.settlementDialog.selection = []; await this.loadSettlementCandidates(); + await this.$nextTick(); + const selectedIds = new Set(this.settlementIds.map(id => String(id))); + this.settlementDialog.rows.forEach(row => { + if (selectedIds.has(String(row.id))) { + this.$refs.settlementTable?.toggleRowSelection(row, true); + } + }); }, async loadSettlementCandidates() { this.settlementDialog.loading = true; @@ -468,11 +479,21 @@ export default { this.$message.warning('关联结算单必须属于同一项目、组织及收付款方'); return; } + const previousAmount = new Map( + this.form.settlements.map(item => [ + String(item.formalSettlementId || item.settlementId), + item.allocatedInvoiceAmount, + ]) + ); this.form.settlements = rows.map(item => ({ ...item, formalSettlementId: item.id, settlementId: item.id, - allocatedInvoiceAmount: rows.length === 1 ? Number(item.settlementAmount || 0) : 0, + allocatedInvoiceAmount: previousAmount.has(String(item.id)) + ? Number(previousAmount.get(String(item.id)) || 0) + : rows.length === 1 + ? Number(item.settlementAmount || 0) + : 0, })); this.form.projectName = first.projectName; this.form.deptName = first.deptName; @@ -482,9 +503,8 @@ export default { await this.loadReferenceInformation(); this.$refs.formRef?.validateField('settlementIds').catch(() => {}); }, - async selectSettlement(row) { - this.settlementDialog.selection = [row]; - await this.confirmSettlements(); + selectSettlement(row) { + this.$refs.settlementTable?.toggleRowSelection(row); }, async loadReferenceInformation() { const data = this.unwrapData(await api.getReferenceInformation(this.settlementIds.join(','))); diff --git a/src/views/payment/payment-application-form.vue b/src/views/payment/payment-application-form.vue index acec661..4c4d340 100644 --- a/src/views/payment/payment-application-form.vue +++ b/src/views/payment/payment-application-form.vue @@ -852,6 +852,8 @@ export default { billOptions: [], billLoading: false, billDialogVisible: false, + settlementPayerName: '', + settlementPayeeName: '', billQuery: { keyword: '', }, @@ -1221,8 +1223,12 @@ export default { callback(); return; } - if (selected.billType && selected.billType !== 'received') { - callback(new Error('汇票付款仅可选择收票类型的汇票')); + if ( + selected.billType && + selected.billType !== 'received' && + !this.billMatchesSettlementParties(selected) + ) { + callback(new Error('汇票付款仅可选择收票类型,或出票单位与收票单位均为当前结算收付款方的汇票')); return; } if (selected.maturityDate) { @@ -1580,6 +1586,7 @@ export default { this.createInvoiceRows(item.invoices || [], item.formalSettlementNo || item.settlementNo) ), }); + this.rememberSettlementParties(first.payerName, first.payeeName); this.paymentRecords = rows.flatMap(item => (item.paymentRecords || item.paymentApplications || []).map(record => ({ ...record, @@ -1667,7 +1674,11 @@ export default { ? 0 : Number(row.payableAmount || 0); return { - id: row.preSettlementId || (isPre ? row.settlementId : null) || row.settlementId || row.id, + id: + row.preSettlementId || + (isPre ? row.settlementId : null) || + row.settlementId || + (row.paymentNo ? null : row.id), settlementNo: row.preSettlementNo || row.formalSettlementNo || row.settlementNo || '', referenceType: isPre ? 'pre' : 'formal', settlementAmount: Number(Number(settlementAmount || 0).toFixed(2)), @@ -1868,6 +1879,7 @@ export default { paymentRatio, appliedAmount: headerAppliedAmount, }); + this.rememberSettlementParties(first.payerName, first.payeeName); this.paymentRecords = []; this.clearReceiptAccount(); await this.fillBillTypeFromContract(first.contractId, true); @@ -2063,12 +2075,31 @@ export default { }, resolveAttachmentMaterial(file, materials = Object.values(ATTACHMENT_MATERIALS)) { if (!this.attachmentFileUrl(file)) return null; - const typeMatched = (materials || []).find( - material => file.attachmentType === MATERIAL_TYPE_MAP[material.key] - ); + const typeText = String(file.attachmentType || file.type || '') + .trim() + .toLocaleLowerCase(); + const typeMatched = (materials || []).find(material => { + if (file.attachmentType === MATERIAL_TYPE_MAP[material.key]) return true; + if (!typeText) return false; + const label = String(material.label || '').toLocaleLowerCase(); + return ( + typeText === label || + material.keywords.some(keyword => { + const normalizedKeyword = String(keyword).toLocaleLowerCase(); + return normalizedKeyword && typeText.includes(normalizedKeyword); + }) + ); + }); return typeMatched || this.matchAttachmentMaterialByFileName(file, materials); }, - mergeAutomaticAttachments(files, materials, sourceName, defaultMaterial = null, sourceKey = '') { + mergeAutomaticAttachments( + files, + materials, + sourceName, + defaultMaterial = null, + sourceKey = '', + keepUnmatched = false + ) { const existingUrls = new Set( (this.form.attachments || []).map(file => this.attachmentFileUrl(file)).filter(Boolean) ); @@ -2077,16 +2108,20 @@ export default { file, material: this.resolveAttachmentMaterial(file, materials) || defaultMaterial, })) - .filter(item => item.material && !existingUrls.has(this.attachmentFileUrl(item.file))) + .filter(item => { + const url = this.attachmentFileUrl(item.file); + return Boolean(url) && !existingUrls.has(url) && (item.material || keepUnmatched); + }) .map(({ file, material }) => { - existingUrls.add(this.attachmentFileUrl(file)); + const url = this.attachmentFileUrl(file); + existingUrls.add(url); return { ...file, originalName: this.attachmentFileName(file), name: this.attachmentFileName(file), - url: this.attachmentFileUrl(file), - link: file.link || this.attachmentFileUrl(file), - attachmentType: MATERIAL_TYPE_MAP[material.key], + url, + link: file.link || url, + attachmentType: material ? MATERIAL_TYPE_MAP[material.key] : 'other', description: file.description || `自动取自${sourceName}`, sourceImported: true, sourceKey: sourceKey || undefined, @@ -2119,11 +2154,7 @@ export default { }, /** 按当前结算单附件整批同步:先清旧关联,再写入新结算单上传的附件 */ syncSettlementAttachments(settlementRows = []) { - const settlementMaterials = [ - ATTACHMENT_MATERIALS.weighingSlip, - ATTACHMENT_MATERIALS.entrustOrder, - ATTACHMENT_MATERIALS.settlement, - ]; + const settlementMaterials = Object.values(ATTACHMENT_MATERIALS); const settlementFiles = (settlementRows || []).flatMap(row => this.parse(row.attachmentsJson)); const nextUrlSet = new Set( settlementFiles.map(file => this.attachmentFileUrl(file)).filter(Boolean) @@ -2140,7 +2171,8 @@ export default { settlementMaterials, '结算单', null, - 'settlement' + 'settlement', + true ); this.linkedSettlementAttachmentUrls = imported .map(file => this.attachmentFileUrl(file)) @@ -2547,7 +2579,13 @@ export default { try { this.billOptions = this.unwrapData( - await billLedgerApi.getAvailableOptions(keyword, this.form.deptId, selectedId) + await billLedgerApi.getAvailableOptions( + keyword, + this.form.deptId, + selectedId, + this.settlementPayerName, + this.settlementPayeeName + ) ) || []; } finally { this.billLoading = false; @@ -2569,7 +2607,9 @@ export default { this.billPage.size, String(this.billQuery.keyword || '').trim() || undefined, this.form.deptId, - this.form.billLedgerId + this.form.billLedgerId, + this.settlementPayerName, + this.settlementPayeeName ); const data = this.unwrapData(response) || {}; this.billRows = data.records || []; @@ -2590,9 +2630,27 @@ export default { this.billPage.current = 1; this.loadBillPage(); }, + rememberSettlementParties(payerName, payeeName) { + this.settlementPayerName = String(payerName || '').trim(); + this.settlementPayeeName = String(payeeName || '').trim(); + }, + billMatchesSettlementParties(row) { + const issuer = String(row?.issuerName || '').trim(); + const receiver = String(row?.receiverName || '').trim(); + const payer = this.settlementPayerName; + const payee = this.settlementPayeeName; + if (!issuer || !receiver || !payer || !payee) return false; + return ( + (issuer === payer && receiver === payee) || (issuer === payee && receiver === payer) + ); + }, selectBill(row) { - if (row?.billType && row.billType !== 'received') { - ElMessage.warning('汇票付款仅可选择收票类型的汇票'); + if ( + row?.billType && + row.billType !== 'received' && + !this.billMatchesSettlementParties(row) + ) { + ElMessage.warning('汇票付款仅可选择收票类型,或出票单位与收票单位均为当前结算收付款方的汇票'); return; } if (row?.maturityDate) { @@ -2848,6 +2906,7 @@ export default { payerName: row.payerName, payeeName: '', }); + this.rememberSettlementParties(row.payerName, row.payeeName); this.referenceVisible = false; this.refreshReferenceValidation(); const detail = this.unwrapData(detailResponse); @@ -2930,6 +2989,7 @@ export default { payerName: row.payerName, payeeName: '', }); + this.rememberSettlementParties(row.payerName, row.payeeName); this.referenceVisible = false; this.refreshReferenceValidation(); const detail = this.unwrapData(detailResponse); @@ -3147,6 +3207,14 @@ export default { : resolvedPreSettlementId ? [resolvedPreSettlementId] : []; + const preSettlementNo = usePreSettlementOnly + ? this.form.preSettlementNo || + (this.referenceRows || []) + .filter(item => item.referenceType === 'pre') + .map(item => item.settlementNo) + .filter(Boolean) + .join('、') + : this.form.preSettlementNo; return { id, paymentType, @@ -3154,6 +3222,7 @@ export default { settlementIds: usePreSettlementOnly ? [] : settlementIds, preSettlementId: usePreSettlementOnly ? resolvedPreSettlementId : preSettlementId, preSettlementIds: usePreSettlementOnly ? resolvedPreSettlementIds : preSettlementIds, + preSettlementNo, projectId, projectName, deptId, diff --git a/src/views/settlement/components/formal-settlement-editor.vue b/src/views/settlement/components/formal-settlement-editor.vue index d90b2b8..2fe8b24 100644 --- a/src/views/settlement/components/formal-settlement-editor.vue +++ b/src/views/settlement/components/formal-settlement-editor.vue @@ -53,7 +53,7 @@ @@ -1070,7 +1070,6 @@ import { import { getDictionary } from '@/api/system/dictbiz'; import { h } from 'vue'; import { mapGetters } from 'vuex'; -import { formatContractOptionLabel } from '@/utils/contract'; import { isTierUnitPrice, isUnitPriceAdjustable } from '@/utils/settlement-adjust-rule'; import { normalizeCurrencyCode } from '@/utils/currency'; import { @@ -1389,7 +1388,6 @@ export default { methods: { isTierUnitPrice, isUnitPriceAdjustable, - formatContractOptionLabel, hasPermission(code) { if (this.isAdmin) return true; const action = String(code || '').replace( diff --git a/src/views/settlement/components/pre-settlement-editor.vue b/src/views/settlement/components/pre-settlement-editor.vue index 6ec7d51..8bda5df 100644 --- a/src/views/settlement/components/pre-settlement-editor.vue +++ b/src/views/settlement/components/pre-settlement-editor.vue @@ -251,29 +251,32 @@ :key="item.key" :label="item.kind === 'fee' ? item.name : item.column.label" :prop="item.kind === 'fee' ? undefined : item.column.prop" - :min-width="item.kind === 'fee' ? 130 : item.column.minWidth" + :min-width="item.kind === 'fee' ? 150 : item.column.minWidth" align="center" show-overflow-tooltip > -