Forráskód Böngészése

流程自选,命题修改

zhangjie 3 éve
szülő
commit
7fa9e5a5ff

+ 17 - 0
src/assets/styles/pages.scss

@@ -206,6 +206,10 @@
   }
 
   .flow-item {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+
     &-time {
       color: #909399;
       line-height: 1;
@@ -250,6 +254,19 @@
         }
       }
     }
+
+    &-action {
+      text-align: center;
+    }
+
+    // action
+    .user-select {
+      width: 40px;
+      height: 40px;
+      padding: 10px;
+      font-size: 18px;
+      line-height: 1;
+    }
   }
 }
 

+ 4 - 0
src/constants/enumerate.js

@@ -114,6 +114,10 @@ export const FLOW_TYPE = {
   // QUESTION_ELECTRON_FLOW: "题库试题提交审核",
   // PAPER_ELECTRON_FLOW: "题库试卷审核"
 };
+export const FLOW_MODEL_TYPE = {
+  USER_FIXED: "管理员指定",
+  APPROVE_SET: "发起人自选"
+};
 
 export const FLOW_APPROVE_OPERATION_TYPE = {
   SUBMIT: "提交",

+ 26 - 3
src/modules/base/components/ModifyFlow.vue

@@ -28,7 +28,6 @@
           v-model="modalForm.type"
           style="width:100%"
           placeholder="请选择流程类型"
-          clearable
           :disabled="isEdit"
         >
           <el-option
@@ -39,6 +38,21 @@
           ></el-option>
         </el-select>
       </el-form-item>
+      <el-form-item prop="modelType" label="指定审批人类型:">
+        <el-select
+          v-model="modalForm.modelType"
+          style="width:100%"
+          placeholder="请选择流程指定审批人类型"
+          :disabled="isEdit"
+        >
+          <el-option
+            v-for="(val, key) in FLOW_MODEL_TYPE"
+            :key="key"
+            :value="key"
+            :label="val"
+          ></el-option>
+        </el-select>
+      </el-form-item>
     </el-form>
     <div slot="footer">
       <el-button type="primary" :disabled="isSubmit" @click="submit"
@@ -50,13 +64,14 @@
 </template>
 
 <script>
-import { FLOW_TYPE } from "../../../constants/enumerate";
+import { FLOW_TYPE, FLOW_MODEL_TYPE } from "../../../constants/enumerate";
 import { updateFlowName } from "../api";
 
 const initModalForm = {
   id: null,
   name: "",
-  type: "ELECTRON_FLOW"
+  type: "ELECTRON_FLOW",
+  modelType: "USER_FIXED"
 };
 
 export default {
@@ -82,6 +97,7 @@ export default {
       modalIsShow: false,
       isSubmit: false,
       FLOW_TYPE,
+      FLOW_MODEL_TYPE,
       modalForm: {},
       rules: {
         name: [
@@ -102,6 +118,13 @@ export default {
             message: "请选择流程类型",
             trigger: "change"
           }
+        ],
+        modelType: [
+          {
+            required: true,
+            message: "请选择流程指定审批人类型",
+            trigger: "change"
+          }
         ]
       }
     };

+ 245 - 82
src/modules/base/components/ModifyFlowDetail.vue

@@ -45,31 +45,58 @@
                 >审批人
               </div>
               <div v-if="node.type === 'PROCESS'" class="flow-node-content">
-                <div v-if="node.property.approveUserType === 'USER'">
+                <template v-if="!IS_APPROVE_SET">
+                  <div v-if="node.property.approveUserType === 'USER'">
+                    <p>
+                      {{
+                        node.property.approveUsers
+                          .map(item => item.name)
+                          .join(",")
+                      }}
+                    </p>
+                    <p v-if="node.property.copyForUsers.length">
+                      抄送:{{
+                        node.property.copyForUsers
+                          .map(item => item.name)
+                          .join(",")
+                      }}
+                    </p>
+                  </div>
+                  <div v-else>
+                    <p>
+                      {{
+                        node.property.approveRoles
+                          .map(item => item.name)
+                          .join(",")
+                      }}
+                    </p>
+                  </div>
+                </template>
+                <template
+                  v-if="IS_APPROVE_SET && node.property.approveUserCountType"
+                >
                   <p>
-                    {{
-                      node.property.approveUsers
-                        .map(item => item.name)
-                        .join(",")
+                    方式:{{
+                      APPROVE_USER_COUNT_TYPE[
+                        node.property.approveUserCountType
+                      ]
                     }}
                   </p>
-                  <p v-if="node.property.copyForUsers.length">
-                    抄送:{{
-                      node.property.copyForUsers
-                        .map(item => item.name)
-                        .join(",")
+                  <p>
+                    范围:{{
+                      APPROVE_USER_SELECT_RANGE[
+                        node.property.approveUserSelectRange
+                      ]
                     }}
                   </p>
-                </div>
-                <div v-else>
-                  <p>
-                    {{
-                      node.property.approveRoles
+                  <p v-if="node.property.approveUserSelectRange === 'ROLE'">
+                    角色:{{
+                      node.property.approveUserSelectRoles
                         .map(item => item.name)
                         .join(",")
                     }}
                   </p>
-                </div>
+                </template>
               </div>
               <div v-else class="flow-node-content">
                 <span><i :class="nodeIcons[node.type]"></i></span>
@@ -86,7 +113,8 @@
         <div class="flow-property">
           <div v-if="curNode.id" class="flow-property-main" :key="curNode.id">
             <h3 class="flow-property-title">节点属性</h3>
-            <div class="property-part">
+            <!-- 管理员指定审批人 -->
+            <div v-if="!IS_APPROVE_SET" class="property-part">
               <h4 class="property-part-title">设置审批人</h4>
               <div class="flow-radio">
                 <el-radio-group
@@ -102,6 +130,7 @@
                   >
                 </el-radio-group>
               </div>
+              <!-- 添加成员 -->
               <div
                 v-if="curNode.property.approveUserType === 'USER'"
                 class="flow-users"
@@ -120,7 +149,7 @@
                     size="small"
                     closable
                     :disable-transitions="false"
-                    @close="deleteApproveUser(user)"
+                    @close="deleteApproveUser('approveUsers', user)"
                   >
                     {{ user.name }}
                   </el-tag>
@@ -129,13 +158,17 @@
                     type="danger"
                     size="mini"
                     plain
-                    @click="clearApproveUsers"
+                    @click="clearApproveUsers('approveUsers')"
                     >清空</el-button
                   >
                 </div>
               </div>
+              <!-- 添加角色 -->
               <div v-else class="flow-users">
-                <el-button size="small" type="primary" @click="toAddApproveRole"
+                <el-button
+                  size="small"
+                  type="primary"
+                  @click="toAddApproveRole('approveRoles')"
                   >添加角色</el-button
                 >
                 <div class="user-list">
@@ -145,7 +178,7 @@
                     size="small"
                     closable
                     :disable-transitions="false"
-                    @close="deleteApproveRole(role)"
+                    @close="deleteApproveRole('approveRoles', role)"
                   >
                     {{ role.name }}
                   </el-tag>
@@ -154,12 +187,82 @@
                     type="danger"
                     size="mini"
                     plain
-                    @click="clearApproveRole"
+                    @click="clearApproveRole('approveRoles')"
                     >清空</el-button
                   >
                 </div>
               </div>
             </div>
+            <!-- 发起人指定审批人 -->
+            <div v-else class="property-part">
+              <h4 class="property-part-title">设置审批人</h4>
+              <el-form label-width="80px">
+                <el-form-item label="选择方式:">
+                  <el-select
+                    v-model="curNode.property.approveUserCountType"
+                    class="width-full"
+                    placeholder="请选择方式"
+                    :disabled="curNodeIsLast"
+                    @change="curNodeChange"
+                  >
+                    <el-option
+                      v-for="(val, key) in APPROVE_USER_COUNT_TYPE"
+                      :key="key"
+                      :value="key"
+                      :label="val"
+                    ></el-option>
+                  </el-select>
+                </el-form-item>
+                <el-form-item label="选择范围:">
+                  <el-select
+                    v-model="curNode.property.approveUserSelectRange"
+                    class="width-full"
+                    placeholder="请选择范围"
+                    :disabled="curNodeIsLast"
+                    @change="curNodeChange"
+                  >
+                    <el-option
+                      v-for="(val, key) in APPROVE_USER_SELECT_RANGE"
+                      :key="key"
+                      :value="key"
+                      :label="val"
+                    ></el-option>
+                  </el-select>
+                </el-form-item>
+                <el-form-item
+                  v-if="curNode.property.approveUserSelectRange === 'ROLE'"
+                  label="指定角色:"
+                >
+                  <el-button
+                    size="small"
+                    type="primary"
+                    @click="toAddApproveRole('approveUserSelectRoles')"
+                    >添加角色</el-button
+                  >
+                  <div class="user-list">
+                    <el-tag
+                      v-for="role in curNode.property.approveUserSelectRoles"
+                      :key="role.id"
+                      size="small"
+                      closable
+                      :disable-transitions="false"
+                      @close="deleteApproveRole('approveUserSelectRoles', role)"
+                    >
+                      {{ role.name }}
+                    </el-tag>
+                    <el-button
+                      class="user-clear"
+                      type="danger"
+                      size="mini"
+                      plain
+                      @click="clearApproveRole('approveUserSelectRoles')"
+                      >清空</el-button
+                    >
+                  </div>
+                </el-form-item>
+              </el-form>
+            </div>
+            <!-- 抄送人 -->
             <div
               v-if="curNode.property.approveUserType === 'USER'"
               class="property-part"
@@ -180,7 +283,7 @@
                     closable
                     size="small"
                     :disable-transitions="false"
-                    @close="deleteCopyForUser(user)"
+                    @close="deleteApproveUser('copyForUsers', user)"
                   >
                     {{ user.name }}
                   </el-tag>
@@ -189,12 +292,13 @@
                     type="danger"
                     size="mini"
                     plain
-                    @click="clearCopyForUsers"
+                    @click="clearApproveUsers('copyForUsers')"
                     >清空</el-button
                   >
                 </div>
               </div>
             </div>
+            <!-- 高级设置 -->
             <div class="property-part">
               <h4 class="property-part-title">高级设置</h4>
               <p class="property-desc">多人审批时采用的审批方式</p>
@@ -213,6 +317,7 @@
                 </el-radio-group>
               </div>
             </div>
+            <!-- 驳回设置 -->
             <div class="property-part">
               <h4 class="property-part-title">驳回设置</h4>
               <p class="property-desc">允许驳回节点</p>
@@ -275,24 +380,35 @@ import { flowDetail, updateFlowDetail } from "../api";
 import SelectUserDialog from "./SelectUserDialog";
 import SelectRoleDialog from "./SelectRoleDialog";
 
+const initFlowInfo = {
+  id: null,
+  name: "",
+  type: "ELECTRON_FLOW",
+  modelType: "USER_FIXED"
+};
+
 const DEFAULT_NODE = {
   id: "",
-  type: "PROCESS",
+  type: "PROCESS", // 节点类型,START:开始节点,PROCESS:过程节点,END:结束节点
   content: "",
   w: 150,
   h: 40,
   x: 0,
   y: 0,
   property: {
-    approveUserType: "USER",
-    approveUsers: [],
-    approveRoles: [],
-    copyForUsers: [],
-    multipleUserApproveType: "ORDER",
-    rejectType: "PREV",
-    rejectResubmitType: "NORMAL"
+    approveUserType: "USER", // 审批用户类型,USER:成员,ROLE:角色
+    approveUsers: [], // 审批用户列表
+    approveRoles: [], // 审批角色列表
+    copyForUsers: [], // 抄送用户列表
+    multipleUserApproveType: "ORDER", // 审批用户类别,ORDER:依次审批,ALL:会签(所有人必须审批),SOME:或签(一名审批人同意或拒绝即可)
+    rejectType: "PREV", // 驳回类型,PREV:上一节点,START:发起人节点,PREV_ALL:该节点前全部节点
+    rejectResubmitType: "NORMAL", // 驳回再提交类型,NORMAL:按正常流程提交,PREV_STEP:提交到驳回节点
+    approveUserCountType: "ONE", // 选择方式,ONE:一个,MORE:多个
+    approveUserSelectRange: "ALL", // 选择范围,ALL:全单位,ROLE:角色
+    approveUserSelectRoles: [] // 指定角色范围
   }
 };
+// 详细参数:https://doc.qmth.com.cn/pages/viewpage.action?pageId=40435764
 
 export default {
   name: "modify-flow-detail",
@@ -331,11 +447,14 @@ export default {
           property: null
         }
       ],
+      flowInfo: { ...initFlowInfo },
       nodes: [],
       curNode: {},
+      curNodeIsLast: false,
       curAddUsers: [],
-      curAddRoles: [],
       curAddUserType: "",
+      curAddRoles: [],
+      curAddRoleType: "",
       nodeIcons: {
         START: "el-icon-video-play",
         END: "el-icon-switch-button",
@@ -358,19 +477,37 @@ export default {
       REJECT_RESUBMIT_TYPE: {
         NORMAL: "按正常流程提交",
         PREV_STEP: "提交到驳回节点"
+      },
+      APPROVE_USER_COUNT_TYPE: {
+        ONE: "一次选择一个人",
+        MORE: "一次选择多个人"
+      },
+      APPROVE_USER_SELECT_RANGE: {
+        ALL: "全单位",
+        ROLE: "指定角色"
       }
     };
   },
+  computed: {
+    IS_APPROVE_SET() {
+      return this.flowInfo.modelType === "APPROVE_SET";
+    }
+  },
   methods: {
     async initData(val) {
       if (val.id) {
         const data = await flowDetail(val.id);
+        this.flowInfo = this.$objAssign(initFlowInfo, data);
+        this.flowInfo.id = data.customFlowId;
         this.nodes = data.customFlowLists;
         this.toSelectNode(this.nodes[1]);
       } else {
+        this.flowInfo = this.$objAssign(initFlowInfo, val);
         this.nodes = deepCopy(this.initNodes);
         // this.toSelectNode(this.nodes[1]);
         this.toAddNode(this.nodes[0].id);
+        // 发起人自选模式,默认新增两个节点
+        if (this.IS_APPROVE_SET) this.toAddNode(this.nodes[0].id);
       }
     },
     visibleChange() {
@@ -414,13 +551,25 @@ export default {
         return;
       }
       this.curNode = node ? deepCopy(node) : {};
+      if (!this.IS_APPROVE_SET) return;
+
+      // 发起人自选模式,最后一个节点禁止编辑审批人
+      const pos = this.nodes.findIndex(item => item.id === node.id);
+      this.curNodeIsLast = pos === this.nodes.length - 2;
+      if (this.curNodeIsLast) {
+        this.curNode.property.approveUserCountType = null;
+        this.curNode.property.approveUserSelectRange = null;
+        this.curNode.property.approveUserSelectRoles = [];
+        this.curNodeChange();
+      }
     },
     curNodeChange() {
       const pos = this.nodes.findIndex(node => node.id === this.curNode.id);
       this.nodes.splice(pos, 1, deepCopy(this.curNode));
     },
-    toAddUser(type) {
-      this.curAddUserType = type;
+    // approveUser
+    toAddUser(curAddUserType) {
+      this.curAddUserType = curAddUserType;
       this.curAddUsers = this.curNode.property[this.curAddUserType];
       this.$refs.SelectUserDialog.open();
     },
@@ -428,46 +577,37 @@ export default {
       this.curNode.property[this.curAddUserType] = users;
       this.curNodeChange();
     },
-    deleteApproveUser(user) {
+    deleteApproveUser(curAddUserType, user) {
       console.log(user);
-      const pos = this.curNode.property.approveUsers.findIndex(
+      const pos = this.curNode.property[curAddUserType].findIndex(
         item => item.id === user.id
       );
-      this.curNode.property.approveUsers.splice(pos, 1);
+      this.curNode.property[curAddUserType].splice(pos, 1);
       this.curNodeChange();
     },
-    clearApproveUsers() {
-      this.curNode.property.approveUsers = [];
+    clearApproveUsers(curAddUserType) {
+      this.curNode.property[curAddUserType] = [];
       this.curNodeChange();
     },
-    deleteCopyForUser(user) {
-      const pos = this.curNode.property.copyForUsers.findIndex(
-        item => item.id === user.id
-      );
-      this.curNode.property.copyForUsers.splice(pos, 1);
-      this.curNodeChange();
-    },
-    clearCopyForUsers() {
-      this.curNode.property.copyForUsers = [];
-      this.curNodeChange();
-    },
-    toAddApproveRole() {
-      this.curAddRoles = this.curNode.property.approveRoles;
+    // approveRole
+    toAddApproveRole(curAddRoleType) {
+      this.curAddRoleType = curAddRoleType;
+      this.curAddRoles = this.curNode.property[this.curAddRoleType];
       this.$refs.SelectRoleDialog.open();
     },
     roleModified(roles) {
-      this.curNode.property.approveRoles = roles;
+      this.curNode.property[this.curAddRoleType] = roles;
       this.curNodeChange();
     },
-    deleteApproveRole(role) {
-      const pos = this.curNode.property.approveRoles.findIndex(
+    deleteApproveRole(curAddRoleType, role) {
+      const pos = this.curNode.property[curAddRoleType].findIndex(
         item => item.id === role.id
       );
-      this.curNode.property.approveRoles.splice(pos, 1);
+      this.curNode.property[curAddRoleType].splice(pos, 1);
       this.curNodeChange();
     },
-    clearApproveRole() {
-      this.curNode.property.approveRoles = [];
+    clearApproveRole(curAddRoleType) {
+      this.curNode.property[curAddRoleType] = [];
       this.curNodeChange();
     },
     checkData() {
@@ -476,22 +616,38 @@ export default {
         return;
       }
 
-      const nodeUserValid = !this.nodes
-        .filter(node => node.type === "PROCESS")
-        .some(node => {
-          if (node.property.approveUserType === "USER") {
-            return !node.property.approveUsers.length;
-            // return !(
-            //   node.property.approveUsers.length &&
-            //   node.property.copyForUsers.length
-            // );
-          } else {
-            return !node.property.approveRoles.length;
-          }
-        });
-      if (!nodeUserValid) {
-        this.$message.error("请完成节点设置");
-        return;
+      if (this.IS_APPROVE_SET) {
+        const nodeUserValid = !this.nodes
+          .filter(node => node.type === "PROCESS")
+          .some(node => {
+            if (node.property.approveUserSelectRange === "ROLE") {
+              return !node.property.approveUserSelectRoles.length;
+            } else {
+              return false;
+            }
+          });
+        if (!nodeUserValid) {
+          this.$message.error("请完成节点设置");
+          return;
+        }
+      } else {
+        const nodeUserValid = !this.nodes
+          .filter(node => node.type === "PROCESS")
+          .some(node => {
+            if (node.property.approveUserType === "USER") {
+              return !node.property.approveUsers.length;
+              // return !(
+              //   node.property.approveUsers.length &&
+              //   node.property.copyForUsers.length
+              // );
+            } else {
+              return !node.property.approveRoles.length;
+            }
+          });
+        if (!nodeUserValid) {
+          this.$message.error("请完成节点设置");
+          return;
+        }
       }
 
       return true;
@@ -521,11 +677,17 @@ export default {
         let nnode = deepCopy(node);
         nnode.id = index + 1;
         if (node.property) {
-          if (node.property.approveUserType === "USER") {
-            nnode.approveRoles = [];
+          if (this.IS_APPROVE_SET) {
+            if (node.property.approveUserSelectRange === "ALL") {
+              node.property.approveUserSelectRoles = [];
+            }
           } else {
-            nnode.approveUsers = [];
-            nnode.copyForUsers = [];
+            if (node.property.approveUserType === "USER") {
+              nnode.approveRoles = [];
+            } else {
+              nnode.approveUsers = [];
+              nnode.copyForUsers = [];
+            }
           }
         }
 
@@ -535,9 +697,10 @@ export default {
       this.isSubmit = true;
 
       const res = await updateFlowDetail({
-        customFlowId: this.instance.id,
-        name: this.instance.name,
-        type: this.instance.type,
+        customFlowId: this.flowInfo.id,
+        name: this.flowInfo.name,
+        type: this.flowInfo.type,
+        modelType: this.flowInfo.modelType,
         publish: true,
         customFlowLists: nodes
       }).catch(() => {});

+ 28 - 6
src/modules/base/components/SelectUserDialog.vue

@@ -111,6 +111,13 @@ export default {
     canSelectAll: {
       type: Boolean,
       default: false
+    },
+    filterRoles: {
+      type: Array,
+      default() {
+        // id 数组
+        return [];
+      }
     }
   },
   data() {
@@ -118,6 +125,7 @@ export default {
       modalIsShow: false,
       filterLabel: "",
       userType: "org",
+      orgData: [],
       orgUsers: [],
       roleUsers: [],
       userTree: [],
@@ -132,12 +140,12 @@ export default {
       }
     };
   },
-  mounted() {
-    this.getOrgUser();
-  },
   methods: {
-    async getOrgUser() {
+    async getOrgData() {
       const data = await organizationList();
+      this.orgData = data;
+    },
+    parseUserData(data) {
       let roleUserMap = {};
       const parseUser = list => {
         return list.map(item => {
@@ -155,7 +163,18 @@ export default {
           }
           // user
           if (item["sysUserList"] && item["sysUserList"].length) {
-            const users = item.sysUserList.map(user => {
+            let sysUserList = item.sysUserList;
+            // 过滤符合需求角色的用户
+            if (this.filterRoles.length) {
+              sysUserList = sysUserList.filter(
+                user =>
+                  user.roleResultList &&
+                  user.roleResultList.some(role =>
+                    this.filterRoles.includes(role.roleId)
+                  )
+              );
+            }
+            const users = sysUserList.map(user => {
               const nuser = {
                 id: user.id,
                 userId: user.id,
@@ -231,7 +250,10 @@ export default {
         !this.userLimitCount ||
         this.selectedUsers.length <= this.userLimitCount;
     },
-    visibleChange() {
+    async visibleChange() {
+      if (!this.orgUsers.length) await this.getOrgData();
+      this.parseUserData(this.orgData);
+
       this.filterLabel = "";
       this.userType = "org";
       this.userTree = this.orgUsers;

+ 130 - 43
src/modules/exam/components/ApplyContent.vue

@@ -194,32 +194,67 @@
             :class="{ 'timeline-item-stop': flow.nextIsNewFlow }"
           >
             <div class="flow-item">
-              <p v-if="flow.createTime" class="flow-item-time">
-                {{ flow.createTime | timestampFilter }}
-              </p>
-              <h4 class="flow-item-title">{{ flow.approveUserName }}</h4>
-              <p class="flow-item-desc">
-                <span
-                  v-if="flow.approveOperation"
-                  :class="{
-                    'color-danger': flow.approveOperation === 'REJECT'
-                  }"
-                  >{{
-                    flow.approveOperation | flowApproveOperationTypeFilter
-                  }}</span
+              <div class="flow-item-content">
+                <p v-if="flow.createTime" class="flow-item-time">
+                  {{ flow.createTime | timestampFilter }}
+                </p>
+                <h4 class="flow-item-title">{{ flow.approveUserName }}</h4>
+                <p class="flow-item-desc">
+                  <span
+                    v-if="flow.approveOperation"
+                    :class="{
+                      'color-danger': flow.approveOperation === 'REJECT'
+                    }"
+                    >{{
+                      flow.approveOperation | flowApproveOperationTypeFilter
+                    }}</span
+                  >
+                  <span>{{ flow.approveRemark }}</span>
+                </p>
+                <div
+                  v-if="flow.attachments.length"
+                  class="flow-item-attachment"
                 >
-                <span>{{ flow.approveRemark }}</span>
-              </p>
-              <div v-if="flow.attachments.length" class="flow-item-attachment">
-                <span>附件:</span>
-                <el-button
-                  v-for="item in flow.attachments"
-                  :key="item.name"
-                  type="text"
-                  class="btn-primary"
-                  @click="downloadPaper(item)"
-                  >{{ item.name }}卷:{{ item.filename }}</el-button
+                  <span>附件:</span>
+                  <el-button
+                    v-for="item in flow.attachments"
+                    :key="item.name"
+                    type="text"
+                    class="btn-primary"
+                    @click="downloadPaper(item)"
+                    >{{ item.name }}卷:{{ item.filename }}</el-button
+                  >
+                </div>
+                <div
+                  v-if="flow.isApproveSetFlowNextNode && approveUsers.length"
+                  class="flow-item-users"
                 >
+                  <span>审批人:</span>
+                  <el-tag
+                    v-for="user in approveUsers"
+                    :key="user.id"
+                    size="small"
+                    :disable-transitions="false"
+                  >
+                    {{ user.name }}
+                  </el-tag>
+                </div>
+              </div>
+              <div
+                v-if="
+                  flow.isApproveSetFlowNextNode &&
+                    auditModal.approvePass === 'PASS'
+                "
+                class="flow-item-action"
+              >
+                <el-button
+                  class="user-select"
+                  icon="el-icon-plus"
+                  @click="toSelectNextFlowUser"
+                ></el-button>
+                <p v-if="!approveUsers.length" class="tips-info tips-error">
+                  请选择用户
+                </p>
               </div>
             </div>
           </el-timeline-item>
@@ -237,8 +272,10 @@
             :type="flow.type"
           >
             <div class="flow-item">
-              <h4 class="flow-item-title">{{ flow.taskName }}</h4>
-              <p class="flow-item-desc">{{ flow.approveUserNames }}</p>
+              <div class="flow-item-content">
+                <h4 class="flow-item-title">{{ flow.taskName }}</h4>
+                <p class="flow-item-desc">{{ flow.approveUserNames }}</p>
+              </div>
             </div>
           </el-timeline-item>
         </el-timeline>
@@ -384,9 +421,10 @@
     <select-user-dialog
       v-if="IS_AUDIT"
       ref="SelectUserDialog"
-      :user-limit-count="1"
-      :users="exchangeUsers"
-      @modified="exchangeUserSelected"
+      :user-limit-count="userLimitCount"
+      :filter-roles="userFilterRoles"
+      :users="curSelectedUsers"
+      @modified="userSelected"
     ></select-user-dialog>
     <!-- ModifyCard -->
     <modify-card ref="ModifyCard" @modified="cardModified"></modify-card>
@@ -445,9 +483,7 @@ const initTaskApply = {
   // 考务规则
   review: false,
   includePaper: false,
-  customCard: false,
-  // 是否有子流程
-  subFlowReject: false
+  customCard: false
 };
 
 export default {
@@ -535,8 +571,15 @@ export default {
         ]
       },
       flowHistoryList: [],
-      rejectSetupList: [],
+      rejectSetupList: [], // 可以驳回的节点
       exchangeUsers: [],
+      selectUserType: "exchange", // exchange:转审,approve:下一节点审核
+      // 选择下一节点审批人
+      IS_NEED_SELECT_APPROVE_USER: false,
+      nextFlowTaskResult: {}, //下一节点信息
+      approveUsers: [],
+      userLimitCount: 1,
+      userFilterRoles: [],
       // image-preview
       curImage: {},
       curImageIndex: 0
@@ -629,12 +672,12 @@ export default {
         : [];
 
       if (this.curTaskApply.flowStatus === "START") {
-        this.getFlowList();
+        await this.getFlowList();
       } else {
-        this.getFlowHistory();
+        await this.getFlowHistory();
       }
 
-      if (this.IS_AUDIT) this.getRejectSetupList();
+      if (this.IS_AUDIT) this.getCurFlowNodeInfo();
     },
     async getFlowHistory() {
       if (!this.curTaskApply.flowId) return;
@@ -660,6 +703,7 @@ export default {
         item.attachments = item.paperAttachmentId
           ? JSON.parse(item.paperAttachmentId)
           : [];
+        item.isApproveSetFlowNextNode = false;
         return item;
       });
       const flowIsEnd = data.currFlowTaskResult.taskKey === "end";
@@ -672,6 +716,7 @@ export default {
         approveUserName: data.currFlowTaskResult.approveUserNames, //审批人
         createTime: null,
         nextIsNewFlow: false,
+        isApproveSetFlowNextNode: false,
         attachments: []
       });
     },
@@ -688,12 +733,20 @@ export default {
         this.flowList[0].type = "success";
       }
     },
-    async getRejectSetupList() {
+    async getCurFlowNodeInfo() {
       const data = await taskFlowNodeInfo(this.curTaskApply.flowTaskId);
       this.rejectSetupList = (data && data.rejectSetupList) || [];
       this.rejectSetupList.forEach(item => {
         item.name = `【${item.taskName}】${item.approveUserNames}`;
       });
+      this.nextFlowTaskResult = data.nextFlowTaskResult;
+      // 判断发起人自选,是否需要选择下个审核人员
+      this.IS_NEED_SELECT_APPROVE_USER =
+        this.nextFlowTaskResult.modelType === "APPROVE_SET" &&
+        !this.nextFlowTaskResult.approveUserNames;
+      this.flowHistoryList[
+        this.flowHistoryList.length - 1
+      ].isApproveSetFlowNextNode = this.IS_NEED_SELECT_APPROVE_USER;
     },
     approvePassChange() {
       this.auditRules.remark[0].required =
@@ -860,13 +913,33 @@ export default {
       this.curTaskApply.makeMethod = "";
       this.curTaskApply.cardId = "";
     },
+    toSelectNextFlowUser() {
+      if (!this.IS_NEED_SELECT_APPROVE_USER) return;
+
+      this.userLimitCount =
+        this.nextFlowTaskResult.approveUserCountType === "ONE" ? 1 : 0;
+      this.userFilterRoles =
+        this.nextFlowTaskResult.approveUserSelectRange === "ROLE"
+          ? this.nextFlowTaskResult.approveUserSelectRoles.map(item => item.id)
+          : [];
+      this.selectUserType = "approve";
+      this.curSelectedUsers = this.approveUsers;
+      this.$refs.SelectUserDialog.open();
+    },
     toSelectExchangeUser() {
+      this.userLimitCount = 1;
+      this.userFilterRoles = [];
+      this.curSelectedUsers = this.exchangeUsers;
+      this.selectUserType = "exchange";
       this.$refs.SelectUserDialog.open();
     },
-    exchangeUserSelected(users) {
-      console.log(users);
-      this.exchangeUsers = users;
-      this.auditModal.userId = users[0].id;
+    userSelected(users) {
+      if (this.selectUserType === "exchange") {
+        this.exchangeUsers = users;
+        this.auditModal.userId = users[0].id;
+      } else {
+        this.approveUsers = users;
+      }
     },
     getTaskData() {
       let data = { ...this.curTaskApply };
@@ -947,6 +1020,15 @@ export default {
       const valid = await this.$refs.auditModalComp.validate().catch(() => {});
       if (!valid) return;
 
+      if (
+        this.auditModal.approvePass === "PASS" &&
+        this.IS_NEED_SELECT_APPROVE_USER &&
+        !this.approveUsers.length
+      ) {
+        this.$message.error("请设置审核人员!");
+        return;
+      }
+
       const actionName = this.TASK_AUDIT_RESULT[this.auditModal.approvePass];
       const result = await this.$confirm(
         `确定${actionName}该申请吗?`,
@@ -967,8 +1049,13 @@ export default {
       } else {
         let datas = { ...this.auditModal };
         datas.taskId = this.curTaskApply.flowTaskId;
-        if (this.auditModal.approvePass === "PASS")
-          datas.approveUserIds = this.approveUserIds;
+
+        if (
+          this.auditModal.approvePass === "PASS" &&
+          this.IS_NEED_SELECT_APPROVE_USER
+        )
+          datas.approveUserIds = this.approveUsers.map(item => item.id);
+
         const data = await taskFlowApprover(datas).catch(() => {});
         if (!data) return;
       }

+ 4 - 2
src/modules/exam/components/CreateTaskApply.vue

@@ -232,8 +232,10 @@
               :type="flow.type"
             >
               <div class="flow-item">
-                <h4 class="flow-item-title">{{ flow.taskName }}</h4>
-                <p class="flow-item-desc">{{ flow.approveUserNames }}</p>
+                <div class="flow-item-content">
+                  <h4 class="flow-item-title">{{ flow.taskName }}</h4>
+                  <p class="flow-item-desc">{{ flow.approveUserNames }}</p>
+                </div>
               </div>
             </el-timeline-item>
           </el-timeline>

+ 121 - 49
src/modules/exam/components/createExamAndPrintTask/CreateExamAndPrintTask.vue

@@ -1,53 +1,96 @@
 <template>
-  <el-dialog
-    class="create-exam-and-print-task"
-    :visible.sync="modalIsShow"
-    :close-on-click-modal="false"
-    :close-on-press-escape="false"
-    :show-close="false"
-    append-to-body
-    fullscreen
-    @open="visibleChange"
-    @closed="disalogClosed"
-  >
-    <div slot="title">
-      <span class="el-dialog__title">新增命题申请</span>
-    </div>
-
-    <div class="apply-body" v-if="dataReady">
-      <div class="apply-part">
-        <h3 class="apply-part-title">命题信息</h3>
-        <info-exam-task ref="InfoExamTask"></info-exam-task>
+  <div>
+    <el-dialog
+      class="create-exam-and-print-task"
+      :visible.sync="modalIsShow"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      :show-close="false"
+      append-to-body
+      fullscreen
+      @open="visibleChange"
+      @closed="disalogClosed"
+    >
+      <div slot="title">
+        <span class="el-dialog__title">新增命题申请</span>
       </div>
-      <div v-if="!IS_MODEL3" class="apply-part">
-        <h3 class="apply-part-title">考务信息</h3>
-        <info-print-task ref="InfoPrintTask"></info-print-task>
+
+      <div class="apply-body" v-if="dataReady">
+        <div class="apply-part">
+          <h3 class="apply-part-title">命题信息</h3>
+          <info-exam-task ref="InfoExamTask"></info-exam-task>
+        </div>
+        <div v-if="!IS_MODEL3" class="apply-part">
+          <h3 class="apply-part-title">考务信息</h3>
+          <info-print-task ref="InfoPrintTask"></info-print-task>
+        </div>
+        <div class="apply-part flow-timeline">
+          <h3 class="apply-part-title">流程</h3>
+          <el-timeline>
+            <el-timeline-item
+              v-for="flow in flowList"
+              :key="flow.taskKey"
+              :type="flow.type"
+            >
+              <div class="flow-item">
+                <div class="flow-item-content">
+                  <h4 class="flow-item-title">{{ flow.taskName }}</h4>
+                  <p v-if="flow.approveUserNames" class="flow-item-desc">
+                    {{ flow.approveUserNames }}
+                  </p>
+                  <div
+                    v-if="flow.isApproveSetFlowNextNode && approveUsers.length"
+                    class="flow-item-users"
+                  >
+                    <span>审批人:</span>
+                    <el-tag
+                      v-for="user in approveUsers"
+                      :key="user.id"
+                      size="small"
+                      :disable-transitions="false"
+                    >
+                      {{ user.name }}
+                    </el-tag>
+                  </div>
+                </div>
+                <div
+                  v-if="flow.isApproveSetFlowNextNode"
+                  class="flow-item-action"
+                >
+                  <el-button
+                    class="user-select"
+                    icon="el-icon-plus"
+                    @click="toSelectUser"
+                  ></el-button>
+                  <p v-if="!approveUsers.length" class="tips-info tips-error">
+                    请选择用户
+                  </p>
+                </div>
+              </div>
+            </el-timeline-item>
+          </el-timeline>
+        </div>
       </div>
-      <div class="apply-part">
-        <h3 class="apply-part-title">流程</h3>
-        <el-timeline>
-          <el-timeline-item
-            v-for="flow in flowList"
-            :key="flow.taskKey"
-            :type="flow.type"
-          >
-            <div class="flow-item">
-              <h4 class="flow-item-title">{{ flow.taskName }}</h4>
-              <p class="flow-item-desc">{{ flow.approveUserNames }}</p>
-            </div>
-          </el-timeline-item>
-        </el-timeline>
+
+      <div class="text-center">
+        <el-button type="success" :disabled="loading" @click="submit"
+          >确认提交</el-button
+        >
+        <el-button @click="cancel">取消</el-button>
       </div>
-    </div>
+      <div slot="footer"></div>
+    </el-dialog>
 
-    <div class="text-center">
-      <el-button type="success" :disabled="loading" @click="submit"
-        >确认提交</el-button
-      >
-      <el-button @click="cancel">取消</el-button>
-    </div>
-    <div slot="footer"></div>
-  </el-dialog>
+    <!-- SelectUserDialog -->
+    <select-user-dialog
+      v-if="IS_APPROVE_SET"
+      ref="SelectUserDialog"
+      :users="approveUsers"
+      :user-limit-count="userLimitCount"
+      :filter-roles="userFilterRoles"
+      @modified="userModified"
+    ></select-user-dialog>
+  </div>
 </template>
 
 <script>
@@ -56,6 +99,7 @@ import { examRuleDetail, flowDetailByType } from "../../../base/api";
 import { teacherSubmitTaskApply } from "../../api";
 import InfoExamTask from "./InfoExamTask";
 import InfoPrintTask from "./InfoPrintTask";
+import SelectUserDialog from "../../../base/components/SelectUserDialog";
 
 const initExamTask = {
   semesterId: "",
@@ -133,15 +177,17 @@ const initPrintTask = {
 
 export default {
   name: "create-exam-and-print-task",
-  components: { InfoExamTask, InfoPrintTask },
+  components: { InfoExamTask, InfoPrintTask, SelectUserDialog },
   data() {
     return {
       modalIsShow: false,
       needReview: false,
       examRule: {},
-      // infos: {},
       flowList: [],
       flowInfo: {},
+      approveUsers: [],
+      userLimitCount: 1,
+      userFilterRoles: [],
       loading: false,
       dataReady: false
     };
@@ -155,6 +201,9 @@ export default {
     ]),
     IS_MODEL3() {
       return this.infoExamTask.examModel === "MODEL3";
+    },
+    IS_APPROVE_SET() {
+      return this.flowInfo.modelType === "APPROVE_SET";
     }
   },
   mounted() {
@@ -170,14 +219,29 @@ export default {
     async getFlowList() {
       const data = await flowDetailByType();
       if (!data) return;
+
+      const modelType =
+        data.flowTaskResultList[0] && data.flowTaskResultList[0].modelType;
       this.flowInfo = {
         customFlowId: data.id,
-        version: data.version
+        version: data.version,
+        modelType
       };
-      this.flowList = data.flowTaskResultList || [];
+      const flowList = data.flowTaskResultList || [];
+      this.flowList = flowList.map((flow, index) => {
+        flow.isApproveSetFlowNextNode =
+          modelType === "APPROVE_SET" && index === 1;
+        return flow;
+      });
       if (this.flowList.length) {
         this.flowList[0].type = "success";
       }
+      const nextFlowNode = this.flowList[1];
+      this.userLimitCount = nextFlowNode.approveUserCountType === "ONE" ? 1 : 0;
+      this.userFilterRoles =
+        nextFlowNode.approveUserSelectRange === "ROLE"
+          ? nextFlowNode.approveUserSelectRoles.map(item => item.id)
+          : [];
     },
     initData() {
       const infoExamTaskDetail = Object.assign({}, initExamTaskDetail, {
@@ -219,6 +283,12 @@ export default {
     open() {
       this.modalIsShow = true;
     },
+    toSelectUser() {
+      this.$refs.SelectUserDialog.open();
+    },
+    userModified(users) {
+      this.approveUsers = users;
+    },
     async submit() {
       if (this.loading) return;
       const result = await this.$confirm("确定提交该任务?", "提示", {
@@ -247,6 +317,8 @@ export default {
         examTaskContent: JSON.stringify(examTaskContent),
         ...this.flowInfo
       };
+      if (this.IS_APPROVE_SET)
+        datas.approveUserIds = this.approveUsers.map(item => item.id);
 
       const data = await teacherSubmitTaskApply(datas).catch(() => {});
       this.loading = false;

+ 15 - 0
src/modules/exam/components/lins.txt

@@ -0,0 +1,15 @@
+approveIds: null
+approveRoleIds: null
+approveUserCountType: "MORE"
+approveUserNames: null
+approveUserSelectRange: "ROLE"
+approveUserSelectRoles: [{id: "190470915627155456", name: "测试课组长1"}]
+0: {id: "190470915627155456", name: "测试课组长1"}
+id: "190470915627155456"
+name: "测试课组长1"
+approveUserType: "USER"
+flowId: "236874146733096960"
+modelType: "APPROVE_SET"
+setup: 3
+taskKey: "usertask2"
+taskName: "审核人"