فهرست منبع

合并test分支到dev

xieyong 1 هفته پیش
والد
کامیت
6848c86540

+ 1 - 1
.gitignore

@@ -12,7 +12,7 @@ yarn-debug.log*
 yarn-error.log*
 .pnpm-debug.log
 .eslintcache
-
+.history
 # Editor directories and files
 .idea
 .vscode

+ 10 - 0
src/api/factoryModel/index.js

@@ -36,6 +36,16 @@ export async function getFactoryarea(params) {
   }
   return Promise.reject(new Error(res.data.message));
 }
+
+// 获取所有工厂
+export async function getFactoryList() {
+  const res = await request.get(`/main/factoryarea/getFactoryList`);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
 // 删除
 export async function deletefactoryarea(params) {
   const res = await request.get(`/main/factoryarea/delete/${params}`);

+ 9 - 14
src/components/CommomSelect/factory-select.vue

@@ -12,13 +12,13 @@
       v-for="item in dictList"
       :key="item.id"
       :label="item.name"
-      :value="item.id"
+      :value="String(item.id)"
     ></el-option>
   </el-select>
 </template>
 
 <script>
-  import route from '@/api/technology/route';
+  import { getFactoryList } from '@/api/factoryModel';
   export default {
     model: {
       prop: 'value',
@@ -34,39 +34,34 @@
         default: true
       }
     },
-    data () {
+    data() {
       return {
         dictList: []
       };
     },
     computed: {
       selectVal: {
-        set (val) {
+        set(val) {
           this.$emit(
             'selfChange',
             val,
-            this.dictList.find((i) => i.id === val)
+            this.dictList.find((i) => String(i.id) === String(val))
           );
           this.$emit('updateVal', val);
         },
-        get () {
+        get() {
           return this.value;
         }
       }
     },
-    created () {
+    created() {
       if (this.init) {
         this.getList();
       }
     },
     methods: {
-      async getList () {
-        const res = await route.Flist({
-          pageNum: 1,
-          size: -1,
-          type: 1
-        });
-        this.dictList = res.list;
+      async getList() {
+        this.dictList = (await getFactoryList()) || [];
       }
     }
   };

+ 87 - 4
src/components/processSubmitDialog/processSubmitDialog.vue

@@ -91,10 +91,10 @@
           fixed
         />
         <el-table-column
-          label="执行人"
+          label="需要审批的角色"
           align="center"
           prop="options"
-          min-width="140px"
+          min-width="200px"
         >
           <template v-slot="scope">
             <div
@@ -104,18 +104,21 @@
                 scope.row.type !== 71 &&
                 scope.row.options.length > 0
               "
+              style="display: flex; flex-wrap: wrap; gap: 4px; justify-content: center;"
             >
               <el-tag
-                size="medium"
+                size="small"
                 :key="option"
                 v-for="option in scope.row.options"
+                style="margin: 1px;"
+                hit
               >
                 {{ getAssignRuleOptionName(scope.row, option) }}
               </el-tag>
             </div>
 
             <el-tag
-              size="medium"
+              size="small"
               v-if="
                 scope.row.type === 60 ||
                 scope.row.type === 70 ||
@@ -127,6 +130,46 @@
             </el-tag>
           </template>
         </el-table-column>
+        <el-table-column
+          label="审批人选择"
+          align="center"
+          min-width="240px"
+        >
+          <template v-slot="scope">
+            <el-select
+              v-if="!isInitiatorNode(scope.row)"
+              v-model="scope.row.selectedUsers"
+              multiple
+              filterable
+              clearable
+              placeholder="请选择审批人"
+              style="width: 100%;"
+            >
+              <el-option
+                v-for="user in getFilteredUserOptions(scope.row)"
+                :key="user.id"
+                :label="user.nickname || user.name"
+                :value="user.id"
+              />
+            </el-select>
+            <span v-else style="color: #909399; font-size: 12px;">发起人</span>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="审批方式"
+          align="center"
+          prop="approvalType"
+          width="100"
+        >
+          <template v-slot="scope">
+            <el-tag
+              :type="scope.row.approvalType === 1 ? 'success' : 'warning'"
+              size="small"
+            >
+              {{ getApprovalTypeName(scope.row.approvalType) }}
+            </el-tag>
+          </template>
+        </el-table-column>
       </el-table>
       <my-process-viewer
         v-show="active == 0"
@@ -354,6 +397,21 @@
           modelId: find.modelId,
           processDefinitionId: find.processDefinitionId
         });
+        // 从 bpmnXML 解析每个节点是否是多实例,用于判断审批方式
+        this.datasource.forEach(row => {
+          row.approvalType = this.getApprovalTypeFromBpmn(row.taskDefinitionKey);
+        });
+      },
+      getApprovalTypeFromBpmn(taskDefinitionKey) {
+        // 如果 bpmnXML 不存在,默认单人审批
+        if (!this.bpmnXML) return 1;
+        // 在 BPMN XML 中找到该 userTask 节点的内容
+        const regex = new RegExp(
+          `<[^:]*:?userTask[^>]*\\s(id="${taskDefinitionKey}")[^>]*>[\\s\\S]*?</[^:]*:?userTask>`
+        );
+        const match = this.bpmnXML.match(regex);
+        // 没有匹配到或没有多实例配置 → 单人审批;有多实例配置 → 多人审批
+        return match && match[0].includes('multiInstanceLoopCharacteristics') ? 2 : 1;
       },
       getAssignRuleOptionName(row, option) {
         if (row.type == 10) {
@@ -404,6 +462,15 @@
         }
         return '未知(' + option + ')';
       },
+      isInitiatorNode(row) {
+        return row.type === 60;
+      },
+      getFilteredUserOptions(row) {
+        return this.userOptions;
+      },
+      getApprovalTypeName(type) {
+        return type === 1 ? '单人审批' : '多人审批';
+      },
       /**
        * 构造树型结构数据
        * @param {*} data 数据源
@@ -459,6 +526,14 @@
           text: '提交中',
           background: 'rgba(0, 0, 0, 0.7)'
         });
+        // 从表格中构建审批人映射: { taskDefinitionKey: [userId1, userId2] }
+        const assignee = {};
+        this.datasource.forEach(row => {
+          if (row.selectedUsers && row.selectedUsers.length > 0) {
+            assignee[row.taskDefinitionKey] = row.selectedUsers;
+          }
+        });
+        this.form.assignee = assignee;
         const submitPromise = this.customSubmit
           ? this.customSubmit(this.form.businessId, this.form)
           : processInstanceCreateAPI(this.form);
@@ -485,6 +560,14 @@
           approvalStatus: this.approvalStatus,
           ...this.form
         };
+        // 从表格中构建审批人映射
+        const assignee = {};
+        this.datasource.forEach(row => {
+          if (row.selectedUsers && row.selectedUsers.length > 0) {
+            assignee[row.taskDefinitionKey] = row.selectedUsers;
+          }
+        });
+        params.assignee = assignee;
         await this[this.apiFunName](params)
           .then((res) => {
             console.log(res, 'res');

+ 24 - 1
src/views/bpm/model/index.vue

@@ -92,6 +92,22 @@
             <el-radio :label="0">否</el-radio>
           </el-radio-group>
         </el-form-item>
+        <el-form-item label="超时自动同意" prop="timeoutAutoApprove">
+          <el-radio-group v-model="form.timeoutAutoApprove">
+            <el-radio :label="1">是</el-radio>
+            <el-radio :label="0">否</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item v-if="form.timeoutAutoApprove === 1" label="超时时间" prop="timeoutHours">
+          <el-input-number 
+            v-model="form.timeoutHours" 
+            :min="1" 
+            :max="720"
+            :step="1"
+            style="width: 200px;"
+          />
+          <div style="line-height: 1.5; color: #909399; font-size: 12px; margin-top: 4px;">单位:小时,超过此时间未处理将自动同意</div>
+        </el-form-item>
         <div v-if="form.id">
           <el-form-item label="表单路由" prop="formCustomCreatePath">
             <el-input v-model="form.formCustomCreatePath" placeholder="请按格式输入表单路由" style="width: 330px;"
@@ -245,6 +261,11 @@ export default {
         name: [{required: true, message: "流程名称不能为空", trigger: "blur"}],
         processTypeId: [{required: true, message: "流程分类不能为空", trigger: "change"}],
         autoSkipApprove: [{required: true, message: "请选择是否自动跳过审批", trigger: "change"}],
+        timeoutAutoApprove: [{required: true, message: "请选择是否开启超时自动同意", trigger: "change"}],
+        timeoutHours: [
+          {required: true, message: "请输入超时时间", trigger: "blur"},
+          {type: 'number', min: 1, message: '超时时间必须大于0', trigger: 'blur'}
+        ],
       },
       pageSize: this.$store.state.tablePageSize,
       cacheKeyUrl: '05790f5b-bpm-model'
@@ -318,7 +339,9 @@ export default {
         formId: undefined,
         formCustomCreatePath: undefined,
         formCustomViewPath: undefined,
-        autoSkipApprove: 0
+        autoSkipApprove: 0,
+        timeoutAutoApprove: 0,
+        timeoutHours: 24
       };
       console.log(this.$refs.form);
       this.$refs.form.resetFields();

+ 32 - 21
src/views/material/product/detail.vue

@@ -677,7 +677,7 @@
   import { addMaterial } from '@/api/material/list.js';
   import { deepClone } from '@/utils/index';
   import { finishPageTab, reloadPageTab } from '@/utils/page-tab-util';
-  import { produceTypeList } from '@/enum/dict.js';
+  // import { produceTypeList } from '@/enum/dict.js';
   import { copyObj } from '@/utils/util';
   import { parameterGetByCode } from '@/api/system/dictionary/index.js';
   import GetCodeDialog from '@/components/addDoc/getCode.vue';
@@ -769,7 +769,7 @@
         isReadOnly: false,
         isShow: true,
         industryAttribute: '',
-        produceTypeList,
+        produceTypeList:[],
         packagingSpecificationList: [],
         loading: false,
         productImgs: [],
@@ -842,24 +842,7 @@
             needProductSequence: 0
           }
         },
-        lbjtList: [
-          {
-            label: '自制件',
-            value: 1
-          },
-          {
-            label: '采购件',
-            value: 2
-          },
-          {
-            label: '外协件',
-            value: 3
-          },
-          {
-            label: '受托件',
-            value: 4
-          }
-        ],
+        lbjtList: [],
         attributeList: [],
         remarkform: {
           remarkAttach: [],
@@ -1151,7 +1134,6 @@
           this.industryAttribute = res.value;
         }
       });
-
       //新增
 
       this.$set(
@@ -1189,6 +1171,8 @@
       this.getAttributeList('inventory_type');
 
       this.getDictList('zeroPartPros');
+      this.getLbjtListList('categoryComponentAttribute');
+      this.getProduceTypeList('categoryProduceTypeList');
     },
     mounted() {
       if (this.clientEnvironmentId == 4) {
@@ -1634,7 +1618,34 @@
           }
         }
       },
+      // 生产类型字典
+      async getProduceTypeList(code) {
+        let { data: res } = await getByCode(code);
+        console.log('res----', res);
+
+        this.produceTypeList = res.map((item) => {
+          let values = Object.keys(item);
+          return {
+            value: values[0],
+            label: item[values[0]]
+          };
+        });
+        console.log('produceTypeList',this.produceTypeList);
+      },
+      // 属性类型字典
+      async getLbjtListList(code) {
+        let { data: res } = await getByCode(code);
+        console.log('res----', res);
 
+        this.lbjtList = res.map((item) => {
+          let values = Object.keys(item);
+          return {
+            value: Number(values[0]),
+            label: item[values[0]]
+          };
+        });
+        console.log('lbjtList',this.lbjtList);
+      },
       async getDictList(code) {
         let { data: res } = await getByCode(code);
 

+ 12 - 3
src/views/system/externalClient/components/external-client-edit.vue

@@ -170,11 +170,19 @@
       }
     },
     methods: {
+      normalizeFactoriesId(value) {
+        return value === null || value === undefined || value === ''
+          ? ''
+          : String(value);
+      },
       async initForm() {
         this.isUpdate = Boolean(this.data && this.data.id);
         this.form = {
           ...defaultForm,
-          ...(this.data || {})
+          ...(this.data || {}),
+          factoriesId: this.normalizeFactoriesId(
+            this.data && this.data.factoriesId
+          )
         };
         if (!this.isUpdate) {
           return;
@@ -184,7 +192,8 @@
           const detail = await getExternalClient(this.data.id);
           this.form = {
             ...defaultForm,
-            ...detail
+            ...detail,
+            factoriesId: this.normalizeFactoriesId(detail && detail.factoriesId)
           };
         } catch (error) {
           this.$message.error(error.message || '获取客户端详情失败');
@@ -216,7 +225,7 @@
             systemName: this.form.systemName,
             systemCode: this.form.systemCode,
             factoriesId: this.form.factoriesId
-              ? Number(this.form.factoriesId)
+              ? String(this.form.factoriesId)
               : undefined,
             effectiveTime: this.form.effectiveTime || undefined,
             expireTime: this.form.expireTime || undefined,

+ 21 - 1
src/views/system/externalClient/index.vue

@@ -30,6 +30,9 @@
             {{ Number(row.status) === 1 ? '启用' : '禁用' }}
           </el-tag>
         </template>
+        <template v-slot:factoriesId="{ row }">
+          {{ getFactoryName(row.factoriesId) }}
+        </template>
         <template v-slot:action="{ row }">
           <el-link
             v-if="row.clientId"
@@ -134,6 +137,7 @@
   import tabMixins from '@/mixins/tableColumnsMixin';
   import ExternalClientSearch from './components/external-client-search.vue';
   import ExternalClientEdit from './components/external-client-edit.vue';
+  import { getFactoryList } from '@/api/factoryModel';
   import {
     pageExternalClients,
     setExternalClientStatus,
@@ -234,10 +238,26 @@
         pageSize: this.$store.state.tablePageSize,
         cacheKeyUrl: 'external-client-management',
         secretVisible: false,
-        clientSecret: ''
+        clientSecret: '',
+        factoryList: []
       };
     },
+    created() {
+      this.loadFactoryList();
+    },
     methods: {
+      async loadFactoryList() {
+        this.factoryList = (await getFactoryList()) || [];
+      },
+      getFactoryName(id) {
+        if (!id) {
+          return '全部工厂';
+        }
+        const factory = this.factoryList.find(
+          (item) => String(item.id) === String(id)
+        );
+        return factory ? factory.name : id;
+      },
       datasource({ page, limit, where, order }) {
         return pageExternalClients({
           ...where,

+ 43 - 3
src/views/system/role/components/role-data-auth.vue

@@ -42,6 +42,24 @@
             :default-checked-keys="checkedKeys"
           />
         </el-scrollbar>
+        <!-- 指定工厂数据权限(7):勾选工厂(t_main_factory type=1),集合存 dataScopeDeptIds -->
+        <el-scrollbar
+          v-if="form.dataScope=='7'"
+          v-loading="authLoading"
+          style="height: 40vh"
+          wrap-style="overflow-x: hidden;">
+          <el-tree
+            ref="factoryTree"
+            :data="factoryData"
+            :check-strictly="true"
+            highlight-current
+            node-key="id"
+            :props="{ label: 'name' }"
+            :expand-on-click-node="false"
+            show-checkbox
+            :default-expand-all="true"
+          />
+        </el-scrollbar>
       </el-form-item>
     </el-form>
 
@@ -58,6 +76,7 @@
 <script>
 import {putRoles} from '@/api/system/role';
 import {listOrganizations} from "@/api/system/organization";
+import {getFactoryarea} from "@/api/factoryModel/index";
 
 export default {
   components: {},
@@ -74,6 +93,8 @@ export default {
       rules: {},
       // 权限数据
       deptData: [],
+      // 指定工厂(7)勾选用的工厂列表(t_main_factory type=1)
+      factoryData: [],
       // 权限数据请求状态
       authLoading: false,
       // 提交状态
@@ -85,6 +106,7 @@ export default {
   },
   created() {
    this.getListOrganizations()
+   this.getFactoryTreeData()
   },
   methods: {
 
@@ -92,13 +114,28 @@ export default {
     init(row) {
       this.form = row
       this.form.dataScope = this.form.dataScope + ''
+      console.log(this.form.dataScope,"scopeeeeee")
       this.$nextTick(()=>{
         if(this.form.dataScope=='2'){
-          this.$refs.tree.setCheckedKeys(this.form.dataScopeDeptIds.split(','))
+          this.$refs.tree.setCheckedKeys(this.form.dataScopeDeptIds ? this.form.dataScopeDeptIds.split(',') : [])
+        } else if(this.form.dataScope=='7'){
+          if(this.$refs.factoryTree){
+            this.$refs.factoryTree.setCheckedKeys(this.form.dataScopeDeptIds ? this.form.dataScopeDeptIds.split(',') : [])
+          }
         }
-
       })
-
+    },
+    /* 指定工厂(7):查询 type=1 工厂列表 */
+    getFactoryTreeData(){
+      this.authLoading = true;
+      getFactoryarea({ pageNum: 1, size: 999, type: 1 })
+        .then((res) => {
+          this.authLoading = false;
+          this.factoryData = (res && res.list) || [];
+        })
+        .catch(() => {
+          this.authLoading = false;
+        });
     },
     getListOrganizations(){
       this.authLoading = true;
@@ -123,6 +160,9 @@ export default {
       if(this.form.dataScope=='2'){
         const ids = this.$refs.tree.getCheckedKeys().concat(this.$refs.tree.getHalfCheckedKeys());
         this.form.dataScopeDeptIds = ids.join(',');
+      } else if(this.form.dataScope=='7'){
+        const factoryIds = this.$refs.factoryTree.getCheckedKeys();
+        this.form.dataScopeDeptIds = factoryIds.join(',');
       }
       putRoles(this.form)
         .then((msg) => {

+ 47 - 2
src/views/system/role/components/role-edit.vue

@@ -68,6 +68,25 @@
             :default-checked-keys="checkedKeys"
           />
         </el-scrollbar>
+        <!-- 指定工厂数据权限(7):勾选工厂(t_main_factory type=1),集合存 dataScopeDeptIds -->
+        <el-scrollbar
+          v-if="form.dataScope == '7'"
+          v-loading="authLoading"
+          style="height: 40vh"
+          wrap-style="overflow-x: hidden;"
+        >
+          <el-tree
+            ref="factoryTree"
+            :data="factoryData"
+            :check-strictly="true"
+            highlight-current
+            node-key="id"
+            :props="{ label: 'name' }"
+            :expand-on-click-node="false"
+            show-checkbox
+            :default-expand-all="true"
+          />
+        </el-scrollbar>
       </el-form-item>
     </el-form>
     <template v-slot:footer>
@@ -82,6 +101,7 @@
 <script>
   import { addRole, putRoles } from '@/api/system/role';
   import { listOrganizations } from '@/api/system/organization';
+  import { getFactoryarea } from '@/api/factoryModel/index';
   import { secretLevelList } from '@/enum/dict';
   import { getCode } from '@/api/codeManagement/index.js';
 
@@ -94,6 +114,7 @@
     },
     created() {
       this.getListOrganizations();
+      this.getFactoryTreeData();
     },
     data() {
       const defaultForm = {
@@ -132,7 +153,9 @@
         isUpdate: false,
         authLoading: false,
 
-        organizationList: []
+        organizationList: [],
+        // 指定工厂(7)勾选用的工厂列表(t_main_factory type=1)
+        factoryData: []
       };
     },
     methods: {
@@ -172,6 +195,19 @@
             // this.$message.error(e.message);
           });
       },
+      /* 指定工厂(7):查询 type=1 工厂列表 */
+      getFactoryTreeData() {
+        this.authLoading = true;
+        getFactoryarea({ pageNum: 1, size: 999, type: 1 })
+          .then((res) => {
+            this.authLoading = false;
+            this.factoryData = (res && res.list) || [];
+          })
+          .catch((e) => {
+            this.authLoading = false;
+            // this.$message.error(e.message);
+          });
+      },
       /* 保存编辑 */
       save() {
         this.$refs.form.validate((valid) => {
@@ -184,6 +220,9 @@
               .getCheckedKeys()
               .concat(this.$refs.tree.getHalfCheckedKeys());
             this.form.dataScopeDeptIds = ids.join(',');
+          } else if (this.form.dataScope == '7') {
+            const factoryIds = this.$refs.factoryTree.getCheckedKeys();
+            this.form.dataScopeDeptIds = factoryIds.join(',');
           }
           const saveOrUpdate = this.isUpdate ? putRoles : addRole;
           saveOrUpdate(this.form)
@@ -216,8 +255,14 @@
             this.$nextTick(() => {
               if (this.form.dataScope == '2') {
                 this.$refs.tree.setCheckedKeys(
-                  this.form.dataScopeDeptIds.split(',')
+                  this.form.dataScopeDeptIds ? this.form.dataScopeDeptIds.split(',') : []
                 );
+              } else if (this.form.dataScope == '7') {
+                if (this.$refs.factoryTree) {
+                  this.$refs.factoryTree.setCheckedKeys(
+                    this.form.dataScopeDeptIds ? this.form.dataScopeDeptIds.split(',') : []
+                  );
+                }
               }
             });
             this.isUpdate = true;

+ 13 - 0
src/views/technology/route/components/user-detail.vue

@@ -122,6 +122,10 @@
       <template v-slot:orderNum="{ row }">
         {{ row.orderNum }}
       </template>
+
+      <template v-slot:isParallel="{ row }">
+        {{ !!row.isParallel ? '是' : '否' }}
+      </template>
     </ele-pro-table>
     <bpmDetail
       v-if="activeComp == 'bpm' && form.processInstanceId"
@@ -211,6 +215,14 @@
             align: 'center',
             minWidth: 110
           },
+          {
+            prop: 'isParallel',
+            label: '并行工序',
+            showOverflowTooltip: true,
+            align: 'center',
+            slot: 'isParallel',
+            minWidth: 110
+          },
           {
             align: 'center',
             prop: 'controlName',
@@ -257,6 +269,7 @@
 
           let arr = res?.list?.map((it) => {
             it.detail.orderNum = it.orderNum;
+            it.detail.isParallel = it.isParallel;
             return it.detail;
           });
           return {