|
|
|
|
@ -111,9 +111,9 @@
|
|
|
|
|
{{ formatUserNames(row.handler_admin_ids) }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column prop="apply_handler_id" label="申请科室经办人" width="150" show-overflow-tooltip>
|
|
|
|
|
<el-table-column prop="apply_handler_id" label="申请科室经办人" width="180" show-overflow-tooltip>
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
{{ getUserName(row.apply_handler_id) }}
|
|
|
|
|
{{ formatUserNames(row.apply_handler_id) }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column prop="purchase_handler_id" label="采购科室经办人" width="150" show-overflow-tooltip>
|
|
|
|
|
@ -319,8 +319,8 @@
|
|
|
|
|
<el-row :gutter="20">
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="申请科室经办人">
|
|
|
|
|
<el-select v-model="form.apply_handler_id" placeholder="请选择" filterable clearable style="width: 100%">
|
|
|
|
|
<el-option v-for="user in userList" :key="user.id" :label="user.name" :value="user.id" />
|
|
|
|
|
<el-select v-model="form.apply_handler_id_array" placeholder="请选择" filterable multiple clearable style="width: 100%">
|
|
|
|
|
<el-option v-for="user in userList" :key="user.id" :label="user.name" :value="String(user.id)" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
@ -595,10 +595,10 @@
|
|
|
|
|
<div v-if="row._warnings?.handler_admin_ids" class="warning-text">{{ row._warnings.handler_admin_ids }}</div>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column prop="apply_handler_id" label="申请科室经办人" width="150">
|
|
|
|
|
<el-table-column prop="apply_handler_id" label="申请科室经办人" width="180">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-select v-model="row.apply_handler_id" filterable clearable size="small" style="width: 100%">
|
|
|
|
|
<el-option v-for="user in userList" :key="user.id" :label="user.name" :value="user.id" />
|
|
|
|
|
<el-select v-model="row.apply_handler_id_array" filterable multiple clearable size="small" style="width: 100%">
|
|
|
|
|
<el-option v-for="user in userList" :key="user.id" :label="user.name" :value="String(user.id)" />
|
|
|
|
|
</el-select>
|
|
|
|
|
<div v-if="row._warnings?.apply_handler_id" class="warning-text">{{ row._warnings.apply_handler_id }}</div>
|
|
|
|
|
</template>
|
|
|
|
|
@ -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
|
|
|
|
|
|