index_new.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. <template>
  2. <ContentWrap>
  3. <div class="flex justify-between pl-20px items-center">
  4. <h3 class="font-extrabold">表单管理</h3>
  5. <!-- 搜索工作栏 -->
  6. <el-form
  7. class="-mb-15px flex"
  8. :model="queryParams"
  9. ref="queryFormRef"
  10. :inline="true"
  11. label-width="68px"
  12. >
  13. <el-form-item align="right" prop="key" class="ml-auto">
  14. <el-input
  15. v-model="queryParams.key"
  16. placeholder="搜索流程"
  17. clearable
  18. @keyup.enter="handleQuery"
  19. class="!w-240px"
  20. >
  21. <template #prefix>
  22. <Icon icon="ep:search" class="mx-10px" />
  23. </template>
  24. </el-input>
  25. </el-form-item>
  26. <el-form-item>
  27. <el-button type="primary" @click="openForm('create')" v-hasPermi="['bpm:model:create']">
  28. <Icon icon="ep:plus" class="mr-5px" /> 新建流程
  29. </el-button>
  30. </el-form-item>
  31. <el-form-item>
  32. <el-dropdown placement="bottom-end">
  33. <el-button class="w-30px" plain>
  34. <Icon icon="ep:setting" />
  35. </el-button>
  36. <template #dropdown>
  37. <el-dropdown-menu>
  38. <el-dropdown-item>
  39. <Icon icon="ep:circle-plus" size="13" class="mr-5px" />
  40. 新建分组
  41. </el-dropdown-item>
  42. <el-dropdown-item>
  43. <Icon icon="fa:sort-amount-desc" size="13" class="mr-5px" />
  44. 分组排序
  45. </el-dropdown-item>
  46. </el-dropdown-menu>
  47. </template>
  48. </el-dropdown>
  49. </el-form-item>
  50. </el-form>
  51. </div>
  52. <el-divider />
  53. <!-- 分类卡片组 -->
  54. <div class="px-15px">
  55. <ContentWrap :body-style="{ padding: 0 }" v-for="(list, title) in categoryGroup" :key="title">
  56. <!-- 默认使其全部展开 -->
  57. <el-collapse :modelValue="title">
  58. <el-collapse-item :name="title">
  59. <template #icon="{ isActive }">
  60. <div
  61. class="ml-20px flex items-center"
  62. :class="['transition-transform duration-300', isActive ? 'rotate-180' : 'rotate-0']"
  63. >
  64. <Icon icon="ep:arrow-down-bold" color="#999" />
  65. </div>
  66. <div class="ml-auto mr-30px">
  67. <el-button link type="info" class="mr-10px" @click.stop="handleSort">
  68. <Icon icon="fa:sort-amount-desc" class="mr-5px" />
  69. 排序
  70. </el-button>
  71. <el-button link type="info" @click.stop="handleGroup">
  72. <Icon icon="ep:setting" class="mr-5px" />
  73. 分组
  74. </el-button>
  75. </div>
  76. </template>
  77. <template #title>
  78. <div class="flex items-center">
  79. <h3 class="ml-20px mr-8px text-18px">{{ title }}</h3>
  80. <div class="color-gray-600 text-16px"> ({{ list?.length || 0 }}) </div>
  81. </div>
  82. </template>
  83. <el-table
  84. :header-cell-style="{ backgroundColor: isDark ? '' : '#edeff0' }"
  85. v-loading="loading"
  86. :data="list"
  87. >
  88. <el-table-column label="流程名" prop="name" min-width="150">
  89. <template #default="scope">
  90. <div class="flex items-center">
  91. <el-image :src="scope.row.icon" class="h-32px w-32px mr-10px rounded" />
  92. {{ scope.row.name }}
  93. </div>
  94. </template>
  95. </el-table-column>
  96. <el-table-column label="可见范围" prop="startUserIds" min-width="100">
  97. <template #default="scope">
  98. <el-text v-if="!scope.row.startUsers || scope.row.startUsers.length === 0">
  99. 全部可见
  100. </el-text>
  101. <el-text v-else-if="scope.row.startUsers.length == 1">
  102. {{ scope.row.startUsers[0].nickname }}
  103. </el-text>
  104. <el-text v-else>
  105. <el-tooltip
  106. class="box-item"
  107. effect="dark"
  108. placement="top"
  109. :content="scope.row.startUsers.map((user: any) => user.nickname).join('、')"
  110. >
  111. {{ scope.row.startUsers[0].nickname }}等
  112. {{ scope.row.startUsers.length }} 人可见
  113. </el-tooltip>
  114. </el-text>
  115. </template>
  116. </el-table-column>
  117. <el-table-column label="表单信息" prop="formType" min-width="200">
  118. <template #default="scope">
  119. <el-button
  120. v-if="scope.row.formType === 10"
  121. type="primary"
  122. link
  123. @click="handleFormDetail(scope.row)"
  124. >
  125. <span>{{ scope.row.formName }}</span>
  126. </el-button>
  127. <el-button
  128. v-else-if="scope.row.formType === 20"
  129. type="primary"
  130. link
  131. @click="handleFormDetail(scope.row)"
  132. >
  133. <span>{{ scope.row.formCustomCreatePath }}</span>
  134. </el-button>
  135. <label v-else>暂无表单</label>
  136. </template>
  137. </el-table-column>
  138. <el-table-column label="最后发布" prop="deploymentTime" min-width="250">
  139. <template #default="scope">
  140. <span v-if="scope.row.processDefinition">
  141. {{ formatDate(scope.row.processDefinition.deploymentTime) }}
  142. </span>
  143. <el-tag v-if="scope.row.processDefinition" class="ml-10px">
  144. v{{ scope.row.processDefinition.version }}
  145. </el-tag>
  146. <el-tag v-else type="warning">未部署</el-tag>
  147. <el-tag
  148. v-if="scope.row.processDefinition?.suspensionState === 2"
  149. type="warning"
  150. class="ml-10px"
  151. >
  152. 已停用
  153. </el-tag>
  154. </template>
  155. </el-table-column>
  156. <el-table-column label="操作" width="200" fixed="right">
  157. <template #default="scope">
  158. <el-button
  159. link
  160. type="primary"
  161. @click="openForm('update', scope.row.id)"
  162. v-hasPermi="['bpm:model:update']"
  163. :disabled="!isManagerUser(scope.row)"
  164. >
  165. 修改
  166. </el-button>
  167. <el-button
  168. link
  169. class="!ml-5px"
  170. type="primary"
  171. @click="handleDesign(scope.row)"
  172. v-hasPermi="['bpm:model:update']"
  173. :disabled="!isManagerUser(scope.row)"
  174. >
  175. 设计
  176. </el-button>
  177. <el-button
  178. link
  179. class="!ml-5px"
  180. type="primary"
  181. @click="handleDeploy(scope.row)"
  182. v-hasPermi="['bpm:model:deploy']"
  183. :disabled="!isManagerUser(scope.row)"
  184. >
  185. 发布
  186. </el-button>
  187. <el-dropdown
  188. class="!align-middle ml-5px"
  189. @command="(command) => handleCommand(command, scope.row)"
  190. v-hasPermi="[
  191. 'bpm:process-definition:query',
  192. 'bpm:model:update',
  193. 'bpm:model:delete'
  194. ]"
  195. >
  196. <el-button type="primary" link>更多</el-button>
  197. <template #dropdown>
  198. <el-dropdown-menu>
  199. <el-dropdown-item
  200. command="handleDefinitionList"
  201. v-if="checkPermi(['bpm:process-definition:query'])"
  202. >
  203. 历史
  204. </el-dropdown-item>
  205. <el-dropdown-item
  206. command="handleChangeState"
  207. v-if="checkPermi(['bpm:model:update']) && scope.row.processDefinition"
  208. :disabled="!isManagerUser(scope.row)"
  209. >
  210. {{ scope.row.processDefinition.suspensionState === 1 ? '停用' : '启用' }}
  211. </el-dropdown-item>
  212. <el-dropdown-item
  213. type="danger"
  214. command="handleDelete"
  215. v-if="checkPermi(['bpm:model:delete'])"
  216. :disabled="!isManagerUser(scope.row)"
  217. >
  218. 删除
  219. </el-dropdown-item>
  220. </el-dropdown-menu>
  221. </template>
  222. </el-dropdown>
  223. </template>
  224. </el-table-column>
  225. </el-table>
  226. </el-collapse-item>
  227. </el-collapse>
  228. </ContentWrap>
  229. </div>
  230. </ContentWrap>
  231. <!-- 表单弹窗:添加/修改流程 -->
  232. <ModelForm ref="formRef" @success="getList" />
  233. <!-- 弹窗:表单详情 -->
  234. <Dialog title="表单详情" v-model="formDetailVisible" width="800">
  235. <form-create :rule="formDetailPreview.rule" :option="formDetailPreview.option" />
  236. </Dialog>
  237. </template>
  238. <script lang="ts" setup>
  239. import { formatDate } from '@/utils/formatTime'
  240. import * as ModelApi from '@/api/bpm/model'
  241. import * as FormApi from '@/api/bpm/form'
  242. import ModelForm from './ModelForm.vue'
  243. import { setConfAndFields2 } from '@/utils/formCreate'
  244. import { CategoryApi } from '@/api/bpm/category'
  245. import { BpmModelType } from '@/utils/constants'
  246. import { checkPermi } from '@/utils/permission'
  247. import { useUserStoreWithOut } from '@/store/modules/user'
  248. import { useAppStore } from '@/store/modules/app'
  249. import { groupBy } from 'lodash-es'
  250. defineOptions({ name: 'BpmModel' })
  251. const appStore = useAppStore()
  252. const message = useMessage() // 消息弹窗
  253. const isDark = computed(() => appStore.getIsDark)
  254. const { t } = useI18n() // 国际化
  255. const { push } = useRouter() // 路由
  256. const userStore = useUserStoreWithOut() // 用户信息缓存
  257. const loading = ref(true) // 列表的加载中
  258. const queryParams = reactive({
  259. pageNo: 1,
  260. pageSize: 10,
  261. key: undefined,
  262. name: undefined,
  263. category: undefined
  264. })
  265. const queryFormRef = ref() // 搜索的表单
  266. const categoryList = ref([]) // 流程分类列表
  267. const categoryGroup = ref<any>({}) // 按照category分组的数据
  268. /** 查询列表 */
  269. const getList = async () => {
  270. loading.value = true
  271. try {
  272. // TODO 芋艿:这里需要一个不分页查全部的流程模型接口
  273. const data = await ModelApi.getModelPage(queryParams)
  274. categoryGroup.value = groupBy(data.list, 'categoryName')
  275. } finally {
  276. loading.value = false
  277. }
  278. }
  279. /** 搜索按钮操作 */
  280. const handleQuery = () => {
  281. queryParams.pageNo = 1
  282. getList()
  283. }
  284. /** '更多'操作按钮 */
  285. const handleCommand = (command: string, row: any) => {
  286. switch (command) {
  287. case 'handleDefinitionList':
  288. handleDefinitionList(row)
  289. break
  290. case 'handleDelete':
  291. handleDelete(row)
  292. break
  293. case 'handleChangeState':
  294. handleChangeState(row)
  295. break
  296. default:
  297. break
  298. }
  299. }
  300. /** 添加/修改操作 */
  301. const formRef = ref()
  302. const openForm = (type: string, id?: number) => {
  303. formRef.value.open(type, id)
  304. }
  305. /** 删除按钮操作 */
  306. const handleDelete = async (row: any) => {
  307. try {
  308. // 删除的二次确认
  309. await message.delConfirm()
  310. // 发起删除
  311. await ModelApi.deleteModel(row.id)
  312. message.success(t('common.delSuccess'))
  313. // 刷新列表
  314. await getList()
  315. } catch {}
  316. }
  317. /** 更新状态操作 */
  318. const handleChangeState = async (row: any) => {
  319. const state = row.processDefinition.suspensionState
  320. const newState = state === 1 ? 2 : 1
  321. try {
  322. // 修改状态的二次确认
  323. const id = row.id
  324. debugger
  325. const statusState = state === 1 ? '停用' : '启用'
  326. const content = '是否确认' + statusState + '流程名字为"' + row.name + '"的数据项?'
  327. await message.confirm(content)
  328. // 发起修改状态
  329. await ModelApi.updateModelState(id, newState)
  330. message.success(statusState + '成功')
  331. // 刷新列表
  332. await getList()
  333. } catch {}
  334. }
  335. /** 设计流程 */
  336. const handleDesign = (row: any) => {
  337. if (row.type == BpmModelType.BPMN) {
  338. push({
  339. name: 'BpmModelEditor',
  340. query: {
  341. modelId: row.id
  342. }
  343. })
  344. } else {
  345. push({
  346. name: 'SimpleWorkflowDesignEditor',
  347. query: {
  348. modelId: row.id
  349. }
  350. })
  351. }
  352. }
  353. /** 发布流程 */
  354. const handleDeploy = async (row: any) => {
  355. try {
  356. // 删除的二次确认
  357. await message.confirm('是否部署该流程!!')
  358. // 发起部署
  359. await ModelApi.deployModel(row.id)
  360. message.success(t('部署成功'))
  361. // 刷新列表
  362. await getList()
  363. } catch {}
  364. }
  365. /** 跳转到指定流程定义列表 */
  366. const handleDefinitionList = (row) => {
  367. push({
  368. name: 'BpmProcessDefinition',
  369. query: {
  370. key: row.key
  371. }
  372. })
  373. }
  374. /** 流程表单的详情按钮操作 */
  375. const formDetailVisible = ref(false)
  376. const formDetailPreview = ref({
  377. rule: [],
  378. option: {}
  379. })
  380. const handleFormDetail = async (row: any) => {
  381. if (row.formType == 10) {
  382. // 设置表单
  383. const data = await FormApi.getForm(row.formId)
  384. setConfAndFields2(formDetailPreview, data.conf, data.fields)
  385. // 弹窗打开
  386. formDetailVisible.value = true
  387. } else {
  388. await push({
  389. path: row.formCustomCreatePath
  390. })
  391. }
  392. }
  393. /** 判断是否可以操作 */
  394. const isManagerUser = (row: any) => {
  395. const userId = userStore.getUser.id
  396. return row.managerUserIds && row.managerUserIds.includes(userId)
  397. }
  398. /* 排序 */
  399. const handleSort = () => {
  400. console.log('排序')
  401. }
  402. /* 分组 */
  403. const handleGroup = () => {
  404. console.log('分组')
  405. }
  406. /** 初始化 **/
  407. onMounted(async () => {
  408. await getList()
  409. // 查询流程分类列表
  410. categoryList.value = await CategoryApi.getCategorySimpleList()
  411. })
  412. </script>
  413. <style lang="scss" scoped>
  414. :deep() {
  415. .el-form--inline .el-form-item {
  416. margin-right: 10px;
  417. }
  418. .el-divider--horizontal {
  419. margin-top: 6px;
  420. }
  421. .el-collapse,
  422. .el-collapse-item__header,
  423. .el-collapse-item__wrap {
  424. border: none;
  425. }
  426. .el-collapse-item__arrow {
  427. margin-left: 10px;
  428. font-size: 20px;
  429. font-weight: 500;
  430. }
  431. }
  432. </style>