🔀 合并 feature/20261106-fix禅道-web,解决付款申请与正式结算单冲突

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-10 16:54:33 +08:00
co-authored by Cursor
5 changed files with 77 additions and 100 deletions
+36 -31
View File
@@ -97,7 +97,7 @@
<el-option
v-for="item in contractOptions"
:key="item.id"
:label="formatContractOptionLabel(item)"
:label="item.contractName || item.contractNo"
:value="item.id"
/>
</el-select>
@@ -427,7 +427,8 @@
><el-input
v-model="row.description"
:disabled="readonly"
maxlength="200" /></template></el-table-column
maxlength="200"
placeholder="请输入" /></template></el-table-column
><el-table-column label="文件大小" width="120"
><template #default="{ row }">{{
formatAttachmentSize(row)
@@ -744,7 +745,6 @@ import {
getList as getCustomerArchiveList,
} from '@/api/vehicle/customer-archive';
import { readSettlementTransfer, removeSettlementTransfer } from '@/utils/settlement-transfer';
import { formatContractOptionLabel } from '@/utils/contract';
import { downloadFile, downloadFileByUrl } from '@/utils/util';
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
import {
@@ -1155,7 +1155,6 @@ export default {
this.initialize();
},
methods: {
formatContractOptionLabel,
hasPermission(code) {
return this.permission?.[code] !== false;
},
@@ -2126,10 +2125,10 @@ export default {
mergeAutomaticAttachments(
files,
materials,
sourceName,
defaultMaterial = null,
sourceKey = '',
keepUnmatched = false
keepUnmatched = false,
insertIndex = -1
) {
const existingUrls = new Set(
(this.form.attachments || []).map(file => this.attachmentFileUrl(file)).filter(Boolean)
@@ -2153,35 +2152,39 @@ export default {
url,
link: file.link || url,
attachmentType: material ? MATERIAL_TYPE_MAP[material.key] : 'other',
description: file.description || `自动取自${sourceName}`,
description: file.description || '',
sourceImported: true,
sourceKey: sourceKey || undefined,
};
});
if (imported.length) this.form.attachments = [...this.form.attachments, ...imported];
if (imported.length) {
// 回填到被替换附件的原起始位置,避免回显/切换时把用户新传附件顶到前面
const attachments = [...this.form.attachments];
const insertAt =
insertIndex >= 0 ? Math.min(insertIndex, attachments.length) : attachments.length;
attachments.splice(insertAt, 0, ...imported);
this.form.attachments = attachments;
}
return imported;
},
/** 移除指定来源的自动关联附件(切换结算单/合同时整批替换) */
removeAutomaticAttachments(sourceKey, sourceName = '') {
/** 移除指定来源的自动关联附件(切换结算单/合同时整批替换),返回首个被移除的位置 */
removeAutomaticAttachments(sourceKey) {
const previousUrls = new Set(
sourceKey === 'settlement' ? this.linkedSettlementAttachmentUrls || [] : []
);
this.form.attachments = (this.form.attachments || []).filter(file => {
let firstRemovedIndex = -1;
this.form.attachments = (this.form.attachments || []).filter((file, index) => {
const url = this.attachmentFileUrl(file);
if (sourceKey && file.sourceKey === sourceKey) return false;
if (
sourceName &&
file.sourceImported &&
String(file.description || '').includes(`自动取自${sourceName}`)
) {
return false;
}
if (previousUrls.size && url && previousUrls.has(url)) return false;
return true;
const keep =
!(sourceKey && file.sourceKey === sourceKey) &&
!(previousUrls.size && url && previousUrls.has(url));
if (!keep && firstRemovedIndex === -1) firstRemovedIndex = index;
return keep;
});
if (sourceKey === 'settlement') {
this.linkedSettlementAttachmentUrls = [];
}
return firstRemovedIndex;
},
/** 按当前结算单附件整批同步:先清旧关联,再写入新结算单上传的附件 */
syncSettlementAttachments(settlementRows = []) {
@@ -2196,14 +2199,14 @@ export default {
.map(file => this.attachmentFileUrl(file))
.filter(url => url && nextUrlSet.has(url));
}
this.removeAutomaticAttachments('settlement', '结算单');
const removedAt = this.removeAutomaticAttachments('settlement');
const imported = this.mergeAutomaticAttachments(
settlementFiles,
settlementMaterials,
'结算单',
null,
'settlement',
true
true,
removedAt
);
this.linkedSettlementAttachmentUrls = imported
.map(file => this.attachmentFileUrl(file))
@@ -2254,7 +2257,7 @@ export default {
this.syncSettlementAttachments(settlementRows);
if (!this.form.contractId) {
this.firstContractPayment = false;
this.removeAutomaticAttachments('contract', '合同');
this.removeAutomaticAttachments('contract');
return;
}
const contractResponse = await getContractDetail(this.form.contractId);
@@ -2266,13 +2269,14 @@ export default {
this.contractSignatureFileAvailable = contractFiles.some(file =>
Boolean(this.attachmentFileUrl(file))
);
this.removeAutomaticAttachments('contract', '合同');
const contractRemovedAt = this.removeAutomaticAttachments('contract');
this.mergeAutomaticAttachments(
contractFiles,
[ATTACHMENT_MATERIALS.contract],
'合同',
ATTACHMENT_MATERIALS.contract,
'contract'
'contract',
false,
contractRemovedAt
);
return;
}
@@ -2286,14 +2290,15 @@ export default {
String(item.id || '') !== String(this.form.id || '') &&
item.approvalStatus !== 'voided'
);
this.removeAutomaticAttachments('contract', '合同');
const contractRemovedAt = this.removeAutomaticAttachments('contract');
if (this.firstContractPayment) {
this.mergeAutomaticAttachments(
this.parse(contract.contractFileJson),
[ATTACHMENT_MATERIALS.contract],
'合同',
ATTACHMENT_MATERIALS.contract,
'contract'
'contract',
false,
contractRemovedAt
);
}
} catch (error) {