From fbad28b71736feb21c67ad443526e3689ce80c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com> Date: Thu, 15 Oct 2026 11:29:29 +0800 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20=E4=BB=98=E6=AC=BE=E7=94=B3?= =?UTF-8?q?=E8=AF=B7/=E5=BC=80=E7=A5=A8=E7=94=B3=E8=AF=B7/=E6=AD=A3?= =?UTF-8?q?=E5=BC=8F=E7=BB=93=E7=AE=97=E5=8D=95=E5=88=97=E8=A1=A8=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E9=87=91=E8=9D=B6=E8=A7=A3=E9=94=81=E7=BB=88=E6=80=81?= =?UTF-8?q?=E6=A0=87=E8=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - lockTag 增加解锁终态分支:金蝶删单红tag、金蝶解锁橙tag,tooltip 展示操作人与解锁时间 - 数据来自后端 fillUnlockMarks 回填的 kingdeeUnlockTime/kingdeeUnlockOperator/kingdeeUnlockOperationName --- src/views/payment/invoice-application.vue | 10 ++++++++++ src/views/payment/payment-application.vue | 10 ++++++++++ .../components/formal-settlement-table-panel.vue | 10 ++++++++++ 3 files changed, 30 insertions(+) diff --git a/src/views/payment/invoice-application.vue b/src/views/payment/invoice-application.vue index c64e07f..6faa32d 100644 --- a/src/views/payment/invoice-application.vue +++ b/src/views/payment/invoice-application.vue @@ -481,6 +481,16 @@ export default { tip: row.kingdeeLockFailReason || '系统将自动补偿重锁', }; } + // 金蝶解锁终态标识:删单解锁的源单已作废,此标签用于区分人工作废与金蝶删单 + if (row.kingdeeUnlockTime) { + const isDelete = row.kingdeeUnlockOperationName === '金蝶删单'; + return { + show: true, + type: isDelete ? 'danger' : 'warning', + label: row.kingdeeUnlockOperationName || '金蝶解锁', + tip: `${row.kingdeeUnlockOperator || '-'} 于 ${this.$dayjs(row.kingdeeUnlockTime).format('YYYY-MM-DD HH:mm:ss')} 解锁`, + }; + } return { show: false }; }, displayValue(value) { diff --git a/src/views/payment/payment-application.vue b/src/views/payment/payment-application.vue index 9980357..0f8b072 100644 --- a/src/views/payment/payment-application.vue +++ b/src/views/payment/payment-application.vue @@ -529,6 +529,16 @@ export default { tip: row.kingdeeLockFailReason || '系统将自动补偿重锁', }; } + // 金蝶解锁终态标识:后端按 unlocked 锁记录回填(付款申请不作废,仅此标识可区分金蝶删单与推送失败) + if (row.kingdeeUnlockTime) { + const isDelete = row.kingdeeUnlockOperationName === '金蝶删单'; + return { + show: true, + type: isDelete ? 'danger' : 'warning', + label: row.kingdeeUnlockOperationName || '金蝶解锁', + tip: `${row.kingdeeUnlockOperator || '-'} 于 ${this.$dayjs(row.kingdeeUnlockTime).format('YYYY-MM-DD HH:mm:ss')} 解锁`, + }; + } return { show: false }; }, displayValue(value) { diff --git a/src/views/settlement/components/formal-settlement-table-panel.vue b/src/views/settlement/components/formal-settlement-table-panel.vue index eaa1868..8813d6e 100644 --- a/src/views/settlement/components/formal-settlement-table-panel.vue +++ b/src/views/settlement/components/formal-settlement-table-panel.vue @@ -252,6 +252,16 @@ export default { tip: row.kingdeeLockFailReason || '系统将自动补偿重锁', }; } + // 金蝶解锁终态标识:删单解锁的源单已作废,此标签用于区分人工作废与金蝶删单 + if (row.kingdeeUnlockTime) { + const isDelete = row.kingdeeUnlockOperationName === '金蝶删单'; + return { + show: true, + type: isDelete ? 'danger' : 'warning', + label: row.kingdeeUnlockOperationName || '金蝶解锁', + tip: `${row.kingdeeUnlockOperator || '-'} 于 ${this.$dayjs(row.kingdeeUnlockTime).format('YYYY-MM-DD HH:mm:ss')} 解锁`, + }; + } return { show: false }; }, displayValue(value) { From cf542313f7d212f371781431121cfe3cfdcb72fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Thu, 15 Oct 2026 14:24:31 +0800 Subject: [PATCH 2/7] =?UTF-8?q?style(contract-manage):=20=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=E6=8E=89=E8=A1=A8=E5=8D=95=E9=A1=B9=E5=BA=95=E9=83=A8?= =?UTF-8?q?=E5=A4=96=E8=BE=B9=E8=B7=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将.el-form-item的margin-bottom属性注释,调整样式间距 - 保持对齐方式不变以确保布局稳定 - 便于后续样式调整和优化维护 --- src/views/business/contract-manage.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/business/contract-manage.vue b/src/views/business/contract-manage.vue index 088ea83..e2fb98e 100644 --- a/src/views/business/contract-manage.vue +++ b/src/views/business/contract-manage.vue @@ -3338,7 +3338,7 @@ export default { :deep(.el-form-item) { align-items: center; - margin-bottom: 24px; + //margin-bottom: 24px; } :deep(.el-form-item__label) { From f23d65e41e3e63e3698c13a812b736e81036d5eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com> Date: Thu, 15 Oct 2026 21:03:30 +0800 Subject: [PATCH 3/7] =?UTF-8?q?:fix:=20=E5=AE=A2=E5=95=86=E5=AE=A1?= =?UTF-8?q?=E6=A0=B8=E9=80=9A=E8=BF=87=E5=90=8E=E6=8E=A8=E9=80=81=E9=87=91?= =?UTF-8?q?=E8=9D=B6=E5=A4=B1=E8=B4=A5=E5=8E=9F=E5=9B=A0=E4=BB=A5=E8=AD=A6?= =?UTF-8?q?=E5=91=8A=E6=8F=90=E7=A4=BA=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/vehicle/customer-archive.vue | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/views/vehicle/customer-archive.vue b/src/views/vehicle/customer-archive.vue index c8c1ab7..4f4dea4 100644 --- a/src/views/vehicle/customer-archive.vue +++ b/src/views/vehicle/customer-archive.vue @@ -5178,17 +5178,20 @@ export default { this.$message.error('获取审核跳转地址失败'); }); }, - handleApprove(row) { - this.$confirm('是否确认审核通过?', '提示', { + async handleApprove(row) { + await this.$confirm('是否确认审核通过?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', - }) - .then(() => approve(row.id)) - .then(() => { - this.onLoad(this.page); - this.$message({ type: 'success', message: '审核通过!' }); - }); + }); + const res = await approve(row.id); + // data=false 表示审核已通过但推送金蝶失败,msg 带具体原因 + if (res.data.data === false) { + this.$message.warning(res.data.msg); + } else { + this.$message.success(res.data.msg || '审核通过!'); + } + this.onLoad(this.page); }, async handleSyncKingdee(row) { if (this.kingdeeSyncingId) return; From c5a74acd00c0fdb6cbf990015ad36babb410bc42 Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Fri, 16 Oct 2026 00:56:55 +0800 Subject: [PATCH 4/7] =?UTF-8?q?:sparkles:=20=E6=96=B0=E5=A2=9E=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E7=BB=93=E7=AE=97=E5=8D=95=E9=A1=B5=E9=9D=A2=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E4=BC=98=E5=8C=96=20MK=20=E5=BC=80=E5=85=B3=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E4=B8=8E=E5=AE=A2=E5=95=86=E5=90=8C=E6=AD=A5=E5=85=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- src/api/settlement/settlementImport.js | 55 + src/router/views/index.js | 15 + src/utils/mk-approval.js | 78 +- src/views/business/contract-manage-change.vue | 4 +- src/views/business/contract-manage.vue | 6 +- src/views/business/project-apply.vue | 3 + src/views/payment/bill-payment-form.vue | 3 +- src/views/payment/bill-payment.vue | 1 + .../payment/invoice-application-form.vue | 3 +- src/views/payment/invoice-application.vue | 1 + src/views/payment/invoice-receipt-form.vue | 3 +- src/views/payment/invoice-receipt.vue | 1 + .../payment/payment-application-form.vue | 3 +- src/views/payment/payment-application.vue | 1 + .../components/pre-settlement-editor.vue | 7 +- .../components/settlement-import-page.vue | 1110 +++++++++++++++++ src/views/settlement/formal-settlement.vue | 1 + src/views/settlement/pre-settlement.vue | 1 + .../settlement/settlement-import-form.vue | 49 + src/views/settlement/settlement-import.vue | 507 ++++++++ src/views/vehicle/customer-archive.vue | 50 +- vite.config.mjs | 44 +- 22 files changed, 1891 insertions(+), 55 deletions(-) create mode 100644 src/api/settlement/settlementImport.js create mode 100644 src/views/settlement/components/settlement-import-page.vue create mode 100644 src/views/settlement/settlement-import-form.vue create mode 100644 src/views/settlement/settlement-import.vue diff --git a/src/api/settlement/settlementImport.js b/src/api/settlement/settlementImport.js new file mode 100644 index 0000000..131493b --- /dev/null +++ b/src/api/settlement/settlementImport.js @@ -0,0 +1,55 @@ +import request from '@/axios'; + +const baseUrl = '/blade-transport/settlement-import'; + +export const getOptions = () => request({ url: `${baseUrl}/options`, method: 'get' }); + +export const getNextCode = () => request({ url: `${baseUrl}/next-code`, method: 'get' }); + +export const getList = (current, size, params) => + request({ + url: `${baseUrl}/list`, + method: 'get', + params: { current, size, ...params }, + }); + +export const getDetail = id => + request({ url: `${baseUrl}/detail`, method: 'get', params: { id } }); + +export const exportTemplate = () => + request({ + url: `${baseUrl}/export-template`, + method: 'get', + responseType: 'blob', + }); + +export const parseFile = file => { + const data = new FormData(); + data.append('file', file); + return request({ + url: `${baseUrl}/parse`, + method: 'post', + data, + timeout: 120000, + }); +}; + +export const validate = data => + request({ url: `${baseUrl}/validate`, method: 'post', data, timeout: 120000 }); + +export const exportFailures = data => + request({ + url: `${baseUrl}/export-failures`, + method: 'post', + data, + responseType: 'blob', + timeout: 120000, + }); + +export const saveDraft = data => + request({ url: `${baseUrl}/draft`, method: 'post', data, timeout: 120000 }); + +export const confirm = data => + request({ url: `${baseUrl}/confirm`, method: 'post', data, timeout: 120000 }); + +export const voidBatch = data => request({ url: `${baseUrl}/void`, method: 'post', data }); diff --git a/src/router/views/index.js b/src/router/views/index.js index 11d72aa..546ffb2 100644 --- a/src/router/views/index.js +++ b/src/router/views/index.js @@ -119,6 +119,21 @@ export default [ }, ], }, + { + path: '/settlement/settlement-import/form', + component: Layout, + children: [ + { + path: '', + name: '新增导入结算单', + meta: { + keepAlive: false, + activeMenu: '/settlement/settlement-import', + }, + component: loadView(() => import('@/views/settlement/settlement-import-form.vue')), + }, + ], + }, { path: '/settlement/transport-reconciliation/form', component: Layout, diff --git a/src/utils/mk-approval.js b/src/utils/mk-approval.js index 60ee800..83806da 100644 --- a/src/utils/mk-approval.js +++ b/src/utils/mk-approval.js @@ -61,9 +61,18 @@ export const MK_BIZ = { const MK_SWITCH_NAME = '开启MK'; -async function loadMkTemplateList() { +/** 进程内缓存,避免每次提交都打字典;登录后同一会话开关一般不变 */ +let mkEnabledCache = null; +let mkEnabledPromise = null; +let mkTemplateListCache = null; + +async function loadMkTemplateList(force = false) { + if (!force && mkTemplateListCache) { + return mkTemplateListCache; + } const res = await getDictionary({ code: 'mk_template' }); - return res?.data?.data || []; + mkTemplateListCache = res?.data?.data || []; + return mkTemplateListCache; } /** 业务字典 mk_template:名称「开启MK」且键值为 1 时才请求 MK */ @@ -72,10 +81,37 @@ export function isMkEnabled(list = []) { return String(matched?.dictKey ?? '').trim() === '1'; } -/** 加载业务字典并判断是否开启 MK */ -export async function loadMkEnabled() { - const list = await loadMkTemplateList(); - return isMkEnabled(list); +/** 清除 MK 开关缓存(字典变更后可调用) */ +export function clearMkEnabledCache() { + mkEnabledCache = null; + mkEnabledPromise = null; + mkTemplateListCache = null; +} + +/** + * 加载业务字典并判断是否开启 MK + * @param {boolean} [force=false] 是否强制刷新缓存 + */ +export async function loadMkEnabled(force = false) { + if (!force && mkEnabledCache !== null) { + return mkEnabledCache; + } + if (!force && mkEnabledPromise) { + return mkEnabledPromise; + } + mkEnabledPromise = loadMkTemplateList(force) + .then(list => { + mkEnabledCache = isMkEnabled(list); + return mkEnabledCache; + }) + .catch(error => { + mkEnabledCache = false; + throw error; + }) + .finally(() => { + mkEnabledPromise = null; + }); + return mkEnabledPromise; } export async function resolveMkTemplateCode(dictName, list) { @@ -99,19 +135,22 @@ export function isMkApprovedStatus(bizType, approvalStatus) { } /** - * 提交 MK 审核流 + * 提交 MK 审核流。未开启 MK 时直接跳过,不请求流程实例相关接口。 * @param {Object} options * @param {string} options.bizType 业务类型 - * @param {string|number} options.bizId 业务主键(后端据此生成/复用 formInstanceId) + * @param {string|number} options.bizId 业务主键 * @param {string} [options.subjectName] 标题后缀 - * @param {string} [options.approvalStatus] 提交前的审批状态(须在业务 submit 改状态前取值;通过态强制新建实例) + * @param {string} [options.approvalStatus] 提交前的审批状态 + * @param {boolean} [options.mkEnabled] 调用方已知开关时传入,false 时立即跳过 + * @returns {Promise} true=已发起/复用 MK 流程,false=未开启或已跳过 */ export async function submitMkApprovalFlow({ bizType, bizId, subjectName = '', approvalStatus = '', -}) { + mkEnabled, +} = {}) { const conf = MK_BIZ[bizType]; if (!conf) { return Promise.reject(new Error(`不支持的MK业务类型:${bizType}`)); @@ -119,17 +158,29 @@ export async function submitMkApprovalFlow({ if (bizId == null || bizId === '') { return Promise.reject(new Error('业务id不能为空')); } + + // 调用方显式关闭,或字典未开启:不请求 hasMkProcessInstance / processSubmit 等 MK 接口 + if (mkEnabled === false) { + return false; + } + const enabled = mkEnabled === true ? true : await loadMkEnabled(); + if (!enabled) { + return false; + } + const list = await loadMkTemplateList(); if (!isMkEnabled(list)) { - return; + mkEnabledCache = false; + return false; } + const bizIdText = String(bizId); const forceNewFormInstance = isMkApprovedStatus(bizType, approvalStatus); // 审核通过后再提交必须新建流程;其余场景已有有效实例则跳过 if (!forceNewFormInstance) { const existsRes = await hasMkProcessInstance({ bizId: bizIdText }); if (existsRes?.data?.data) { - return; + return true; } } if ((conf.rejected || []).includes(approvalStatus)) { @@ -139,11 +190,12 @@ export async function submitMkApprovalFlow({ const subject = subjectName ? `${conf.subjectPrefix}:${subjectName}` : `${conf.subjectPrefix}:${bizIdText}`; - return processSubmit({ + await processSubmit({ templateCode, bizId: bizIdText, subject, bizType, forceNewFormInstance, }); + return true; } diff --git a/src/views/business/contract-manage-change.vue b/src/views/business/contract-manage-change.vue index 016efb8..344a997 100644 --- a/src/views/business/contract-manage-change.vue +++ b/src/views/business/contract-manage-change.vue @@ -332,7 +332,7 @@ diff --git a/src/views/business/contract-manage.vue b/src/views/business/contract-manage.vue index 088ea83..ad38682 100644 --- a/src/views/business/contract-manage.vue +++ b/src/views/business/contract-manage.vue @@ -1986,7 +1986,9 @@ export default { bizType: 'contract-manage', bizId: id, subjectName: saved.contractName || submit.contractName || '', - approvalStatus: previousApprovalStatus || saved.approvalStatus || submit.approvalStatus || '', + approvalStatus: + previousApprovalStatus || saved.approvalStatus || submit.approvalStatus || '', + mkEnabled: this.mkEnabled, }) ); } @@ -2025,6 +2027,7 @@ export default { bizId: payload.id || this.form.id, subjectName: payload.contractName || this.form.contractName || '', approvalStatus: this.form.approvalStatus || 'change_rejected', + mkEnabled: this.mkEnabled, }).then(() => { this.$message.success('已重新提交变更审批'); this.closeForm(); @@ -3001,6 +3004,7 @@ export default { bizId: row.id, subjectName: row.contractName || '', approvalStatus: row.approvalStatus || '', + mkEnabled: this.mkEnabled, }) ) : request(); diff --git a/src/views/business/project-apply.vue b/src/views/business/project-apply.vue index 4580fd0..6ae6c21 100644 --- a/src/views/business/project-apply.vue +++ b/src/views/business/project-apply.vue @@ -1750,6 +1750,7 @@ export default { bizId: row.id, subjectName: row.projectName || '', approvalStatus: row.approvalStatus || '', + mkEnabled: this.mkEnabled, }).then(() => request()) : request(); afterMk.then(res => { @@ -1956,6 +1957,7 @@ export default { bizId: id, subjectName: name, approvalStatus: status, + mkEnabled: this.mkEnabled, }).then(() => { this.$message.success('提交成功!'); this.closeProjectForm(); @@ -2027,6 +2029,7 @@ export default { bizId: id, subjectName: this.form.projectName || '', approvalStatus: this.form.approvalStatus || 'change_rejected', + mkEnabled: this.mkEnabled, }).then(() => { this.$message.success('提交成功!'); this.closeProjectForm(); diff --git a/src/views/payment/bill-payment-form.vue b/src/views/payment/bill-payment-form.vue index c8654a9..77297ed 100644 --- a/src/views/payment/bill-payment-form.vue +++ b/src/views/payment/bill-payment-form.vue @@ -140,7 +140,7 @@ import * as api from '@/api/payment/billPayment'; import * as billLedgerApi from '@/api/payment/billLedger'; import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue'; import { getMkPublicDetail } from '@/api/mk-process'; -import { submitMkApprovalFlow } from '@/utils/mk-approval'; +import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval'; const emptyForm = () => ({ id: null, @@ -348,6 +348,7 @@ export default { bizId: this.form.id, subjectName: this.form.paymentNo || this.form.billNo || '', approvalStatus: previousApprovalStatus, + mkEnabled: await loadMkEnabled().catch(() => false), }); this.$message.success('提交成功'); this.goBack(); diff --git a/src/views/payment/bill-payment.vue b/src/views/payment/bill-payment.vue index d44acbd..defddf9 100644 --- a/src/views/payment/bill-payment.vue +++ b/src/views/payment/bill-payment.vue @@ -132,6 +132,7 @@ export default { bizId: row.id, subjectName: row.paymentNo || row.billNo || '', approvalStatus: previousApprovalStatus, + mkEnabled: this.mkEnabled, }); this.$message.success('提交成功'); this.loadTable(); diff --git a/src/views/payment/invoice-application-form.vue b/src/views/payment/invoice-application-form.vue index 0f47253..0c6f4cd 100644 --- a/src/views/payment/invoice-application-form.vue +++ b/src/views/payment/invoice-application-form.vue @@ -492,7 +492,7 @@ import { getDictionary } from '@/api/system/dictbiz'; import { invoiceApplicationDetailColumns } from '@/option/payment/invoiceApplication'; import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue'; import { getMkPublicDetail } from '@/api/mk-process'; -import { submitMkApprovalFlow } from '@/utils/mk-approval'; +import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval'; let localId = 0; const emptyLine = () => ({ @@ -1208,6 +1208,7 @@ export default { bizId: this.form.id, subjectName: this.form.applicationNo || '', approvalStatus: previousApprovalStatus, + mkEnabled: await loadMkEnabled().catch(() => false), }); this.$message.success('提交成功'); this.goBack(); diff --git a/src/views/payment/invoice-application.vue b/src/views/payment/invoice-application.vue index 8cc072b..7d8509e 100644 --- a/src/views/payment/invoice-application.vue +++ b/src/views/payment/invoice-application.vue @@ -384,6 +384,7 @@ export default { bizId: row.id, subjectName: row.applicationNo || '', approvalStatus: previousApprovalStatus, + mkEnabled: this.mkEnabled, }); this.$message.success('提交成功'); this.loadTable(); diff --git a/src/views/payment/invoice-receipt-form.vue b/src/views/payment/invoice-receipt-form.vue index ac424da..1d06b34 100644 --- a/src/views/payment/invoice-receipt-form.vue +++ b/src/views/payment/invoice-receipt-form.vue @@ -219,7 +219,7 @@ import * as api from '@/api/payment/invoiceReceipt'; import KingdeeInvoicePoolDialog from '@/components/kingdee-invoice-pool-dialog/main.vue'; import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue'; import { getMkPublicDetail } from '@/api/mk-process'; -import { submitMkApprovalFlow } from '@/utils/mk-approval'; +import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval'; const emptyForm = () => ({ id: null, @@ -571,6 +571,7 @@ export default { bizId: this.form.id, subjectName: this.form.invoiceNo || '', approvalStatus: previousApprovalStatus, + mkEnabled: await loadMkEnabled().catch(() => false), }); this.$message.success('提交成功'); this.goBack(); diff --git a/src/views/payment/invoice-receipt.vue b/src/views/payment/invoice-receipt.vue index 02b2ad1..8710449 100644 --- a/src/views/payment/invoice-receipt.vue +++ b/src/views/payment/invoice-receipt.vue @@ -341,6 +341,7 @@ export default { bizId: row.id, subjectName: row.invoiceNo || '', approvalStatus: previousApprovalStatus, + mkEnabled: this.mkEnabled, }); this.$message.success('提交成功'); this.loadTable(); diff --git a/src/views/payment/payment-application-form.vue b/src/views/payment/payment-application-form.vue index acee4e5..140937f 100644 --- a/src/views/payment/payment-application-form.vue +++ b/src/views/payment/payment-application-form.vue @@ -673,7 +673,7 @@ import { import { readSettlementTransfer, removeSettlementTransfer } from '@/utils/settlement-transfer'; import { formatContractOptionLabel } from '@/utils/contract'; import { downloadFileByUrl } from '@/utils/util'; -import { submitMkApprovalFlow } from '@/utils/mk-approval'; +import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval'; import { downloadKingdeeInvoiceFile, isKingdeePathAttachment, @@ -2709,6 +2709,7 @@ export default { bizId: this.form.id, subjectName: this.form.paymentNo || '', approvalStatus: previousApprovalStatus, + mkEnabled: await loadMkEnabled().catch(() => false), }); this.$message.success('提交成功'); this.goBack(); diff --git a/src/views/payment/payment-application.vue b/src/views/payment/payment-application.vue index 90b6915..5067dfa 100644 --- a/src/views/payment/payment-application.vue +++ b/src/views/payment/payment-application.vue @@ -296,6 +296,7 @@ export default { bizId: row.id, subjectName: row.paymentNo || '', approvalStatus: previousApprovalStatus, + mkEnabled: this.mkEnabled, }); this.$message.success('提交成功'); this.refreshChange(); diff --git a/src/views/settlement/components/pre-settlement-editor.vue b/src/views/settlement/components/pre-settlement-editor.vue index 7a63786..b46cec5 100644 --- a/src/views/settlement/components/pre-settlement-editor.vue +++ b/src/views/settlement/components/pre-settlement-editor.vue @@ -975,7 +975,7 @@ import { } from '@/option/settlement/preSettlementTable'; import { formatContractOptionLabel } from '@/utils/contract'; import { downloadFileByUrl } from '@/utils/util'; -import { submitMkApprovalFlow } from '@/utils/mk-approval'; +import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval'; import * as XLSX from 'xlsx'; import { isAdministrator } from '@/utils/auth'; @@ -1623,13 +1623,14 @@ export default { if (shouldSubmit) { const previousApprovalStatus = this.form.approvalStatus || ''; await submit({ id: this.form.id }); - await submitMkApprovalFlow({ + const mkStarted = await submitMkApprovalFlow({ bizType: 'pre-settlement', bizId: this.form.id, subjectName: this.form.preSettlementNo || this.form.contractName || '', approvalStatus: previousApprovalStatus, + mkEnabled: await loadMkEnabled().catch(() => false), }); - this.$message.success('审批流程已发起'); + this.$message.success(mkStarted ? '审批流程已发起' : '提交成功'); this.visible = false; this.$emit('success', this.form.id); return; diff --git a/src/views/settlement/components/settlement-import-page.vue b/src/views/settlement/components/settlement-import-page.vue new file mode 100644 index 0000000..396f495 --- /dev/null +++ b/src/views/settlement/components/settlement-import-page.vue @@ -0,0 +1,1110 @@ + + + + + diff --git a/src/views/settlement/formal-settlement.vue b/src/views/settlement/formal-settlement.vue index b65d98c..c4b9da0 100644 --- a/src/views/settlement/formal-settlement.vue +++ b/src/views/settlement/formal-settlement.vue @@ -368,6 +368,7 @@ export default { bizId: row.id, subjectName: row.formalSettlementNo || '', approvalStatus: previousApprovalStatus, + mkEnabled: this.mkEnabled, }); this.$message.success('提交成功'); this.loadTable(); diff --git a/src/views/settlement/pre-settlement.vue b/src/views/settlement/pre-settlement.vue index 414acc6..75caf7a 100644 --- a/src/views/settlement/pre-settlement.vue +++ b/src/views/settlement/pre-settlement.vue @@ -661,6 +661,7 @@ export default { bizId: row.id, subjectName: row.preSettlementNo || '', approvalStatus: previousApprovalStatus, + mkEnabled: this.mkEnabled, }); this.$message.success('提交成功'); this.loadTable(); diff --git a/src/views/settlement/settlement-import-form.vue b/src/views/settlement/settlement-import-form.vue new file mode 100644 index 0000000..77f982f --- /dev/null +++ b/src/views/settlement/settlement-import-form.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/src/views/settlement/settlement-import.vue b/src/views/settlement/settlement-import.vue new file mode 100644 index 0000000..4070d1e --- /dev/null +++ b/src/views/settlement/settlement-import.vue @@ -0,0 +1,507 @@ + + + + + diff --git a/src/views/vehicle/customer-archive.vue b/src/views/vehicle/customer-archive.vue index 53156a8..1c1c2e2 100644 --- a/src/views/vehicle/customer-archive.vue +++ b/src/views/vehicle/customer-archive.vue @@ -208,7 +208,7 @@ v-if=" hasPermission('customer_archive_sync') && row.approvalStatus === 'approved' && - row.kingdeeStatus === 'failed' + !['synced', 'exist'].includes(row.kingdeeStatus) " @click="handleSyncKingdee(row)" > @@ -1791,6 +1791,8 @@ export default { }, /** 业务字典 mk_template「开启MK」=1 */ mkEnabled: false, + /** 是否已完成 MK 开关加载,避免提交时误判 */ + _mkEnabledResolved: false, formRules: { customerCode: [{ max: 20, message: '客商编号最多20个字符', trigger: 'blur' }], fullName: [{ required: true, message: '请输入客商名称', trigger: 'blur' }], @@ -1871,7 +1873,7 @@ export default { editBtn: false, selection: true, dialogClickModal: false, - menuWidth: 320, + menuWidth: 400, column: [ { label: '客商编号', @@ -2340,7 +2342,13 @@ export default { sessionStorage.setItem(customerArchiveListRefreshKey, '1'); }, hasPermission(code) { - return this.isAdmin || this.validData(this.permission[code], false); + if (this.isAdmin) { + return true; + } + const permission = this.permission || {}; + const value = permission[code]; + // 不依赖 Avue validData,避免权限值为 true 以外的真值时被误判 + return value === true || value === 'true' || value === 1 || value === '1'; }, mergeSelectOptions(target, values) { const list = Array.isArray(values) ? values : values ? [values] : []; @@ -3100,12 +3108,15 @@ export default { }); }, initMkEnabled() { + this._mkEnabledResolved = false; loadMkEnabled() .then(enabled => { this.mkEnabled = !!enabled; + this._mkEnabledResolved = true; }) .catch(() => { this.mkEnabled = false; + this._mkEnabledResolved = true; }); }, initScoreQuantificationOptions() { @@ -4481,15 +4492,34 @@ export default { }); }, /** - * 列表 / 新增 / 编辑提交共用:开启 MK 时提交审核流,并更新客商审批状态 + * 列表 / 新增 / 编辑提交共用: + * 未开启 MK 时仅走本地提交审批(与新增一致,不请求 MK 流程); + * 开启 MK 时先提交审核流,再更新客商审批状态。 */ submitCustomerApproval(id, fullName = '', approvalStatus = '') { - return submitMkApprovalFlow({ - bizType: 'customer-archive', - bizId: id, - subjectName: fullName, - approvalStatus, - }).then(() => submitApproval(id)); + const submitLocal = () => submitApproval(id); + const run = enabled => { + this.mkEnabled = !!enabled; + this._mkEnabledResolved = true; + if (!this.mkEnabled) { + return submitLocal(); + } + return submitMkApprovalFlow({ + bizType: 'customer-archive', + bizId: id, + subjectName: fullName, + approvalStatus, + mkEnabled: true, + }).then(submitLocal); + }; + + // 与列表按钮一致:以页面 mkEnabled 为准,未开启时不请求 MK + if (this._mkEnabledResolved) { + return run(this.mkEnabled); + } + return loadMkEnabled() + .then(enabled => run(enabled)) + .catch(() => run(false)); }, openReviewScore() { const scores = this.archiveForm.scores || []; diff --git a/vite.config.mjs b/vite.config.mjs index b59a8be..3e0426e 100644 --- a/vite.config.mjs +++ b/vite.config.mjs @@ -49,28 +49,28 @@ export default ({ mode, command }) => { __VUE_I18N_LEGACY_API__: true, __INTLIFY_PROD_DEVTOOLS__: false, }, - server: { - port: 2888, - proxy: { - '/api': { - // 本地网关默认 80 端口(blade-gateway bootstrap.yml);开发时需先启动网关及 blade-auth/blade-system 等服务 - // 注意:本机 8080 一般是 Nacos,不是业务网关 - target: 'http://localhost', - // target: 'http://172.16.203.228:8000', // 仅联调远程网关时启用(且不要 rewrite) - changeOrigin: true, - rewrite: path => path.replace(/^\/api/, ''), - }, - }, - }, - //server: { - // port: 2889, - // proxy: { - // '/api': { - // target: 'http://172.16.203.221:8000', - // changeOrigin: true, - // }, - // }, - //}, + server: { + port: 2888, + proxy: { + '/api': { + // 本地网关默认 80 端口(blade-gateway bootstrap.yml);开发时需先启动网关及 blade-auth/blade-system 等服务 + // 注意:本机 8080 一般是 Nacos,不是业务网关 + target: 'http://localhost', + // target: 'http://172.16.203.228:8000', // 仅联调远程网关时启用(且不要 rewrite) + changeOrigin: true, + rewrite: path => path.replace(/^\/api/, ''), + }, + }, + }, + // server: { + // port: 2889, + // proxy: { + // '/api': { + // target: 'http://172.16.203.228:8000', + // changeOrigin: true, + // }, + // }, + // }, resolve: { alias: { '~': resolve(__dirname, './'), From 4b01316f64ae113606b4819cf982165d9d5f02a8 Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Fri, 16 Oct 2026 02:02:00 +0800 Subject: [PATCH 5/7] =?UTF-8?q?:sparkles:=20=E6=96=B0=E5=A2=9E=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E9=A2=84=E4=BB=98=E6=A0=B8=E9=94=80=20UI=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E4=BF=AE=E5=A4=8D=E5=B8=B8=E7=94=A8=E7=BA=BF=E8=B7=AF?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=B8=8D=E6=98=BE=E7=A4=BA=E4=B8=8E=E4=BB=98?= =?UTF-8?q?=E6=AC=BE=E6=8F=90=E4=BA=A4=E5=90=8E=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 结算单附件增加项目预付核销;常用线路超管默认查全部组织并按筛选归属新增;付款申请提交后关页并刷新列表;平台公司选项 ID 按字符串绑定。 Co-authored-by: Cursor --- src/api/payment/paymentApplication.js | 7 + src/views/business/common-route.vue | 59 +++- src/views/business/project-apply.vue | 5 +- .../payment/payment-application-form.vue | 7 +- src/views/payment/payment-application.vue | 24 ++ .../components/formal-settlement-editor.vue | 29 +- .../components/pre-settlement-editor.vue | 28 +- .../components/project-advance-write-off.vue | 300 ++++++++++++++++++ 8 files changed, 447 insertions(+), 12 deletions(-) create mode 100644 src/views/settlement/components/project-advance-write-off.vue diff --git a/src/api/payment/paymentApplication.js b/src/api/payment/paymentApplication.js index 3e4c802..59c9aa1 100644 --- a/src/api/payment/paymentApplication.js +++ b/src/api/payment/paymentApplication.js @@ -10,6 +10,13 @@ export const getReferenceAmount = (paymentType, referenceId, excludeId) => method: 'get', params: { paymentType, referenceId, excludeId: excludeId || undefined }, }); +/** 按合同查询可选项目预付核销列表(审批通过且仍有可核销余额) */ +export const getProjectAdvanceWriteOffOptions = params => + request({ + url: `${baseUrl}/project-advance-write-off-options`, + method: 'get', + params, + }); export const save = data => request({ url: `${baseUrl}/save`, method: 'post', data }); export const remove = id => request({ url: `${baseUrl}/remove`, method: 'post', params: { id } }); export const submit = data => request({ url: `${baseUrl}/submit`, method: 'post', data }); diff --git a/src/views/business/common-route.vue b/src/views/business/common-route.vue index 99095ac..15b8cba 100644 --- a/src/views/business/common-route.vue +++ b/src/views/business/common-route.vue @@ -396,8 +396,11 @@ export default { return this.selectionList.map(item => item.id).join(','); }, }, - created() { - this.applyDefaultDeptSearch(); + created() { + // 超管默认查全部组织,不预填组织筛选;普通用户预填当前组织。 + if (!this.isAdmin) { + this.applyDefaultDeptSearch(); + } this.initDeptTree(); }, methods: { @@ -455,10 +458,23 @@ export default { normalizeListQuery(params = {}) { // 搜索条件优先于缓存的 query,避免默认组织 ID 覆盖用户新选择的组织。 const query = { ...this.query, ...params }; - const rawDeptId = query.deptId ?? query.searchDeptId; + // 本次搜索显式带了组织字段时,以本次为准(含清空);否则沿用缓存。 + const hasDeptInParams = + Object.prototype.hasOwnProperty.call(params, 'deptId') || + Object.prototype.hasOwnProperty.call(params, 'searchDeptId'); + const rawDeptId = hasDeptInParams + ? params.deptId ?? params.searchDeptId + : query.deptId ?? query.searchDeptId; delete query.searchDeptId; if (rawDeptId !== undefined && rawDeptId !== null && String(rawDeptId).trim() !== '') { - query.deptId = String(rawDeptId).split(',')[0].trim(); + // 级联可能回传路径数组,统一取末级并保持字符串,避免雪花 ID 精度丢失。 + const deptValue = Array.isArray(rawDeptId) ? rawDeptId.at(-1) : rawDeptId; + query.deptId = String(deptValue ?? '') + .split(',')[0] + .trim(); + if (!query.deptId) { + delete query.deptId; + } } else { delete query.deptId; } @@ -466,6 +482,23 @@ export default { return query; }, applyDefaultDeptSearch() { + // 超管默认不限组织,避免预填组织后新增落在其他组织时列表看不见。 + if (this.isAdmin) { + const deptColumn = this.findColumn(this.option.column, 'searchDeptId'); + if (deptColumn) { + deptColumn.searchValue = undefined; + } + const nextQuery = { ...this.query }; + delete nextQuery.deptId; + delete nextQuery.searchDeptId; + this.query = nextQuery; + this.$nextTick(() => { + if (this.$refs.crud?.searchForm) { + this.$refs.crud.searchForm.searchDeptId = undefined; + } + }); + return; + } const deptId = this.defaultDeptId(); const deptColumn = this.findColumn(this.option.column, 'searchDeptId'); if (!deptId || !deptColumn) return; @@ -477,6 +510,11 @@ export default { } }); }, + resolveDeptName(deptId) { + if (!deptId) return ''; + const matched = this.deptOptions.find(item => String(item.value) === String(deptId)); + return matched ? String(matched.label || '').trim() : ''; + }, resetAddressQuery() { this.addressQuery = { addressName: '', @@ -933,7 +971,8 @@ export default { } api.submit(submitRow).then( () => { - this.onLoad(this.page); + this.page.currentPage = 1; + this.onLoad(this.page, this.query); this.$message({ type: 'success', message: '操作成功!' }); done(); }, @@ -951,7 +990,7 @@ export default { } api.submit(submitRow).then( () => { - this.onLoad(this.page); + this.onLoad(this.page, this.query); this.$message({ type: 'success', message: '操作成功!' }); done(); }, @@ -1006,7 +1045,13 @@ export default { beforeOpen(done, type) { this.dialogReadonly = type === 'view'; if (type === 'add') { - this.form = {}; + const form = {}; + // 超管若当前按某组织筛选,新增默认归属该组织,避免保存后被筛选条件滤掉。 + if (this.isAdmin && this.query.deptId) { + form.deptId = String(this.query.deptId); + form.deptName = this.resolveDeptName(form.deptId); + } + this.form = form; done(); return; } diff --git a/src/views/business/project-apply.vue b/src/views/business/project-apply.vue index 6ae6c21..e05c03d 100644 --- a/src/views/business/project-apply.vue +++ b/src/views/business/project-apply.vue @@ -2506,8 +2506,9 @@ export default { const list = res.data.data || []; this.platformCompanyOptions = list.map(item => ({ label: item.deptName || item.fullName || item.title || item.name, - value: item.id, - })); + // 雪花 ID 必须以字符串绑定,避免 JS Number 精度丢失导致多选提交异常 + value: item.id == null || item.id === '' ? '' : String(item.id), + })).filter(item => item.value); this.ensureCurrentPlatformOption(); }); }, diff --git a/src/views/payment/payment-application-form.vue b/src/views/payment/payment-application-form.vue index 140937f..9dd62e0 100644 --- a/src/views/payment/payment-application-form.vue +++ b/src/views/payment/payment-application-form.vue @@ -688,6 +688,9 @@ const attachmentViewerPlugins = [ fallbackPlugin(), ]; +const paymentApplicationListRoute = '/payment/payment-application'; +const paymentApplicationListRefreshKey = 'payment-application-list-refresh'; + const ATTACHMENT_MATERIALS = { weighingSlip: { key: 'weighingSlip', label: '磅单', keywords: ['磅单'] }, entrustOrder: { key: 'entrustOrder', label: '委托单', keywords: ['委托单'] }, @@ -2712,12 +2715,14 @@ export default { mkEnabled: await loadMkEnabled().catch(() => false), }); this.$message.success('提交成功'); + // 通知列表页(keep-alive)重新拉取数据 + sessionStorage.setItem(paymentApplicationListRefreshKey, '1'); this.goBack(); }, goBack() { removeSettlementTransfer(this.$route.query.transferToken); this.$router.$avueRouter?.closeTag?.(); - this.$router.push('/payment/payment-application'); + this.$router.push(paymentApplicationListRoute); }, formatMoney(value) { const amount = Number(value); diff --git a/src/views/payment/payment-application.vue b/src/views/payment/payment-application.vue index b3e63d3..44cde10 100644 --- a/src/views/payment/payment-application.vue +++ b/src/views/payment/payment-application.vue @@ -152,6 +152,9 @@ import organizationSearch from '@/mixins/organization-search'; import { getMKApprovalUrl } from '@/api/system/business-process'; import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval'; +const paymentApplicationListRoute = '/payment/payment-application'; +const paymentApplicationListRefreshKey = 'payment-application-list-refresh'; + export default { name: 'PaymentApplication', mixins: [organizationSearch], @@ -193,7 +196,28 @@ export default { this.mkEnabled = false; }); }, + activated() { + this.consumeListRefreshFlag(); + }, + watch: { + $route(to, from) { + if ( + to.path === paymentApplicationListRoute && + from?.path?.includes('/payment-application/form') + ) { + this.consumeListRefreshFlag(); + } + }, + }, methods: { + /** 表单提交成功后带标记返回时,重新拉取列表 */ + consumeListRefreshFlag() { + if (!sessionStorage.getItem(paymentApplicationListRefreshKey)) { + return; + } + sessionStorage.removeItem(paymentApplicationListRefreshKey); + this.onLoad(this.page); + }, hasPermission(code) { return this.permission?.[code] !== false; }, diff --git a/src/views/settlement/components/formal-settlement-editor.vue b/src/views/settlement/components/formal-settlement-editor.vue index 5da1c16..b5dce08 100644 --- a/src/views/settlement/components/formal-settlement-editor.vue +++ b/src/views/settlement/components/formal-settlement-editor.vue @@ -378,6 +378,16 @@ + +
@@ -1011,12 +1021,13 @@ import { viewKingdeeInvoiceFile, } from '@/utils/kingdee-attachment'; import KingdeeInvoicePoolDialog from '@/components/kingdee-invoice-pool-dialog/main.vue'; +import ProjectAdvanceWriteOff from './project-advance-write-off.vue'; import * as XLSX from 'xlsx'; import { isAdministrator } from '@/utils/auth'; export default { name: 'FormalSettlementEditor', - components: { InfoFilled, KingdeeInvoicePoolDialog }, + components: { InfoFilled, KingdeeInvoicePoolDialog, ProjectAdvanceWriteOff }, props: { modelValue: Boolean, recordId: [String, Number], @@ -1045,6 +1056,7 @@ export default { receiptClaims: [], adjustments: [], changeRecords: [], + projectAdvanceWriteOffs: [], attachments: [], selectedAttachmentRows: [], invoices: [], @@ -1275,6 +1287,13 @@ export default { } this.adjustments = data.adjustments || []; this.changeRecords = data.changeRecords || []; + this.projectAdvanceWriteOffs = (data.projectAdvanceWriteOffs || []).map(row => ({ + ...row, + writeOffAmount: Number(row.writeOffAmount || 0), + projectAdvanceAmount: Number(row.projectAdvanceAmount || 0), + availableWriteOffAmount: Number(row.availableWriteOffAmount || 0), + createTime: row.paymentCreateTime || row.createTime, + })); this.attachments = this.parseAttachments(data.attachmentsJson); this.selectedAttachmentRows = []; this.invoices = data.invoices || []; @@ -1305,6 +1324,7 @@ export default { this.receiptClaims = []; this.adjustments = []; this.changeRecords = []; + this.projectAdvanceWriteOffs = []; this.attachments = []; this.selectedAttachmentRows = []; this.invoices = []; @@ -2549,6 +2569,7 @@ export default { matchedAmount: Number(row.matchedAmount || 0), attachmentJson: row.attachmentJson || '', })), + projectAdvanceWriteOffs: this.$refs.projectAdvanceWriteOffRef?.toSavePayload?.() || [], remark: this.form.remark, }; }, @@ -2557,6 +2578,9 @@ export default { }, async handleDraftSave() { // 保存草稿:不做表单校验,直接落草稿并返回列表刷新 + if (this.$refs.projectAdvanceWriteOffRef?.validateBeforeSave?.() === false) { + return; + } this.draftSaving = true; try { await save({ @@ -2584,6 +2608,9 @@ export default { return this.$message.warning('请完善手工费用的费用类型和费用项'); } if (!this.validateInvoices()) return; + if (this.$refs.projectAdvanceWriteOffRef?.validateBeforeSave?.() === false) { + return; + } // 附件类型仅做提示,不阻断正式结算单新增/保存提交。 // this.validateAttachments(); this.saving = true; diff --git a/src/views/settlement/components/pre-settlement-editor.vue b/src/views/settlement/components/pre-settlement-editor.vue index b46cec5..e60bedc 100644 --- a/src/views/settlement/components/pre-settlement-editor.vue +++ b/src/views/settlement/components/pre-settlement-editor.vue @@ -421,6 +421,16 @@
+ + @@ -939,6 +949,7 @@ import { h } from 'vue'; import { mapGetters } from 'vuex'; import { InfoFilled } from '@element-plus/icons-vue'; import ChangeRecordDetailDialog from '@/components/change-record-detail-dialog/main.vue'; +import ProjectAdvanceWriteOff from './project-advance-write-off.vue'; import { getMkPublicDetail } from '@/api/mk-process'; import { adjustDetail, @@ -981,7 +992,7 @@ import { isAdministrator } from '@/utils/auth'; export default { name: 'PreSettlementEditor', - components: { InfoFilled, ChangeRecordDetailDialog }, + components: { InfoFilled, ChangeRecordDetailDialog, ProjectAdvanceWriteOff }, props: { modelValue: { type: Boolean, @@ -1084,6 +1095,7 @@ export default { size: 10, }, advances: [], + projectAdvanceWriteOffs: [], changeRecords: [], attachments: [], attachmentTypeOptions: [], @@ -1396,6 +1408,7 @@ export default { this.appliedDetailQuery = { ...this.detailQuery }; this.detailPage = { current: 1, size: 10 }; this.advances = []; + this.projectAdvanceWriteOffs = []; this.changeRecords = []; this.attachments = []; this.selectedAttachmentRows = []; @@ -1435,6 +1448,13 @@ export default { createUserName: row.createUserName || detail.createUserName, createTime: row.createTime || detail.createTime, })); + this.projectAdvanceWriteOffs = (detail.projectAdvanceWriteOffs || []).map(row => ({ + ...row, + writeOffAmount: Number(row.writeOffAmount || 0), + projectAdvanceAmount: Number(row.projectAdvanceAmount || 0), + availableWriteOffAmount: Number(row.availableWriteOffAmount || 0), + createTime: row.paymentCreateTime || row.createTime, + })); this.changeRecords = detail.changeRecords || []; this.attachments = this.parseAttachments(detail.attachmentsJson); this.sortAttachments(); @@ -1579,6 +1599,7 @@ export default { this.form.payerName = ''; this.form.payeeName = ''; this.summaryFees = []; + this.projectAdvanceWriteOffs = []; return; } this.form.contractId = contract.id; @@ -1593,6 +1614,7 @@ export default { this.form.payeeName = contract.partyB; this.form.currency = contract.settlementCurrency || 'RMB'; this.summaryFees = []; + this.projectAdvanceWriteOffs = []; if (this.form.currency !== 'RMB') { this.form.exchangeRateDate = this.$dayjs().format('YYYY-MM-DD'); this.fetchBfiExchangeRate(); @@ -1613,6 +1635,9 @@ export default { this.$message.warning(`请完善结算合计第${invalidManualFeeIndex + 1}行的费用类型和费用项`); return; } + if (this.$refs.projectAdvanceWriteOffRef?.validateBeforeSave?.() === false) { + return; + } const stateKey = shouldSubmit ? 'submitting' : 'saving'; this[stateKey] = true; try { @@ -1660,6 +1685,7 @@ export default { remark: row.remark, manualFlag: row.manualFlag || 0, })), + projectAdvanceWriteOffs: this.$refs.projectAdvanceWriteOffRef?.toSavePayload?.() || [], }; }, addSummaryFee() { diff --git a/src/views/settlement/components/project-advance-write-off.vue b/src/views/settlement/components/project-advance-write-off.vue new file mode 100644 index 0000000..a273121 --- /dev/null +++ b/src/views/settlement/components/project-advance-write-off.vue @@ -0,0 +1,300 @@ + + + + + From f7759fd9e75161808b31a8a06a847b5edd83a622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com> Date: Fri, 16 Oct 2026 15:26:56 +0800 Subject: [PATCH 6/7] =?UTF-8?q?fix:=20=E6=89=B9=E9=87=8F=E8=AE=A4=E9=A2=86?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=89=A9=E4=BD=99=E5=8F=AF=E8=AE=A4=E9=A2=86?= =?UTF-8?q?=E9=87=91=E9=A2=9D=E5=B1=95=E7=A4=BA=E4=B8=8E=E6=B5=81=E6=B0=B4?= =?UTF-8?q?=E7=BB=B4=E5=BA=A6=E5=88=86=E6=91=8A=E4=B8=8A=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/payment/receipt-flow-batch-form.vue | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/views/payment/receipt-flow-batch-form.vue b/src/views/payment/receipt-flow-batch-form.vue index b7f0bd8..156a5f5 100644 --- a/src/views/payment/receipt-flow-batch-form.vue +++ b/src/views/payment/receipt-flow-batch-form.vue @@ -46,6 +46,9 @@ + + + @@ -298,10 +301,21 @@ export default { 0 ); }, + flowRemaining(flow) { + return Math.max(Number(flow.unclaimedAmount || 0) - Number(flow.claimedAmount || 0), 0); + }, maxAllocation(row) { const current = Number(row.settlement.allocations?.[row.flow.id] || 0); const otherTotal = this.settlementAllocatedTotal(row.settlement) - current; - return Math.max(this.settlementRemaining(row.settlement) - otherTotal, 0); + // 流水维度:该流水其余分摊已占用部分同样挤占剩余可认领金额 + const flowOtherTotal = this.flowAllocatedTotal(row.flow.id) - current; + return Math.max( + Math.min( + this.settlementRemaining(row.settlement) - otherTotal, + this.flowRemaining(row.flow) - flowOtherTotal + ), + 0 + ); }, handleAllocationChange(settlement) { const total = this.settlementAllocatedTotal(settlement); From 6067d24e3bc33a511f1044de92201ef3b73b0391 Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Fri, 16 Oct 2026 16:03:26 +0800 Subject: [PATCH 7/7] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E4=BA=8C=E6=AC=A1=E6=90=9C=E7=B4=A2=E4=B8=8E=E7=BB=93?= =?UTF-8?q?=E7=AE=97=E5=AF=BC=E5=85=A5=E5=9B=9E=E5=88=97=E8=A1=A8=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E5=AE=8C=E5=96=84=E4=BB=98=E6=AC=BE=E7=94=B3=E8=AF=B7?= =?UTF-8?q?=E5=A4=9A=E7=BB=93=E7=AE=97=E5=8D=95=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- src/utils/search-range.js | 32 ++++++++ src/views/base/airport-master.vue | 3 +- src/views/base/currency.vue | 3 +- src/views/base/customer-type.vue | 3 +- src/views/base/fee-item.vue | 3 +- src/views/base/measurement-unit.vue | 3 +- src/views/base/port-terminal.vue | 3 +- src/views/base/railway-station.vue | 3 +- src/views/business/process-config.vue | 4 +- .../payment/payment-application-form.vue | 62 ++++++++++++--- .../components/formal-settlement-editor.vue | 53 +++++++++---- .../components/pre-settlement-editor.vue | 78 ++++++++++--------- .../components/settlement-import-page.vue | 12 ++- .../settlement/receivable-payable-detail.vue | 8 +- src/views/settlement/settlement-import.vue | 24 ++++++ src/views/vehicle/accident-record.vue | 4 +- .../vehicle/credit-score-quantification.vue | 10 ++- src/views/vehicle/customer-archive.vue | 46 +++++++---- src/views/vehicle/insurance-record.vue | 4 +- src/views/vehicle/maintenance-plan.vue | 4 +- src/views/vehicle/maintenance-record.vue | 4 +- src/views/vehicle/mileage-record.vue | 4 +- src/views/vehicle/tire-replacement-record.vue | 4 +- src/views/vehicle/transport-change-record.vue | 4 +- src/views/vehicle/violation-record.vue | 4 +- 25 files changed, 276 insertions(+), 106 deletions(-) diff --git a/src/utils/search-range.js b/src/utils/search-range.js index e5723cd..34865bb 100644 --- a/src/utils/search-range.js +++ b/src/utils/search-range.js @@ -1,3 +1,35 @@ +/** + * 合并列表搜索条件:新条件覆盖旧条件,本次显式清空的字段从查询中移除。 + */ +export const applyIncomingSearch = (current = {}, incoming = {}) => { + const merged = { ...current, ...incoming }; + Object.keys(incoming || {}).forEach(key => { + const value = incoming[key]; + if ( + value === '' || + value === null || + value === undefined || + (Array.isArray(value) && value.length === 0) + ) { + delete merged[key]; + } + }); + return merged; +}; + +/** + * 启停状态下拉「全部」在数字类型下会被转成 0/-1,不能当作查询条件。 + * 状态 0 表示草稿等有效值的页面不要调用。 + */ +export const omitInvalidListStatus = (params = {}) => { + const values = { ...params }; + const status = values.status; + if (status === 0 || status === '0' || status === -1 || status === '-1') { + delete values.status; + } + return values; +}; + export const normalizeSearchRangeParams = (params = {}, rangeMap = {}) => { const values = { ...params }; diff --git a/src/views/base/airport-master.vue b/src/views/base/airport-master.vue index 3c94260..9969378 100644 --- a/src/views/base/airport-master.vue +++ b/src/views/base/airport-master.vue @@ -150,6 +150,7 @@ import { mapGetters } from 'vuex'; import { downloadXls } from '@/utils/util'; import { openImportDialog } from '@/utils/import-excel'; import { formatUpdateUserName } from '@/utils/audit'; +import { omitInvalidListStatus } from '@/utils/search-range'; import { getToken, isAdministrator } from '@/utils/auth'; import { getCoordinateValidationMessage, normalizeCoordinateInput } from '@/utils/coordinate'; import AddressMapPicker from '@/components/address-map-picker/main.vue'; @@ -1182,7 +1183,7 @@ export default { }, onLoad(page, params = {}) { this.loading = true; - getList(page.currentPage, page.pageSize, { ...params, ...this.query }) + getList(page.currentPage, page.pageSize, omitInvalidListStatus({ ...params, ...this.query })) .then(res => { const data = res.data.data; this.page.total = data.total; diff --git a/src/views/base/currency.vue b/src/views/base/currency.vue index 0ad39fe..903fae0 100644 --- a/src/views/base/currency.vue +++ b/src/views/base/currency.vue @@ -104,6 +104,7 @@ import { mapGetters } from 'vuex'; import { downloadXls } from '@/utils/util'; import { openImportDialog } from '@/utils/import-excel'; import { formatUpdateUserName } from '@/utils/audit'; +import { omitInvalidListStatus } from '@/utils/search-range'; import { getToken } from '@/utils/auth'; import NProgress from 'nprogress'; import 'nprogress/nprogress.css'; @@ -507,7 +508,7 @@ export default { this.onLoad(this.page, this.query); }, buildQuery(params = {}) { - const values = { ...params, ...this.query }; + const values = omitInvalidListStatus({ ...params, ...this.query }); const rangeMap = { effectiveDateRange: ['effectiveDateStart', 'effectiveDateEnd'], expiryDateRange: ['expiryDateStart', 'expiryDateEnd'], diff --git a/src/views/base/customer-type.vue b/src/views/base/customer-type.vue index fb68968..6653a85 100644 --- a/src/views/base/customer-type.vue +++ b/src/views/base/customer-type.vue @@ -63,6 +63,7 @@