dengzedong
5 天以前 f9b459a3fefd5fab0ee8e19268adb9d9eadab2a7
提交 | 用户 | 时间
3e359e 1 <template>
H 2
3   <!-- 第一步,通过流程定义的列表,选择对应的流程 -->
4   <ContentWrap v-if="!selectProcessDefinition" v-loading="loading">
5     <el-tabs tab-position="left" v-model="categoryActive">
6       <el-tab-pane
7         :label="category.name"
8         :name="category.code"
9         :key="category.code"
10         v-for="category in categoryList"
11       >
12         <el-row :gutter="20">
13           <el-col
14             :lg="6"
15             :sm="12"
16             :xs="24"
17             v-for="definition in categoryProcessDefinitionList"
18             :key="definition.id"
19           >
20             <el-card
21               shadow="hover"
22               class="mb-20px cursor-pointer"
23               @click="handleSelect(definition)"
24             >
25               <template #default>
26                 <div class="flex">
27                   <el-image :src="definition.icon" class="w-32px h-32px" />
28                   <el-text class="!ml-10px" size="large">{{ definition.name }}</el-text>
29                 </div>
30               </template>
31             </el-card>
32           </el-col>
33         </el-row>
34       </el-tab-pane>
35     </el-tabs>
36   </ContentWrap>
37
38   <!-- 第二步,填写表单,进行流程的提交 -->
39   <ContentWrap v-else>
40     <el-card class="box-card">
41       <div class="clearfix">
42         <span class="el-icon-document">申请信息【{{ selectProcessDefinition.name }}】</span>
43         <el-button style="float: right" type="primary" @click="selectProcessDefinition = undefined">
44           <Icon icon="ep:delete" /> 选择其它流程
45         </el-button>
46       </div>
47       <el-col :span="16" :offset="6" style="margin-top: 20px">
48         <form-create
49           :rule="detailForm.rule"
50           v-model:api="fApi"
51           v-model="detailForm.value"
52           :option="detailForm.option"
53           @submit="submitForm"
54         >
55           <template #type-startUserSelect>
56             <el-col :span="24">
57               <el-card class="mb-10px">
58                 <template #header>指定审批人</template>
59                 <el-form
60                   :model="startUserSelectAssignees"
61                   :rules="startUserSelectAssigneesFormRules"
62                   ref="startUserSelectAssigneesFormRef"
63                 >
64                   <el-form-item
65                     v-for="userTask in startUserSelectTasks"
66                     :key="userTask.id"
67                     :label="`任务【${userTask.name}】`"
68                     :prop="userTask.id"
69                   >
70                     <el-select
71                       v-model="startUserSelectAssignees[userTask.id]"
72                       multiple
73                       placeholder="请选择审批人"
74                     >
75                       <el-option
76                         v-for="user in userList"
77                         :key="user.id"
78                         :label="user.nickname"
79                         :value="user.id"
80                       />
81                     </el-select>
82                   </el-form-item>
83                 </el-form>
84               </el-card>
85             </el-col>
86           </template>
87         </form-create>
88       </el-col>
89     </el-card>
90     <!-- 流程图预览 -->
91     <ProcessInstanceBpmnViewer :bpmn-xml="bpmnXML as any" />
92   </ContentWrap>
93 </template>
94 <script lang="ts" setup>
95 import * as DefinitionApi from '@/api/bpm/definition'
96 import * as ProcessInstanceApi from '@/api/bpm/processInstance'
97 import { decodeFields, setConfAndFields2 } from '@/utils/formCreate'
98 import type { ApiAttrs } from '@form-create/element-ui/types/config'
99 import ProcessInstanceBpmnViewer from '../detail/ProcessInstanceBpmnViewer.vue'
100 import { CategoryApi } from '@/api/bpm/category'
101 import { useTagsViewStore } from '@/store/modules/tagsView'
102 import * as UserApi from '@/api/system/user'
103
104 defineOptions({ name: 'BpmProcessInstanceCreate' })
105
106 const route = useRoute() // 路由
107 const { push, currentRoute } = useRouter() // 路由
108 const message = useMessage() // 消息
109 const { delView } = useTagsViewStore() // 视图操作
110
111 const processInstanceId = route.query.processInstanceId
112 const loading = ref(true) // 加载中
113 const categoryList = ref([]) // 分类的列表
114 const categoryActive = ref('') // 选中的分类
115 const processDefinitionList = ref([]) // 流程定义的列表
116
117 /** 查询列表 */
118 const getList = async () => {
119   loading.value = true
120   try {
121     // 流程分类
122     categoryList.value = await CategoryApi.getCategorySimpleList()
123     if (categoryList.value.length > 0) {
124       categoryActive.value = categoryList.value[0].code
125     }
126     // 流程定义
127     processDefinitionList.value = await DefinitionApi.getProcessDefinitionList({
128       suspensionState: 1
129     })
130
131     // 如果 processInstanceId 非空,说明是重新发起
132     if (processInstanceId?.length > 0) {
133       const processInstance = await ProcessInstanceApi.getProcessInstance(processInstanceId)
134       if (!processInstance) {
135         message.error('重新发起流程失败,原因:流程实例不存在')
136         return
137       }
138       const processDefinition = processDefinitionList.value.find(
139         (item) => item.key == processInstance.processDefinition?.key
140       )
141       if (!processDefinition) {
142         message.error('重新发起流程失败,原因:流程定义不存在')
143         return
144       }
145       await handleSelect(processDefinition, processInstance.formVariables)
146     }
147   } finally {
148     loading.value = false
149   }
150 }
151
152 /** 选中分类对应的流程定义列表 */
153 const categoryProcessDefinitionList = computed(() => {
154   return processDefinitionList.value.filter((item) => item.category == categoryActive.value)
155 })
156
157 // ========== 表单相关 ==========
158 const fApi = ref<ApiAttrs>()
159 const detailForm = ref({
160   rule: [],
161   option: {},
162   value: {}
163 }) // 流程表单详情
164 const selectProcessDefinition = ref() // 选择的流程定义
165
166 // 指定审批人
167 const bpmnXML = ref(null) // BPMN 数据
168 const startUserSelectTasks = ref([]) // 发起人需要选择审批人的用户任务列表
169 const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据
170 const startUserSelectAssigneesFormRef = ref() // 发起人选择审批人的表单 Ref
171 const startUserSelectAssigneesFormRules = ref({}) // 发起人选择审批人的表单 Rules
172 const userList = ref<any[]>([]) // 用户列表
173
174 /** 处理选择流程的按钮操作 **/
175 const handleSelect = async (row, formVariables) => {
176   // 设置选择的流程
177   selectProcessDefinition.value = row
178
179   // 重置指定审批人
180   startUserSelectTasks.value = []
181   startUserSelectAssignees.value = {}
182   startUserSelectAssigneesFormRules.value = {}
183
184   // 情况一:流程表单
185   if (row.formType == 10) {
186     // 设置表单
187     // 注意:需要从 formVariables 中,移除不在 row.formFields 的值。
188     // 原因是:后端返回的 formVariables 里面,会有一些非表单的信息。例如说,某个流程节点的审批人。
189     //        这样,就可能导致一个流程被审批不通过后,重新发起时,会直接后端报错!!!
190     const allowedFields = decodeFields(row.formFields).map((fieldObj: any) => fieldObj.field)
191     for (const key in formVariables) {
192       if (!allowedFields.includes(key)) {
193         delete formVariables[key]
194       }
195     }
196     setConfAndFields2(detailForm, row.formConf, row.formFields, formVariables)
197
198     // 加载流程图
199     const processDefinitionDetail = await DefinitionApi.getProcessDefinition(row.id)
200     if (processDefinitionDetail) {
201       bpmnXML.value = processDefinitionDetail.bpmnXml
202       startUserSelectTasks.value = processDefinitionDetail.startUserSelectTasks
203
204       // 设置指定审批人
205       if (startUserSelectTasks.value?.length > 0) {
206         detailForm.value.rule.push({
207           type: 'startUserSelect',
208           props: {
209             title: '指定审批人'
210           }
211         })
212         // 设置校验规则
213         for (const userTask of startUserSelectTasks.value) {
214           startUserSelectAssignees.value[userTask.id] = []
215           startUserSelectAssigneesFormRules.value[userTask.id] = [
216             { required: true, message: '请选择审批人', trigger: 'blur' }
217           ]
218         }
219         // 加载用户列表
220         userList.value = await UserApi.getSimpleUserList()
221       }
222     }
223     // 情况二:业务表单
224   } else if (row.formCustomCreatePath) {
225     await push({
226       path: row.formCustomCreatePath
227     })
228     // 这里暂时无需加载流程图,因为跳出到另外个 Tab;
229   }
230 }
231
232 /** 提交按钮 */
233 const submitForm = async (formData) => {
234   if (!fApi.value || !selectProcessDefinition.value) {
235     return
236   }
237   // 如果有指定审批人,需要校验
238   if (startUserSelectTasks.value?.length > 0) {
239     await startUserSelectAssigneesFormRef.value.validate()
240   }
241
242   // 提交请求
243   fApi.value.btn.loading(true)
244   try {
245     await ProcessInstanceApi.createProcessInstance({
246       processDefinitionId: selectProcessDefinition.value.id,
247       variables: formData,
248       startUserSelectAssignees: startUserSelectAssignees.value
249     })
250     // 提示
251     message.success('发起流程成功')
252     // 跳转回去
253     delView(unref(currentRoute))
254     await push({
255       name: 'BpmProcessInstanceMy'
256     })
257   } finally {
258     fApi.value.btn.loading(false)
259   }
260 }
261
262 /** 初始化 */
263 onMounted(() => {
264   getList()
265 })
266 </script>