diff --git a/docs/canvas-groups-source.md b/docs/canvas-groups-source.md index 7e23a79..3e2a719 100644 --- a/docs/canvas-groups-source.md +++ b/docs/canvas-groups-source.md @@ -176,3 +176,5 @@ budget_template_elements (元素表) + + diff --git a/docs/payment-template-element-types.md b/docs/payment-template-element-types.md index 5e7763f..06d1034 100644 --- a/docs/payment-template-element-types.md +++ b/docs/payment-template-element-types.md @@ -243,3 +243,5 @@ + + diff --git a/src/components/PlannedExpenditureTemplateReadonly.vue b/src/components/PlannedExpenditureTemplateReadonly.vue index fdc2bc9..36449ed 100644 --- a/src/components/PlannedExpenditureTemplateReadonly.vue +++ b/src/components/PlannedExpenditureTemplateReadonly.vue @@ -326,3 +326,5 @@ const formatDefaultValue = (value) => { + + diff --git a/src/views/payment/ContractManagement.vue b/src/views/payment/ContractManagement.vue index 6ee79f8..0bbbdb7 100644 --- a/src/views/payment/ContractManagement.vue +++ b/src/views/payment/ContractManagement.vue @@ -111,9 +111,9 @@ {{ formatUserNames(row.handler_admin_ids) }} - + @@ -319,8 +319,8 @@ - - + + @@ -595,10 +595,10 @@
{{ row._warnings.handler_admin_ids }}
- + @@ -974,7 +974,8 @@ function getEmptyForm() { purchase_category: '', handler_admin_ids: '', handler_admin_ids_array: [], - apply_handler_id: null, + apply_handler_id: '', + apply_handler_id_array: [], purchase_handler_id: null, owner_department_id: null, owner_department_ids: '', @@ -1057,6 +1058,13 @@ async function loadDetail(id) { } else { data.handler_admin_ids_array = [] } + + // 处理申请科室经办人多选(逗号分隔字符串) + if (data.apply_handler_id) { + data.apply_handler_id_array = String(data.apply_handler_id).split(',').map(id => id.trim()).filter(id => id) + } else { + data.apply_handler_id_array = [] + } // 处理附件 if (data.attachment_id && data.attachment) { @@ -1109,6 +1117,11 @@ function collectPayload() { const handlerAdminIds = Array.isArray(form.handler_admin_ids_array) ? form.handler_admin_ids_array.join(',') : (form.handler_admin_ids || '') + + // 申请科室经办人多选:将数组转换为逗号分隔的字符串 + const applyHandlerIds = Array.isArray(form.apply_handler_id_array) + ? form.apply_handler_id_array.filter(id => id).join(',') + : (form.apply_handler_id || '') const base = { contract_no: form.contract_no, @@ -1131,7 +1144,7 @@ function collectPayload() { is_accepted: form.is_accepted || false, purchase_category: form.purchase_category, handler_admin_ids: handlerAdminIds, - apply_handler_id: form.apply_handler_id, + apply_handler_id: applyHandlerIds, purchase_handler_id: form.purchase_handler_id, owner_department_id: form.owner_department_id, owner_department_ids: ownerDepartmentIds, @@ -1572,12 +1585,15 @@ async function processExcelDataWithProgress(jsonData) { departmentList.value.forEach(dept => { if (dept.name) { departmentMap.set(dept.name, dept.id) + // 同步存入“去空白/换行”等预处理后的key,提升精确匹配命中率 + departmentMap.set(normalizeLookupKey(dept.name), dept.id) } // 如果short_name存在且不为空,也添加到映射表中 if (dept.short_name && dept.short_name.trim()) { // 如果short_name和name不同,才添加映射(避免重复) if (dept.short_name !== dept.name) { departmentMap.set(dept.short_name, dept.id) + departmentMap.set(normalizeLookupKey(dept.short_name), dept.id) } } }) @@ -1586,6 +1602,8 @@ async function processExcelDataWithProgress(jsonData) { userList.value.forEach(user => { if (user.name) { userMap.set(user.name, user.id) + // 同步存入“去空白/换行”等预处理后的key,提升精确匹配命中率 + userMap.set(normalizeLookupKey(user.name), user.id) } }) @@ -1862,12 +1880,13 @@ function processDepartmentAndUserIds(processed, row, index, departmentMap, userM const departmentNames = row['科室'] || '' if (departmentNames) { // 支持中文顿号(、)、逗号(,)和中文逗号(,)分隔 - const names = departmentNames.split(/[、,,]/).map(n => n.trim()).filter(n => n) + const names = String(departmentNames).split(/[、,,]/).map(n => n.trim()).filter(n => n) const matchedIds = [] const warnings = [] for (const name of names) { - const matched = matchDepartment(name, departmentMap) + const cleaned = normalizeLookupKey(name) + const matched = matchDepartment(cleaned, departmentMap, name) if (matched.exact) { matchedIds.push(String(matched.exact)) } else if (matched.fuzzy && matched.fuzzy.length === 1) { @@ -1897,12 +1916,13 @@ function processDepartmentAndUserIds(processed, row, index, departmentMap, userM const handlerNames = row['合同签订/变更经办人'] || row['合同签订/合同变更经办人'] || '' if (handlerNames) { // 支持中文顿号(、)、逗号(,)和中文逗号(,)分隔 - const names = handlerNames.split(/[、,,]/).map(n => n.trim()).filter(n => n) + const names = String(handlerNames).split(/[、,,]/).map(n => n.trim()).filter(n => n) const matchedIds = [] const warnings = [] for (const name of names) { - const matched = matchUser(name, userMap) + const cleaned = normalizeLookupKey(name) + const matched = matchUser(cleaned, userMap, name) if (matched.exact) { matchedIds.push(String(matched.exact)) } else if (matched.fuzzy && matched.fuzzy.length === 1 && matched.fuzzy[0] && matched.fuzzy[0].id) { @@ -1926,22 +1946,41 @@ function processDepartmentAndUserIds(processed, row, index, departmentMap, userM } // 处理申请科室经办人(apply_handler_id) + // 支持中文顿号(、)、逗号(,)和中文逗号(,)分隔多选 const applyHandlerName = row['申请科室经办人'] || '' if (applyHandlerName) { - const matched = matchUser(applyHandlerName, userMap) - if (matched.exact) { - processed.apply_handler_id = matched.exact - } else if (matched.fuzzy && matched.fuzzy.length === 1 && matched.fuzzy[0] && matched.fuzzy[0].id) { - processed.apply_handler_id = matched.fuzzy[0].id + const names = String(applyHandlerName).split(/[、,,]/).map(n => n.trim()).filter(n => n) + const matchedIds = [] + const warnings = [] + for (const name of names) { + const cleaned = normalizeLookupKey(name) + const matched = matchUser(cleaned, userMap, name) + if (matched.exact) { + matchedIds.push(String(matched.exact)) + } else if (matched.fuzzy && matched.fuzzy.length === 1 && matched.fuzzy[0] && matched.fuzzy[0].id) { + matchedIds.push(String(matched.fuzzy[0].id)) + } else { + warnings.push(`申请科室经办人"${name}"匹配不精确`) + } + } + if (matchedIds.length > 0) { + processed.apply_handler_id = matchedIds.join(',') + processed.apply_handler_id_array = matchedIds } else { - processed._warnings.apply_handler_id = `申请科室经办人"${applyHandlerName}"匹配不精确,请手动选择` + processed.apply_handler_id_array = [] } + if (warnings.length > 0) { + processed._warnings.apply_handler_id = warnings.join(';') + ',请手动选择' + } + } else { + processed.apply_handler_id_array = [] } // 处理采购科室经办人(purchase_handler_id) const purchaseHandlerName = row['采购科室经办人'] || '' if (purchaseHandlerName) { - const matched = matchUser(purchaseHandlerName, userMap) + const cleaned = normalizeLookupKey(purchaseHandlerName) + const matched = matchUser(cleaned, userMap, purchaseHandlerName) if (matched.exact) { processed.purchase_handler_id = matched.exact } else if (matched.fuzzy && matched.fuzzy.length === 1 && matched.fuzzy[0] && matched.fuzzy[0].id) { @@ -1952,14 +1991,24 @@ function processDepartmentAndUserIds(processed, row, index, departmentMap, userM } } -function matchDepartment(name, departmentMap) { - if (!name) { +// 名称预处理:去首尾空格、去所有空白字符(含换行/tab/全角空格),用于提升匹配准确率 +function normalizeLookupKey(value) { + if (value === null || value === undefined) return '' + return String(value) + .replace(/[\r\n\t]/g, '') + .replace(/[\u00A0\u3000]/g, '') // 不间断空格/全角空格 + .replace(/\s+/g, '') // 其它空白字符 + .trim() +} + +function matchDepartment(cleanedName, departmentMap, rawName = '') { + if (!cleanedName) { return { exact: null, fuzzy: [] } } // 精确匹配:使用Map查找,O(1)时间复杂度 // Map中已经包含了name和short_name的映射 - const exactId = departmentMap.get(name) + const exactId = departmentMap.get(cleanedName) || departmentMap.get(rawName) if (exactId) { return { exact: exactId, fuzzy: [] } } @@ -1967,7 +2016,7 @@ function matchDepartment(name, departmentMap) { // 模糊匹配:限制匹配数量,避免性能问题 // 只匹配前10个,避免在大数据量时卡顿 const fuzzy = [] - const nameLower = name.toLowerCase() + const nameLower = String(cleanedName).toLowerCase() let matchCount = 0 const maxFuzzyMatches = 10 @@ -1977,7 +2026,7 @@ function matchDepartment(name, departmentMap) { // 同时匹配name和short_name let matched = false if (dept.name) { - const deptNameLower = dept.name.toLowerCase() + const deptNameLower = normalizeLookupKey(dept.name).toLowerCase() // 只做单向匹配(搜索名包含在部门名中),避免性能问题 if (deptNameLower.includes(nameLower)) { fuzzy.push({ id: dept.id, name: dept.name, short_name: dept.short_name || '' }) @@ -1988,7 +2037,7 @@ function matchDepartment(name, departmentMap) { // 如果name没有匹配,尝试匹配short_name if (!matched && dept.short_name && dept.short_name.trim()) { - const deptShortNameLower = dept.short_name.toLowerCase() + const deptShortNameLower = normalizeLookupKey(dept.short_name).toLowerCase() if (deptShortNameLower.includes(nameLower)) { fuzzy.push({ id: dept.id, name: dept.name, short_name: dept.short_name }) matchCount++ @@ -1999,13 +2048,13 @@ function matchDepartment(name, departmentMap) { return { exact: null, fuzzy } } -function matchUser(name, userMap) { - if (!name) { +function matchUser(cleanedName, userMap, rawName = '') { + if (!cleanedName) { return { exact: null, fuzzy: [] } } // 精确匹配:使用Map查找,O(1)时间复杂度 - const exactId = userMap.get(name) + const exactId = userMap.get(cleanedName) || userMap.get(rawName) if (exactId) { return { exact: exactId, fuzzy: [] } } @@ -2013,7 +2062,7 @@ function matchUser(name, userMap) { // 模糊匹配:限制匹配数量,避免性能问题 // 只匹配前10个,避免在大数据量时卡顿 const fuzzy = [] - const nameLower = name.toLowerCase() + const nameLower = String(cleanedName).toLowerCase() let matchCount = 0 const maxFuzzyMatches = 10 @@ -2021,7 +2070,7 @@ function matchUser(name, userMap) { if (matchCount >= maxFuzzyMatches) break if (user.name) { - const userNameLower = user.name.toLowerCase() + const userNameLower = normalizeLookupKey(user.name).toLowerCase() // 只做单向匹配(搜索名包含在用户名中),避免性能问题 if (userNameLower.includes(nameLower)) { fuzzy.push({ id: user.id, name: user.name }) @@ -2083,12 +2132,20 @@ async function submitImport() { } else if (!contract.owner_department_ids) { contract.owner_department_ids = '' } + + // 处理apply_handler_id_array转换为apply_handler_id字符串 + if (contract.apply_handler_id_array && Array.isArray(contract.apply_handler_id_array)) { + contract.apply_handler_id = contract.apply_handler_id_array.filter(id => id).join(',') + } else if (!contract.apply_handler_id) { + contract.apply_handler_id = '' + } // 删除不需要提交的字段 delete contract._errors delete contract._warnings delete contract.handler_admin_ids_array delete contract.owner_department_ids_array + delete contract.apply_handler_id_array delete contract._attachment_match return contract diff --git a/src/views/payment/CreatePayment.vue b/src/views/payment/CreatePayment.vue index efaf4aa..607bb02 100644 --- a/src/views/payment/CreatePayment.vue +++ b/src/views/payment/CreatePayment.vue @@ -2483,8 +2483,7 @@ const handleOaFlowMessage = (event) => { extraInfo: event.data.extraInfo || null } ElMessage.success('主审批流程创建成功') - // 关闭抽屉 - showOaDrawer.value = false + // 不自动关闭抽屉:让用户在抽屉内继续完成OA流程流转;需要时用户可手动关闭 } else { ElMessage.error(event.data.message || '创建OA流程失败') } diff --git a/src/views/payment/IndirectPayment.vue b/src/views/payment/IndirectPayment.vue index d6b9da1..36b2252 100644 --- a/src/views/payment/IndirectPayment.vue +++ b/src/views/payment/IndirectPayment.vue @@ -5570,8 +5570,7 @@ const handlePaymentOaFlowMessage = (event) => { extraInfo: event.data.extraInfo || null } ElMessage.success('主审批流程创建成功') - // 关闭抽屉 - showPaymentOaDrawer.value = false + // 不自动关闭抽屉:让用户在抽屉内继续完成OA流程流转;需要时用户可手动关闭 } else { ElMessage.error(event.data.message || '创建OA流程失败') } diff --git a/ui/pages/payment/indirect-payment-print-demo-simple.html b/ui/pages/payment/indirect-payment-print-demo-simple.html index 6e83f79..c0018c9 100644 --- a/ui/pages/payment/indirect-payment-print-demo-simple.html +++ b/ui/pages/payment/indirect-payment-print-demo-simple.html @@ -293,6 +293,8 @@ + + diff --git a/模板元素显示条件说明.md b/模板元素显示条件说明.md index 4917873..d45c2d2 100644 --- a/模板元素显示条件说明.md +++ b/模板元素显示条件说明.md @@ -310,3 +310,5 @@ watch( + +