fix: 附件同步不再自动填充描述,调整附件类型不再重排,回显保持原位置

This commit is contained in:
刘泉佳
2026-11-06 14:06:01 +08:00
parent 7def9869ff
commit 53aa1f66ef
3 changed files with 35 additions and 74 deletions
+35 -28
View File
@@ -415,7 +415,8 @@
><el-input ><el-input
v-model="row.description" v-model="row.description"
:disabled="readonly" :disabled="readonly"
maxlength="200" /></template></el-table-column maxlength="200"
placeholder="请输入" /></template></el-table-column
><el-table-column label="文件大小" width="120" ><el-table-column label="文件大小" width="120"
><template #default="{ row }">{{ ><template #default="{ row }">{{
formatAttachmentSize(row) formatAttachmentSize(row)
@@ -2084,10 +2085,10 @@ export default {
mergeAutomaticAttachments( mergeAutomaticAttachments(
files, files,
materials, materials,
sourceName,
defaultMaterial = null, defaultMaterial = null,
sourceKey = '', sourceKey = '',
keepUnmatched = false keepUnmatched = false,
insertIndex = -1
) { ) {
const existingUrls = new Set( const existingUrls = new Set(
(this.form.attachments || []).map(file => this.attachmentFileUrl(file)).filter(Boolean) (this.form.attachments || []).map(file => this.attachmentFileUrl(file)).filter(Boolean)
@@ -2111,35 +2112,39 @@ export default {
url, url,
link: file.link || url, link: file.link || url,
attachmentType: material ? MATERIAL_TYPE_MAP[material.key] : 'other', attachmentType: material ? MATERIAL_TYPE_MAP[material.key] : 'other',
description: file.description || `自动取自${sourceName}`, description: file.description || '',
sourceImported: true, sourceImported: true,
sourceKey: sourceKey || undefined, 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; return imported;
}, },
/** 移除指定来源的自动关联附件(切换结算单/合同时整批替换) */ /** 移除指定来源的自动关联附件(切换结算单/合同时整批替换),返回首个被移除的位置 */
removeAutomaticAttachments(sourceKey, sourceName = '') { removeAutomaticAttachments(sourceKey) {
const previousUrls = new Set( const previousUrls = new Set(
sourceKey === 'settlement' ? this.linkedSettlementAttachmentUrls || [] : [] 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); const url = this.attachmentFileUrl(file);
if (sourceKey && file.sourceKey === sourceKey) return false; const keep =
if ( !(sourceKey && file.sourceKey === sourceKey) &&
sourceName && !(previousUrls.size && url && previousUrls.has(url));
file.sourceImported && if (!keep && firstRemovedIndex === -1) firstRemovedIndex = index;
String(file.description || '').includes(`自动取自${sourceName}`) return keep;
) {
return false;
}
if (previousUrls.size && url && previousUrls.has(url)) return false;
return true;
}); });
if (sourceKey === 'settlement') { if (sourceKey === 'settlement') {
this.linkedSettlementAttachmentUrls = []; this.linkedSettlementAttachmentUrls = [];
} }
return firstRemovedIndex;
}, },
/** 按当前结算单附件整批同步:先清旧关联,再写入新结算单上传的附件 */ /** 按当前结算单附件整批同步:先清旧关联,再写入新结算单上传的附件 */
syncSettlementAttachments(settlementRows = []) { syncSettlementAttachments(settlementRows = []) {
@@ -2154,14 +2159,14 @@ export default {
.map(file => this.attachmentFileUrl(file)) .map(file => this.attachmentFileUrl(file))
.filter(url => url && nextUrlSet.has(url)); .filter(url => url && nextUrlSet.has(url));
} }
this.removeAutomaticAttachments('settlement', '结算单'); const removedAt = this.removeAutomaticAttachments('settlement');
const imported = this.mergeAutomaticAttachments( const imported = this.mergeAutomaticAttachments(
settlementFiles, settlementFiles,
settlementMaterials, settlementMaterials,
'结算单',
null, null,
'settlement', 'settlement',
true true,
removedAt
); );
this.linkedSettlementAttachmentUrls = imported this.linkedSettlementAttachmentUrls = imported
.map(file => this.attachmentFileUrl(file)) .map(file => this.attachmentFileUrl(file))
@@ -2212,7 +2217,7 @@ export default {
this.syncSettlementAttachments(settlementRows); this.syncSettlementAttachments(settlementRows);
if (!this.form.contractId) { if (!this.form.contractId) {
this.firstContractPayment = false; this.firstContractPayment = false;
this.removeAutomaticAttachments('contract', '合同'); this.removeAutomaticAttachments('contract');
return; return;
} }
const contractResponse = await getContractDetail(this.form.contractId); const contractResponse = await getContractDetail(this.form.contractId);
@@ -2224,13 +2229,14 @@ export default {
this.contractSignatureFileAvailable = contractFiles.some(file => this.contractSignatureFileAvailable = contractFiles.some(file =>
Boolean(this.attachmentFileUrl(file)) Boolean(this.attachmentFileUrl(file))
); );
this.removeAutomaticAttachments('contract', '合同'); const contractRemovedAt = this.removeAutomaticAttachments('contract');
this.mergeAutomaticAttachments( this.mergeAutomaticAttachments(
contractFiles, contractFiles,
[ATTACHMENT_MATERIALS.contract], [ATTACHMENT_MATERIALS.contract],
'合同',
ATTACHMENT_MATERIALS.contract, ATTACHMENT_MATERIALS.contract,
'contract' 'contract',
false,
contractRemovedAt
); );
return; return;
} }
@@ -2244,14 +2250,15 @@ export default {
String(item.id || '') !== String(this.form.id || '') && String(item.id || '') !== String(this.form.id || '') &&
item.approvalStatus !== 'voided' item.approvalStatus !== 'voided'
); );
this.removeAutomaticAttachments('contract', '合同'); const contractRemovedAt = this.removeAutomaticAttachments('contract');
if (this.firstContractPayment) { if (this.firstContractPayment) {
this.mergeAutomaticAttachments( this.mergeAutomaticAttachments(
this.parse(contract.contractFileJson), this.parse(contract.contractFileJson),
[ATTACHMENT_MATERIALS.contract], [ATTACHMENT_MATERIALS.contract],
'合同',
ATTACHMENT_MATERIALS.contract, ATTACHMENT_MATERIALS.contract,
'contract' 'contract',
false,
contractRemovedAt
); );
} }
} catch (error) { } catch (error) {
@@ -362,7 +362,6 @@
v-model="row.type" v-model="row.type"
filterable filterable
placeholder="请选择类型" placeholder="请选择类型"
@change="sortAttachments"
> >
<el-option <el-option
v-for="item in attachmentTypeOptions" v-for="item in attachmentTypeOptions"
@@ -1530,7 +1529,6 @@ export default {
this.selectedAttachmentRows = []; this.selectedAttachmentRows = [];
this.invoices = data.invoices || []; this.invoices = data.invoices || [];
this.issuedInvoices = data.issuedInvoices || []; this.issuedInvoices = data.issuedInvoices || [];
this.sortAttachments();
this.contracts = this.filterContracts(this.allContracts, this.form.projectId); this.contracts = this.filterContracts(this.allContracts, this.form.projectId);
}, },
async initialize() { async initialize() {
@@ -3235,7 +3233,6 @@ export default {
label: item.dictValue, label: item.dictValue,
value: item.dictValue, value: item.dictValue,
})); }));
this.sortAttachments();
}, },
resolveAttachmentType(fileName) { resolveAttachmentType(fileName) {
const normalizedName = String(fileName || '').toLocaleLowerCase(); const normalizedName = String(fileName || '').toLocaleLowerCase();
@@ -3259,25 +3256,6 @@ export default {
const typeText = String(item?.label || item?.value || '').trim(); const typeText = String(item?.label || item?.value || '').trim();
return typeText === '其他' || typeText === '其他附件'; return typeText === '其他' || typeText === '其他附件';
}, },
getAttachmentTypeOrder(type) {
const normalizedType = String(type || '').trim();
const index = this.attachmentTypeOptions.findIndex(
item =>
String(item.value || '').trim() === normalizedType ||
String(item.label || '').trim() === normalizedType
);
return index === -1 ? Number.MAX_SAFE_INTEGER : index;
},
sortAttachments() {
this.attachments = (this.attachments || [])
.map((file, index) => ({ file, index }))
.sort((a, b) => {
const orderDifference =
this.getAttachmentTypeOrder(a.file.type) - this.getAttachmentTypeOrder(b.file.type);
return orderDifference || a.index - b.index;
})
.map(item => item.file);
},
getMissingAttachmentTypes(files = this.attachments) { getMissingAttachmentTypes(files = this.attachments) {
const uploadedTypes = new Set( const uploadedTypes = new Set(
(files || []) (files || [])
@@ -3307,7 +3285,6 @@ export default {
size: this.formatAttachmentSize(file.size), size: this.formatAttachmentSize(file.size),
url: file.url || file.link || '', url: file.url || file.link || '',
}); });
this.sortAttachments();
}, },
handleAttachmentSelectionChange(rows) { handleAttachmentSelectionChange(rows) {
this.selectedAttachmentRows = rows || []; this.selectedAttachmentRows = rows || [];
@@ -371,7 +371,6 @@
v-model="row.type" v-model="row.type"
filterable filterable
placeholder="请选择" placeholder="请选择"
@change="sortAttachments"
> >
<el-option <el-option
v-for="item in attachmentTypeOptions" v-for="item in attachmentTypeOptions"
@@ -1573,7 +1572,6 @@ export default {
row => !isApprovalSettlementChange(row.changeContent) row => !isApprovalSettlementChange(row.changeContent)
); );
this.attachments = this.parseAttachments(detail.attachmentsJson); this.attachments = this.parseAttachments(detail.attachmentsJson);
this.sortAttachments();
this.selectedAttachmentRows = []; this.selectedAttachmentRows = [];
if (detail.contractId) { if (detail.contractId) {
await this.ensureContractPartyOptions(detail.contractId, { await this.ensureContractPartyOptions(detail.contractId, {
@@ -3099,7 +3097,6 @@ export default {
uploadTime: existing?.uploadTime || file.uploadTime || uploadTime, uploadTime: existing?.uploadTime || file.uploadTime || uploadTime,
}; };
}); });
this.sortAttachments();
this.selectedAttachmentRows = []; this.selectedAttachmentRows = [];
}, },
async loadAttachmentTypeOptions() { async loadAttachmentTypeOptions() {
@@ -3115,7 +3112,6 @@ export default {
} catch (error) { } catch (error) {
this.attachmentTypeOptions = []; this.attachmentTypeOptions = [];
} }
this.sortAttachments();
}, },
resolveAttachmentType(fileName) { resolveAttachmentType(fileName) {
const normalizedName = String(fileName || '').toLocaleLowerCase(); const normalizedName = String(fileName || '').toLocaleLowerCase();
@@ -3143,25 +3139,6 @@ export default {
typeText === '其它附件' typeText === '其它附件'
); );
}, },
getAttachmentTypeOrder(type) {
const normalizedType = String(type || '').trim();
const index = this.attachmentTypeOptions.findIndex(
item =>
String(item.value || '').trim() === normalizedType ||
String(item.label || '').trim() === normalizedType
);
return index === -1 ? Number.MAX_SAFE_INTEGER : index;
},
sortAttachments() {
this.attachments = (this.attachments || [])
.map((file, index) => ({ file, index }))
.sort((a, b) => {
const orderDifference =
this.getAttachmentTypeOrder(a.file.type) - this.getAttachmentTypeOrder(b.file.type);
return orderDifference || a.index - b.index;
})
.map(item => item.file);
},
attachmentTypeName(type) { attachmentTypeName(type) {
const option = this.attachmentTypeOptions.find( const option = this.attachmentTypeOptions.find(
item => String(item.value) === String(type) || String(item.label) === String(type) item => String(item.value) === String(type) || String(item.label) === String(type)