From 74780b8777aa8cd1a356546cd0b345d6c6682609 Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Wed, 14 Oct 2026 12:59:27 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E6=AD=A3=E5=BC=8F=E7=BB=93?= =?UTF-8?q?=E7=AE=97=E4=BF=9D=E5=AD=98=E8=8D=89=E7=A8=BF=E4=B8=8E=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E5=85=A5=E5=8F=A3=EF=BC=8C=E9=A1=B9=E7=9B=AE=E7=94=B3?= =?UTF-8?q?=E8=AF=B7=E5=B9=B3=E5=8F=B0=E5=85=AC=E5=8F=B8=E5=A4=9A=E9=80=89?= =?UTF-8?q?=E7=AD=89=E4=B8=9A=E5=8A=A1=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- src/option/business/project-apply.js | 2 +- .../components/waybill-import-dialog.vue | 35 +++++-- .../components/waybill-manage-page.vue | 39 ++++---- src/views/business/project-apply.vue | 97 +++++++++++++++---- .../payment/payment-application-form.vue | 80 ++++++++++----- .../components/formal-settlement-editor.vue | 12 ++- .../settlement/formal-settlement-form.vue | 6 ++ src/views/settlement/formal-settlement.vue | 6 ++ .../settlement/receivable-payable-detail.vue | 15 +++ src/views/vehicle/customer-archive.vue | 2 +- 10 files changed, 219 insertions(+), 75 deletions(-) diff --git a/src/option/business/project-apply.js b/src/option/business/project-apply.js index 445f9cb..9a96ad6 100644 --- a/src/option/business/project-apply.js +++ b/src/option/business/project-apply.js @@ -185,7 +185,7 @@ export const option = createCrudOption([ searchPlaceholder: '请输入', searchOrder: 8, minWidth: 150, - rules: textRule('平台公司', 50, true), + rules: textRule('平台公司', 500, true), }, { label: '平台公司ID', diff --git a/src/views/business/components/waybill-import-dialog.vue b/src/views/business/components/waybill-import-dialog.vue index b08eedd..3645750 100644 --- a/src/views/business/components/waybill-import-dialog.vue +++ b/src/views/business/components/waybill-import-dialog.vue @@ -887,11 +887,14 @@ const syncCarrierOptions = () => { carrierOptions.value = []; return; } - const carrierId = + const carrierId = resolveCarrierArchiveId( customerContract?.partyBId || - customerContract?.partyBUserId || - customerContract?.customerId || - ''; + customerContract?.partyBUserId || + customerContract?.contractPartyBId || + customerContract?.customerId || + '', + carrierName + ); carrierOptions.value = [{ id: carrierId, carrierName, label: carrierName, value: carrierName }]; form.carrierId = carrierId; form.carrierIds = carrierId ? [carrierId] : []; @@ -1317,12 +1320,20 @@ const contractChange = id => { const carrierTypeChange = () => { syncCarrierOptions(); }; +const resolveCarrierArchiveId = (carrierId, carrierName) => { + if (carrierId) return carrierId; + const name = String(carrierName || '').trim(); + if (!name) return ''; + const matched = queryCarriers.value.find(item => String(item.name || '').trim() === name); + return matched?.id || ''; +}; const carrierChange = value => { const item = carrierOptions.value.find(option => String(option.value) === String(value || '')); clearCarrier(); if (!item) return; - form.carrierId = item.id || ''; - form.carrierIds = item.id ? [item.id] : []; + const carrierId = resolveCarrierArchiveId(item.id, item.carrierName); + form.carrierId = carrierId; + form.carrierIds = carrierId ? [carrierId] : []; form.carrierName = item.carrierName || ''; if (!isSelfOperated.value) form.carrierContractId = item.carrierContractId || ''; }; @@ -1738,11 +1749,23 @@ const normalizeOptionalPlanId = value => { const buildImportPayload = (status = form.status, rowList = rows.value, extra = {}) => { const { file, ...batch } = form; const planId = normalizeOptionalPlanId(batch.planId); + const carrierId = resolveCarrierArchiveId( + batch.carrierId || batch.carrierIds?.[0] || '', + batch.carrierName + ); + const carrierIds = + Array.isArray(batch.carrierIds) && batch.carrierIds.length + ? batch.carrierIds + : carrierId + ? [carrierId] + : []; return { ...batch, planId, // 未选计划时不带占位名称,避免落库/回显 -1 planName: planId ? batch.planName || undefined : null, + carrierId: carrierId || undefined, + carrierIds, status, rows: rowList.map(normalizeImportRow).map(stripInternalRowFields), ...extra, diff --git a/src/views/business/components/waybill-manage-page.vue b/src/views/business/components/waybill-manage-page.vue index 6bd03c8..8e331a3 100644 --- a/src/views/business/components/waybill-manage-page.vue +++ b/src/views/business/components/waybill-manage-page.vue @@ -6508,17 +6508,10 @@ export default { if (isAddMode) { delete submitRow.id; delete submitRow.waybillNo; - // 复制提交:状态列未进表单,Avue 可能不带回;显式保留原运单业务状态(含已完成) + // 复制新建:业务状态固定为草稿,不沿用原运单状态 const copyId = this.$route.query.copyId || ''; - if (copyId && !saveAsDraft) { - const copiedStatus = - this.form?.businessStatus || - this.copySourceBusinessStatus || - submitRow.businessStatus || - ''; - if (copiedStatus) { - submitRow.businessStatus = copiedStatus; - } + if (copyId) { + submitRow.businessStatus = this.config.draftStatus || 'draft'; } } const skipValidation = saveAsDraft && this.config.skipDraftValidation === true; @@ -6541,14 +6534,16 @@ export default { if (this.config.enableAttachmentTable) { submitRow.attachmentsJson = JSON.stringify(this.attachmentRows); } + const isCopyAdd = Boolean(this.$route.query.copyId) && isAddMode; if (this.config.saveAsDraft && saveAsDraft) { submitRow[this.config.draftStatusProp || 'approvalStatus'] = this.config.draftStatus || 'draft'; } else if ( + !isCopyAdd && this.config.draftStatusProp === 'businessStatus' && submitRow.businessStatus === (this.config.draftStatus || 'draft') ) { - // 正式提交:清空草稿态,由后端按过程配置校正为待执行/进行中 + // 正式提交:清空草稿态,由后端按过程配置校正为待执行/进行中(复制新建除外,保持草稿) submitRow.businessStatus = ''; } delete submitRow.basicInfoTitle; @@ -6811,12 +6806,18 @@ export default { } }, rowSave(row, done, loading) { - const submitRow = this.normalizeRow(row); + // 复制页提交走草稿保存,避免 submit 接口把 draft 校正为待执行/进行中 + const isCopySubmit = Boolean(this.$route.query.copyId); + const submitRow = this.normalizeRow(row, isCopySubmit); if (!submitRow) { this.stopSubmitLoading(loading); return; } - this.getSaveRequest()(submitRow).then( + const request = + isCopySubmit && typeof this.api.saveDraft === 'function' + ? this.api.saveDraft + : this.getSaveRequest(); + request(submitRow).then( res => { this.markWaybillListNeedRefresh(); this.onLoad(this.page); @@ -6945,13 +6946,14 @@ export default { if (type === 'add' && copyId) { this.api.getDetail(copyId).then(res => { const sourceData = res.data.data || {}; - // 原运单业务状态(如 completed)提交时需原样带回并生成应收应付 - this.copySourceBusinessStatus = sourceData.businessStatus || ''; - // 清除不应该复制的字段(业务状态与原运单保持一致) + this.copySourceBusinessStatus = ''; + // 清除不应该复制的字段;复制新建固定为草稿 delete sourceData.id; delete sourceData.code; delete sourceData.waybillNo; delete sourceData.waybillStatus; + delete sourceData.businessStatus; + delete sourceData.businessStatusName; delete sourceData.driverAcceptStatus; delete sourceData.driverAcceptTime; delete sourceData.driverAcceptDriverId; @@ -6968,11 +6970,8 @@ export default { delete sourceData.updateUserName; this.applyFormDetail(sourceData); - // 标记为手工创建;业务状态沿用原运单(含已完成) this.form.dataSource = '手工创建'; - if (this.copySourceBusinessStatus) { - this.form.businessStatus = this.copySourceBusinessStatus; - } + this.form.businessStatus = this.config.draftStatus || 'draft'; done(); }); return; diff --git a/src/views/business/project-apply.vue b/src/views/business/project-apply.vue index 13f28b1..4580fd0 100644 --- a/src/views/business/project-apply.vue +++ b/src/views/business/project-apply.vue @@ -231,6 +231,9 @@ ({ projectShortName: '', businessDeptId: '', businessDeptName: '', - undertakeDeptId: '', + undertakeDeptId: [], undertakeDeptName: '', projectSource: '', sourceRemark: '', @@ -1146,7 +1149,15 @@ export default { { max: 20, message: '项目简称不能超过20个字符', trigger: 'blur' }, ], businessDeptId: [{ required: true, message: '请选择业务部门', trigger: 'change' }], - undertakeDeptId: [{ required: true, message: '请选择平台公司', trigger: 'change' }], + undertakeDeptId: [ + { + type: 'array', + required: true, + min: 1, + message: '请选择平台公司', + trigger: 'change', + }, + ], businessMode: [{ required: true, message: '请选择业务模式', trigger: 'change' }], customerNames: [{ required: true, message: '请选择客户名称', trigger: 'change' }], carrierNames: [{ required: true, message: '请选择下游承运商', trigger: 'change' }], @@ -1508,13 +1519,13 @@ export default { this.businessDeptTreeOptions = [{ label, value: detail.businessDeptId }]; this.deptOptions = [{ label, rawLabel: label, value: detail.businessDeptId }]; } - if (detail.undertakeDeptId) { - this.platformCompanyOptions = [ - { - label: detail.undertakeDeptName || String(detail.undertakeDeptId), - value: detail.undertakeDeptId, - }, - ]; + const undertakeDeptIds = this.parseUndertakeDeptIds(detail.undertakeDeptId); + const undertakeDeptNames = this.parseUndertakeDeptNames(detail.undertakeDeptName); + if (undertakeDeptIds.length) { + this.platformCompanyOptions = undertakeDeptIds.map((id, index) => ({ + label: undertakeDeptNames[index] || String(id), + value: id, + })); } }, resolvePublicDictOptions(options, currentValue) { @@ -1860,6 +1871,8 @@ export default { this.form = { ...form, ...situation, + undertakeDeptId: this.parseUndertakeDeptIds(form.undertakeDeptId), + undertakeDeptName: form.undertakeDeptName || '', customerNames: this.getCustomerRowNames(customerRows, 'customer') || form.customerNames, carrierNames: this.getCustomerRowNames(carrierRows, 'carrier') || form.carrierNames, }; @@ -2026,8 +2039,13 @@ export default { }); }, normalizeSubmitForm(options = {}) { + const undertakeDeptIds = this.parseUndertakeDeptIds(this.form.undertakeDeptId); + const undertakeDeptName = + this.resolveUndertakeDeptNames(undertakeDeptIds) || this.form.undertakeDeptName || ''; const submitRow = { ...this.form, + undertakeDeptId: undertakeDeptIds.join(','), + undertakeDeptName, fundLimit: this.formatAmount(this.form.fundLimit), receivableLimit: this.formatAmount(this.form.receivableLimit), ...Object.fromEntries( @@ -2419,11 +2437,40 @@ export default { this.$refs.projectForm?.validateField('businessDeptId'); }, handleUndertakeDeptChange(value) { - const id = Array.isArray(value) ? value.at(-1) : value; - const dept = this.platformCompanyOptions.find(item => String(item.value) === String(id)); - this.form.undertakeDeptName = dept ? dept.label : ''; + const ids = this.parseUndertakeDeptIds(value); + this.form.undertakeDeptId = ids; + this.form.undertakeDeptName = this.resolveUndertakeDeptNames(ids); this.$refs.projectForm?.validateField('undertakeDeptId'); }, + parseUndertakeDeptIds(value) { + if (Array.isArray(value)) { + return value + .map(item => String(item ?? '').trim()) + .filter(item => item && item !== '-1'); + } + return String(value ?? '') + .split(/[,,]/) + .map(item => item.trim()) + .filter(item => item && item !== '-1'); + }, + parseUndertakeDeptNames(value) { + return String(value ?? '') + .split(/[,,、]/) + .map(item => item.trim()) + .filter(Boolean); + }, + resolveUndertakeDeptNames(ids = []) { + return ids + .map(id => { + const dept = this.platformCompanyOptions.find(item => String(item.value) === String(id)); + return dept?.label || ''; + }) + .filter(Boolean) + .join('、'); + }, + firstUndertakeDeptId(value) { + return this.parseUndertakeDeptIds(value)[0] || ''; + }, // 由部门 id 反查「根 → 该节点」的 id 路径,供级联回显(找不到返回 null) findDeptPath(id, tree = [], parents = []) { const target = Array.isArray(id) ? id.at(-1) : id; @@ -2462,15 +2509,23 @@ export default { }); }, ensureCurrentPlatformOption() { - const id = this.form.undertakeDeptId; - if (!id) return; - const exists = this.platformCompanyOptions.some(item => String(item.value) === String(id)); - if (!exists) { - this.platformCompanyOptions.unshift({ - label: this.form.undertakeDeptName || String(id), - value: id, - }); - } + const ids = this.parseUndertakeDeptIds(this.form.undertakeDeptId); + if (!ids.length) return; + const names = this.parseUndertakeDeptNames(this.form.undertakeDeptName); + ids.forEach((id, index) => { + const exists = this.platformCompanyOptions.some(item => String(item.value) === String(id)); + if (!exists) { + this.platformCompanyOptions.unshift({ + label: names[index] || String(id), + value: id, + }); + } + }); + // 选项 value 与已选 ID 类型对齐,避免回显失败 + this.form.undertakeDeptId = ids.map(id => { + const matched = this.platformCompanyOptions.find(item => String(item.value) === String(id)); + return matched ? matched.value : id; + }); }, excludeExternalOrganization(list = []) { return list.reduce((result, item) => { diff --git a/src/views/payment/payment-application-form.vue b/src/views/payment/payment-application-form.vue index 38f4b19..f2e904b 100644 --- a/src/views/payment/payment-application-form.vue +++ b/src/views/payment/payment-application-form.vue @@ -36,23 +36,25 @@
- - + class="payment-form-page__settlement-select" + @visible-change="handleSettlementSelectVisible" + > + + - + @@ -874,6 +876,10 @@ export default { isSettlementRelatedType() { return ['progress_advance', 'settlement_payment'].includes(this.form.paymentType); }, + // 项目预付、进度预付不展示发票信息,仅尾款付款展示 + showInvoiceSection() { + return this.form.paymentType === 'settlement_payment'; + }, canSelectPreSettlement() { // 进度预付仅预结算;尾款付款可正式/预结算 return ['progress_advance', 'settlement_payment'].includes(this.form.paymentType); @@ -926,6 +932,9 @@ export default { referenceType: this.form.preSettlementId ? 'pre' : 'formal', })); }, + referenceSettlementNos() { + return this.referenceSettlementLinks.map(item => item.settlementNo).filter(Boolean); + }, settlementRows() { if (this.referenceRows.length) { return this.referenceRows.map(item => ({ @@ -1110,7 +1119,7 @@ export default { callback(); }, validateInvoices() { - if (this.form.paymentType === 'project_advance') return; + if (!this.showInvoiceSection) return; for (const invoice of this.form.invoices || []) { if (String(invoice.settlementNo || '').length > 100) { throw new Error('结算单号不能超过100个字符'); @@ -1869,6 +1878,7 @@ export default { this.contractPaymentRatioExceeded = exceeded; }, handleTypeChange() { + // 切换类型时清空发票;预付类型本身也不再展示/提交发票 this.form.invoices = []; this.paymentRecords = []; Object.assign(this.form, { @@ -2056,8 +2066,20 @@ export default { const project = this.projectOptions.find(item => String(item.id) === String(projectId)); this.form.projectName = project?.projectName || project?.projectShortName || ''; Object.assign(this.form, { - deptId: project?.undertakeDeptId || project?.businessDeptId || null, - deptName: project?.undertakeDeptName || project?.businessDeptName || '', + deptId: + String(project?.undertakeDeptId || '') + .split(/[,,]/) + .map(item => item.trim()) + .find(Boolean) || + project?.businessDeptId || + null, + deptName: + String(project?.undertakeDeptName || '') + .split(/[,,、]/) + .map(item => item.trim()) + .find(Boolean) || + project?.businessDeptName || + '', contractId: null, contractNo: '', contractName: '', @@ -2225,6 +2247,13 @@ export default { } return Promise.all(loaders); }, + handleSettlementSelectVisible(visible) { + // 结算单号仅做展示,通过「选择」按钮维护,避免下拉改值 + if (!visible) return; + this.$nextTick(() => { + this.$refs.settlementNoSelectRef?.blur?.(); + }); + }, handleReferenceSearch() { this.formalPage.current = 1; this.prePage.current = 1; @@ -2588,7 +2617,7 @@ export default { attachmentsJson: JSON.stringify(attachments || []), remark, invoices: - paymentType === 'project_advance' + paymentType === 'project_advance' || paymentType === 'progress_advance' ? [] : invoices.map(invoice => ({ settlementNo: invoice.settlementNo || '', @@ -2669,13 +2698,18 @@ export default { display: flex; align-items: center; gap: 8px; - flex-wrap: wrap; width: 100%; min-height: 32px; } -.payment-form-page__settlement-nos :deep(.el-input) { +.payment-form-page__settlement-select { flex: 1; - min-width: 120px; + min-width: 0; +} +.payment-form-page__settlement-select :deep(.el-select__wrapper) { + width: 100%; +} +.payment-form-page__settlement-select :deep(.el-tag__close) { + display: none; } .payment-form-page__settlement-select-btn { flex-shrink: 0; diff --git a/src/views/settlement/components/formal-settlement-editor.vue b/src/views/settlement/components/formal-settlement-editor.vue index a52229b..6225372 100644 --- a/src/views/settlement/components/formal-settlement-editor.vue +++ b/src/views/settlement/components/formal-settlement-editor.vue @@ -658,9 +658,10 @@
+ 同步 取消 @@ -16,6 +17,8 @@ import FormalSettlementEditor from './components/formal-settlement-editor.vue'; import { readSettlementTransfer, removeSettlementTransfer } from '@/utils/settlement-transfer'; +const LIST_REFRESH_KEY = 'formal-settlement-list-refresh'; + export default { name: 'FormalSettlementForm', components: { FormalSettlementEditor }, @@ -54,6 +57,9 @@ export default { this.syncTagTitle(); }, methods: { + markListNeedRefresh() { + sessionStorage.setItem(LIST_REFRESH_KEY, '1'); + }, goBack() { removeSettlementTransfer(this.$route.query.transferToken); this.$router.$avueRouter.closeTag(); diff --git a/src/views/settlement/formal-settlement.vue b/src/views/settlement/formal-settlement.vue index f1c1e05..2658d55 100644 --- a/src/views/settlement/formal-settlement.vue +++ b/src/views/settlement/formal-settlement.vue @@ -262,6 +262,12 @@ export default { this.mkEnabled = false; }); }, + activated() { + if (sessionStorage.getItem('formal-settlement-list-refresh') === '1') { + sessionStorage.removeItem('formal-settlement-list-refresh'); + this.loadTable(); + } + }, methods: { hasPermission(code) { return this.permission?.[code] !== false; diff --git a/src/views/settlement/receivable-payable-detail.vue b/src/views/settlement/receivable-payable-detail.vue index 956fe3e..0257356 100644 --- a/src/views/settlement/receivable-payable-detail.vue +++ b/src/views/settlement/receivable-payable-detail.vue @@ -2693,6 +2693,10 @@ export default { return this.formatCell(row?.[prop]); }, formatFeeDetailCell(row, prop) { + if (prop === 'transportQuantityText' || prop === 'transportQuantity') { + if (this.isBlankTransportQuantity(row)) return '-'; + return this.formatDetailCell(row, prop); + } if (prop !== 'originalAmountText') return this.formatDetailCell(row, prop); const adjustAmount = Number(row?.adjustAmount || 0); @@ -2711,6 +2715,17 @@ export default { formatCell(value) { return value === null || value === undefined || value === '' ? '-' : value; }, + isBlankTransportQuantity(row = {}) { + const quantity = row.transportQuantity; + if (quantity !== null && quantity !== undefined && quantity !== '') { + const amount = Number(quantity); + return !Number.isFinite(amount) || amount === 0 || amount === -1; + } + const text = String(row.transportQuantityText || '').trim(); + if (!text || text === '-') return true; + const amount = Number(text.split(/\s+/)[0]); + return Number.isFinite(amount) && (amount === 0 || amount === -1); + }, parseJson(value) { if (!value) return []; try { diff --git a/src/views/vehicle/customer-archive.vue b/src/views/vehicle/customer-archive.vue index 1fd1182..daa7b9e 100644 --- a/src/views/vehicle/customer-archive.vue +++ b/src/views/vehicle/customer-archive.vue @@ -138,7 +138,7 @@ type="warning" v-if=" hasPermission('customer_archive_submit') && - ['draft', 'reviewing'].includes(row.approvalStatus) + row.approvalStatus === 'reviewing' " @click="handleWithdrawApproval(row)" >