From 868ec4aac01c00bf8efbf81a6e030c816004b46b Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Fri, 21 Aug 2026 12:07:56 +0800 Subject: [PATCH] fix bug --- .../settlement/receivable-payable-detail.js | 3 + .../settlement/receivable-payable-detail.js | 2 +- .../components/billing-plan-editor.vue | 8 +- .../settlement/receivable-payable-detail.vue | 587 +++++++++++------- vite.config.mjs | 26 +- 5 files changed, 394 insertions(+), 232 deletions(-) diff --git a/src/api/settlement/receivable-payable-detail.js b/src/api/settlement/receivable-payable-detail.js index 55ff903..3a6e871 100644 --- a/src/api/settlement/receivable-payable-detail.js +++ b/src/api/settlement/receivable-payable-detail.js @@ -24,6 +24,9 @@ export const getUpdateFeeContracts = params => export const adjustFee = data => request({ url: `${baseUrl}/adjust-fee`, method: 'post', data }); +export const calculateAdjustedFee = data => + request({ url: `${baseUrl}/calculate-adjusted-fee`, method: 'post', data }); + export const transferSettlement = data => request({ url: `${baseUrl}/transfer-settlement`, method: 'post', data }); diff --git a/src/option/settlement/receivable-payable-detail.js b/src/option/settlement/receivable-payable-detail.js index 6c1420c..54b61bb 100644 --- a/src/option/settlement/receivable-payable-detail.js +++ b/src/option/settlement/receivable-payable-detail.js @@ -37,7 +37,7 @@ export const tableColumns = [ { label: '来源', prop: 'sourceType', minWidth: 110 }, { label: '预结算单号', prop: 'preSettlementNo', minWidth: 140, link: true }, { label: '正式结算单号', prop: 'formalSettlementNo', minWidth: 140, link: true }, - { label: '运单号', prop: 'waybillNo', minWidth: 140 }, + { label: '运单号', prop: 'waybillNo', minWidth: 140, link: true }, { label: '车号', prop: 'vehicleNo', minWidth: 120 }, { label: '运输类型', prop: 'transportType', minWidth: 120 }, { label: '货物名称', prop: 'cargoName', minWidth: 130 }, diff --git a/src/views/business/components/billing-plan-editor.vue b/src/views/business/components/billing-plan-editor.vue index 7fd95fb..a9d0376 100644 --- a/src/views/business/components/billing-plan-editor.vue +++ b/src/views/business/components/billing-plan-editor.vue @@ -30,7 +30,7 @@ v && ensureRegions()" @change="v => matchRegionChange('origin', v)" /> v && ensureRegions()" @change="v => matchRegionChange('destination', v)" /> - + v && ensureCargo()" @change="matchCargoChange" /> @@ -50,7 +50,7 @@ const defaultRule = () => ({ feeType: '', feeItem: '', billingElement: '', billi export default { props: { modelValue: Boolean, value: { type: Object, default: () => ({}) }, index: { type: Number, default: -1 }, readonly: Boolean }, emits: ['update:modelValue', 'save'], - data() { return { draft: this.normalizePlan(this.value), feeCategories: [], feeCategoryLoading: false, feeItems: {}, feeItemLoadingMap: {}, unitOptions: [], unitLoading: false, billingElements: ['按重量', '按体积', '按车辆', '按里程', '按吨·公里', '固定金额(整单一口价)', '按数量'], typeMap: { 按重量: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'], 按体积: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'], 按车辆: ['固定单价'], 按里程: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'], '按吨·公里': ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'], '固定金额(整单一口价)': ['固定一口价'], 按数量: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'] }, matchVisible: false, matchIndex: -1, matchForm: defaultRule().matchCondition, regionOptions: [], regionLoading: false, regionRequest: null, cargoOptions: [], cargoFlatOptions: [], cargoLoading: false, cargoRequest: null, formRules: { planName: [{ required: true, message: '请输入方案名称', trigger: 'blur' }] } }; }, + data() { return { draft: this.normalizePlan(this.value), feeCategories: [], feeCategoryLoading: false, feeItems: {}, feeItemLoadingMap: {}, unitOptions: [], unitLoading: false, billingElements: ['按重量', '按体积', '按车辆', '按里程', '按吨·公里', '固定金额(整单一口价)', '按数量'], typeMap: { 按重量: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'], 按体积: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'], 按车辆: ['固定单价'], 按里程: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'], '按吨·公里': ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'], '固定金额(整单一口价)': ['固定一口价'], 按数量: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'] }, matchVisible: false, matchIndex: -1, matchForm: defaultRule().matchCondition, transportModeOptions: [], transportModeLoading: false, transportModeRequest: null, regionOptions: [], regionLoading: false, regionRequest: null, cargoOptions: [], cargoFlatOptions: [], cargoLoading: false, cargoRequest: null, formRules: { planName: [{ required: true, message: '请输入方案名称', trigger: 'blur' }] } }; }, computed: { visible: { get() { return this.modelValue; }, set(value) { this.$emit('update:modelValue', value); } }, regionProps() { return { label: 'title', value: 'id', children: 'children', leaf: 'leaf', emitPath: true }; }, cargoProps() { return { label: 'cargoName', value: 'id', children: 'children', disabled: (data, node) => node.level === 1, leaf: 'leaf', checkStrictly: true, emitPath: true }; } }, mounted() { this.loadDictionaries(); }, watch: { value: { deep: true, handler(value) { if (this.modelValue) { this.draft = this.normalizePlan(value); this.loadRuleItems(); } } } }, @@ -86,7 +86,9 @@ export default { validateRules() { const groups = {}; const feeItemSet = new Set(); const required = [['feeType', '费用类型'], ['feeItem', '费用项'], ['billingElement', '计费要素'], ['billingType', '计费类型'], ['billingUnit', '计费单位']]; for (const [i, row] of this.draft.rules.entries()) { const empty = required.find(([key]) => row[key] === undefined || row[key] === null || String(row[key]).trim() === ''); if (empty) { this.$message.warning(`第${i + 1}行${empty[1]}不能为空`); return false; } const feeItem = String(row.feeItem).trim(); if (feeItemSet.has(feeItem)) { this.$message.warning(`费用项“${feeItem}”不能重复`); return false; } feeItemSet.add(feeItem); if (!this.usesRangeUnitPrice(row) && (row.unitPrice === undefined || row.unitPrice === null || String(row.unitPrice).trim() === '')) { this.$message.warning(`第${i + 1}行单价不能为空`); return false; } if (!this.billingTypes(row).includes(row.billingType)) { this.$message.warning('请选择计费要素对应的计费类型'); return false; } if (!this.canEditLimit(row)) continue; const key = row.billingElement; groups[key] = groups[key] || []; for (const range of this.getRanges(row)) { const lower = Number(range.lowerLimit); const upper = Number(range.upperLimit); if (range.lowerLimit === '' || range.upperLimit === '' || Number.isNaN(lower) || Number.isNaN(upper)) { this.$message.warning('请完整填写计费要素上下限'); return false; } if (this.usesRangeUnitPrice(row) && (range.unitPrice === undefined || range.unitPrice === null || String(range.unitPrice).trim() === '')) { this.$message.warning('请为每组计费要素区间填写单价'); return false; } if (lower > upper) { this.$message.warning('计费要素下限不能大于上限'); return false; } groups[key].push({ lower, upper }); } } return this.validateRangeGroups(groups); }, validateRangeGroups(groups) { const precision = 0.000001; for (const [element, ranges] of Object.entries(groups)) { const sorted = [...ranges].sort((a, b) => a.lower - b.lower || a.upper - b.upper); for (let i = 1; i < sorted.length; i += 1) { if (sorted[i].lower < sorted[i - 1].upper - precision) { this.$message.warning(`${element}的计费要素区间不能重叠`); return false; } if (sorted[i].lower > sorted[i - 1].upper + precision) { this.$message.warning(`${element}的计费要素区间必须连续,不能存在间隙`); return false; } } } return true; }, save() { this.$refs.formRef.validate(valid => { if (!valid || !this.validateRules()) return; const plan = clone(this.draft); plan.rules = plan.rules.map(rule => { const ranges = this.canEditLimit(rule) ? this.getRanges(rule) : []; return { ...rule, unitPrice: this.usesRangeUnitPrice(rule) ? ranges[0]?.unitPrice || '' : rule.unitPrice, limitRanges: ranges, lowerLimit: ranges[0]?.lowerLimit || '', upperLimit: ranges[0]?.upperLimit || '', minimumBillingWeight: this.canEditMinimum(rule) ? (this.usesRangeMinimum(rule) ? ranges[0]?.minimumBillingWeight || '' : rule.minimumBillingWeight) : '' }; }); this.$emit('save', plan, this.index); this.visible = false; }); }, - openMatch(row, index) { this.matchIndex = index; this.matchForm = { ...defaultRule().matchCondition, ...(row.matchCondition || {}) }; this.matchVisible = true; this.ensureRegions(); this.ensureCargo().then(() => { const path = this.resolveCargoPath(this.matchForm); if (path.length) this.matchCargoChange(path); }); }, + openMatch(row, index) { this.matchIndex = index; this.matchForm = { ...defaultRule().matchCondition, ...(row.matchCondition || {}) }; this.matchVisible = true; this.ensureTransportModes().then(() => { this.matchForm.transportMode = this.normalizeTransportMode(this.matchForm.transportMode); }); this.ensureRegions(); this.ensureCargo().then(() => { const path = this.resolveCargoPath(this.matchForm); if (path.length) this.matchCargoChange(path); }); }, + ensureTransportModes() { if (this.transportModeOptions.length) return Promise.resolve(this.transportModeOptions); if (this.transportModeRequest) return this.transportModeRequest; this.transportModeLoading = true; this.transportModeRequest = getDictionary({ code: 'transport_type' }).then(res => { this.transportModeOptions = (res.data?.data || []).map(item => ({ label: item.dictValue || item.label || item.value || '', value: item.dictKey || item.value || item.dictValue || '' })); return this.transportModeOptions; }).finally(() => { this.transportModeLoading = false; this.transportModeRequest = null; }); return this.transportModeRequest; }, + normalizeTransportMode(value) { const transportMode = String(value || '').trim(); if (!transportMode) return ''; const option = this.transportModeOptions.find(item => { const optionValue = String(item.value || '').trim(); const optionLabel = String(item.label || '').trim(); return optionValue === transportMode || optionLabel === transportMode || optionLabel.includes(transportMode) || transportMode.includes(optionLabel); }); return option?.value || transportMode; }, ensureRegions() { if (this.regionOptions.length) return Promise.resolve(this.regionOptions); if (this.regionRequest) return this.regionRequest; this.regionLoading = true; this.regionRequest = getRegionLazyTree().then(res => { const raw = res.data?.data || []; this.regionOptions = this.normalizeRegionTree(raw); return this.regionOptions; }).finally(() => { this.regionLoading = false; this.regionRequest = null; }); return this.regionRequest; }, normalizeRegionTree(list) { const map = new Map(); const flat = []; const walk = items => (items || []).forEach(item => { flat.push({ ...item, children: undefined }); if (item.children?.length) walk(item.children); }); walk(list); flat.forEach(item => map.set(String(item.id ?? item.code ?? item.value), { ...item, id: String(item.id ?? item.code ?? item.value), parentId: String(item.parentId ?? item.parentCode ?? ''), children: [] })); const roots = []; map.forEach(node => { const parent = map.get(node.parentId); if (parent && parent !== node) parent.children.push(node); else roots.push(node); }); const china = roots.find(item => ['中国', '中华人民共和国'].includes(item.title || item.name)); const source = china?.children?.length ? china.children : roots; const normalize = (node, level) => ({ ...node, title: node.title || node.name || '', leaf: level === 2, children: level === 1 ? (node.children || []).map(child => normalize(child, 2)) : undefined }); return source.map(node => normalize(node, 1)); }, regionLabels(path) { let options = this.regionOptions; const labels = []; (path || []).forEach(value => { const option = options.find(item => String(item.id) === String(value)); if (option) { labels.push(option.title || option.name || ''); options = option.children || []; } }); return labels; }, diff --git a/src/views/settlement/receivable-payable-detail.vue b/src/views/settlement/receivable-payable-detail.vue index 2faac17..ea245a2 100644 --- a/src/views/settlement/receivable-payable-detail.vue +++ b/src/views/settlement/receivable-payable-detail.vue @@ -69,7 +69,7 @@ {{ row[column.prop] }} @@ -103,186 +103,166 @@ - - - - + + + + 单据详情:{{ detailDialog.title }} + + + + + + + + + + + {{ formatDetailCell(row, column.prop) }} + + + + + + + + + + + + + + + + + + + + + + 调整费用:{{ adjustDialog.row?.documentNo || '-' }} + + + + + + - {{ formatDetailCell(row, column.prop) }} + + handleAdjustDecimalInput(row, 'transportQuantity', value)" + /> + handleAdjustDecimalInput(row, 'mileage', value)" + /> + handleAdjustDecimalInput(row, 'freightAmount', value, 'freight')" + /> + handleAdjustFeeItemInput(row, column.feeItemName, value)" + /> + + + + {{ fixedTwoDecimals(row.adjustAmount) }} + + + {{ fixedTwoDecimals(row.afterAmount) }} + + {{ formatDetailCell(row, column.prop) }} + - - - - - - - - - - - - - - - - 新增费用项 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 删除 - - - - - - - - - - - - - - {{ fixedTwoDecimals(row.adjustAmount) }} - - - {{ fixedTwoDecimals(row.afterAmount) }} - - {{ formatDetailCell(row, column.prop) }} - - - - - 取消 - - 保存 - - - + + + + @@ -423,7 +405,14 @@ - + 取消 - 生成费用 + + 生成费用 + @@ -528,7 +523,7 @@