调整收付款问题

This commit is contained in:
2026-08-31 12:40:35 +08:00
parent 2451efdfcb
commit dea760ea1c
12 changed files with 544 additions and 164 deletions
+135 -32
View File
@@ -238,9 +238,19 @@
<el-table-column label="匹配金额(含税)" min-width="150">
<template #default="{ row }">{{ formatMoney(row.matchedAmount) }}</template>
</el-table-column>
<el-table-column v-if="!readonly" label="操作" width="100">
<template #default="{ $index }">
<el-link type="danger" @click="form.invoices.splice($index, 1)">删除</el-link>
<el-table-column label="操作" width="220" fixed="right" align="center">
<template #default="{ row, $index }">
<div class="payment-form-page__invoice-actions">
<el-link type="primary" @click="viewInvoiceAttachment(row)">查看</el-link>
<el-link type="primary" @click="downloadInvoiceAttachment(row)">下载</el-link>
<el-link
v-if="!readonly"
type="danger"
@click="form.invoices.splice($index, 1)"
>
删除
</el-link>
</div>
</template>
</el-table-column>
</el-table>
@@ -333,7 +343,7 @@
>
<div class="payment-form-page__attachment-upload">
<vehicle-attachment-upload
v-model="form.attachments"
:model-value="form.attachments"
:readonly="readonly"
:multiple="true"
:limit="20"
@@ -341,7 +351,7 @@
:file-types="attachmentFileTypes"
:show-file-list="false"
button-text="上传附件"
@change="normalizeAttachments"
@update:model-value="normalizeAttachments"
@success="handleManualAttachmentUpload"
/>
</div>
@@ -598,7 +608,7 @@ const emptyForm = () => ({
settlementAmount: 0,
payableAmount: 0,
billType: '',
paymentRatio: 50,
paymentRatio: null,
appliedAmount: 0,
paymentMethod: 'bank_transfer',
billLedgerId: null,
@@ -661,6 +671,8 @@ export default {
receiptAccountLoading: false,
firstContractPayment: false,
contractSignatureFileAvailable: false,
contractPaymentRatioLimit: null,
contractPaymentRatioExceeded: false,
payeeCustomerLevel: '',
attachmentRuleLoading: false,
attachmentImagePreviewVisible: false,
@@ -794,6 +806,7 @@ export default {
}
},
'form.paymentRatio'(value) {
this.checkContractPaymentRatio(value);
if (this.amountSyncing || !this.paymentAmountBase) return;
this.amountSyncing = true;
this.form.appliedAmount = Number(
@@ -1013,7 +1026,8 @@ export default {
);
},
isValidReferenceId(id) {
return id !== null && id !== undefined && String(id).trim() !== '-1';
const value = String(id ?? '').trim();
return value !== '' && value !== '-1' && value !== '0';
},
async loadTransferredPreSettlements() {
const fallbackRows = this.transferPayload?.sourcePreSettlements || [];
@@ -1246,6 +1260,44 @@ export default {
return [];
}
},
invoiceAttachment(row = {}) {
const value = row.attachmentJson;
if (!value) return {};
if (typeof value === 'object') return Array.isArray(value) ? value[0] || {} : value;
try {
const parsed = JSON.parse(value);
return Array.isArray(parsed) ? parsed[0] || {} : parsed || {};
} catch {
return {};
}
},
invoiceAttachmentUrl(row) {
return this.attachmentFileUrl(this.invoiceAttachment(row));
},
invoiceAttachmentName(row) {
return (
this.attachmentFileName(this.invoiceAttachment(row)) || `${row.invoiceNo || '发票'}.pdf`
);
},
viewInvoiceAttachment(row) {
const attachment = this.invoiceAttachment(row);
if (!this.attachmentFileUrl(attachment)) {
this.$message.warning('当前发票暂无可查看附件');
return;
}
this.previewAttachment(
{ ...attachment, name: this.invoiceAttachmentName(row) },
[attachment]
);
},
downloadInvoiceAttachment(row) {
const url = this.invoiceAttachmentUrl(row);
if (!url) {
this.$message.warning('当前发票暂无可下载附件');
return;
}
downloadFileByUrl(url, this.invoiceAttachmentName(row));
},
attachmentFileName(file) {
return String(file?.originalName || file?.name || file?.fileName || '').trim();
},
@@ -1282,14 +1334,14 @@ export default {
this.attachmentFileExtension(file) === 'pdf'
);
},
previewAttachment(file) {
previewAttachment(file, files = this.form.attachments) {
const url = this.attachmentFileUrl(file);
if (!url) {
this.$message.warning('附件地址为空,无法预览');
return;
}
if (this.isAttachmentImage(file)) {
this.attachmentImagePreviewUrls = (this.form.attachments || [])
this.attachmentImagePreviewUrls = (files || [])
.filter(item => this.isAttachmentImage(item) && this.attachmentFileUrl(item))
.map(item => this.attachmentFileUrl(item));
this.attachmentImagePreviewIndex = Math.max(
@@ -1384,22 +1436,29 @@ export default {
if (imported.length) this.form.attachments = [...this.form.attachments, ...imported];
},
async loadCurrentSettlementDetails() {
if (this.form.paymentType === 'project_advance') return [];
const requests = [];
if (this.isValidReferenceId(this.form.preSettlementId)) {
requests.push(preApi.getDetail(this.form.preSettlementId));
if (
this.form.paymentType === 'progress_advance' &&
this.isValidReferenceId(this.form.preSettlementId)
) {
const response = await preApi.getDetail(this.form.preSettlementId);
const detail = this.unwrapData(response);
return detail?.id ? [detail] : [];
}
if (this.isValidReferenceId(this.form.settlementId)) {
requests.push(formalApi.getDetail(this.form.settlementId));
if (
this.form.paymentType === 'settlement_payment' &&
this.isValidReferenceId(this.form.settlementId)
) {
const response = await formalApi.getDetail(this.form.settlementId);
const detail = this.unwrapData(response);
return detail?.id ? [detail] : [];
}
if (!requests.length) return [];
const responses = await Promise.all(requests);
return responses.map(response => this.unwrapData(response)).filter(item => item?.id);
return [];
},
async loadAttachmentRuleData(settlementRows = []) {
this.attachmentRuleLoading = true;
this.firstContractPayment = false;
this.contractSignatureFileAvailable = false;
this.resetContractPaymentRatioLimit();
try {
const settlementMaterials = [
ATTACHMENT_MATERIALS.weighingSlip,
@@ -1417,6 +1476,7 @@ export default {
const contractResponse = await getContractDetail(this.form.contractId);
const contract = this.unwrapData(contractResponse) || {};
if (this.form.paymentType === 'project_advance') {
this.applyContractPaymentRatioLimit(contract);
const contractFiles = this.parse(contract.contractFileJson);
this.contractSignatureFileAvailable = contractFiles.some(file =>
Boolean(this.attachmentFileUrl(file))
@@ -1453,6 +1513,41 @@ export default {
this.attachmentRuleLoading = false;
}
},
resetContractPaymentRatioLimit() {
this.contractPaymentRatioLimit = null;
this.contractPaymentRatioExceeded = false;
},
applyContractPaymentRatioLimit(contract = {}) {
const [firstRatio] = this.parse(contract.paymentRatioJson);
const ratioLimitValue = firstRatio?.ratioLimit;
const ratioLimit = Number(ratioLimitValue);
this.contractPaymentRatioLimit =
ratioLimitValue === null ||
ratioLimitValue === undefined ||
String(ratioLimitValue).trim() === '' ||
!Number.isFinite(ratioLimit)
? null
: ratioLimit;
this.checkContractPaymentRatio(this.form.paymentRatio);
},
checkContractPaymentRatio(value) {
const paymentRatio = Number(value);
const hasPaymentRatio =
value !== null &&
value !== undefined &&
String(value).trim() !== '' &&
Number.isFinite(paymentRatio);
const exceeded =
this.form.paymentType === 'project_advance' &&
Boolean(this.form.contractId) &&
this.contractPaymentRatioLimit !== null &&
hasPaymentRatio &&
paymentRatio > this.contractPaymentRatioLimit;
if (exceeded && !this.contractPaymentRatioExceeded) {
this.$message.warning('已超合同配置比例');
}
this.contractPaymentRatioExceeded = exceeded;
},
validateRequiredAttachments() {
if (!this.missingAttachmentMaterials.length) return true;
this.$message.warning(
@@ -1949,20 +2044,22 @@ export default {
this.referenceVisible = false;
},
normalizeAttachments(files) {
this.form.attachments = (files || []).map(file => ({
...file,
attachmentType: (() => {
const matchedType = this.resolveAttachmentTypeByFileName(file);
const existingType = [
...Object.values(MATERIAL_TYPE_MAP),
'other',
].includes(file.attachmentType)
? file.attachmentType
: 'other';
return matchedType !== 'other' ? matchedType : existingType;
})(),
description: file.description || '',
}));
const existingAttachments = this.form.attachments || [];
this.form.attachments = (files || []).map(file => {
const fileUrl = this.attachmentFileUrl(file);
const existingFile = existingAttachments.find(item => {
const sameUid = file.uid && item.uid && String(file.uid) === String(item.uid);
const sameUrl = fileUrl && this.attachmentFileUrl(item) === fileUrl;
return sameUid || sameUrl;
});
return {
...existingFile,
...file,
attachmentType:
existingFile?.attachmentType || this.resolveAttachmentTypeByFileName(file),
description: file.description || existingFile?.description || '',
};
});
},
handleManualAttachmentUpload(uploadedFile) {
const uploadedUrl = this.attachmentFileUrl(uploadedFile);
@@ -2206,6 +2303,12 @@ export default {
.payment-form-page__attachment-download {
margin-left: auto;
}
.payment-form-page__invoice-actions {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 8px;
}
.payment-form-page__attachment-file-name-cell {
display: block;
width: 100%;