Преглед на файлове

merge: 将 dev 分支的更新合并到 test

xieyong преди 4 дни
родител
ревизия
d012aac020

+ 39 - 1
src/components/CommomSelect/dept-select.vue

@@ -20,6 +20,7 @@
 
 <script>
   import { listOrganizations } from '@/api/system/organization';
+  import { getCurrentGroupId, createCompanyFinder } from '@/utils/org-util';
   export default {
     props: {
       // 选中的数据(v-model)
@@ -34,6 +35,11 @@
         type: Boolean,
         default: false
       },
+      // 是否只显示当前登录用户所属公司的机构
+      companyScope: {
+        type: Boolean,
+        default: false
+      },
       // 自定义搜索过滤方法(关键字, 节点数据),不传则默认按部门名称/部门编码匹配
       filterMethod: {
         type: Function,
@@ -68,12 +74,44 @@
       },
       async getData(parmas = {}) {
         const data = await listOrganizations(parmas);
+        const list = this.companyScope
+          ? this.filterByCurrentCompany(data || [])
+          : data || [];
         this.treeData = this.$util.toTreeData({
-          data: data || [],
+          data: list,
           idField: 'id',
           parentIdField: 'parentId'
         });
       },
+      /* 只保留当前登录用户所属公司(顶级机构)下的机构 */
+      filterByCurrentCompany(list) {
+        const userInfo = this.$store?.state?.user?.info;
+        const groupId = getCurrentGroupId(userInfo);
+        if (groupId == null || groupId === '') {
+          return list;
+        }
+        const findCompany = createCompanyFinder(list);
+        const companyNode = findCompany(groupId);
+        if (!companyNode) {
+          // 找不到当前用户所属公司时不做过滤, 避免下拉框被清空
+          return list;
+        }
+        const companyId = String(companyNode.id);
+        const selectedNode =
+          this.value == null || this.value === ''
+            ? null
+            : findCompany(this.value);
+        const selectedCompanyId = selectedNode ? String(selectedNode.id) : '';
+        return list.filter((item) => {
+          const itemCompany = findCompany(item.id);
+          const itemCompanyId = itemCompany ? String(itemCompany.id) : '';
+          if (itemCompanyId === companyId) {
+            return true;
+          }
+          // 已选机构属于其他公司时一并保留, 保证编辑/详情能正常回显名称
+          return !!selectedCompanyId && itemCompanyId === selectedCompanyId;
+        });
+      },
       /* 更新选中数据 */
       updateValue(value) {
         this.$emit('input', value);

+ 94 - 0
src/utils/org-util.js

@@ -0,0 +1,94 @@
+import { getCurrentUser } from '@/utils/token-util';
+import { listOrganizations } from '@/api/system/organization';
+
+/**
+ * 当前登录用户所属机构id
+ * 优先取顶栏切换后的当前机构, 兜底取用户自身所属机构
+ * @param {Object} userInfo 当前登录用户信息
+ * @returns {String|Number|null}
+ */
+export function getCurrentGroupId(userInfo) {
+  let currentGroupId = null;
+  try {
+    currentGroupId = getCurrentUser()?.currentGroupId;
+  } catch (e) {
+    currentGroupId = null;
+  }
+  return currentGroupId || userInfo?.groupId || null;
+}
+
+/**
+ * 生成"查所属公司"的函数
+ * 机构树顶级节点即为公司, 沿 parentId 向上找到顶级节点
+ * @param {Array} list 机构列表(扁平)
+ * @returns {Function} (groupId) => 公司节点 | null
+ */
+export function createCompanyFinder(list) {
+  const nodeMap = new Map();
+  (list || []).forEach((item) => nodeMap.set(String(item.id), item));
+  return (groupId) => {
+    if (groupId == null || groupId === '') {
+      return null;
+    }
+    let node = nodeMap.get(String(groupId));
+    if (!node) {
+      return null;
+    }
+    // 沿 parentId 上溯, 同时用 visited 防止脏数据造成的死循环
+    const visited = new Set();
+    while (node) {
+      const parentKey =
+        node.parentId == null || node.parentId === ''
+          ? ''
+          : String(node.parentId);
+      if (
+        !parentKey ||
+        !nodeMap.has(parentKey) ||
+        visited.has(String(node.id))
+      ) {
+        break;
+      }
+      visited.add(String(node.id));
+      node = nodeMap.get(parentKey);
+    }
+    return node;
+  };
+}
+
+/**
+ * 查找机构所属的公司(顶级机构)
+ * @param {Array} list 机构列表(扁平)
+ * @param {String|Number} groupId 机构id
+ * @returns {Object|null} 公司节点
+ */
+export function findCompanyNode(list, groupId) {
+  return createCompanyFinder(list)(groupId);
+}
+
+/**
+ * 当前登录用户所属的公司
+ * @param {Object} userInfo 当前登录用户信息
+ * @returns {Promise<Object|null>} 公司节点
+ */
+export async function getCurrentUserCompany(userInfo) {
+  const groupId = getCurrentGroupId(userInfo);
+  if (!groupId) {
+    return null;
+  }
+  const list = await listOrganizations();
+  return findCompanyNode(list, groupId);
+}
+
+/**
+ * 当前登录用户所属公司的名称
+ * @param {Object} userInfo 当前登录用户信息
+ * @returns {Promise<String>} 公司名称, 取不到时为空串
+ */
+export async function getCurrentUserCompanyName(userInfo) {
+  try {
+    const company = await getCurrentUserCompany(userInfo);
+    return company?.name || '';
+  } catch (e) {
+    return '';
+  }
+}

+ 2 - 0
src/views/inspectionPlan/components/new-baseInfo.vue

@@ -143,6 +143,8 @@
           <deptSelect
             :disabled="btnType == 'detail'"
             v-model="form.groupId"
+            filterable
+            company-scope
             @changeGroup="searchDeptNodeClick"
           />
         </el-form-item>

+ 2 - 0
src/views/inspectionProjectTask/components/inspectionProjectTaskSend.vue

@@ -90,6 +90,7 @@
               v-model="form.executeDeptId"
               @changeGroup="searchDeptNodeClick"
               filterable
+              company-scope
             /> </el-form-item
         ></el-col>
         <el-col :span="8">
@@ -110,6 +111,7 @@
               @changeGroup="searchDeptNodeClick"
               ref="deptSelect"
               filterable
+              company-scope
             /> </el-form-item
         ></el-col>
         <el-col :span="8">

+ 25 - 5
src/views/inspectionReport/components/reportTemplateWrapper.vue

@@ -144,6 +144,7 @@
   import unqualifiedProduct from '@/views/inspectionReport/components/unqualifiedProduct.vue';
   import { getList } from '@/api/unacceptedProduct/index';
   import { getCode } from '@/api/login';
+  import { getCurrentUserCompanyName } from '@/utils/org-util';
   const defForm = {
     poList: [],
     unqualifiedProductsCode: '',
@@ -480,11 +481,30 @@
         return enterprisePage({
           pageNum: 1,
           size: 200
-        }).then((res) => {
-          if (res.list?.length > 0) {
-            this.$set(this.basicInfoData, 'groupName', res.list[0].name);
-          }
-        });
+        })
+          .then((res) => {
+            if (res.list?.length > 0) {
+              this.$set(this.basicInfoData, 'groupName', res.list[0].name);
+            }
+          })
+          .catch(() => {
+            // 企业列表取不到不影响报告
+          })
+          .then(() => {
+            // 报告抬头默认取当前登录用户所属公司, 取不到时沿用企业列表第一个
+            return this.setDefaultGroupName();
+          });
+      },
+      /* 报告抬头公司: 当前登录用户所属公司 */
+      async setDefaultGroupName() {
+        const userInfo = this.$store?.state?.user?.info;
+        if (!userInfo) {
+          return;
+        }
+        const companyName = await getCurrentUserCompanyName(userInfo);
+        if (companyName) {
+          this.$set(this.basicInfoData, 'groupName', companyName);
+        }
       },
 
       setLossNumber({

+ 37 - 2
src/views/inspectionReport/template/inspection_report3.vue

@@ -18,7 +18,11 @@
           margin-bottom: 5px;
           font-weight: bold;
         "
-        >{{ basicInfoData.groupName || '湖南特瑞精密医疗器器械有限公司' }}</div
+        >{{
+          basicInfoData.groupName ||
+          currentCompanyName ||
+          '湖南特瑞精密医疗器器械有限公司'
+        }}</div
       >
       <div style="text-align: center; font-size: 18px; margin-bottom: 5px"
         >不合格品通知及评审处置表</div
@@ -489,7 +493,11 @@
           margin-bottom: 5px;
           font-weight: bold;
         "
-        >{{ basicInfoData.groupName || '湖南特瑞精密医疗器器械有限公司' }}</div
+        >{{
+          basicInfoData.groupName ||
+          currentCompanyName ||
+          '湖南特瑞精密医疗器器械有限公司'
+        }}</div
       >
       <div style="text-align: center; font-size: 18px; margin-bottom: 5px"
         >不合格品通知及评审处置表</div
@@ -1106,6 +1114,7 @@
 <script>
   // import imageUpload from '@/components/upload/imageUpload.vue';
   import { getFmImagePath } from '@/utils/file';
+  import { getCurrentUserCompanyName } from '@/utils/org-util';
 
   export default {
     name: 'QualityReport',
@@ -1121,7 +1130,11 @@
     data() {
       return {
         getFmImagePath,
+        // 当前登录用户所属公司名称(groupName 为空时的默认值)
+        currentCompanyName: '',
         basicInfoData: {
+          // 公司名称(为空时默认取当前登录用户所属公司)
+          groupName: '',
           // 表单编号、版本(两处共用)
           reportNumber: '',
           version: '',
@@ -1204,6 +1217,10 @@
         }
       };
     },
+    created() {
+      // 提前获取当前登录用户所属公司, 作为报告抬头的默认值
+      this.setDefaultCompanyName();
+    },
     methods: {
       /* 打开质检报告 */
       async init(basicInfoData) {
@@ -1214,6 +1231,24 @@
             ...basicInfoData
           });
         }
+        // groupName 为空时默认取当前登录用户所属公司
+        if (!this.basicInfoData.groupName) {
+          await this.setDefaultCompanyName();
+        }
+      },
+      /* 默认公司名称: 当前登录用户所属公司 */
+      async setDefaultCompanyName() {
+        if (!this.currentCompanyName) {
+          const userInfo = this.$store?.state?.user?.info;
+          if (!userInfo) {
+            return '';
+          }
+          this.currentCompanyName = await getCurrentUserCompanyName(userInfo);
+        }
+        if (this.currentCompanyName && !this.basicInfoData.groupName) {
+          this.$set(this.basicInfoData, 'groupName', this.currentCompanyName);
+        }
+        return this.currentCompanyName;
       },
       /* 保存编辑 */
       async getValue() {

+ 58 - 5
src/views/inspectionWork/components/baseInfo.vue

@@ -118,6 +118,8 @@
             <deptSelect
               :disabled="btnType == 'detail'"
               v-model="form.executeDeptId"
+              filterable
+              company-scope
               @changeGroup="searchDeptNodeClick"
             />
           </el-form-item>
@@ -159,7 +161,7 @@
                 v-for="item in form.qualityType == 2
                   ? form.jobList.find((job) => job.id == form.executeJobId)
                       ?.userVOList
-                  : executorList"
+                  : executorOptions"
                 :key="item.id"
                 :value="item.id"
                 :label="item.name"
@@ -456,6 +458,7 @@
   import { getProduceTaskList } from '@/api/aps';
   import { getByCode } from '@/api/system/dictionary-data';
   import { parameterGetByCode } from '@/api/main/index';
+  import { getCurrentUserCompany } from '@/utils/org-util';
   export default {
     components: {
       EquipmentDialog,
@@ -518,6 +521,20 @@
       //   deep: true
       // }
     },
+    computed: {
+      /* 执行人候选项: 保证当前登录用户始终在列表中, 否则多选框回显不出姓名 */
+      executorOptions() {
+        const list = Array.isArray(this.executorList) ? this.executorList : [];
+        const userInfo = this.$store?.state?.user?.info;
+        if (!userInfo || !userInfo.userId) {
+          return list;
+        }
+        if (list.find((item) => item.id == userInfo.userId)) {
+          return list;
+        }
+        return [...list, { id: userInfo.userId, name: userInfo.name }];
+      }
+    },
     data() {
       return {
         executorList: [],
@@ -766,9 +783,45 @@
             data = Object.assign(data, params);
           }
           const res = await getUserPage(data);
-          this.executorList = res.list;
+          this.executorList = (res && res.list) || [];
         } catch (error) {}
       },
+      /* 默认执行部门=当前登录用户所属公司, 默认执行人=当前登录用户 */
+      async setDefaultExecuteInfo() {
+        const userInfo = this.$store?.state?.user?.info;
+        if (!userInfo || !userInfo.userId) {
+          return;
+        }
+        // 班组模式下执行人由班组决定, 检验方案模式下没有执行部门, 均不处理
+        if (this.form.assignType == 1 || this.form.qualityType == 2) {
+          return;
+        }
+        // 执行人: 未指定时默认当前登录用户
+        // 注意: 不能依赖执行部门是否取到公司, 否则公司查不到时执行人也不会默认值
+        if (
+          !this.form.executeUserIdList ||
+          !this.form.executeUserIdList.length
+        ) {
+          this.$set(this.form, 'executeUserIdList', [userInfo.userId]);
+          this.$set(this.form, 'executeUserName', userInfo.name);
+          this.$set(this.form, 'executeUserId', String(userInfo.userId));
+        }
+        // 执行部门: 未指定时默认当前登录用户所属公司
+        if (!this.form.executeDeptId) {
+          let company = null;
+          try {
+            company = await getCurrentUserCompany(userInfo);
+          } catch (e) {
+            company = null;
+          }
+          if (company) {
+            this.$set(this.form, 'executeDeptId', company.id);
+            this.$set(this.form, 'executeDeptName', company.name);
+            // 执行部门变了, 人员范围跟着变
+            await this.getUserList({ groupId: company.id });
+          }
+        }
+      },
       // 人员选择
       changeExecutor(val) {
         this.$set(this.form, 'qualityIdList', val);
@@ -777,7 +830,7 @@
           this.form.qualityName = val
             .map(
               (item) =>
-                this.executorList.find((item2) => item2.id == item)?.name
+                this.executorOptions.find((item2) => item2.id == item)?.name
             )
             .join(',');
         } else {
@@ -794,9 +847,9 @@
               ? this.form.jobList.find(
                   (job) => job.id == this.form.executeJobId
                 )?.userVOList
-              : this.executorList;
+              : this.executorOptions;
           this.form.executeUserName = val
-            .map((item) => arr.find((item2) => item2.id == item)?.name)
+            .map((item) => arr?.find((item2) => item2.id == item)?.name)
             .join(',');
         } else {
           this.form.executeUserName = '';

+ 5 - 5
src/views/inspectionWork/components/inspectionWorkList.vue

@@ -159,7 +159,7 @@
           @click="openNumber(row)"
           >合格证</el-link
         >
-        <jimureportBrowse
+        <!-- <jimureportBrowse
           style="display: inline-block; margin-left: 5px"
           v-if="
             row.status == 1 &&
@@ -169,12 +169,12 @@
           text="检测报告"
           :businessId="row.id"
           businessCode="qmsqualityinspectionprint"
-        ></jimureportBrowse>
+        ></jimureportBrowse> -->
         <el-dropdown
           trigger="click"
           v-if="
             row.status == 1 &&
-            isReportApproval == 1 &&
+            // isReportApproval == 1 &&
             (!row.reportApprovalStatus || row.reportApprovalStatus == 0) &&
             isEmptyObject(row.reportTemplateJson) &&
             $hasPermission('qms:quality_work_order:generateReport')
@@ -201,7 +201,7 @@
         <el-link
           v-if="
             row.status == 1 &&
-            isReportApproval == 1 &&
+            // isReportApproval == 1 &&
             row.reportApprovalStatus &&
             !isEmptyObject(row.reportTemplateJson) &&
             $hasPermission('qms:quality_work_order:generateReport')
@@ -215,7 +215,7 @@
         <el-link
           v-if="
             row.status == 1 &&
-            isReportApproval == 1 &&
+            // isReportApproval == 1 &&
             (!row.reportApprovalStatus || row.reportApprovalStatus == 3) &&
             !isEmptyObject(row.reportTemplateJson) &&
             $hasPermission('qms:quality_work_order:qualityReportApproval')

+ 13 - 0
src/views/inspectionWork/edit.vue

@@ -1200,6 +1200,8 @@
         if (this.type == 'add') {
           const code = await getCode('quality_work_order_code');
           this.form.code = code;
+          // 新增时默认执行部门/执行人
+          this.setDefaultExecuteInfo();
         } else {
           if (
             this.$route.query.qualityTimeStart != '' ||
@@ -1210,6 +1212,15 @@
           }
         }
       },
+      /* 执行部门/执行人默认值: 执行部门=当前登录用户所属公司, 执行人=当前登录用户 */
+      setDefaultExecuteInfo() {
+        this.$nextTick(() => {
+          const baseInfo = this.$refs.baseInfoRefs;
+          if (baseInfo && baseInfo.setDefaultExecuteInfo) {
+            baseInfo.setDefaultExecuteInfo();
+          }
+        });
+      },
       getNowTime() {
         const nowDate = this.getNowDate();
         return (
@@ -1286,6 +1297,8 @@
               this.$refs.baseInfoRefs?.getTaskList();
             });
           }
+          // 执行部门/执行人默认值(仅在未指定时生效)
+          this.setDefaultExecuteInfo();
         });
 
         // await this.queryQualityInventory()

+ 8 - 0
src/views/unqualifiedProduct/unqualifiedList/index.vue

@@ -274,6 +274,14 @@
             width: 110,
             showOverflowTooltip: true,
             formatter: (_row, _column, cellValue) => {
+              // 无流程实例说明单据未进入审批流程,直接展示为“无流程”
+              const processInstanceId = _row.processInstanceId;
+              if (
+                !processInstanceId ||
+                !String(processInstanceId).trim()
+              ) {
+                return '无流程';
+              }
               return reviewStatus[_row.approvalStatus];
             }
           },