|
|
@@ -35,6 +35,15 @@ export const BUILT_IN_WORK_TYPES = [
|
|
|
// 泛词不参与包含匹配,避免「作业」「其他」这类名称误命中
|
|
|
const GENERIC_NAMES = ['作业', '类型', '其他', '其它', '作业票', '其它作业'];
|
|
|
|
|
|
+/**
|
|
|
+ * 归一化作业类型名称:去掉首尾空白,非字符串统一转成字符串
|
|
|
+ * @param {*} value 原始值
|
|
|
+ * @returns {string} 归一化后的名称
|
|
|
+ */
|
|
|
+function normalizeWorkTypeName(value) {
|
|
|
+ return value === undefined || value === null ? '' : String(value).trim();
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* 两个作业类型名称是否指向同一种类型
|
|
|
* 完全相同直接命中;否则按包含关系兜底(如字典「受限空间」vs 内置「受限空间作业」)
|
|
|
@@ -43,8 +52,8 @@ const GENERIC_NAMES = ['作业', '类型', '其他', '其它', '作业票', '其
|
|
|
* @returns 是否为同一种作业类型
|
|
|
*/
|
|
|
export function isSameWorkTypeName(a, b) {
|
|
|
- const x = String(a === undefined || a === null ? '' : a).trim();
|
|
|
- const y = String(b === undefined || b === null ? '' : b).trim();
|
|
|
+ const x = normalizeWorkTypeName(a);
|
|
|
+ const y = normalizeWorkTypeName(b);
|
|
|
if (!x || !y) {
|
|
|
return false;
|
|
|
}
|
|
|
@@ -59,30 +68,73 @@ export function isSameWorkTypeName(a, b) {
|
|
|
return long.indexOf(short) > -1;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * 按作业类型名称在候选列表中查找
|
|
|
+ *
|
|
|
+ * 匹配分两轮:
|
|
|
+ * 第一轮:名称完全相等(含别名),命中即返回。
|
|
|
+ * 放在最前面是为了避免「更短更泛」的类型抢答:
|
|
|
+ * 内置表里若同时存在「高处作业」和「登高作业」,
|
|
|
+ * 字典值「高处作业」必须命中前者,而不是被包含关系先扫到后者。
|
|
|
+ * 第二轮:包含关系兜底(排除「作业」「其他」这类泛词),
|
|
|
+ * 命中多条时取匹配名称最长、也就是最具体的那一条。
|
|
|
+ *
|
|
|
+ * @param {Array} list 候选列表
|
|
|
+ * @param {string} name 作业类型名称(后端字典值)
|
|
|
+ * @param {Function} getNames 从候选项取出可参与匹配的名称数组
|
|
|
+ * @returns {object|null} 命中的候选项,未命中返回 null
|
|
|
+ */
|
|
|
+export function findByWorkTypeName(list, name, getNames) {
|
|
|
+ const target = normalizeWorkTypeName(name);
|
|
|
+ if (!target) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ const candidates = list || [];
|
|
|
+ // 第一轮:名称完全相等
|
|
|
+ for (const item of candidates) {
|
|
|
+ const names = (getNames && getNames(item)) || [];
|
|
|
+ if (names.some((n) => normalizeWorkTypeName(n) === target)) {
|
|
|
+ return item;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 第二轮:包含关系兜底,命中最具体(名称最长)的一条
|
|
|
+ let hit = null;
|
|
|
+ let hitLength = -1;
|
|
|
+ for (const item of candidates) {
|
|
|
+ const names = (getNames && getNames(item)) || [];
|
|
|
+ for (const n of names) {
|
|
|
+ if (!isSameWorkTypeName(n, target)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ const length = normalizeWorkTypeName(n).length;
|
|
|
+ if (length > hitLength) {
|
|
|
+ hit = item;
|
|
|
+ hitLength = length;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return hit;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* 匹配内置作业类型
|
|
|
- * 匹配顺序:作业类型名称 -> 源系统编码 HO_PERMIT_* -> 内置字典码
|
|
|
+ * 匹配顺序:作业类型名称(全等优先 + 包含兜底) -> 内置字典码 / 源系统 HO_PERMIT_* 编码
|
|
|
* @param workType 作业类型编码(后端字典码或源系统 HO_PERMIT_* 编码)
|
|
|
* @param workTypeName 作业类型名称
|
|
|
* @returns 匹配到的内置作业类型,未匹配返回 null
|
|
|
*/
|
|
|
export function matchBuiltInWorkType(workType, workTypeName) {
|
|
|
- const name =
|
|
|
- workTypeName === undefined || workTypeName === null
|
|
|
- ? ''
|
|
|
- : String(workTypeName).trim();
|
|
|
- if (name) {
|
|
|
- const byName = BUILT_IN_WORK_TYPES.find((item) =>
|
|
|
- [item.name, ...(item.aliases || [])].some((n) =>
|
|
|
- isSameWorkTypeName(n, name)
|
|
|
- )
|
|
|
- );
|
|
|
- if (byName) {
|
|
|
- return byName;
|
|
|
- }
|
|
|
+ // 1) 作业类型名称(后端字典值)
|
|
|
+ const byName = findByWorkTypeName(
|
|
|
+ BUILT_IN_WORK_TYPES,
|
|
|
+ workTypeName,
|
|
|
+ (item) => [item.name, ...(item.aliases || [])]
|
|
|
+ );
|
|
|
+ if (byName) {
|
|
|
+ return byName;
|
|
|
}
|
|
|
- const code =
|
|
|
- workType === undefined || workType === null ? '' : String(workType).trim();
|
|
|
+ // 2) 编码兜底:内置字典码,或源系统编码 HO_PERMIT_*
|
|
|
+ const code = normalizeWorkTypeName(workType);
|
|
|
if (code) {
|
|
|
const byCode = BUILT_IN_WORK_TYPES.find(
|
|
|
(item) => item.code === code || item.sourceCode === code
|