fix
This commit is contained in:
@@ -190,7 +190,7 @@
|
||||
<div
|
||||
class="settlement-detail-page__detail-panel-body settlement-detail-page__adjust-panel-body"
|
||||
>
|
||||
<div v-if="isReceivable" class="settlement-detail-page__adjust-toolbar">
|
||||
<div v-if="isPayable" class="settlement-detail-page__adjust-toolbar">
|
||||
<el-button type="primary" :disabled="adjustDialog.loading" @click="addAdjustFee">
|
||||
新增费用
|
||||
</el-button>
|
||||
@@ -209,18 +209,26 @@
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span
|
||||
v-if="row.manualFee && ['billingFactor', 'billingType'].includes(column.prop)"
|
||||
>
|
||||
-
|
||||
</span>
|
||||
<el-input
|
||||
v-else-if="column.prop === 'cargoName' && row.manualFee"
|
||||
<el-select
|
||||
v-if="column.prop === 'cargoName' && row.manualFee"
|
||||
v-model="row.cargoName"
|
||||
class="settlement-detail-page__adjust-control"
|
||||
:loading="commonCargoLoading"
|
||||
clearable
|
||||
maxlength="100"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
placeholder="请选择或输入"
|
||||
@visible-change="visible => visible && loadCommonCargoOptions()"
|
||||
@change="value => handleAdjustCargoNameChange(row, value)"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in manualCargoNameOptions(row)"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<span v-else-if="column.prop === 'cargoName'">
|
||||
{{ formatDetailCell(row, column.prop) }}
|
||||
</span>
|
||||
@@ -241,6 +249,56 @@
|
||||
<span v-else-if="column.prop === 'cargoType'">
|
||||
{{ formatDetailCell(row, column.prop) }}
|
||||
</span>
|
||||
<el-select
|
||||
v-else-if="column.prop === 'specification' && row.manualFee"
|
||||
v-model="row.specification"
|
||||
class="settlement-detail-page__adjust-control"
|
||||
:loading="commonCargoLoading"
|
||||
clearable
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
placeholder="请选择或输入"
|
||||
@visible-change="visible => visible && loadCommonCargoOptions()"
|
||||
@change="value => handleAdjustSpecificationChange(row, value)"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in manualSpecificationOptions(row)"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-else-if="column.prop === 'billingFactor' && row.manualFee"
|
||||
v-model="row.billingFactor"
|
||||
class="settlement-detail-page__adjust-control"
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
@change="handleAdjustBillingFactorChange(row)"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in adjustBillingElements"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-else-if="column.prop === 'billingType' && row.manualFee"
|
||||
v-model="row.billingType"
|
||||
class="settlement-detail-page__adjust-control"
|
||||
:disabled="!row.billingFactor"
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in adjustBillingTypes(row)"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-else-if="column.prop === 'priceUnit'"
|
||||
v-model="row.priceUnit"
|
||||
@@ -327,7 +385,12 @@
|
||||
</div>
|
||||
<footer class="settlement-detail-page__detail-panel-footer">
|
||||
<el-button @click="closeAdjustPanel">取消</el-button>
|
||||
<el-button type="primary" :loading="adjustDialog.submitting" @click="saveAdjustFee">
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="adjustDialog.submitting"
|
||||
:disabled="hasAdjustCalculationPending"
|
||||
@click="saveAdjustFee"
|
||||
>
|
||||
保存
|
||||
</el-button>
|
||||
</footer>
|
||||
@@ -392,7 +455,6 @@
|
||||
v-model="transferForm.settlementBillType"
|
||||
@change="handleTransferTypeChange"
|
||||
>
|
||||
<el-radio label="pre">预结算单</el-radio>
|
||||
<el-radio label="formal">正式结算单</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
@@ -621,6 +683,7 @@ import {
|
||||
} from '@/option/settlement/receivable-payable-detail';
|
||||
import * as api from '@/api/settlement/receivable-payable-detail';
|
||||
import { getList as getContractList } from '@/api/business/contract-manage';
|
||||
import { getList as getCommonCargoList } from '@/api/business/common-cargo';
|
||||
import { getContractOptions as getSettlementContractOptions } from '@/api/settlement/preSettlement';
|
||||
import { getList as getCargoTypeList } from '@/api/base/cargo-type';
|
||||
import { exportBlob } from '@/api/common';
|
||||
@@ -628,6 +691,26 @@ import { getDictionary } from '@/api/system/dictbiz';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { createSettlementTransfer } from '@/utils/settlement-transfer';
|
||||
|
||||
const ADJUST_BILLING_ELEMENTS = [
|
||||
'按重量',
|
||||
'按体积',
|
||||
'按车辆',
|
||||
'按里程',
|
||||
'按吨·公里',
|
||||
'固定金额(整单一口价)',
|
||||
'按数量',
|
||||
];
|
||||
|
||||
const ADJUST_BILLING_TYPE_MAP = {
|
||||
按重量: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
||||
按体积: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
||||
按车辆: ['固定单价'],
|
||||
按里程: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
||||
'按吨·公里': ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
||||
'固定金额(整单一口价)': ['固定一口价'],
|
||||
按数量: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
||||
};
|
||||
|
||||
export default {
|
||||
props: {
|
||||
settlementType: {
|
||||
@@ -667,6 +750,10 @@ export default {
|
||||
adjustRows: [],
|
||||
adjustDynamicFeeColumns: [],
|
||||
adjustTextProps: ['specification', 'model', 'billingFactor', 'billingType'],
|
||||
adjustBillingElements: ADJUST_BILLING_ELEMENTS,
|
||||
commonCargoOptions: [],
|
||||
commonCargoLoading: false,
|
||||
commonCargoRequest: null,
|
||||
cargoTypeOptions: [],
|
||||
cargoTypeFlatOptions: [],
|
||||
cargoTypeLoading: false,
|
||||
@@ -718,6 +805,12 @@ export default {
|
||||
isReceivable() {
|
||||
return this.settlementType === 'receivable';
|
||||
},
|
||||
isPayable() {
|
||||
return this.settlementType === 'payable';
|
||||
},
|
||||
hasAdjustCalculationPending() {
|
||||
return this.adjustRows.some(row => row.calculating);
|
||||
},
|
||||
visibleSearchFields() {
|
||||
return this.searchExpanded ? this.searchFields : this.searchFields.slice(0, 4);
|
||||
},
|
||||
@@ -725,7 +818,9 @@ export default {
|
||||
return [...feeDetailBaseColumns, ...this.dynamicFeeColumns, ...feeDetailTailColumns];
|
||||
},
|
||||
displayTableColumns() {
|
||||
const columns = [...this.tableColumns];
|
||||
const columns = this.tableColumns.filter(
|
||||
column => this.settlementType !== 'receivable' || column.prop !== 'preSettlementNo'
|
||||
);
|
||||
const totalIndex = columns.findIndex(column => column.prop === 'totalAmountText');
|
||||
const dynamicColumns = this.tableFeeItemNames.map((name, index) => ({
|
||||
label: name,
|
||||
@@ -760,6 +855,9 @@ export default {
|
||||
this.loadPriceUnitOptions();
|
||||
this.loadTable();
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.clearAdjustCalculations();
|
||||
},
|
||||
methods: {
|
||||
async loadTransportTypeOptions() {
|
||||
const res = await getDictionary({ code: 'transport_type' });
|
||||
@@ -806,6 +904,82 @@ export default {
|
||||
});
|
||||
return this.priceUnitRequest;
|
||||
},
|
||||
loadCommonCargoOptions() {
|
||||
if (this.commonCargoOptions.length) return Promise.resolve(this.commonCargoOptions);
|
||||
if (this.commonCargoRequest) return this.commonCargoRequest;
|
||||
this.commonCargoLoading = true;
|
||||
this.commonCargoRequest = getCommonCargoList(1, 9999)
|
||||
.then(res => {
|
||||
this.commonCargoOptions = this.extractRecords(res);
|
||||
return this.commonCargoOptions;
|
||||
})
|
||||
.finally(() => {
|
||||
this.commonCargoLoading = false;
|
||||
this.commonCargoRequest = null;
|
||||
});
|
||||
return this.commonCargoRequest;
|
||||
},
|
||||
commonCargoMatches(row) {
|
||||
const cargoType = String(row.cargoType || '').trim();
|
||||
if (!cargoType) return this.commonCargoOptions;
|
||||
const matched = this.commonCargoOptions.filter(item =>
|
||||
[item.secondCargoTypeName, item.firstCargoTypeName, item.cargoType].some(
|
||||
value => String(value || '').trim() === cargoType
|
||||
)
|
||||
);
|
||||
return matched.length ? matched : this.commonCargoOptions;
|
||||
},
|
||||
manualCargoNameOptions(row) {
|
||||
const options = new Map();
|
||||
this.commonCargoMatches(row).forEach(item => {
|
||||
const value = String(item.cargoName || '').trim();
|
||||
if (value && !options.has(value)) options.set(value, { label: value, value });
|
||||
});
|
||||
return Array.from(options.values());
|
||||
},
|
||||
manualSpecificationOptions(row) {
|
||||
const cargoName = String(row.cargoName || '').trim();
|
||||
const records = cargoName
|
||||
? this.commonCargoMatches(row).filter(
|
||||
item => String(item.cargoName || '').trim() === cargoName
|
||||
)
|
||||
: this.commonCargoMatches(row);
|
||||
const options = new Map();
|
||||
records.forEach(item => {
|
||||
const value = String(item.specification || item.spec || '').trim();
|
||||
if (value && !options.has(value)) options.set(value, { label: value, value });
|
||||
});
|
||||
return Array.from(options.values());
|
||||
},
|
||||
handleAdjustCargoNameChange(row, value) {
|
||||
row.cargoName = String(value || '').trim();
|
||||
const cargo = this.commonCargoMatches(row).find(
|
||||
item => String(item.cargoName || '').trim() === row.cargoName
|
||||
);
|
||||
if (cargo) this.applyCommonCargoToAdjustRow(row, cargo);
|
||||
},
|
||||
handleAdjustSpecificationChange(row, value) {
|
||||
row.specification = String(value || '').trim();
|
||||
const cargo = this.commonCargoMatches(row).find(
|
||||
item =>
|
||||
String(item.cargoName || '').trim() === String(row.cargoName || '').trim() &&
|
||||
String(item.specification || item.spec || '').trim() === row.specification
|
||||
);
|
||||
if (cargo) this.applyCommonCargoToAdjustRow(row, cargo);
|
||||
},
|
||||
applyCommonCargoToAdjustRow(row, cargo) {
|
||||
row.cargoName = cargo.cargoName || row.cargoName || '';
|
||||
row.specification = cargo.specification || cargo.spec || row.specification || '';
|
||||
row.model = cargo.model || row.model || '';
|
||||
row.priceUnit = cargo.priceUnit || row.priceUnit || '';
|
||||
row.cargoType =
|
||||
cargo.secondCargoTypeName ||
|
||||
cargo.cargoType ||
|
||||
cargo.firstCargoTypeName ||
|
||||
row.cargoType ||
|
||||
'';
|
||||
row.cargoTypePath = this.resolveCargoTypePath(row.cargoType);
|
||||
},
|
||||
buildCargoTypeTree(cargoTypes = []) {
|
||||
const flatCargoTypes = [];
|
||||
const collectCargoTypes = list => {
|
||||
@@ -920,6 +1094,8 @@ export default {
|
||||
row.cargoTypePath = path;
|
||||
row.cargoType = labels.length ? labels[labels.length - 1] : '';
|
||||
row.cargoName = '';
|
||||
row.specification = '';
|
||||
row.model = '';
|
||||
},
|
||||
formatColumnValue(row, column) {
|
||||
if (column.prop === 'transportType') return this.transportTypeLabel(row[column.prop]);
|
||||
@@ -989,6 +1165,7 @@ export default {
|
||||
this.detailDialog.row = null;
|
||||
},
|
||||
closeAdjustPanel() {
|
||||
this.clearAdjustCalculations();
|
||||
this.adjustDialog.visible = false;
|
||||
this.adjustDialog.row = null;
|
||||
},
|
||||
@@ -1041,6 +1218,7 @@ export default {
|
||||
try {
|
||||
const [res] = await Promise.all([
|
||||
api.getFeeDetail(row.id),
|
||||
this.loadCommonCargoOptions(),
|
||||
this.loadCargoTypeOptions(),
|
||||
this.loadPriceUnitOptions(),
|
||||
]);
|
||||
@@ -1068,9 +1246,14 @@ export default {
|
||||
originalAmount: Number(item.originalAmount || 0),
|
||||
feeItems,
|
||||
dataSource:
|
||||
item.dataSource || (item.billingFactor === '手工调整' ? '手工录入' : '自动生成'),
|
||||
manualFee: item.dataSource === '手工录入' || item.billingFactor === '手工调整',
|
||||
item.dataSource || (item.billingFactor === '手工调整' ? '手动录入' : '自动生成'),
|
||||
manualFee:
|
||||
['手动录入', '手动添加', '手工录入'].includes(item.dataSource) ||
|
||||
item.billingFactor === '手工调整',
|
||||
cargoTypePath: this.resolveCargoTypePath(item.cargoType),
|
||||
calculating: false,
|
||||
adjustCalculateTimer: null,
|
||||
adjustCalculateVersion: 0,
|
||||
};
|
||||
this.recalculateAdjustRow(adjusted);
|
||||
return adjusted;
|
||||
@@ -1080,20 +1263,20 @@ export default {
|
||||
}
|
||||
},
|
||||
addAdjustFee() {
|
||||
if (!this.isReceivable) return;
|
||||
if (!this.isPayable) return;
|
||||
const feeItems = this.adjustDynamicFeeColumns.reduce((items, column) => {
|
||||
items[column.feeItemName] = 0;
|
||||
return items;
|
||||
}, {});
|
||||
this.adjustRows.push({
|
||||
dataSource: '手工录入',
|
||||
dataSource: '手动录入',
|
||||
cargoName: '',
|
||||
cargoType: '',
|
||||
cargoTypePath: [],
|
||||
specification: '',
|
||||
model: '',
|
||||
billingFactor: '-',
|
||||
billingType: '-',
|
||||
billingFactor: '',
|
||||
billingType: '',
|
||||
transportQuantity: '',
|
||||
priceUnit: '',
|
||||
unitPrice: '',
|
||||
@@ -1107,10 +1290,19 @@ export default {
|
||||
manualFee: true,
|
||||
remark: '',
|
||||
changeReason: '',
|
||||
calculating: false,
|
||||
adjustCalculateTimer: null,
|
||||
adjustCalculateVersion: 0,
|
||||
});
|
||||
},
|
||||
feeSourceLabel(value) {
|
||||
return value === '手工录入' ? '手工录入' : '自动生成';
|
||||
return ['手动录入', '手动添加', '手工录入'].includes(value) ? '手动录入' : '自动生成';
|
||||
},
|
||||
adjustBillingTypes(row) {
|
||||
return ADJUST_BILLING_TYPE_MAP[row.billingFactor] || [];
|
||||
},
|
||||
handleAdjustBillingFactorChange(row) {
|
||||
if (!this.adjustBillingTypes(row).includes(row.billingType)) row.billingType = '';
|
||||
},
|
||||
adjustTextMaxlength(prop) {
|
||||
return ['billingFactor', 'billingType'].includes(prop) ? 100 : 255;
|
||||
@@ -1131,6 +1323,10 @@ export default {
|
||||
},
|
||||
handleAdjustDecimalInput(row, prop, value, changedField) {
|
||||
row[prop] = this.normalizeAdjustDecimal(value);
|
||||
if (!row.manualFee && row.id && ['transportQuantity', 'mileage'].includes(prop)) {
|
||||
this.scheduleAdjustFeeCalculation(row);
|
||||
return;
|
||||
}
|
||||
this.recalculateAdjustRow(row, changedField);
|
||||
},
|
||||
handleAdjustFeeItemInput(row, feeItemName, value) {
|
||||
@@ -1155,6 +1351,50 @@ export default {
|
||||
);
|
||||
row.adjustAmount = Number((row.afterAmount - Number(row.originalAmount || 0)).toFixed(2));
|
||||
},
|
||||
scheduleAdjustFeeCalculation(row) {
|
||||
if (row.adjustCalculateTimer) clearTimeout(row.adjustCalculateTimer);
|
||||
row.adjustCalculateVersion += 1;
|
||||
row.calculating = true;
|
||||
const version = row.adjustCalculateVersion;
|
||||
row.adjustCalculateTimer = setTimeout(() => {
|
||||
row.adjustCalculateTimer = null;
|
||||
this.calculateAdjustRow(row, version);
|
||||
}, 300);
|
||||
},
|
||||
async calculateAdjustRow(row, version) {
|
||||
try {
|
||||
const res = await api.calculateAdjustedFee({
|
||||
detailId: this.adjustDialog.row?.id,
|
||||
feeId: row.id,
|
||||
transportQuantity: Number(row.transportQuantity || 0),
|
||||
mileage: Number(row.mileage || 0),
|
||||
freightAmount: Number(row.freightAmount || 0),
|
||||
feeItems: Object.fromEntries(
|
||||
Object.entries(row.feeItems || {}).map(([name, amount]) => [name, Number(amount || 0)])
|
||||
),
|
||||
});
|
||||
if (version !== row.adjustCalculateVersion || !this.adjustDialog.visible) return;
|
||||
const data = res.data?.data || res.data || res || {};
|
||||
row.freightAmount = Number(data.freightAmount || 0);
|
||||
row.feeItems = Object.fromEntries(
|
||||
Object.entries(data.feeItems || {}).map(([name, amount]) => [name, Number(amount || 0)])
|
||||
);
|
||||
row.adjustAmount = Number(data.adjustAmount || 0);
|
||||
row.afterAmount = Number(data.afterAmount || 0);
|
||||
} catch (error) {
|
||||
row.calculateError = error.message;
|
||||
} finally {
|
||||
if (version === row.adjustCalculateVersion) row.calculating = false;
|
||||
}
|
||||
},
|
||||
clearAdjustCalculations() {
|
||||
this.adjustRows.forEach(row => {
|
||||
if (row.adjustCalculateTimer) clearTimeout(row.adjustCalculateTimer);
|
||||
row.adjustCalculateTimer = null;
|
||||
row.adjustCalculateVersion += 1;
|
||||
row.calculating = false;
|
||||
});
|
||||
},
|
||||
isFreightFeeItem(name) {
|
||||
return String(name || '').includes('运费') || String(name || '').includes('运输费');
|
||||
},
|
||||
@@ -1163,15 +1403,20 @@ export default {
|
||||
this.$message.warning('没有可调整的费用明细');
|
||||
return;
|
||||
}
|
||||
if (this.hasAdjustCalculationPending) {
|
||||
this.$message.warning('费用正在重新计算,请稍候');
|
||||
return;
|
||||
}
|
||||
if (!this.validateManualAdjustRows()) return;
|
||||
this.adjustDialog.submitting = true;
|
||||
try {
|
||||
await api.adjustFee({
|
||||
detailId: this.adjustDialog.row.id,
|
||||
rows: this.adjustRows.map(row => ({
|
||||
id: row.id,
|
||||
cargoName: row.cargoName,
|
||||
cargoName: String(row.cargoName || '').trim(),
|
||||
cargoType: row.cargoType,
|
||||
specification: row.specification,
|
||||
specification: String(row.specification || '').trim(),
|
||||
model: row.model,
|
||||
billingFactor: row.billingFactor,
|
||||
billingType: row.billingType,
|
||||
@@ -1193,6 +1438,32 @@ export default {
|
||||
this.adjustDialog.submitting = false;
|
||||
}
|
||||
},
|
||||
validateManualAdjustRows() {
|
||||
for (const [index, row] of this.adjustRows.entries()) {
|
||||
if (!row.manualFee) continue;
|
||||
if (!String(row.cargoName || '').trim()) {
|
||||
this.$message.warning(`第${index + 1}行货物名称不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (String(row.cargoName).trim().length > 100) {
|
||||
this.$message.warning(`第${index + 1}行货物名称不能超过100个字`);
|
||||
return false;
|
||||
}
|
||||
if (String(row.specification || '').trim().length > 255) {
|
||||
this.$message.warning(`第${index + 1}行规格不能超过255个字`);
|
||||
return false;
|
||||
}
|
||||
if (!ADJUST_BILLING_ELEMENTS.includes(row.billingFactor)) {
|
||||
this.$message.warning(`第${index + 1}行请选择计费要素`);
|
||||
return false;
|
||||
}
|
||||
if (!this.adjustBillingTypes(row).includes(row.billingType)) {
|
||||
this.$message.warning(`第${index + 1}行请选择计费要素对应的计费类型`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
async loadFeeDetail() {
|
||||
if (!this.detailDialog.row) return;
|
||||
this.detailDialog.loading = true;
|
||||
@@ -1841,6 +2112,7 @@ export default {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.settlement-detail-page__pagination {
|
||||
|
||||
Reference in New Issue
Block a user