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, './'),