From 1ef050dda68a7ba185b27eb288c5d2aad9d1cdf2 Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Tue, 25 Aug 2026 00:10:45 +0800 Subject: [PATCH] fix bug --- src/components/address-map-picker/main.vue | 252 ++++ src/option/business/common-cargo.js | 4 + src/option/vehicle/accident-record.js | 3 + .../vehicle/annual-inspection-record.js | 2 + src/option/vehicle/equipment-ledger.js | 1 + src/option/vehicle/etc-record.js | 1 + src/option/vehicle/insurance-record.js | 1 + src/option/vehicle/mileage-record.js | 5 + src/option/vehicle/oil-electric-record.js | 2 + src/option/vehicle/other-expense-record.js | 1 + src/option/vehicle/tire-replacement-record.js | 1 + src/option/vehicle/transport-change-record.js | 1 + src/option/vehicle/violation-record.js | 3 + src/views/base/fee-item.vue | 20 +- src/views/base/port-terminal.vue | 90 +- src/views/base/region.vue | 1 + src/views/business/common-cargo.vue | 19 +- .../components/billing-plan-editor.vue | 1164 +++++++++++++++-- .../components/business-crud-page.vue | 11 +- .../components/master-order-editor.vue | 4 + src/views/business/contract-manage.vue | 372 +++++- src/views/business/loading-manage.vue | 2 +- src/views/business/process-config.vue | 32 +- .../components/formal-settlement-editor.vue | 34 +- .../components/pre-settlement-editor.vue | 13 +- .../settlement/receivable-payable-detail.vue | 101 +- src/views/transportCapacity/driver.vue | 9 + src/views/vehicle/accident-record.vue | 4 +- .../vehicle/annual-inspection-record.vue | 2 +- src/views/vehicle/customer-archive.vue | 52 +- src/views/vehicle/maintenance-plan.vue | 30 + src/views/vehicle/maintenance-record.vue | 30 + src/views/vehicle/mileage-record.vue | 8 +- src/views/vehicle/oil-electric-record.vue | 2 +- src/views/vehicle/violation-record.vue | 27 + 35 files changed, 2041 insertions(+), 263 deletions(-) create mode 100644 src/components/address-map-picker/main.vue diff --git a/src/components/address-map-picker/main.vue b/src/components/address-map-picker/main.vue new file mode 100644 index 0000000..c7d39f7 --- /dev/null +++ b/src/components/address-map-picker/main.vue @@ -0,0 +1,252 @@ + + + + + diff --git a/src/option/business/common-cargo.js b/src/option/business/common-cargo.js index f64fb66..8eba4d5 100644 --- a/src/option/business/common-cargo.js +++ b/src/option/business/common-cargo.js @@ -202,6 +202,7 @@ export const option = createCrudOption([ label: '单价', prop: 'cargoValue', formslot: true, + formatter: row => (String(row.cargoValue) === '-1' ? '' : row.cargoValue), minWidth: 120, rules: [ { @@ -277,6 +278,9 @@ export const option = createCrudOption([ ), ]); +// 搜索栏标签统一加宽,给较长的货物类型字段预留足够展示空间。 +option.searchLabelWidth = 160; + export const excelOption = { submitBtn: false, emptyBtn: false, diff --git a/src/option/vehicle/accident-record.js b/src/option/vehicle/accident-record.js index 4e1cd7b..96026b6 100644 --- a/src/option/vehicle/accident-record.js +++ b/src/option/vehicle/accident-record.js @@ -50,6 +50,7 @@ export const option = { border: true, index: true, indexLabel: '序号', + indexWidth: 90, viewBtn: true, selection: true, dialogClickModal: false, @@ -128,6 +129,7 @@ export const option = { prop: 'directEconomicLoss', type: 'input', formslot: true, + append: '元', minWidth: 140, span: 12, placeholder: '请输入', @@ -138,6 +140,7 @@ export const option = { prop: 'insuranceClaimAmount', type: 'input', formslot: true, + append: '元', minWidth: 140, span: 12, placeholder: '请输入', diff --git a/src/option/vehicle/annual-inspection-record.js b/src/option/vehicle/annual-inspection-record.js index e017adf..ce6babf 100644 --- a/src/option/vehicle/annual-inspection-record.js +++ b/src/option/vehicle/annual-inspection-record.js @@ -49,6 +49,7 @@ export const option = { border: true, index: true, indexLabel: '序号', + indexWidth: 90, viewBtn: true, selection: true, dialogClickModal: false, @@ -145,6 +146,7 @@ export const option = { prop: 'fee', type: 'input', formslot: true, + append: '元', minWidth: 100, span: 12, placeholder: '请输入', diff --git a/src/option/vehicle/equipment-ledger.js b/src/option/vehicle/equipment-ledger.js index 6be9652..9a95fd1 100644 --- a/src/option/vehicle/equipment-ledger.js +++ b/src/option/vehicle/equipment-ledger.js @@ -30,6 +30,7 @@ export const option = { border: true, index: true, indexLabel: '序号', + indexWidth: 90, viewBtn: true, selection: true, dialogClickModal: false, diff --git a/src/option/vehicle/etc-record.js b/src/option/vehicle/etc-record.js index e212c27..a5a6059 100644 --- a/src/option/vehicle/etc-record.js +++ b/src/option/vehicle/etc-record.js @@ -39,6 +39,7 @@ export const option = { border: true, index: true, indexLabel: '序号', + indexWidth: 90, viewBtn: true, selection: true, dialogClickModal: false, diff --git a/src/option/vehicle/insurance-record.js b/src/option/vehicle/insurance-record.js index efca1e0..462a664 100644 --- a/src/option/vehicle/insurance-record.js +++ b/src/option/vehicle/insurance-record.js @@ -43,6 +43,7 @@ export const option = { border: true, index: true, indexLabel: '序号', + indexWidth: 90, viewBtn: true, selection: true, dialogClickModal: false, diff --git a/src/option/vehicle/mileage-record.js b/src/option/vehicle/mileage-record.js index 419be77..4e310e6 100644 --- a/src/option/vehicle/mileage-record.js +++ b/src/option/vehicle/mileage-record.js @@ -35,6 +35,7 @@ export const option = { border: true, index: true, indexLabel: '序号', + indexWidth: 90, viewBtn: true, selection: true, dialogClickModal: false, @@ -70,6 +71,7 @@ export const option = { prop: 'previousMonthMileage', type: 'input', formslot: true, + append: 'km', minWidth: 210, slot: true, rules: [{ validator: validateNonNegative, trigger: 'blur' }], @@ -79,6 +81,7 @@ export const option = { prop: 'currentMonthMileage', type: 'input', formslot: true, + append: 'km', minWidth: 210, slot: true, rules: [{ validator: validateNonNegative, trigger: 'blur' }], @@ -88,6 +91,7 @@ export const option = { prop: 'monthlyMileage', type: 'input', formslot: true, + append: 'km', minWidth: 170, slot: true, rules: [{ validator: validateNonNegative, trigger: 'blur' }], @@ -97,6 +101,7 @@ export const option = { prop: 'totalMileage', type: 'input', formslot: true, + append: 'km', minWidth: 170, slot: true, rules: [{ validator: validateNonNegative, trigger: 'blur' }], diff --git a/src/option/vehicle/oil-electric-record.js b/src/option/vehicle/oil-electric-record.js index 49c8f07..236ee44 100644 --- a/src/option/vehicle/oil-electric-record.js +++ b/src/option/vehicle/oil-electric-record.js @@ -36,6 +36,7 @@ export const option = { border: true, index: true, indexLabel: '序号', + indexWidth: 90, viewBtn: true, selection: true, dialogClickModal: false, @@ -123,6 +124,7 @@ export const option = { prop: 'unitPrice', type: 'input', formslot: true, + append: '元/升', minWidth: 120, rules: [{ validator: validateNonNegative, trigger: 'blur' }], }, diff --git a/src/option/vehicle/other-expense-record.js b/src/option/vehicle/other-expense-record.js index bba2e1c..ab2111c 100644 --- a/src/option/vehicle/other-expense-record.js +++ b/src/option/vehicle/other-expense-record.js @@ -47,6 +47,7 @@ export const option = { border: true, index: true, indexLabel: '序号', + indexWidth: 90, viewBtn: true, selection: true, dialogClickModal: false, diff --git a/src/option/vehicle/tire-replacement-record.js b/src/option/vehicle/tire-replacement-record.js index 62f914e..043a47b 100644 --- a/src/option/vehicle/tire-replacement-record.js +++ b/src/option/vehicle/tire-replacement-record.js @@ -40,6 +40,7 @@ export const option = { border: true, index: true, indexLabel: '序号', + indexWidth: 90, viewBtn: true, selection: true, dialogClickModal: false, diff --git a/src/option/vehicle/transport-change-record.js b/src/option/vehicle/transport-change-record.js index cf949d5..2791bd5 100644 --- a/src/option/vehicle/transport-change-record.js +++ b/src/option/vehicle/transport-change-record.js @@ -27,6 +27,7 @@ export const option = { border: true, index: true, indexLabel: '序号', + indexWidth: 90, viewBtn: true, selection: true, dialogClickModal: false, diff --git a/src/option/vehicle/violation-record.js b/src/option/vehicle/violation-record.js index 1a1d36e..798623a 100644 --- a/src/option/vehicle/violation-record.js +++ b/src/option/vehicle/violation-record.js @@ -50,6 +50,7 @@ export const option = { border: true, index: true, indexLabel: '序号', + indexWidth: 90, viewBtn: true, selection: true, dialogClickModal: false, @@ -127,6 +128,8 @@ export const option = { { label: '地点', prop: 'location', + slot: true, + formslot: true, minWidth: 180, overHidden: true, span: 12, diff --git a/src/views/base/fee-item.vue b/src/views/base/fee-item.vue index 335fee6..69a8091 100644 --- a/src/views/base/fee-item.vue +++ b/src/views/base/fee-item.vue @@ -14,6 +14,7 @@ @row-del="rowDel" @search-change="searchChange" @search-reset="searchReset" + @selection-change="selectionChange" @current-change="currentChange" @size-change="sizeChange" @refresh-change="refreshChange" @@ -40,6 +41,7 @@ v-model="form.feeCategory" class="fee-item-form-control" clearable + :disabled="dialogType !== 'add'" filterable placeholder="请选择费用类型" @change="handleFeeCategoryChange" @@ -58,6 +60,7 @@ :maxlength="feeItemCodeMaxlength" class="fee-item-form-control" clearable + :disabled="dialogType !== 'add'" placeholder="请输入费用项代码" @change="handleFeeItemCodeChange" > @@ -114,12 +117,14 @@ export default { data: [], excelBox: false, excelForm: {}, + dialogType: 'add', feeCategoryOptions: [], page: { pageSize: 10, currentPage: 1, total: 0, }, + selectionList: [], option: { height: 'auto', calcHeight: 32, @@ -139,7 +144,7 @@ export default { viewBtn: false, delBtn: false, editBtn: false, - selection: false, + selection: true, dialogClickModal: false, menuWidth: 180, column: [ @@ -294,6 +299,9 @@ export default { feeItemCodeMaxlength() { return Math.max(1, 100 - this.feeCategoryPrefix.length); }, + ids() { + return this.selectionList.map(item => item.id).join(','); + }, permissionList() { return { addBtn: this.validData(this.permission.fee_item_add, false), @@ -418,6 +426,7 @@ export default { }); }, beforeOpen(done, type) { + this.dialogType = type || 'add'; if (['edit', 'view'].includes(type)) { getDetail(this.form.id).then(res => { this.form = this.normalizeFormForEdit(res.data.data || {}); @@ -439,6 +448,13 @@ export default { this.onLoad(this.page, params); done(); }, + selectionChange(list) { + this.selectionList = list; + }, + selectionClear() { + this.selectionList = []; + this.$refs.crud.toggleSelection(); + }, currentChange(currentPage) { this.page.currentPage = currentPage; }, @@ -454,6 +470,7 @@ export default { buildExportParams() { return { ...this.query, + ids: this.ids, [this.website.tokenHeader]: getToken(), }; }, @@ -490,6 +507,7 @@ export default { const data = res.data.data; this.page.total = data.total; this.data = data.records; + this.selectionClear(); this.loading = false; }); }, diff --git a/src/views/base/port-terminal.vue b/src/views/base/port-terminal.vue index da2e20d..8c8543d 100644 --- a/src/views/base/port-terminal.vue +++ b/src/views/base/port-terminal.vue @@ -250,81 +250,6 @@ import NProgress from 'nprogress'; import 'nprogress/nprogress.css'; const DEFAULT_COUNTRY_CODE = '+86'; -const EXCLUDED_EXCEL_HEADERS = ['数据来源', '启停状态']; -const REQUIRED_EXPORT_HEADERS = [ - '港口码头名称', - '港口/码头名称', - '类型', - '国家', - '城市', - '经度', - '纬度', -]; - -const getExcelCellText = value => { - if (value === undefined || value === null) return ''; - if (typeof value === 'object') { - if (Array.isArray(value.richText)) { - return value.richText.map(item => item.text || '').join(''); - } - if (value.text) return value.text; - if (value.result) return value.result; - } - return String(value); -}; - -const removeWorkbookColumnsByHeaders = workbook => { - workbook.eachSheet(worksheet => { - const maxHeaderRow = Math.min(worksheet.rowCount || 0, 10); - for (let rowIndex = 1; rowIndex <= maxHeaderRow; rowIndex += 1) { - const row = worksheet.getRow(rowIndex); - const indexes = []; - row.eachCell((cell, colNumber) => { - const text = getExcelCellText(cell.value).trim(); - if (EXCLUDED_EXCEL_HEADERS.includes(text)) { - indexes.push(colNumber); - } - }); - if (indexes.length) { - indexes.sort((a, b) => b - a).forEach(colNumber => worksheet.spliceColumns(colNumber, 1)); - break; - } - } - }); -}; - -const addWorkbookRequiredHeaderMarks = workbook => { - workbook.eachSheet(worksheet => { - const maxHeaderRow = Math.min(worksheet.rowCount || 0, 10); - for (let rowIndex = 1; rowIndex <= maxHeaderRow; rowIndex += 1) { - const row = worksheet.getRow(rowIndex); - let hasRequiredHeader = false; - row.eachCell(cell => { - const text = getExcelCellText(cell.value).trim(); - const header = text.replace(/\*+$/g, '').trim(); - if (REQUIRED_EXPORT_HEADERS.includes(header)) { - cell.value = `${header}*`; - hasRequiredHeader = true; - } - }); - if (hasRequiredHeader) break; - } - }); -}; - -const removeExcelColumnsByHeaders = async (blob, options = {}) => { - const { markRequiredHeaders = false } = options; - const ExcelJS = await import('exceljs'); - const workbook = new ExcelJS.Workbook(); - await workbook.xlsx.load(await blob.arrayBuffer()); - removeWorkbookColumnsByHeaders(workbook); - if (markRequiredHeaders) { - addWorkbookRequiredHeaderMarks(workbook); - } - const buffer = await workbook.xlsx.writeBuffer(); - return new Blob([buffer], { type: 'application/vnd.ms-excel' }); -}; - const newLocal = '请选择类型'; export default { data() { @@ -1323,9 +1248,6 @@ export default { '港口码头主数据', () => { this.loadPortOptions(); - }, - { - failDetailDecorator: removeWorkbookColumnsByHeaders, } ); }, @@ -1339,11 +1261,8 @@ export default { exportBlob('/blade-system/port-terminal/export-port-terminal', this.buildExportParams(), { feedback: true, }) - .then(async res => { - const blob = await removeExcelColumnsByHeaders(res.data, { - markRequiredHeaders: true, - }); - downloadXls(blob, `港口码头主数据${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`); + .then(res => { + downloadXls(res.data, `港口码头主数据${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`); }) .finally(() => { NProgress.done(); @@ -1362,9 +1281,8 @@ export default { `/blade-system/port-terminal/export-template?${this.website.tokenHeader}=${getToken()}`, undefined, { feedback: true } - ).then(async res => { - const blob = await removeExcelColumnsByHeaders(res.data); - downloadXls(blob, '港口码头主数据模板.xlsx'); + ).then(res => { + downloadXls(res.data, '港口码头主数据模板.xlsx'); }); }, }, diff --git a/src/views/base/region.vue b/src/views/base/region.vue index f270515..480cc23 100644 --- a/src/views/base/region.vue +++ b/src/views/base/region.vue @@ -689,6 +689,7 @@ export default { feedback: true, }).then(res => { downloadXls(res.data, '行政区划模板.xlsx'); + this.$message.success('模板下载成功'); }); }, }, diff --git a/src/views/business/common-cargo.vue b/src/views/business/common-cargo.vue index abe435d..3230a83 100644 --- a/src/views/business/common-cargo.vue +++ b/src/views/business/common-cargo.vue @@ -399,8 +399,17 @@ export default { if (submitRow.cargoValue !== undefined && submitRow.cargoValue !== null) { submitRow.cargoValue = String(submitRow.cargoValue).trim(); } + if (!submitRow.cargoValue || String(submitRow.cargoValue) === '-1') { + submitRow.cargoValue = null; + } return submitRow; }, + normalizeCargoValue(row) { + if (row && String(row.cargoValue) === '-1') { + row.cargoValue = null; + } + return row; + }, validateRow(row) { if (!row.firstCargoTypeName || !row.firstCargoTypeCode) { this.$message.warning('请选择一级货物类型'); @@ -539,7 +548,7 @@ export default { } if (['edit', 'view'].includes(type)) { api.getDetail(this.form.id).then(res => { - this.form = res.data.data || {}; + this.form = this.normalizeCargoValue(res.data.data || {}); this.ensureCargoTypeOption('firstCargoTypeOptions', { cargoName: this.form.firstCargoTypeName, cargoCode: this.form.firstCargoTypeCode, @@ -593,7 +602,7 @@ export default { .then(res => { const result = res.data.data; this.page.total = result.total; - this.data = result.records; + this.data = (result.records || []).map(row => this.normalizeCargoValue(row)); this.selectionClear(); }) .finally(() => { @@ -654,6 +663,12 @@ export default { + diff --git a/src/views/business/components/business-crud-page.vue b/src/views/business/components/business-crud-page.vue index 9500a13..5d9983a 100644 --- a/src/views/business/components/business-crud-page.vue +++ b/src/views/business/components/business-crud-page.vue @@ -2105,7 +2105,7 @@ @@ -13281,6 +13281,13 @@ export default { } } + &__row-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + justify-content: center; + } + :deep(.business-crud-page__full-form-item) { width: 100%; max-width: 100%; diff --git a/src/views/business/components/master-order-editor.vue b/src/views/business/components/master-order-editor.vue index be9f332..a5dcbcc 100644 --- a/src/views/business/components/master-order-editor.vue +++ b/src/views/business/components/master-order-editor.vue @@ -1705,6 +1705,10 @@ export default { departureAddress: this.form.arrivalAddress, departureContact: this.form.arrivalContact, departurePhone: this.form.arrivalPhone, + departureLongitude: this.form.arrivalLongitude, + departureLatitude: this.form.arrivalLatitude, + departureRegionCode: this.form.arrivalRegionCode, + departureSiteCode: this.form.arrivalSiteCode, transportType: this.form.finalTransportType, }, ], diff --git a/src/views/business/contract-manage.vue b/src/views/business/contract-manage.vue index 8da3b0b..fc3b0c9 100644 --- a/src/views/business/contract-manage.vue +++ b/src/views/business/contract-manage.vue @@ -580,29 +580,205 @@ title="合同详情" append-to-body destroy-on-close - width="1200px" + class="contract-manage-detail-dialog" + width="92%" top="4vh" > -
-
-
{{ section.title }}
- - - {{ detailValue(field[0]) }} - +
+
+
基本信息
+ + {{ + detailValue('signType') + }} + {{ + detailValue('contractNo') + }} + {{ + detailValue('contractName') + }} + {{ + detailValue('contractCategory') + }} + {{ + detailValue('projectName') + }} + {{ detailValue('partyA') }} + {{ detailValue('partyB') }} + {{ detailContractPeriod }} + {{ + detailValue('organizationName') + }} + {{ + detailValue('handlerUserName') + }} + {{ + detailValue('signDate') + }} + {{ + detailValue('settlementMode') + }} + {{ + detailValue('contractFormat') + }} + {{ + detailLegalSealText + }} + {{ + detailUnitValue('copyCount', '份') + }} + {{ + detailUnitValue('paymentDays', '天') + }} + {{ + detailValue('remark') + }}
+ + + +
+
计费信息
+ + {{ + detailFeeGenerationMode + }} + + + + + + + + + + + + +
+ +
+
结算单规则
+ + + + + + {{ + Number(detailSettlementRule.autoGenerate) === 1 ? '开启' : '关闭' + }} + + +
+ +
+
付款比例设置
+ + + + + + + + +
+ + + +
+
变更记录
+ + + + + + + + + + + +
+ + [] }, description: Boolean, + readonly: Boolean, preview: { type: Function, default: null }, }, emits: ['update:rows'], @@ -797,7 +974,9 @@ const AttachmentSection = defineComponent({ const ElTableColumn = resolveComponent('el-table-column'); const VehicleAttachmentUpload = resolveComponent('vehicle-attachment-upload'); const columns = [ - h(ElTableColumn, { type: 'selection', width: 55, align: 'center' }), + ...(!this.readonly + ? [h(ElTableColumn, { type: 'selection', width: 55, align: 'center' })] + : []), h(ElTableColumn, { type: 'index', label: '序号', width: 70, align: 'center' }), h( ElTableColumn, @@ -828,14 +1007,16 @@ const AttachmentSection = defineComponent({ { label: '附件描述', minWidth: 220 }, { default: ({ row }) => - h(ElInput, { - modelValue: row.description, - maxlength: 200, - 'onUpdate:modelValue': value => { - row.description = value; - this.update([...this.rows]); - }, - }), + this.readonly + ? row.description || '-' + : h(ElInput, { + modelValue: row.description, + maxlength: 200, + 'onUpdate:modelValue': value => { + row.description = value; + this.update([...this.rows]); + }, + }), } ) ); @@ -855,16 +1036,20 @@ const AttachmentSection = defineComponent({ width: 180, align: 'center', sortable: true, - }), - h( - ElTableColumn, - { label: '操作', width: 100, align: 'center', fixed: 'right' }, - { - default: ({ $index }) => - h(ElLink, { type: 'danger', onClick: () => this.remove($index) }, () => '删除'), - } - ) + }) ); + if (!this.readonly) { + columns.push( + h( + ElTableColumn, + { label: '操作', width: 100, align: 'center', fixed: 'right' }, + { + default: ({ $index }) => + h(ElLink, { type: 'danger', onClick: () => this.remove($index) }, () => '删除'), + } + ) + ); + } return h( 'section', { @@ -874,16 +1059,20 @@ const AttachmentSection = defineComponent({ h('div', { class: 'dialog-section-title' }, this.title), h('div', { class: 'contract-manage-form__attachment-head' }, [ h('div', { class: 'contract-manage-form__attachment-actions' }, [ - h(VehicleAttachmentUpload, { - modelValue: this.rows, - fileTypes: this.attachmentFileTypes, - maxSize: 500, - showTip: false, - showFileList: false, - buttonText: '上传附件', - 'onUpdate:modelValue': this.handleChange, - onChange: this.handleChange, - }), + ...(!this.readonly + ? [ + h(VehicleAttachmentUpload, { + modelValue: this.rows, + fileTypes: this.attachmentFileTypes, + maxSize: 500, + showTip: false, + showFileList: false, + buttonText: '上传附件', + 'onUpdate:modelValue': this.handleChange, + onChange: this.handleChange, + }), + ] + : []), h( ElButton, { type: 'primary', disabled: !this.rows.length, onClick: this.batchDownload }, @@ -990,6 +1179,17 @@ export default { detailBox: false, detailLoading: false, detailRow: {}, + detailContractFileRows: [], + detailAttachmentRows: [], + detailBillingPlanRows: [], + detailBillingPlanBox: false, + detailBillingPlanIndex: -1, + detailBillingPlanForm: {}, + detailSettlementConfigTab: 'pre', + detailPreSettlementRuleForm: defaultSettlementRule(), + detailFormalSettlementRuleForm: defaultSettlementRule(), + detailPaymentRatioRows: [], + detailChangeRecordRows: [], flowBox: false, flowUrl: '', processInstanceId: '', @@ -1069,6 +1269,27 @@ export default { value: index + 1, })); }, + detailContractPeriod() { + const startDate = this.detailRow.startDate || '-'; + const endDate = this.detailRow.endDate || '-'; + return `${startDate} 至 ${endDate}`; + }, + detailLegalSealText() { + const value = this.detailRow.legalSealFlag; + if (value === undefined || value === null || value === '') return '-'; + return Number(value) === 1 ? '是' : '否'; + }, + detailFeeGenerationMode() { + const value = + this.detailRow.feeGenerationMode || + (Number(this.detailRow.billingEnabled) === 0 ? 'manual' : 'system'); + return value === 'manual' ? '手动生成' : '系统生成'; + }, + detailSettlementRule() { + return this.detailSettlementConfigTab === 'formal' + ? this.detailFormalSettlementRuleForm + : this.detailPreSettlementRuleForm; + }, }, watch: { '$route.fullPath'() { @@ -1718,16 +1939,55 @@ export default { openDetail(row) { this.detailBox = true; this.detailLoading = true; - this.detailRow = { ...row }; + this.applyDetailState(row); this.api .getDetail(row.id) .then(res => { - this.detailRow = res.data?.data || row; + this.applyDetailState(res.data?.data || row); }) .finally(() => { this.detailLoading = false; }); }, + applyDetailState(detail = {}) { + this.detailRow = { ...detail }; + this.detailContractFileRows = parseArray(detail.contractFileJson); + this.detailAttachmentRows = parseArray(detail.attachmentsJson); + this.detailBillingPlanRows = parseArray(detail.billingPlanJson); + this.detailPaymentRatioRows = parseArray(detail.paymentRatioJson); + this.detailChangeRecordRows = parseArray(detail.changeRecordJson); + const payload = parseObject(detail.settlementRuleJson); + const legacy = Object.keys(payload).some( + key => !['preSettlementConfig', 'formalSettlementConfig'].includes(key) + ) + ? payload + : {}; + const preConfig = parseObject(detail.preSettlementConfigJson); + const formalConfig = parseObject(detail.formalSettlementConfigJson); + this.detailPreSettlementRuleForm = this.normalizeSettlementRule( + payload.preSettlementConfig || (Object.keys(preConfig).length ? preConfig : legacy) + ); + this.detailFormalSettlementRuleForm = this.normalizeSettlementRule( + payload.formalSettlementConfig || (Object.keys(formalConfig).length ? formalConfig : legacy) + ); + this.detailSettlementConfigTab = 'pre'; + }, + openDetailBillingPlan(row, index) { + this.detailBillingPlanForm = clone(row); + this.detailBillingPlanIndex = index; + this.detailBillingPlanBox = true; + }, + displayValue(value) { + return value === undefined || value === null || value === '' ? '-' : value; + }, + detailUnitValue(prop, unit) { + const value = this.detailRow[prop]; + return value === undefined || value === null || value === '' ? '-' : `${value}${unit}`; + }, + detailObjectUnitValue(row, prop, unit) { + const value = row?.[prop]; + return value === undefined || value === null || value === '' ? '-' : `${value}${unit}`; + }, detailValue(prop) { const value = this.detailRow[prop]; if ( @@ -1743,7 +2003,7 @@ export default { if (prop === 'settlementRuleJson') return value ? '已配置' : '-'; if (prop === 'paymentRatioJson') return `${parseArray(value).length}项付款比例`; if (prop === 'feeGenerationMode') return value === 'manual' ? '手动生成' : '系统生成'; - return value === undefined || value === null || value === '' ? '-' : value; + return this.displayValue(value); }, handleOperation(operation, row) { if (operation.action === 'startChange') { @@ -2107,8 +2367,28 @@ export default { display: inline-flex; } -.contract-manage-detail section + section { - margin-top: 20px; +.contract-manage-detail { + max-height: 74vh; + overflow-y: auto; + + &__descriptions { + :deep(.el-descriptions__label) { + display: inline-block; + min-width: 96px; + padding-right: 12px; + color: #606266; + text-align: right; + } + + :deep(.el-descriptions__content) { + color: #303133; + word-break: break-word; + } + + :deep(.el-descriptions__cell) { + padding-bottom: 16px; + } + } } @media (max-width: 1200px) { diff --git a/src/views/business/loading-manage.vue b/src/views/business/loading-manage.vue index aa2ccaa..eab0003 100644 --- a/src/views/business/loading-manage.vue +++ b/src/views/business/loading-manage.vue @@ -2175,7 +2175,7 @@ export default { if (draftMode) { return true; } - if (this.dialogMode === 'add' && this.selectedWaybillRows.length < 2) { + if (this.selectedWaybillRows.length < 2) { ElMessage.warning('请至少选择两条待配载运单'); return false; } diff --git a/src/views/business/process-config.vue b/src/views/business/process-config.vue index 39167bf..4fc4639 100644 --- a/src/views/business/process-config.vue +++ b/src/views/business/process-config.vue @@ -797,11 +797,25 @@ export default { result.push( `是否接单:${node.confirmMode === 'no_confirm_accept' ? '无需确认接单' : '是'}` ); + result.push( + `确认接单人(司机):${node.confirmMode === 'yes' && node.confirmDriver ? '是' : '否'}` + ); return result; } if (node.key === 'return') { - if (node.uploadVoucher && node.voucherTypes.length) { - result.push(`上传凭证:${node.voucherTypes.join('、')}`); + result.push( + `是否确认:${node.confirmMode === 'no_confirm_complete' ? '无需确认完成' : '是'}` + ); + result.push( + `确认完成(内部人员):${ + node.confirmMode === 'yes' && node.confirmInternal ? '是' : '否' + }` + ); + if (node.supportVoucher) { + result.push(`上传凭证:${node.uploadVoucher ? '是' : '否'}`); + if (node.uploadVoucher && node.voucherTypes.length) { + result.push(`凭证类型:${node.voucherTypes.join('、')}`); + } } return result; } @@ -816,11 +830,17 @@ export default { result.push(`打卡操作:${node.punch ? '是' : '否'}`); result.push(`打卡定位:${node.location ? '是' : '否'}`); } - if (node.supportCargo && node.uploadCargo && node.cargoTypes.length) { - result.push(`上传货量:${node.cargoTypes.join('、')}`); + if (node.supportCargo) { + result.push(`上传货量:${node.uploadCargo ? '是' : '否'}`); + if (node.uploadCargo && node.cargoTypes.length) { + result.push(`上传货量类型:${node.cargoTypes.join('、')}`); + } } - if (node.supportVoucher && node.uploadVoucher && node.voucherTypes.length) { - result.push(`上传凭证:${node.voucherTypes.join('、')}`); + if (node.supportVoucher) { + result.push(`上传凭证:${node.uploadVoucher ? '是' : '否'}`); + if (node.uploadVoucher && node.voucherTypes.length) { + result.push(`凭证类型:${node.voucherTypes.join('、')}`); + } } return result; }, diff --git a/src/views/settlement/components/formal-settlement-editor.vue b/src/views/settlement/components/formal-settlement-editor.vue index cbb1034..1fa376a 100644 --- a/src/views/settlement/components/formal-settlement-editor.vue +++ b/src/views/settlement/components/formal-settlement-editor.vue @@ -709,18 +709,33 @@ export default { if (!rows.length) return; const first = rows[0]; const settlementType = this.initialData.settlementType || first.settlementType || 'payable'; - const contractId = first.contractId || null; + const matchedContract = this.allContracts.find( + item => + (first.contractId && String(item.id) === String(first.contractId)) || + (first.contractNo && String(item.contractNo) === String(first.contractNo)) + ); + const contractId = first.contractId || matchedContract?.id || null; + const projectId = first.projectId || matchedContract?.projectId || null; + const projectName = first.projectName || matchedContract?.projectName || ''; + this.contracts = this.allContracts.filter( + item => projectId && String(item.projectId) === String(projectId) + ); + if (projectId && !this.projects.some(item => String(item.id) === String(projectId))) { + this.projects.push({ id: projectId, name: projectName }); + } if (!this.contracts.some(item => String(item.id) === String(contractId))) { this.contracts.push({ id: contractId, contractNo: first.contractNo, contractName: first.contractName, - projectId: first.projectId, - projectName: first.projectName, - deptId: first.deptId, - deptName: first.deptName, + projectId, + projectName, + deptId: first.deptId || matchedContract?.deptId, + deptName: first.deptName || matchedContract?.deptName, payerName: first.payerName, payeeName: first.payeeName, + partyA: matchedContract?.partyA, + partyB: matchedContract?.partyB, settlementType, }); } @@ -729,8 +744,8 @@ export default { contractId, contractNo: first.contractNo || this.form.contractNo, contractName: first.contractName || this.form.contractName, - projectId: first.projectId || this.form.projectId, - projectName: first.projectName || this.form.projectName, + projectId: projectId || this.form.projectId, + projectName: projectName || this.form.projectName, deptId: first.deptId || this.form.deptId, deptName: first.deptName || this.form.deptName, payerName: first.payerName || this.form.payerName, @@ -1035,10 +1050,7 @@ export default { return columns.map((column, index) => { if (index === 0) return '合计'; if (!sumProps.includes(column.property)) return ''; - const total = data.reduce( - (sum, row) => sum + Number(row[column.property] || 0), - 0 - ); + const total = data.reduce((sum, row) => sum + Number(row[column.property] || 0), 0); return this.formatMoney(total); }); }, diff --git a/src/views/settlement/components/pre-settlement-editor.vue b/src/views/settlement/components/pre-settlement-editor.vue index 9f2bb62..53f8e59 100644 --- a/src/views/settlement/components/pre-settlement-editor.vue +++ b/src/views/settlement/components/pre-settlement-editor.vue @@ -1010,6 +1010,15 @@ export default { this.$message.warning('请至少选择一条结算明细'); return; } + const invalidManualFeeIndex = this.summaryFees.findIndex( + row => + Number(row.manualFlag) === 1 && + (!String(row.feeType || '').trim() || !String(row.feeItem || '').trim()) + ); + if (invalidManualFeeIndex >= 0) { + this.$message.warning(`请完善结算合计第${invalidManualFeeIndex + 1}行的费用类型和费用项`); + return; + } const stateKey = shouldSubmit ? 'submitting' : 'saving'; this[stateKey] = true; try { @@ -1040,7 +1049,7 @@ export default { sourceDetailIds: this.details.map(row => row.sourceDetailId || row.id), summaryFees: this.summaryFees.map(row => ({ id: row.id || undefined, - feeType: row.feeType, + feeType: row.feeType || '', feeItem: row.feeItem, adjustAmount: Number(row.adjustAmount || 0), remark: row.remark, @@ -1067,7 +1076,7 @@ export default { return this.feeOptions.find(item => item.feeType === feeType)?.feeItems || []; }, feeCategoryName(value) { - if (value === undefined || value === null || value === '') return '-'; + if (value === undefined || value === null || value === '') return ''; const option = this.feeCategoryOptions.find( item => String(item.dictKey) === String(value) || String(item.dictValue) === String(value) ); diff --git a/src/views/settlement/receivable-payable-detail.vue b/src/views/settlement/receivable-payable-detail.vue index 1081026..2ff8577 100644 --- a/src/views/settlement/receivable-payable-detail.vue +++ b/src/views/settlement/receivable-payable-detail.vue @@ -318,7 +318,7 @@ 预结算单 正式结算单 @@ -343,18 +343,26 @@ - 查询 + 查询
- - + + {{ formatColumnValue(row, column) }} +
+ +
- + @@ -4461,6 +4470,41 @@ export default { font-weight: 400; } +.qualification-file-name-cell { + display: block; + width: 100%; + min-width: 0; + overflow: hidden; + text-align: left; + text-overflow: ellipsis; + white-space: nowrap; +} + +.qualification-file-name { + display: block; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: middle; + white-space: nowrap; + cursor: pointer; + color: #606266; + transition: color 0.2s ease; + + &:hover { + color: #409eff; + } + + &.is-disabled { + cursor: default; + color: #606266; + + &:hover { + color: #606266; + } + } +} + .qualification-upload-bar { display: grid; grid-template-columns: 1fr auto 1fr; diff --git a/src/views/vehicle/maintenance-plan.vue b/src/views/vehicle/maintenance-plan.vue index 5e49f5b..b8e06ff 100644 --- a/src/views/vehicle/maintenance-plan.vue +++ b/src/views/vehicle/maintenance-plan.vue @@ -70,6 +70,15 @@ class="maintenance-plan-page__input" /> + @@ -154,6 +168,7 @@ import { openImportDialog } from '@/utils/import-excel'; import { formatUpdateUserName } from '@/utils/audit'; import { getToken } from '@/utils/auth'; import { normalizeSearchRangeParams } from '@/utils/search-range'; +import AddressMapPicker from '@/components/address-map-picker/main.vue'; import { mapGetters } from 'vuex'; import NProgress from 'nprogress'; import 'nprogress/nprogress.css'; @@ -163,6 +178,9 @@ const createTimeRangeMap = { }; export default { + components: { + AddressMapPicker, + }, data() { const validateNonNegative = (rule, value, callback) => { if (value === undefined || value === null || value === '') { @@ -178,6 +196,7 @@ export default { query: {}, loading: true, excelBox: false, + addressMapPickerVisible: false, excelForm: {}, page: { pageSize: 10, @@ -203,6 +222,7 @@ export default { border: true, index: true, indexLabel: '序号', + indexWidth: 90, viewBtn: true, selection: true, dialogClickModal: false, @@ -315,6 +335,8 @@ export default { { label: '地址', prop: 'address', + slot: true, + formslot: true, minWidth: 180, overHidden: true, span: 24, @@ -483,6 +505,14 @@ export default { }, }, methods: { + openAddressMapPicker() { + if (this.boxType !== 'view') { + this.addressMapPickerVisible = true; + } + }, + handleAddressMapConfirm(address) { + this.form.address = address; + }, hasPermission(code) { return this.isAdmin || this.validData(this.permission[code], false); }, diff --git a/src/views/vehicle/maintenance-record.vue b/src/views/vehicle/maintenance-record.vue index 4fdef2e..e9a8985 100644 --- a/src/views/vehicle/maintenance-record.vue +++ b/src/views/vehicle/maintenance-record.vue @@ -70,6 +70,15 @@ class="maintenance-record-page__input" /> + @@ -140,6 +154,7 @@ import { openImportDialog } from '@/utils/import-excel'; import { formatUpdateUserName } from '@/utils/audit'; import { getToken } from '@/utils/auth'; import { normalizeSearchRangeParams } from '@/utils/search-range'; +import AddressMapPicker from '@/components/address-map-picker/main.vue'; import { mapGetters } from 'vuex'; import NProgress from 'nprogress'; import 'nprogress/nprogress.css'; @@ -149,6 +164,9 @@ const createTimeRangeMap = { }; export default { + components: { + AddressMapPicker, + }, data() { const validateNonNegative = (rule, value, callback) => { if (value === undefined || value === null || value === '') { @@ -164,6 +182,7 @@ export default { query: {}, loading: true, excelBox: false, + addressMapPickerVisible: false, excelForm: {}, isDetailLoading: false, page: { @@ -190,6 +209,7 @@ export default { border: true, index: true, indexLabel: '序号', + indexWidth: 90, viewBtn: true, selection: true, dialogClickModal: false, @@ -291,6 +311,8 @@ export default { { label: '地址', prop: 'address', + slot: true, + formslot: true, minWidth: 180, overHidden: true, span: 24, @@ -478,6 +500,14 @@ export default { }, }, methods: { + openAddressMapPicker() { + if (this.boxType !== 'view') { + this.addressMapPickerVisible = true; + } + }, + handleAddressMapConfirm(address) { + this.form.address = address; + }, hasPermission(code) { return this.isAdmin || this.validData(this.permission[code], false); }, diff --git a/src/views/vehicle/mileage-record.vue b/src/views/vehicle/mileage-record.vue index ec42494..1467676 100644 --- a/src/views/vehicle/mileage-record.vue +++ b/src/views/vehicle/mileage-record.vue @@ -90,7 +90,7 @@ placeholder="请输入" @input="value => normalizeIntegerInput('previousMonthMileage', value)" > - + + + @@ -167,6 +181,7 @@ import { downloadXls } from '@/utils/util'; import { openImportDialog } from '@/utils/import-excel'; import { getToken } from '@/utils/auth'; import { normalizeSearchRangeParams } from '@/utils/search-range'; +import AddressMapPicker from '@/components/address-map-picker/main.vue'; import { mapGetters } from 'vuex'; import { excelOption, option } from '@/option/vehicle/violation-record'; import NProgress from 'nprogress'; @@ -177,12 +192,16 @@ const createTimeRangeMap = { }; export default { + components: { + AddressMapPicker, + }, data() { return { form: {}, query: {}, loading: true, excelBox: false, + locationMapPickerVisible: false, excelForm: {}, isDetailLoading: false, option, @@ -242,6 +261,14 @@ export default { }, }, methods: { + openLocationMapPicker() { + if (this.boxType !== 'view') { + this.locationMapPickerVisible = true; + } + }, + handleLocationMapConfirm(address) { + this.form.location = address; + }, hasPermission(code) { return this.isAdmin || this.validData(this.permission[code], false); },