Quellcode durchsuchen

feat: 优化作业类型处理逻辑,新增内置作业类型支持

xieyong vor 6 Tagen
Ursprung
Commit
011a574b69

+ 24 - 25
src/views/DangerousWorkList/add.vue

@@ -241,6 +241,7 @@
     getProcessDefinitionKey,
     getProcessInstanceId
   } from './constants';
+  import { buildWorkTypeOptions, resolveWorkTypeLabel } from './workTypes';
   import bpmDetail from '@/views/bpm/processInstance/detail.vue';
   import {
     ticketSave,
@@ -251,18 +252,7 @@
   } from '@/api/dangerousWork';
   import { listOrganizations, getUserPage } from '@/api/system/organization';
 
-  // 后端字典 ehs_hazardous_work_type 未配置时使用的兜底作业类型
-  const DEFAULT_WORK_TYPES = [
-    { label: '动火作业', value: '1' },
-    { label: '受限空间作业', value: '2' },
-    { label: '高处作业', value: '3' },
-    { label: '吊装作业', value: '4' },
-    { label: '临时用电作业', value: '5' },
-    { label: '动土作业', value: '6' },
-    { label: '断路作业', value: '7' },
-    { label: '盲板抽堵作业', value: '8' }
-  ];
-
+  // 作业票表单初始值
   const defForm = {
     id: '',
     workId: '',
@@ -363,7 +353,13 @@
         const option = this.workTypeList.find(
           (item) => String(item.value) === String(this.form.workType)
         );
-        return option?.label || '';
+        return (
+          option?.label ||
+          resolveWorkTypeLabel(
+            this.form.workType,
+            this.dict[dictEnum['危险作业类型']]
+          )
+        );
       },
       // 是否可提交:
       //  - 新增申请:作业票还没保存,提交时内部会先保存再发起审批流
@@ -377,16 +373,10 @@
         }
         return true;
       },
-      // 作业类型下拉:优先取后端字典,字典没有时使用兜底数据
+      // 作业类型下拉:优先取后端字典,字典没有时使用内置作业类型兜底
+      // 与作业票管理弹窗共用同一套构建逻辑,保证申请页与列表页展示一致
       workTypeList() {
-        const dict = this.dict[dictEnum['危险作业类型']] || [];
-        if (dict.length) {
-          return dict.map((item) => ({
-            label: item.dictValue,
-            value: item.dictCode
-          }));
-        }
-        return DEFAULT_WORK_TYPES;
+        return buildWorkTypeOptions(this.dict[dictEnum['危险作业类型']]);
       },
       // 是否展示模板内容
       showProcess() {
@@ -408,7 +398,7 @@
             name: this.form.applyDeptName
           },
           apply_time: this.getCurrentTime(),
-          work_type: option?.label || '',
+          work_type: option?.label || this.currentWorkTypeName,
           work_site: this.form.workLocation,
           lifting_site: this.form.workLocation
         };
@@ -453,7 +443,10 @@
         const option = this.workTypeList.find(
           (item) => String(item.value) === String(val)
         );
