Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 53aa1f66ef | |||
| 7def9869ff | |||
| f767296e4d | |||
| b879fd3011 | |||
| db55c3ef00 | |||
| af4a9b6d60 |
@@ -23,6 +23,7 @@ export const searchFields = [
|
||||
{ label: '预付结算单号', prop: 'preSettlementNo', type: 'input' },
|
||||
{ label: '正式结算单号', prop: 'formalSettlementNo', type: 'input' },
|
||||
{ label: '批次号', prop: 'batchNo', type: 'input' },
|
||||
{ label: '运单号', prop: 'waybillNo', type: 'input' },
|
||||
{ label: '车号', prop: 'vehicleNo', type: 'input' },
|
||||
];
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -415,7 +415,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)
|
||||
@@ -732,7 +733,6 @@ import {
|
||||
getList as getCustomerArchiveList,
|
||||
} from '@/api/vehicle/customer-archive';
|
||||
import { readSettlementTransfer, removeSettlementTransfer } from '@/utils/settlement-transfer';
|
||||
import { formatContractOptionLabel } from '@/utils/contract';
|
||||
import { downloadFileByUrl } from '@/utils/util';
|
||||
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
|
||||
import {
|
||||
@@ -1140,7 +1140,6 @@ export default {
|
||||
this.initialize();
|
||||
},
|
||||
methods: {
|
||||
formatContractOptionLabel,
|
||||
hasPermission(code) {
|
||||
return this.permission?.[code] !== false;
|
||||
},
|
||||
@@ -2086,10 +2085,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)
|
||||
@@ -2113,35 +2112,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 = []) {
|
||||
@@ -2156,14 +2159,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))
|
||||
@@ -2214,7 +2217,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);
|
||||
@@ -2226,13 +2229,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;
|
||||
}
|
||||
@@ -2246,14 +2250,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) {
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
</el-link>
|
||||
<span v-else-if="item.column.money">{{ formatMoney(row[item.column.prop]) }}</span>
|
||||
<span v-else-if="item.column.prop === 'transportQuantity'">
|
||||
{{ formatDetailTransportQuantity(row[item.column.prop]) }}
|
||||
{{ formatDetailTransportQuantity(row) }}
|
||||
</span>
|
||||
<span v-else-if="item.column.prop === 'mileage'">
|
||||
{{ formatDetailMileage(row[item.column.prop]) }}
|
||||
@@ -362,7 +362,6 @@
|
||||
v-model="row.type"
|
||||
filterable
|
||||
placeholder="请选择类型"
|
||||
@change="sortAttachments"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in attachmentTypeOptions"
|
||||
@@ -1530,7 +1529,6 @@ export default {
|
||||
this.selectedAttachmentRows = [];
|
||||
this.invoices = data.invoices || [];
|
||||
this.issuedInvoices = data.issuedInvoices || [];
|
||||
this.sortAttachments();
|
||||
this.contracts = this.filterContracts(this.allContracts, this.form.projectId);
|
||||
},
|
||||
async initialize() {
|
||||
@@ -3172,13 +3170,13 @@ export default {
|
||||
formatMoney(value) {
|
||||
return `${Number(value || 0).toFixed(2)} RMB`;
|
||||
},
|
||||
formatQuantity(value) {
|
||||
if (value === undefined || value === null || value === '') return '-';
|
||||
formatDetailTransportQuantity(row) {
|
||||
const value = row.transportQuantity;
|
||||
if (this.readonly && Number(value) === -1) return '-';
|
||||
const quantity = Number(value);
|
||||
return Number.isFinite(quantity) ? quantity.toFixed(2) : '-';
|
||||
},
|
||||
formatDetailTransportQuantity(value) {
|
||||
return this.readonly && Number(value) === -1 ? '-' : this.formatQuantity(value);
|
||||
if (!Number.isFinite(quantity)) return '-';
|
||||
// 与预结算编辑器 formatQuantity 保持一致,拼接计量单位
|
||||
return `${quantity.toFixed(2)}${row.quantityUnit ? ` ${row.quantityUnit}` : ''}`;
|
||||
},
|
||||
formatDetailMileage(value) {
|
||||
return this.readonly && Number(value) === -1 ? '-' : this.displayValue(value);
|
||||
@@ -3235,7 +3233,6 @@ export default {
|
||||
label: item.dictValue,
|
||||
value: item.dictValue,
|
||||
}));
|
||||
this.sortAttachments();
|
||||
},
|
||||
resolveAttachmentType(fileName) {
|
||||
const normalizedName = String(fileName || '').toLocaleLowerCase();
|
||||
@@ -3259,25 +3256,6 @@ export default {
|
||||
const typeText = String(item?.label || item?.value || '').trim();
|
||||
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) {
|
||||
const uploadedTypes = new Set(
|
||||
(files || [])
|
||||
@@ -3307,7 +3285,6 @@ export default {
|
||||
size: this.formatAttachmentSize(file.size),
|
||||
url: file.url || file.link || '',
|
||||
});
|
||||
this.sortAttachments();
|
||||
},
|
||||
handleAttachmentSelectionChange(rows) {
|
||||
this.selectedAttachmentRows = rows || [];
|
||||
|
||||
@@ -371,7 +371,6 @@
|
||||
v-model="row.type"
|
||||
filterable
|
||||
placeholder="请选择"
|
||||
@change="sortAttachments"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in attachmentTypeOptions"
|
||||
@@ -1573,7 +1572,6 @@ export default {
|
||||
row => !isApprovalSettlementChange(row.changeContent)
|
||||
);
|
||||
this.attachments = this.parseAttachments(detail.attachmentsJson);
|
||||
this.sortAttachments();
|
||||
this.selectedAttachmentRows = [];
|
||||
if (detail.contractId) {
|
||||
await this.ensureContractPartyOptions(detail.contractId, {
|
||||
@@ -3099,7 +3097,6 @@ export default {
|
||||
uploadTime: existing?.uploadTime || file.uploadTime || uploadTime,
|
||||
};
|
||||
});
|
||||
this.sortAttachments();
|
||||
this.selectedAttachmentRows = [];
|
||||
},
|
||||
async loadAttachmentTypeOptions() {
|
||||
@@ -3115,7 +3112,6 @@ export default {
|
||||
} catch (error) {
|
||||
this.attachmentTypeOptions = [];
|
||||
}
|
||||
this.sortAttachments();
|
||||
},
|
||||
resolveAttachmentType(fileName) {
|
||||
const normalizedName = String(fileName || '').toLocaleLowerCase();
|
||||
@@ -3143,25 +3139,6 @@ export default {
|
||||
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) {
|
||||
const option = this.attachmentTypeOptions.find(
|
||||
item => String(item.value) === String(type) || String(item.label) === String(type)
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
<div class="settlement-detail-page__detail-panel-body">
|
||||
<el-tabs v-model="detailDialog.activeTab" @tab-change="handleDetailTabChange">
|
||||
<el-tab-pane label="费用明细" name="fee">
|
||||
<el-table v-loading="detailDialog.loading" :data="feeRows" border>
|
||||
<el-table v-loading="detailDialog.loading" :data="feeRows" :max-height="detailTableMaxHeight" border>
|
||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||
<el-table-column
|
||||
v-for="column in feeDetailColumns"
|
||||
@@ -168,7 +168,7 @@
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="变更记录" name="change">
|
||||
<el-table v-loading="latestChangeLoading" :data="latestChangeRows" border>
|
||||
<el-table v-loading="latestChangeLoading" :data="latestChangeRows" :max-height="detailTableMaxHeight" border>
|
||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||
<el-table-column
|
||||
v-for="column in changeRecordColumns"
|
||||
@@ -1063,7 +1063,6 @@ import { getContractOptions as getSettlementContractOptions } from '@/api/settle
|
||||
import { getList as getCargoTypeList } from '@/api/base/cargo-type';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { getDictionary } from '@/api/system/dictbiz';
|
||||
import { formatContractOptionLabel } from '@/utils/contract';
|
||||
import { isTierUnitPrice, isUnitPriceAdjustable } from '@/utils/settlement-adjust-rule';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { createSettlementTransfer } from '@/utils/settlement-transfer';
|
||||
@@ -1145,8 +1144,6 @@ export default {
|
||||
rule: null,
|
||||
},
|
||||
adjustDialog: { visible: false, loading: false, submitting: false, row: null },
|
||||
// 表头 + 最多 3 行费用 + 横向滚动条,超出后表体上下滚动
|
||||
adjustTableMaxHeight: 40 + 49 * 3 + 16,
|
||||
adjustRows: [],
|
||||
adjustDynamicFeeColumns: [],
|
||||
adjustTextProps: ['specification', 'model', 'billingFactor', 'billingType'],
|
||||
@@ -1224,18 +1221,28 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
adjustTableMaxHeight() {
|
||||
// 面板满高 min(60vh,560) 减去头部(49)、底部(58)、内边距(32);费用行多时表体内部滚动,表头固定
|
||||
if (!this.adjustDialog.visible) return 40 + 49 * 3 + 16;
|
||||
return Math.round(Math.min(window.innerHeight * 0.6, 560)) - 138;
|
||||
},
|
||||
detailTableMaxHeight() {
|
||||
// 同上,另减 body 下内边距(16) 和 tabs 头(55)
|
||||
if (!this.detailDialog.visible) return 40 + 49 * 3 + 16;
|
||||
return Math.round(Math.min(window.innerHeight * 0.6, 560)) - 178;
|
||||
},
|
||||
generateContractName() {
|
||||
const contract = this.contractOptions.find(
|
||||
item => String(item.id) === String(this.generateQuery.contractId)
|
||||
);
|
||||
return formatContractOptionLabel(contract);
|
||||
return contract?.contractName || '';
|
||||
},
|
||||
updateContractName() {
|
||||
const contractId = this.updateFeeForm.contractId;
|
||||
const contract = [...this.updateContractOptions, ...this.contractOptions].find(
|
||||
item => String(item.id) === String(contractId)
|
||||
);
|
||||
return formatContractOptionLabel(contract);
|
||||
return contract?.contractName || '';
|
||||
},
|
||||
updateBillingPlanName() {
|
||||
const billingPlanId = this.updateFeeForm.billingPlanId;
|
||||
|
||||
Reference in New Issue
Block a user