⚡ 修复运输计划/发货模板列表缓存不刷新,并优化结算导入查看与费用弹窗
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -135,7 +135,7 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="结算单上传" prop="file">
|
||||
<el-form-item v-if="!readonly" label="结算单上传" prop="file">
|
||||
<div class="settlement-import-create__upload">
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
@@ -143,13 +143,13 @@
|
||||
accept=".xls,.xlsx"
|
||||
:auto-upload="false"
|
||||
:limit="1"
|
||||
:disabled="readonly || !formReady"
|
||||
:disabled="!formReady"
|
||||
:file-list="files"
|
||||
:on-change="onFileChange"
|
||||
:on-exceed="onFileExceed"
|
||||
:on-remove="onFileRemove"
|
||||
>
|
||||
<el-button type="primary" :disabled="readonly || !formReady">上传 Excel</el-button>
|
||||
<el-button type="primary" :disabled="!formReady">上传 Excel</el-button>
|
||||
</el-upload>
|
||||
<el-link type="primary" :underline="false" @click="downloadTemplate">
|
||||
下载模板
|
||||
@@ -298,15 +298,10 @@
|
||||
</el-table>
|
||||
</section>
|
||||
|
||||
<div class="settlement-import-create__footer">
|
||||
<div v-if="!readonly" class="settlement-import-create__footer">
|
||||
<el-button @click="goBack">取消</el-button>
|
||||
<el-button v-if="!readonly" :loading="draftLoading" @click="saveDraft">保存草稿</el-button>
|
||||
<el-button
|
||||
v-if="!readonly"
|
||||
type="primary"
|
||||
:loading="confirmLoading"
|
||||
@click="confirmImport"
|
||||
>
|
||||
<el-button :loading="draftLoading" @click="saveDraft">保存草稿</el-button>
|
||||
<el-button type="primary" :loading="confirmLoading" @click="confirmImport">
|
||||
确认导入
|
||||
</el-button>
|
||||
</div>
|
||||
@@ -577,6 +572,8 @@ const syncEditedText = (row, column, value) => {
|
||||
if (column.prop !== 'vehicleNo') return;
|
||||
applyVehicleNo(row, value);
|
||||
};
|
||||
|
||||
const feeColumn = name => ({
|
||||
prop: name,
|
||||
label: name,
|
||||
editor: 'number',
|
||||
@@ -1411,12 +1408,18 @@ const loadDetail = async id => {
|
||||
const detailRows = Array.isArray(detail.rows) ? detail.rows : [];
|
||||
rows.value = detailRows.map((item, index) => normalizeParsedRow(item, index));
|
||||
refreshDuplicateFlags();
|
||||
readonly.value = detail.importStatus && detail.importStatus !== 'draft';
|
||||
// 查看模式始终只读;编辑时仅草稿可改,已完成/已作废强制只读
|
||||
readonly.value =
|
||||
route.query.mode === 'view' ||
|
||||
Boolean(detail.importStatus && detail.importStatus !== 'draft');
|
||||
};
|
||||
|
||||
const initPage = async () => {
|
||||
await loadOptions();
|
||||
const id = route.query.id;
|
||||
if (route.query.mode === 'view') {
|
||||
readonly.value = true;
|
||||
}
|
||||
if (id) {
|
||||
await loadDetail(id);
|
||||
return;
|
||||
|
||||
@@ -450,32 +450,48 @@
|
||||
class="settlement-detail-page__dialog-form"
|
||||
>
|
||||
<el-form-item label="更新范围" prop="contractId">
|
||||
<el-input
|
||||
:model-value="updateContractName"
|
||||
class="settlement-detail-page__generate-select"
|
||||
readonly
|
||||
placeholder="请选择合同"
|
||||
<el-tooltip
|
||||
:content="updateContractName"
|
||||
:disabled="!updateContractName"
|
||||
placement="top"
|
||||
:show-after="200"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="openUpdateContractDialog">选择</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-input
|
||||
:model-value="updateContractName"
|
||||
class="settlement-detail-page__update-fee-control"
|
||||
readonly
|
||||
placeholder="请选择合同"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="openUpdateContractDialog">选择</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
<el-form-item label="合同计费方案" prop="billingPlanId">
|
||||
<el-select
|
||||
v-model="updateFeeForm.billingPlanId"
|
||||
clearable
|
||||
filterable
|
||||
:disabled="!updateFeeForm.contractId"
|
||||
placeholder="请选择"
|
||||
<el-tooltip
|
||||
:content="updateBillingPlanName"
|
||||
:disabled="!updateBillingPlanName"
|
||||
placement="top"
|
||||
:show-after="200"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in billingPlanOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-model="updateFeeForm.billingPlanId"
|
||||
class="settlement-detail-page__update-fee-control"
|
||||
clearable
|
||||
filterable
|
||||
:disabled="!updateFeeForm.contractId"
|
||||
placeholder="请选择"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in billingPlanOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
:title="item.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -1203,6 +1219,14 @@ export default {
|
||||
);
|
||||
return formatContractOptionLabel(contract);
|
||||
},
|
||||
updateBillingPlanName() {
|
||||
const billingPlanId = this.updateFeeForm.billingPlanId;
|
||||
if (!billingPlanId) return '';
|
||||
const plan = this.billingPlanOptions.find(
|
||||
item => String(item.id) === String(billingPlanId)
|
||||
);
|
||||
return plan?.name || '';
|
||||
},
|
||||
generateContractCategory() {
|
||||
return this.settlementType === 'payable' ? '承运商合同' : '客户合同';
|
||||
},
|
||||
@@ -3144,11 +3168,24 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-form-item__content) {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
:deep(.el-tooltip__trigger) {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.el-select) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.settlement-detail-page__update-fee-control {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.settlement-detail-page__adjust-input {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
|
||||
@@ -11,7 +11,10 @@ import { useRoute } from 'vue-router';
|
||||
import SettlementImportPage from './components/settlement-import-page.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const pageTitle = computed(() => (route.query.id ? '编辑导入结算单' : '新增导入结算单'));
|
||||
const pageTitle = computed(() => {
|
||||
if (route.query.mode === 'view') return '查看导入结算单';
|
||||
return route.query.id ? '编辑导入结算单' : '新增导入结算单';
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -129,97 +129,6 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<el-dialog
|
||||
v-model="detailVisible"
|
||||
title="导入结算单详情"
|
||||
width="1100px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
class="settlement-import-detail-dialog"
|
||||
>
|
||||
<el-descriptions :column="3" border>
|
||||
<el-descriptions-item label="导入批次号">{{ detail.batchNo || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
{{ detail.importStatusName || importStatusLabel(detail.importStatus) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="导入类型">
|
||||
{{ detail.settlementTypeName || settlementTypeLabel(detail.settlementType) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="结算单">
|
||||
{{ detail.settlementBillTypeName || settlementBillTypeLabel(detail.settlementBillType) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="运单类型">
|
||||
{{ detail.waybillModeName || waybillModeLabel(detail.waybillMode) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="明细行数">
|
||||
{{ detail.detailCount ?? detail.waybillCount ?? detailRows.length }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="项目">{{ detail.projectName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户">{{ detail.customerName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户合同">
|
||||
{{ detail.customerContractName || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="承运商">{{ detail.carrierName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="承运商合同">
|
||||
{{ detail.carrierContractName || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人">{{ detail.createUserName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ detail.createTime || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="2">{{ detail.remark || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="关联结算单" :span="3">
|
||||
<el-link
|
||||
v-if="detail.formalSettlementId"
|
||||
type="primary"
|
||||
@click="goFormalSettlement(detail)"
|
||||
>
|
||||
{{ detail.formalSettlementNo || detail.formalSettlementId }}
|
||||
</el-link>
|
||||
<el-link
|
||||
v-else-if="detail.preSettlementId"
|
||||
type="primary"
|
||||
@click="goPreSettlement(detail)"
|
||||
>
|
||||
{{ detail.preSettlementNo || detail.preSettlementId }}
|
||||
</el-link>
|
||||
<span v-else>-</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div class="dialog-section-title settlement-import-page__detail-title">导入明细</div>
|
||||
<el-table
|
||||
v-loading="detailLoading"
|
||||
:data="detailRows"
|
||||
border
|
||||
max-height="420"
|
||||
class="settlement-import-page__table"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||
<el-table-column
|
||||
v-for="column in detailDisplayColumns"
|
||||
:key="column.prop"
|
||||
:prop="column.fee ? undefined : column.prop"
|
||||
:label="column.label"
|
||||
:min-width="column.minWidth || 120"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ column.fee ? formatFeeAmount(row, column.prop) : formatDetailCell(row, column) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="detailVisible = false">关闭</el-button>
|
||||
<el-button
|
||||
v-if="detail.importStatus === 'draft'"
|
||||
type="primary"
|
||||
@click="editDraft(detail)"
|
||||
>
|
||||
编辑草稿
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="voidVisible" title="作废导入结算单" width="480px" append-to-body>
|
||||
<el-form
|
||||
ref="voidFormRef"
|
||||
@@ -289,11 +198,6 @@ export default {
|
||||
rows: [],
|
||||
page: { current: 1, size: 10, total: 0 },
|
||||
batchNoOptions: [],
|
||||
detailVisible: false,
|
||||
detailLoading: false,
|
||||
detail: {},
|
||||
detailRows: [],
|
||||
detailFeeItemNames: [],
|
||||
voidVisible: false,
|
||||
voidLoading: false,
|
||||
voidForm: { id: null, reason: '' },
|
||||
@@ -339,52 +243,6 @@ export default {
|
||||
visibleSearchFields() {
|
||||
return this.searchExpanded ? this.searchFields : this.searchFields.slice(0, 4);
|
||||
},
|
||||
detailDisplayColumns() {
|
||||
const before = [
|
||||
{ prop: 'originalNo', label: '原始单号', minWidth: 130 },
|
||||
{ prop: 'loadingIdentifier', label: '配载标识号', minWidth: 140 },
|
||||
{ prop: 'vehicleNo', label: '车牌号/航班号/船号/班列号', minWidth: 190 },
|
||||
{ prop: 'transportType', label: '运输方式', minWidth: 120 },
|
||||
{ prop: 'driverName', label: '司机/船长姓名', minWidth: 130 },
|
||||
{ prop: 'driverPhone', label: '司机/船长手机号', minWidth: 150 },
|
||||
{ prop: 'departureAddress', label: '发货地址', minWidth: 160 },
|
||||
{ prop: 'departureContact', label: '发货联系人', minWidth: 120 },
|
||||
{ prop: 'departurePhone', label: '发货联系人电话', minWidth: 140 },
|
||||
{ prop: 'arrivalAddress', label: '到货地址', minWidth: 160 },
|
||||
{ prop: 'arrivalContact', label: '收货联系人', minWidth: 120 },
|
||||
{ prop: 'arrivalPhone', label: '收货联系人电话', minWidth: 140 },
|
||||
{ prop: 'cargoName', label: '货物名称', minWidth: 140 },
|
||||
{ prop: 'cargoType', label: '货物类型', minWidth: 120 },
|
||||
{ prop: 'packageType', label: '包装', minWidth: 100 },
|
||||
{ prop: 'quantity', label: '数量', minWidth: 90 },
|
||||
{ prop: 'quantityUnit', label: '数量单位', minWidth: 100 },
|
||||
{ prop: 'specification', label: '规格', minWidth: 110 },
|
||||
{ prop: 'model', label: '型号', minWidth: 110 },
|
||||
{ prop: 'mileage', label: '里程(km)', minWidth: 110 },
|
||||
{ prop: 'unitPrice', label: '运输单价', minWidth: 110 },
|
||||
{ prop: 'transportFee', label: '运输费', minWidth: 110, money: true },
|
||||
];
|
||||
const fees = (this.detailFeeItemNames || []).map(name => ({
|
||||
prop: name,
|
||||
label: name,
|
||||
minWidth: 110,
|
||||
fee: true,
|
||||
}));
|
||||
const after = [
|
||||
{ prop: 'settlementAmount', label: '结算金额', minWidth: 120, money: true },
|
||||
{ prop: 'actualStartDate', label: '实际发货时间', minWidth: 140 },
|
||||
{ prop: 'actualEndDate', label: '实际完成时间', minWidth: 140 },
|
||||
{ prop: 'planStartDate', label: '预计发货时间', minWidth: 140 },
|
||||
{ prop: 'planEndDate', label: '预计完成时间', minWidth: 140 },
|
||||
{ prop: 'remark', label: '备注', minWidth: 140 },
|
||||
{ prop: 'waybillIdentifier', label: '同一运单标识号', minWidth: 150 },
|
||||
];
|
||||
const history =
|
||||
this.detail.waybillMode === 'history'
|
||||
? [{ prop: 'waybillNo', label: '运单号', minWidth: 140 }]
|
||||
: [];
|
||||
return [...history, ...before, ...fees, ...after];
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.loadOptions();
|
||||
@@ -436,28 +294,10 @@ export default {
|
||||
if (value === 'pre') return '预结算单';
|
||||
return '正式结算单';
|
||||
},
|
||||
waybillModeLabel(value) {
|
||||
if (value === 'history') return '历史运单';
|
||||
return '新运单';
|
||||
},
|
||||
unwrapData(response) {
|
||||
const body = response?.data || response || {};
|
||||
return body?.data !== undefined ? body.data : body;
|
||||
},
|
||||
formatDetailCell(row, column) {
|
||||
const value = row?.[column.prop];
|
||||
if (value === null || value === undefined || value === '') return '';
|
||||
if (!column.money) return value;
|
||||
const number = Number(value);
|
||||
return Number.isFinite(number) ? number.toFixed(2) : value;
|
||||
},
|
||||
formatFeeAmount(row, name) {
|
||||
const feeItems = row?.feeItems || {};
|
||||
const value = feeItems[name] ?? row?.[name];
|
||||
if (value === null || value === undefined || value === '') return '';
|
||||
const number = Number(value);
|
||||
return Number.isFinite(number) ? number.toFixed(2) : value;
|
||||
},
|
||||
async loadOptions() {
|
||||
try {
|
||||
const response = await api.getOptions();
|
||||
@@ -518,28 +358,14 @@ export default {
|
||||
openCreate() {
|
||||
this.$router.push({ path: '/settlement/settlement-import/form' });
|
||||
},
|
||||
async openDetail(row) {
|
||||
openDetail(row) {
|
||||
if (!row?.id) return;
|
||||
this.detailVisible = true;
|
||||
this.detailLoading = true;
|
||||
this.detail = { ...row };
|
||||
this.detailRows = [];
|
||||
this.detailFeeItemNames = [];
|
||||
try {
|
||||
const response = await api.getDetail(row.id);
|
||||
const detail = this.unwrapData(response) || { ...row };
|
||||
this.detail = detail;
|
||||
this.detailRows = Array.isArray(detail.rows) ? detail.rows : [];
|
||||
this.detailFeeItemNames = Array.isArray(detail.feeItemNames) ? detail.feeItemNames : [];
|
||||
} catch (error) {
|
||||
this.detail = { ...row };
|
||||
this.$message.error(error?.message || '加载详情失败');
|
||||
} finally {
|
||||
this.detailLoading = false;
|
||||
}
|
||||
this.$router.push({
|
||||
path: '/settlement/settlement-import/form',
|
||||
query: { id: row.id, mode: 'view' },
|
||||
});
|
||||
},
|
||||
editDraft(row) {
|
||||
this.detailVisible = false;
|
||||
this.$router.push({
|
||||
path: '/settlement/settlement-import/form',
|
||||
query: { id: row.id },
|
||||
@@ -560,30 +386,6 @@ export default {
|
||||
this.$message.error(error?.message || '删除失败');
|
||||
}
|
||||
},
|
||||
goFormalSettlement(row) {
|
||||
if (!row?.formalSettlementId) return;
|
||||
this.detailVisible = false;
|
||||
this.$router.push({
|
||||
path: '/settlement/formal-settlement/form',
|
||||
query: {
|
||||
mode: 'view',
|
||||
id: row.formalSettlementId,
|
||||
name: '正式结算详情',
|
||||
},
|
||||
});
|
||||
},
|
||||
goPreSettlement(row) {
|
||||
if (!row?.preSettlementId) return;
|
||||
this.detailVisible = false;
|
||||
this.$router.push({
|
||||
path: '/settlement/pre-settlement/form',
|
||||
query: {
|
||||
mode: 'view',
|
||||
id: row.preSettlementId,
|
||||
name: '预结算详情',
|
||||
},
|
||||
});
|
||||
},
|
||||
openVoid(row) {
|
||||
this.voidForm = { id: row.id, reason: '' };
|
||||
this.voidVisible = true;
|
||||
@@ -686,29 +488,6 @@ export default {
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.settlement-import-page__detail-title {
|
||||
margin: 16px 0 8px;
|
||||
}
|
||||
|
||||
.dialog-section-title {
|
||||
position: relative;
|
||||
padding-left: 12px;
|
||||
color: #303133;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 0;
|
||||
width: 4px;
|
||||
height: 16px;
|
||||
border-radius: 2px;
|
||||
background: #409eff;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
.settlement-import-page :deep(.basic-container__card > .el-card__body) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user