-        const template = getWorkTicketTemplate(val, option?.label);
+        const workTypeName =
+          option?.label ||
+          resolveWorkTypeLabel(val, this.dict[dictEnum['危险作业类型']]);
+        const template = getWorkTicketTemplate(val, workTypeName);
         if (template) {
           // 命中前端内置模板,直接按作业类型套用
           this.frontendTemplate = template;
@@ -645,7 +638,13 @@
         const option = this.workTypeList.find(
           (item) => String(item.value) === String(detail.workType)
         );
-        const template = getWorkTicketTemplate(detail.workType, option?.label);
+        const workTypeName =
+          option?.label ||
+          resolveWorkTypeLabel(
+            detail.workType,
+            this.dict[dictEnum['危险作业类型']]
+          );
+        const template = getWorkTicketTemplate(detail.workType, workTypeName);
         if (template) {
           this.frontendTemplate = template;
           this.templateName = template.name;

+ 28 - 24
src/views/DangerousWorkList/constants.js

@@ -1,6 +1,7 @@
 /**
  * 危险作业票(作业票)常量与审批流配置
  */
+import { isSameWorkTypeName, matchBuiltInWorkType } from './workTypes';
 
 /**
  * 作业票状态
@@ -23,7 +24,7 @@ export const DRAFT_STATUS = 1;
  * 需要前端传入审批流编码(流程定义 key),由前端按作业类型决定走哪条审批流程。
  * 目前仅动火作业接入审批流,其余作业类型待后端提供流程定义 key 后补充。
  *
- * code:我方字典 ehs_hazardous_work_type 的字典码
+ * code:前端内置字典码(后端字典未配置时使用)
  * sourceCode:源系统作业票流程编码(HO_PERMIT_*)
  * name:作业类型名称
  * processDefinitionKey:流程定义 key,即提交接口的 processDefinitionKey
@@ -39,39 +40,42 @@ export const WORK_TICKET_PROCESS_MAPPING = [
 
 /**
  * 根据作业类型获取审批流编码
- * 匹配顺序:字典码 -> 源系统编码 -> 作业类型名称
+ *
+ * 匹配顺序:作业类型名称 -> 内置作业类型映射(源系统编码)
+ * 名称优先的原因:后端字典 ehs_hazardous_work_type 的字典码由后端维护,
+ * 取值与顺序不保证与前端内置一致,按字典码优先匹配会给作业类型发错审批流。
+ *
  * @param workType 作业类型编码(字典码或源系统 HO_PERMIT_* 编码)
  * @param workTypeName 作业类型名称
  * @returns 流程定义 key,未配置时返回空字符串
  */
 export function getProcessDefinitionKey(workType, workTypeName) {
-  const code =
-    workType === undefined || workType === null ? '' : String(workType);
-  if (code) {
-    const byCode = WORK_TICKET_PROCESS_MAPPING.find(
-      (item) => item.code === code || item.sourceCode === code
+  const name =
+    workTypeName === undefined || workTypeName === null
+      ? ''
+      : String(workTypeName).trim();
+  // 1) 作业类型名称(后端字典值)优先
+  if (name) {
+    const byName = WORK_TICKET_PROCESS_MAPPING.find((item) =>
+      isSameWorkTypeName(item.name, name)
     );
-    if (byCode) {
-      return byCode.processDefinitionKey;
-    }
-  }
-  // 编码没有命中时,再按作业类型名称匹配
-  if (workTypeName) {
-    const name = String(workTypeName).trim();
-    const byName = WORK_TICKET_PROCESS_MAPPING.find((item) => {
-      if (item.name === name) {
-        return true;
-      }
-      // 名称不完全一致时按包含关系兜底(如字典为「动火」、配置为「动火作业」)
-      if (name.length < 3 || item.name.length < 3) {
-        return false;
-      }
-      return item.name.indexOf(name) > -1 || name.indexOf(item.name) > -1;
-    });
     if (byName) {
       return byName.processDefinitionKey;
     }
   }
+  // 2) 内置作业类型映射:按源系统编码 HO_PERMIT_* 定位审批流
+  //    不再按字典码直接兜底:字典码由后端维护,与内置编码不一定对应,
+  //    直接按字典码匹配会给作业类型发错审批流
+  const builtIn = matchBuiltInWorkType(workType, workTypeName);
+  if (builtIn) {
+    const byBuiltIn = WORK_TICKET_PROCESS_MAPPING.find(
+      (item) =>
+        item.sourceCode === builtIn.sourceCode || item.code === builtIn.code
+    );
+    if (byBuiltIn) {
+      return byBuiltIn.processDefinitionKey;
+    }
+  }
   return '';
 }
 

+ 38 - 36
src/views/DangerousWorkList/templates.js

@@ -22,26 +22,27 @@
  *     multiple: true  多选,提交时名称与 id 用逗号连接
  */
 
+import {
+  BUILT_IN_WORK_TYPES,
+  isSameWorkTypeName,
+  matchBuiltInWorkType
+} from './workTypes';
+
 /**
  * 作业类型编码映射
- * code:我方字典 ehs_hazardous_work_type 的字典码
+ * code:前端内置字典码(后端字典未配置时使用)
  * sourceCode:源系统作业票流程编码(HO_PERMIT_*)
  */
-export const WORK_TYPE_MAPPING = [
-  { code: '1', name: '动火作业', sourceCode: 'HO_PERMIT_FIRE' },
-  { code: '2', name: '受限空间作业', sourceCode: 'HO_PERMIT_CONFINED_SPACE' },
-  { code: '3', name: '高处作业', sourceCode: 'HO_PERMIT_HEIGHT' },
-  { code: '4', name: '吊装作业', sourceCode: 'HO_PERMIT_HOISTING' },
-  { code: '5', name: '临时用电作业', sourceCode: 'HO_PERMIT_ELECTRICITY' },
-  { code: '6', name: '动土作业', sourceCode: 'HO_PERMIT_BREAK_GROUND' },
-  { code: '7', name: '断路作业', sourceCode: 'HO_PERMIT_BREAK_ROAD' },
-  { code: '8', name: '盲板抽堵作业', sourceCode: 'HO_PERMIT_BLIND_PLATE' }
-];
+export const WORK_TYPE_MAPPING = BUILT_IN_WORK_TYPES.map((item) => ({
+  code: item.code,
+  name: item.name,
+  sourceCode: item.sourceCode
+}));
 
 /**
  * 前端内置的作业票模板
- * workTypes:可匹配的作业类型编码(我方字典码 + 源系统 HO_PERMIT_* 编码)
- * workTypeNames:可匹配的作业类型名称,编码与名称任一匹配即可命中
+ * workTypes:可匹配的作业类型编码(前端内置字典码 + 源系统 HO_PERMIT_* 编码)
+ * workTypeNames:可匹配的作业类型名称,名称匹配优先于编码匹配
  * sections:表单分区 [{ title, fields: [...] }]
  * tables:子表(源系统 batch 类型)[{ id, title, columns: [{ label, id, required }] }]
  */
@@ -897,40 +898,41 @@ export const WORK_TICKET_TEMPLATES = [
 
 /**
  * 根据作业类型匹配作业票模板
+ *
+ * 匹配顺序:作业类型名称 -> 内置作业类型映射(源系统编码 HO_PERMIT_*)
+ * 名称优先的原因:后端字典 ehs_hazardous_work_type 的字典码由后端维护,
+ * 取值与顺序不保证与前端内置一致,按字典码优先匹配会套用错误的作业票模板。
+ *
  * @param workType 作业类型编码(我方字典码或源系统 HO_PERMIT_* 编码)
  * @param workTypeName 作业类型名称
  * @returns 匹配的模板,未匹配返回 null
  */
 export function getWorkTicketTemplate(workType, workTypeName) {
-  const code =
-    workType === undefined || workType === null ? '' : String(workType);
-  if (code) {
-    const byCode = WORK_TICKET_TEMPLATES.find((item) =>
-      (item.workTypes || []).some((type) => String(type) === code)
-    );
-    if (byCode) {
-      return byCode;
-    }
-  }
-  // 编码没有命中时,再按作业类型名称匹配
-  if (workTypeName) {
-    const name = String(workTypeName).trim();
+  const name =
+    workTypeName === undefined || workTypeName === null
+      ? ''
+      : String(workTypeName).trim();
+  // 1) 作业类型名称(后端字典值)优先
+  if (name) {
     const byName = WORK_TICKET_TEMPLATES.find((item) =>
-      (item.workTypeNames || []).some((n) => {
-        if (n === name) {
-          return true;
-        }
-        // 名称不完全一致时按包含关系兜底(如字典为「受限空间」、模板为「受限空间作业」)
-        if (name.length < 3 || n.length < 3) {
-          return false;
-        }
-        return n.indexOf(name) > -1 || name.indexOf(n) > -1;
-      })
+      (item.workTypeNames || []).some((n) => isSameWorkTypeName(n, name))
     );
     if (byName) {
       return byName;
     }
   }
+  // 2) 内置作业类型映射:按源系统编码 HO_PERMIT_* 定位模板
+  //    不再按字典码直接兜底:字典码由后端维护,与内置编码不一定对应,
+  //    直接按字典码匹配会套用错误的作业票模板
+  const builtIn = matchBuiltInWorkType(workType, workTypeName);
+  if (builtIn) {
+    const byBuiltIn = WORK_TICKET_TEMPLATES.find(
+      (item) => item.sourceCode === builtIn.sourceCode
+    );
+    if (byBuiltIn) {
+      return byBuiltIn;
+    }
+  }
   return null;
 }
 

+ 8 - 7
src/views/DangerousWorkList/workTicketDialog.vue

@@ -141,6 +141,7 @@
     getProcessDefinitionKey,
     getProcessInstanceId
   } from './constants';
+  import { buildWorkTypeOptions, resolveWorkTypeLabel } from './workTypes';
 
   // 仅草稿(待提交)状态的作业票可以删除
   const DELETE_TIP = '仅草稿(待提交)状态的作业票可以删除';
@@ -274,12 +275,9 @@
         ];
       },
       // 作业类型下拉(字典 ehs_hazardous_work_type)
+      // 与申请作业票弹窗共用同一套构建逻辑,保证列表展示与申请页选择一致
       workTypeList() {
-        const dict = this.dict[dictEnum['危险作业类型']] || [];
-        return dict.map((item) => ({
-          label: item.dictValue,
-          value: item.dictCode
-        }));
+        return buildWorkTypeOptions(this.dict[dictEnum['危险作业类型']]);
       }
     },
 
@@ -411,9 +409,12 @@
         this.$refs.table && this.$refs.table.reload(option);
       },
 
-      // 作业类型名称
+      // 作业类型名称:后端字典值 -> 内置作业类型名称 -> 原始编码
       getWorkTypeName(workType) {
-        return this.getDictValue('危险作业类型', workType) || workType || '-';
+        return (
+          resolveWorkTypeLabel(workType, this.dict[dictEnum['危险作业类型']]) ||
+          '-'
+        );
       },
       // 作业票状态名称
       getStatusName(status) {

+ 161 - 0
src/views/DangerousWorkList/workTypes.js

@@ -0,0 +1,161 @@
+/**
+ * 作业类型统一解析
+ *
+ * 背景:
+ *   - 后端字典 ehs_hazardous_work_type 提供「字典码 -> 作业类型名称」,由后端维护,
+ *     字典码的取值与顺序不保证与前端内置的 1~8 一致;
+ *   - 作业票模板与审批流是按源系统编码 HO_PERMIT_* 关联的;
+ *   - 若按字典码优先匹配,就会出现「下拉选的是动火作业,套用的却是受限空间作业票模板」,
+ *     甚至「受限空间作业发起了动火作业的审批流」这类张冠李戴。
+ *
+ * 约定:
+ *   - 作业类型名称(后端字典值)是与模板、审批流关联的唯一依据;
+ *   - 字典码只用于展示与传参;
+ *   - 后端字典未配置时用内置作业类型兜底,保证申请页与列表页展示一致。
+ */
+
+/**
+ * 内置作业类型
+ * code:内置字典码(后端字典未配置时兜底使用)
+ * sourceCode:源系统作业票流程编码,与作业票模板一一对应
+ * name:作业类型名称
+ * aliases:名称别名,字典名称与内置名称不完全一致时用于匹配
+ */
+export const BUILT_IN_WORK_TYPES = [
+  { code: '1', name: '动火作业', sourceCode: 'HO_PERMIT_FIRE' },
+  { code: '2', name: '受限空间作业', sourceCode: 'HO_PERMIT_CONFINED_SPACE' },
+  {
+    code: '3',
+    name: '高处作业',
+    sourceCode: 'HO_PERMIT_HEIGHT',
+    aliases: ['高空作业']
+  },
+  { code: '4', name: '吊装作业', sourceCode: 'HO_PERMIT_HOISTING' },
+  { code: '5', name: '临时用电作业', sourceCode: 'HO_PERMIT_ELECTRICITY' },
+  { code: '6', name: '动土作业', sourceCode: 'HO_PERMIT_BREAK_GROUND' },
+  { code: '7', name: '断路作业', sourceCode: 'HO_PERMIT_BREAK_ROAD' },
+  { code: '8', name: '盲板抽堵作业', sourceCode: 'HO_PERMIT_BLIND_PLATE' }
+];
+
+// 泛词不参与包含匹配,避免「作业」「其他」这类名称误命中
+const GENERIC_NAMES = ['作业', '类型', '其他', '其它', '作业票', '其它作业'];
+
+/**
+ * 两个作业类型名称是否指向同一种类型
+ * 完全相同直接命中;否则按包含关系兜底(如字典「受限空间」vs 内置「受限空间作业」)
+ * @param a 名称 a
+ * @param b 名称 b
+ * @returns 是否为同一种作业类型
+ */
+export function isSameWorkTypeName(a, b) {
+  const x = String(a === undefined || a === null ? '' : a).trim();
+  const y = String(b === undefined || b === null ? '' : b).trim();
+  if (!x || !y) {
+    return false;
+  }
+  if (x === y) {
+    return true;
+  }
+  const short = x.length <= y.length ? x : y;
+  const long = x.length <= y.length ? y : x;
+  if (short.length < 2 || GENERIC_NAMES.includes(short)) {
+    return false;
+  }
+  return long.indexOf(short) > -1;
+}
+
+/**
+ * 匹配内置作业类型
+ * 匹配顺序:作业类型名称 -> 源系统编码 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;
+    }
+  }
+  const code =
+    workType === undefined || workType === null ? '' : String(workType).trim();
+  if (code) {
+    const byCode = BUILT_IN_WORK_TYPES.find(
+      (item) => item.code === code || item.sourceCode === code
+    );
+    if (byCode) {
+      return byCode;
+    }
+  }
+  return null;
+}
+
+/**
+ * 构建作业类型下拉项
+ * @param {Array} dictList 后端字典 ehs_hazardous_work_type 的字典项 [{dictCode, dictValue}]
+ * @returns [{ value, label, code, name, sourceCode }],value 为字典码(后端传参用)
+ */
+export function buildWorkTypeOptions(dictList) {
+  const dict = (dictList || []).filter(
+    (item) =>
+      item &&
+      item.dictCode !== undefined &&
+      item.dictCode !== null &&
+      item.dictCode !== ''
+  );
+  if (dict.length) {
+    return dict.map((item) => {
+      const builtIn = matchBuiltInWorkType(item.dictCode, item.dictValue);
+      return {
+        value: item.dictCode,
+        label: item.dictValue,
+        // 内置映射(命中的才有),用于作业票模板、审批流匹配
+        code: builtIn ? builtIn.code : '',
+        name: item.dictValue,
+        sourceCode: builtIn ? builtIn.sourceCode : ''
+      };
+    });
+  }
+  // 后端字典未配置时用内置作业类型兜底,保证申请页与列表页展示一致
+  return BUILT_IN_WORK_TYPES.map((item) => ({
+    value: item.code,
+    label: item.name,
+    code: item.code,
+    name: item.name,
+    sourceCode: item.sourceCode
+  }));
+}
+
+/**
+ * 解析作业类型展示名称
+ * 顺序:后端字典值 -> 内置名称 -> 原始值
+ * @param workType 作业类型编码
+ * @param {Array} dictList 后端字典 ehs_hazardous_work_type 的字典项
+ * @returns 作业类型名称,取不到时返回原始编码
+ */
+export function resolveWorkTypeLabel(workType, dictList) {
+  if (workType === undefined || workType === null || workType === '') {
+    return '';
+  }
+  const dict = dictList || [];
+  const hit = dict.find(
+    (item) => item && String(item.dictCode) === String(workType)
+  );
+  if (hit && hit.dictValue) {
+    return hit.dictValue;
+  }
+  const builtIn = matchBuiltInWorkType(workType, '');
+  if (builtIn) {
+    return builtIn.name;
+  }
+  return String(workType);
+}