潘志宝
3 天以前 221918bba28d2384d03c596a68256d7832e4a0e0
提交 | 用户 | 时间
3e359e 1 <template>
H 2   <div class="flex items-center h-50px">
3     <!-- 头部:分类名 -->
4     <div class="flex items-center">
5       <el-tooltip content="拖动排序" v-if="isCategorySorting">
6         <Icon
7           :size="22"
8           icon="ic:round-drag-indicator"
9           class="ml-10px category-drag-icon cursor-move text-#8a909c"
10         />
11       </el-tooltip>
12       <h3 class="ml-20px mr-8px text-18px">{{ categoryInfo.name }}</h3>
13       <div class="color-gray-600 text-16px"> ({{ categoryInfo.modelList?.length || 0 }}) </div>
14     </div>
15     <!-- 头部:操作 -->
16     <div class="flex-1 flex" v-if="!isCategorySorting">
17       <div
18         v-if="categoryInfo.modelList.length > 0"
19         class="ml-20px flex items-center"
20         :class="[
21           'transition-transform duration-300 cursor-pointer',
22           isExpand ? 'rotate-180' : 'rotate-0'
23         ]"
24         @click="isExpand = !isExpand"
25       >
26         <Icon icon="ep:arrow-down-bold" color="#999" />
27       </div>
28       <div class="ml-auto flex items-center" :class="isModelSorting ? 'mr-15px' : 'mr-45px'">
29         <template v-if="!isModelSorting">
30           <el-button
31             v-if="categoryInfo.modelList.length > 0"
32             link
33             type="info"
34             class="mr-20px"
35             @click.stop="handleModelSort"
36           >
37             <Icon icon="fa:sort-amount-desc" class="mr-5px" />
38             排序
39           </el-button>
40           <el-button v-else link type="info" class="mr-20px" @click.stop="openModelForm('create')">
41             <Icon icon="fa:plus" class="mr-5px" />
42             新建
43           </el-button>
44           <el-dropdown
45             @command="(command) => handleCategoryCommand(command, categoryInfo)"
46             placement="bottom"
47           >
48             <el-button link type="info">
49               <Icon icon="ep:setting" class="mr-5px" />
50               分类
51             </el-button>
52             <template #dropdown>
53               <el-dropdown-menu>
54                 <el-dropdown-item command="handleRename"> 重命名 </el-dropdown-item>
55                 <el-dropdown-item command="handleDeleteCategory"> 删除该类 </el-dropdown-item>
56               </el-dropdown-menu>
57             </template>
58           </el-dropdown>
59         </template>
60         <template v-else>
61           <el-button @click.stop="handleModelSortCancel"> 取 消 </el-button>
62           <el-button type="primary" @click.stop="handleModelSortSubmit"> 保存排序 </el-button>
63         </template>
64       </div>
65     </div>
66   </div>
c9a6f7 67
3e359e 68   <!-- 模型列表 -->
H 69   <el-collapse-transition>
70     <div v-show="isExpand">
71       <el-table
72         :class="categoryInfo.name"
73         ref="tableRef"
74         :header-cell-style="{ backgroundColor: isDark ? '' : '#edeff0', paddingLeft: '10px' }"
75         :cell-style="{ paddingLeft: '10px' }"
76         :row-style="{ height: '68px' }"
77         :data="modelList"
78         row-key="id"
79       >
80         <el-table-column label="流程名" prop="name" min-width="150">
81           <template #default="scope">
82             <div class="flex items-center">
83               <el-tooltip content="拖动排序" v-if="isModelSorting">
84                 <Icon
85                   icon="ic:round-drag-indicator"
86                   class="drag-icon cursor-move text-#8a909c mr-10px"
87                 />
88               </el-tooltip>
89               <el-image :src="scope.row.icon" class="h-38px w-38px mr-10px rounded" />
90               {{ scope.row.name }}
91             </div>
92           </template>
93         </el-table-column>
c9a6f7 94         <el-table-column label="可见范围" prop="startUserIds" min-width="150">
3e359e 95           <template #default="scope">
H 96             <el-text v-if="!scope.row.startUsers || scope.row.startUsers.length === 0">
97               全部可见
98             </el-text>
99             <el-text v-else-if="scope.row.startUsers.length == 1">
100               {{ scope.row.startUsers[0].nickname }}
101             </el-text>
102             <el-text v-else>
103               <el-tooltip
104                 class="box-item"
105                 effect="dark"
106                 placement="top"
107                 :content="scope.row.startUsers.map((user: any) => user.nickname).join('、')"
108               >
109                 {{ scope.row.startUsers[0].nickname }}等 {{ scope.row.startUsers.length }} 人可见
110               </el-tooltip>
111             </el-text>
112           </template>
113         </el-table-column>
c9a6f7 114         <el-table-column label="表单信息" prop="formType" min-width="150">
3e359e 115           <template #default="scope">
H 116             <el-button
117               v-if="scope.row.formType === BpmModelFormType.NORMAL"
118               type="primary"
119               link
120               @click="handleFormDetail(scope.row)"
121             >
122               <span>{{ scope.row.formName }}</span>
123             </el-button>
124             <el-button
125               v-else-if="scope.row.formType === BpmModelFormType.CUSTOM"
126               type="primary"
127               link
128               @click="handleFormDetail(scope.row)"
129             >
130               <span>{{ scope.row.formCustomCreatePath }}</span>
131             </el-button>
132             <label v-else>暂无表单</label>
133           </template>
134         </el-table-column>
135         <el-table-column label="最后发布" prop="deploymentTime" min-width="250">
136           <template #default="scope">
137             <div class="flex items-center">
138               <span v-if="scope.row.processDefinition" class="w-150px">
139                 {{ formatDate(scope.row.processDefinition.deploymentTime) }}
140               </span>
141               <el-tag v-if="scope.row.processDefinition">
142                 v{{ scope.row.processDefinition.version }}
143               </el-tag>
144               <el-tag v-else type="warning">未部署</el-tag>
145               <el-tag
146                 v-if="scope.row.processDefinition?.suspensionState === 2"
147                 type="warning"
148                 class="ml-10px"
149               >
150                 已停用
151               </el-tag>
152             </div>
153           </template>
154         </el-table-column>
155         <el-table-column label="操作" width="200" fixed="right">
156           <template #default="scope">
157             <el-button
158               link
159               type="primary"
160               @click="openModelForm('update', scope.row.id)"
161               v-hasPermi="['bpm:model:update']"
162               :disabled="!isManagerUser(scope.row)"
163             >
164               修改
165             </el-button>
166             <el-button
167               link
168               class="!ml-5px"
169               type="primary"
170               @click="handleDeploy(scope.row)"
171               v-hasPermi="['bpm:model:deploy']"
172               :disabled="!isManagerUser(scope.row)"
173             >
174               发布
175             </el-button>
176             <el-dropdown
177               class="!align-middle ml-5px"
178               @command="(command) => handleModelCommand(command, scope.row)"
179               v-hasPermi="['bpm:process-definition:query', 'bpm:model:update', 'bpm:model:delete']"
180             >
181               <el-button type="primary" link>更多</el-button>
182               <template #dropdown>
183                 <el-dropdown-menu>
184                   <el-dropdown-item
185                     command="handleDefinitionList"
186                     v-if="checkPermi(['bpm:process-definition:query'])"
187                   >
188                     历史
189                   </el-dropdown-item>
190                   <el-dropdown-item
191                     command="handleChangeState"
192                     v-if="checkPermi(['bpm:model:update']) && scope.row.processDefinition"
193                     :disabled="!isManagerUser(scope.row)"
194                   >
195                     {{ scope.row.processDefinition.suspensionState === 1 ? '停用' : '启用' }}
196                   </el-dropdown-item>
197                   <el-dropdown-item
198                     type="danger"
199                     command="handleDelete"
200                     v-if="checkPermi(['bpm:model:delete'])"
201                     :disabled="!isManagerUser(scope.row)"
202                   >
203                     删除
204                   </el-dropdown-item>
205                 </el-dropdown-menu>
206               </template>
207             </el-dropdown>
208           </template>
209         </el-table-column>
210       </el-table>
211     </div>
212   </el-collapse-transition>
213
214   <!-- 弹窗:重命名分类 -->
215   <Dialog :fullscreen="false" class="rename-dialog" v-model="renameCategoryVisible" width="400">
216     <template #title>
217       <div class="pl-10px font-bold text-18px"> 重命名分类 </div>
218     </template>
219     <div class="px-30px">
220       <el-input v-model="renameCategoryForm.name" />
221     </div>
222     <template #footer>
223       <div class="pr-25px pb-25px">
224         <el-button @click="renameCategoryVisible = false">取 消</el-button>
225         <el-button type="primary" @click="handleRenameConfirm">确 定</el-button>
226       </div>
227     </template>
228   </Dialog>
229
230   <!-- 表单弹窗:添加流程模型 -->
231   <ModelForm :categoryId="categoryInfo.code" ref="modelFormRef" @success="emit('success')" />
232 </template>
233
234 <script lang="ts" setup>
235 import ModelForm from './ModelForm.vue'
236 import { CategoryApi, CategoryVO } from '@/api/bpm/category'
237 import Sortable from 'sortablejs'
238 import { propTypes } from '@/utils/propTypes'
239 import { formatDate } from '@/utils/formatTime'
240 import * as ModelApi from '@/api/bpm/model'
241 import * as FormApi from '@/api/bpm/form'
242 import { setConfAndFields2 } from '@/utils/formCreate'
c9a6f7 243 import { BpmModelFormType } from '@/utils/constants'
3e359e 244 import { checkPermi } from '@/utils/permission'
H 245 import { useUserStoreWithOut } from '@/store/modules/user'
246 import { useAppStore } from '@/store/modules/app'
247 import { cloneDeep } from 'lodash-es'
248
249 defineOptions({ name: 'BpmModel' })
250
251 const props = defineProps({
252   categoryInfo: propTypes.object.def([]), // 分类后的数据
253   isCategorySorting: propTypes.bool.def(false) // 是否分类在排序
254 })
255 const emit = defineEmits(['success'])
256 const message = useMessage() // 消息弹窗
257 const { t } = useI18n() // 国际化
258 const { push } = useRouter() // 路由
259 const userStore = useUserStoreWithOut() // 用户信息缓存
260 const isDark = computed(() => useAppStore().getIsDark) // 是否黑暗模式
261
262 const isModelSorting = ref(false) // 是否正处于排序状态
263 const originalData: any = ref([]) // 原始数据
264 const modelList: any = ref([]) // 模型列表
265 const isExpand = ref(false) // 是否处于展开状态
266
267 /** '更多'操作按钮 */
268 const handleModelCommand = (command: string, row: any) => {
269   switch (command) {
270     case 'handleDefinitionList':
271       handleDefinitionList(row)
272       break
273     case 'handleDelete':
274       handleDelete(row)
275       break
276     case 'handleChangeState':
277       handleChangeState(row)
278       break
279     default:
280       break
281   }
282 }
283
284 /** '分类'操作按钮 */
285 const handleCategoryCommand = async (command: string, row: any) => {
286   switch (command) {
287     case 'handleRename':
288       renameCategoryForm.value = await CategoryApi.getCategory(row.id)
289       renameCategoryVisible.value = true
290       break
291     case 'handleDeleteCategory':
292       await handleDeleteCategory()
293       break
294     default:
295       break
296   }
297 }
298
299 /** 删除按钮操作 */
300 const handleDelete = async (row: any) => {
301   try {
302     // 删除的二次确认
303     await message.delConfirm()
304     // 发起删除
305     await ModelApi.deleteModel(row.id)
306     message.success(t('common.delSuccess'))
307     // 刷新列表
308     emit('success')
309   } catch {}
310 }
311
312 /** 更新状态操作 */
313 const handleChangeState = async (row: any) => {
314   const state = row.processDefinition.suspensionState
315   const newState = state === 1 ? 2 : 1
316   try {
317     // 修改状态的二次确认
318     const id = row.id
319     debugger
320     const statusState = state === 1 ? '停用' : '启用'
321     const content = '是否确认' + statusState + '流程名字为"' + row.name + '"的数据项?'
322     await message.confirm(content)
323     // 发起修改状态
324     await ModelApi.updateModelState(id, newState)
325     message.success(statusState + '成功')
326     // 刷新列表
327     emit('success')
328   } catch {}
329 }
330
331 /** 发布流程 */
332 const handleDeploy = async (row: any) => {
333   try {
334     // 删除的二次确认
335     await message.confirm('是否部署该流程!!')
336     // 发起部署
337     await ModelApi.deployModel(row.id)
338     message.success(t('部署成功'))
339     // 刷新列表
340     emit('success')
341   } catch {}
342 }
343
344 /** 跳转到指定流程定义列表 */
345 const handleDefinitionList = (row: any) => {
346   push({
347     name: 'BpmProcessDefinition',
348     query: {
349       key: row.key
350     }
351   })
352 }
353
354 /** 流程表单的详情按钮操作 */
355 const formDetailVisible = ref(false)
356 const formDetailPreview = ref({
357   rule: [],
358   option: {}
359 })
360 const handleFormDetail = async (row: any) => {
361   if (row.formType == 10) {
362     // 设置表单
363     const data = await FormApi.getForm(row.formId)
364     setConfAndFields2(formDetailPreview, data.conf, data.fields)
365     // 弹窗打开
366     formDetailVisible.value = true
367   } else {
368     await push({
369       path: row.formCustomCreatePath
370     })
371   }
372 }
373
374 /** 判断是否可以操作 */
375 const isManagerUser = (row: any) => {
376   const userId = userStore.getUser.id
377   return row.managerUserIds && row.managerUserIds.includes(userId)
378 }
379
380 /** 处理模型的排序 **/
381 const handleModelSort = () => {
382   // 保存初始数据
383   originalData.value = cloneDeep(props.categoryInfo.modelList)
384   isModelSorting.value = true
385   initSort()
386 }
387
388 /** 处理模型的排序提交 */
389 const handleModelSortSubmit = async () => {
390   // 保存排序
391   const ids = modelList.value.map((item: any) => item.id)
392   await ModelApi.updateModelSortBatch(ids)
393   // 刷新列表
394   isModelSorting.value = false
395   message.success('排序模型成功')
396   emit('success')
397 }
398
399 /** 处理模型的排序取消 */
400 const handleModelSortCancel = () => {
401   // 恢复初始数据
402   modelList.value = cloneDeep(originalData.value)
403   isModelSorting.value = false
404 }
405
406 /** 创建拖拽实例 */
407 const tableRef = ref()
408 const initSort = () => {
409   const table = document.querySelector(`.${props.categoryInfo.name} .el-table__body-wrapper tbody`)
410   Sortable.create(table, {
411     group: 'shared',
412     animation: 150,
413     draggable: '.el-table__row',
414     handle: '.drag-icon',
415     // 结束拖动事件
416     onEnd: ({ newDraggableIndex, oldDraggableIndex }) => {
417       if (oldDraggableIndex !== newDraggableIndex) {
418         modelList.value.splice(
419           newDraggableIndex,
420           0,
421           modelList.value.splice(oldDraggableIndex, 1)[0]
422         )
423       }
424     }
425   })
426 }
427
428 /** 更新 modelList 模型列表 */
429 const updateModeList = () => {
430   modelList.value = cloneDeep(props.categoryInfo.modelList)
431   if (props.categoryInfo.modelList.length > 0) {
432     isExpand.value = true
433   }
434 }
435
436 /** 重命名弹窗确定 */
437 const renameCategoryVisible = ref(false)
438 const renameCategoryForm = ref({
439   name: ''
440 })
441 const handleRenameConfirm = async () => {
442   if (renameCategoryForm.value?.name.length === 0) {
443     return message.warning('请输入名称')
444   }
445   // 发起修改
446   await CategoryApi.updateCategory(renameCategoryForm.value as CategoryVO)
447   message.success('重命名成功')
448   // 刷新列表
449   renameCategoryVisible.value = false
450   emit('success')
451 }
452
453 /** 删除分类 */
454 const handleDeleteCategory = async () => {
455   try {
456     if (props.categoryInfo.modelList.length > 0) {
457       return message.warning('该分类下仍有流程定义,不允许删除')
458     }
459     await message.confirm('确认删除分类吗?')
460     // 发起删除
461     await CategoryApi.deleteCategory(props.categoryInfo.id)
462     message.success(t('common.delSuccess'))
463     // 刷新列表
464     emit('success')
465   } catch {}
466 }
467
468 /** 添加流程模型弹窗 */
469 const modelFormRef = ref()
470 const openModelForm = (type: string, id?: number) => {
c9a6f7 471   if (type === 'create') {
H 472     push({ name: 'BpmModelCreate' })
473   } else {
474     push({
475       name: 'BpmModelUpdate',
476       params: { id }
477     })
478   }
3e359e 479 }
H 480
481 watch(() => props.categoryInfo.modelList, updateModeList, { immediate: true })
482 watch(
483   () => props.isCategorySorting,
484   (val) => {
485     if (val) isExpand.value = false
486   },
487   { immediate: true }
488 )
489 </script>
490
491 <style lang="scss">
492 .rename-dialog.el-dialog {
493   padding: 0 !important;
494
495   .el-dialog__header {
496     border-bottom: none;
497   }
498
499   .el-dialog__footer {
500     border-top: none !important;
501   }
502 }
503 </style>
504 <style lang="scss" scoped>
505 :deep() {
506   .el-table__cell {
507     overflow: hidden;
508     border-bottom: none !important;
509   }
510 }
511 </style>