listGroup.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. <template>
  2. <div class="list_group_container">
  3. <div v-if="!hideAddInput" class="group_list">
  4. <div class="group_header flex flex-pack-justify items-center">
  5. <div class="group_header_title">
  6. <span>
  7. <el-input v-model="listGroupName" v-autoFocus placeholder="请输入流程组名称" @blur="addFlowGroup" />
  8. </span>
  9. </div>
  10. </div>
  11. </div>
  12. <draggable
  13. v-bind="dragOptions"
  14. v-model="categoryList"
  15. handle=".group_header"
  16. item-key="categoryId"
  17. @update:model-value="update_item_categoryList"
  18. >
  19. <template #item="{ element: item, index }">
  20. <div class="group_list">
  21. <div class="group_header flex flex-pack-justify items-center px-4">
  22. <div class="group_header_title">
  23. <span>
  24. <template v-if="!item.editor">
  25. {{ item.categoryName }}
  26. </template>
  27. <template v-else>
  28. <el-input v-model="item.categoryName" v-autoFocus placeholder="请输入流程组名称" @blur="editFlowGroup('save', item, index)" />
  29. </template>
  30. </span>
  31. </div>
  32. <div class="group_header_nameOperate">
  33. <LeIcon
  34. class="text-lg text-icon-color cursor-pointer"
  35. icon-class="icon-processInfo-mage--edit"
  36. @click="editFlowGroup('edit', item, index)"
  37. />
  38. <el-tooltip effect="dark" content="删除" placement="top">
  39. <LeIcon
  40. class="text-lg ml-2 text-rose-700 cursor-pointer"
  41. icon-class="icon-processInfo-iconoir--trash"
  42. @click="deleteFlowGroup(item)"
  43. />
  44. </el-tooltip>
  45. </div>
  46. </div>
  47. <div class="group_body">
  48. <draggable
  49. v-model="item.processList"
  50. v-bind="dragOptions"
  51. group="categoryId"
  52. tag="ul"
  53. class="group_list_ul"
  54. :component-data="{
  55. type: 'transition-group',
  56. name: !drag ? 'flip-list' : null
  57. }"
  58. item-key="processId"
  59. @update:model-value="update_item_processList"
  60. @start="drag = true"
  61. @end="drag = false"
  62. >
  63. <template #item="{ element }">
  64. <li class="group_item">
  65. <el-row :gutter="5">
  66. <el-col :span="5" :xs="7">
  67. <div class="flex items-center h-[42px]">
  68. <LeIcon class="group_itemIcon text-[2rem]" :icon-class="`${flowIconPrefix}${element.processIcon}`" />
  69. <div class="truncate pr-2">
  70. <div class="group_itemNameWrapper flex items-center">
  71. <div class="group_itemName">
  72. <span :title="element.processName">{{ element.processName }}</span>
  73. </div>
  74. </div>
  75. <div class="group_itemIntro" :title="element.title">
  76. {{ element.title }}
  77. </div>
  78. </div>
  79. </div>
  80. </el-col>
  81. <el-col :span="15" :xs="9">
  82. <div class="flex items-center" style="height: 42px">
  83. <div class="flex-1">
  84. <el-tag size="small" round>V{{ element.processVersion }}</el-tag>
  85. <el-tag v-if="element.processType === 'child'" type="warning" round class="ml-1" size="small">子流程</el-tag>
  86. <el-tag v-if="element.processType === 'business'" type="success" round class="ml-1" size="small">业务流程</el-tag>
  87. <el-tag v-if="element.processState === 0" type="danger" round class="ml-1" size="small">已停用</el-tag>
  88. </div>
  89. <div class="flex-1 max-m:hidden">{{ element.processKey }}</div>
  90. </div>
  91. </el-col>
  92. <el-col :span="4" :xs="8">
  93. <div class="group_itemOperations flex flex-pack-end items-center pr-1" style="height: 42px">
  94. <el-tooltip effect="dark" content="版本控制" placement="top">
  95. <LeIcon
  96. v-if="element.processVersion > 1"
  97. class="text-lg ml-2 text-icon-color cursor-pointer"
  98. icon-class="icon-processInfo-hugeicons--git-merge"
  99. @click="historyEv(element)"
  100. />
  101. </el-tooltip>
  102. <el-tooltip effect="dark" content="编辑" placement="top">
  103. <LeIcon
  104. class="text-lg ml-2 text-icon-color cursor-pointer"
  105. icon-class="icon-processInfo-mage--edit"
  106. @click="updateEv(element)"
  107. />
  108. </el-tooltip>
  109. <el-tooltip content="复制" placement="top" effect="dark">
  110. <LeIcon
  111. class="text-lg ml-2 text-icon-color cursor-pointer"
  112. icon-class="icon-processInfo-lucide--copy"
  113. @click="copyEv(element)"
  114. />
  115. </el-tooltip>
  116. <el-tooltip v-if="element.processState === 1" effect="dark" content="禁用" placement="top">
  117. <LeIcon
  118. class="text-lg ml-2 text-icon-color cursor-pointer"
  119. icon-class="icon-processInfo-solar--forbidden-circle-broken"
  120. @click="enabledEv(element, 0)"
  121. />
  122. </el-tooltip>
  123. <el-tooltip v-if="element.processState === 0" effect="dark" content="启用" placement="top">
  124. <LeIcon
  125. class="text-lg ml-2 text-icon-color cursor-pointer"
  126. icon-class="icon-processInfo-heroicons--lock-open"
  127. @click="enabledEv(element, 1)"
  128. />
  129. </el-tooltip>
  130. <el-tooltip content="删除" placement="top" effect="dark">
  131. <LeIcon
  132. class="text-lg ml-2 text-rose-700 cursor-pointer"
  133. icon-class="icon-processInfo-iconoir--trash"
  134. @click="stopEv(element)"
  135. />
  136. </el-tooltip>
  137. </div>
  138. </el-col>
  139. </el-row>
  140. </li>
  141. </template>
  142. </draggable>
  143. </div>
  144. </div>
  145. </template>
  146. </draggable>
  147. <!-- 历史流程签出 -->
  148. <history-process-list
  149. v-if="visibleHistory"
  150. ref="historyDialog"
  151. v-model="visibleHistory"
  152. :item="currentProcess"
  153. @success-fn="flowGroupListAll"
  154. ></history-process-list>
  155. </div>
  156. </template>
  157. <script lang="tsx" setup>
  158. import Draggable from 'vuedraggable'
  159. // import { Delete, CircleClose, EditPen } from '@element-plus/icons-vue'
  160. import { ref, onActivated, nextTick, onMounted } from 'vue'
  161. import flowGroup from '@/api/flow/group'
  162. import flowDefinition from '@/api/flow/definition'
  163. import { ElMessage, ElMessageBox } from 'element-plus'
  164. import process from '@/api/flow/process'
  165. import router from '@/router'
  166. import { debounce, flowIconPrefix /*, getAssetsFile*/ } from '@/utils/index'
  167. import HistoryProcessList from './historyProcessList.vue'
  168. const categoryList = ref<any[]>([])
  169. const hideAddInput = ref(true)
  170. const visibleHistory = ref(false)
  171. const currentProcess = ref({})
  172. const listGroupName = ref('')
  173. const dragOptions = {
  174. animation: 200,
  175. group: 'processId',
  176. disabled: false,
  177. ghostClass: 'ghost'
  178. }
  179. const drag = ref(false)
  180. // 显示隐藏添加流程组元素
  181. const showAddInput = () => {
  182. hideAddInput.value = !hideAddInput.value
  183. }
  184. // 新增流程组
  185. const addFlowGroup = async () => {
  186. if (!listGroupName.value.trim()) {
  187. showAddInput()
  188. return false
  189. }
  190. try {
  191. const param = {
  192. name: listGroupName.value,
  193. sort: 0
  194. }
  195. await flowGroup.flowGroupAddOrEditSaveApi(param)
  196. showAddInput()
  197. flowGroupListAll()
  198. } catch (e) {
  199. console.log(e)
  200. }
  201. }
  202. const update_categoryListSort = debounce(() => {
  203. // console.error('刷新数据........', xxx)
  204. // {categoryId:string; processIds: string[]}[]
  205. flowDefinition.updateProcessSortApi(
  206. categoryList.value.map(v => ({
  207. categoryId: v.categoryId,
  208. processIds: v.processList && v.processList.length ? v.processList.map(_v => _v.processId) : undefined
  209. }))
  210. )
  211. /*.then(res => {
  212. console.warn(res, 'res......')
  213. })*/
  214. }, 50)
  215. const update_item_categoryList = (values: any[]) => {
  216. // console.warn(values, 'values update_item_categoryList', categoryList.value)
  217. update_categoryListSort()
  218. }
  219. const update_item_processList = (values: any[]) => {
  220. // console.warn(values, 'values update_item_processList', categoryList.value)
  221. update_categoryListSort()
  222. }
  223. // 流程组列表
  224. const flowGroupListAll = async (item = {}) => {
  225. try {
  226. const data: any = await flowDefinition.flowDefinitionListCategoryApi(item)
  227. categoryList.value = data || []
  228. console.log(data)
  229. } catch (e) {
  230. console.log(e)
  231. }
  232. }
  233. // 流程组删除
  234. const deleteFlowGroup = (item: any) => {
  235. try {
  236. // todo 这里需要判断当前的流程组下是否有实例,有不能删除,没有 直接调用删除接口
  237. ElMessageBox.confirm('是否删除该分组?', '提示', {
  238. confirmButtonText: '确认',
  239. cancelButtonText: '取消',
  240. type: 'error',
  241. buttonSize: 'default'
  242. })
  243. .then(async () => {
  244. await flowGroup.flowGroupDeleteApi([item.categoryId])
  245. flowGroupListAll()
  246. ElMessage({
  247. message: '删除成功!',
  248. type: 'success'
  249. })
  250. })
  251. .catch(() => {
  252. console.log('取消')
  253. })
  254. } catch (e) {
  255. console.log(e)
  256. }
  257. }
  258. /**
  259. * 编辑流程组函数
  260. *
  261. * @param type 操作类型,'edit'表示编辑,其他表示保存
  262. * @param item 当前项
  263. * @param idx 当前项的索引
  264. * @returns 如果type为'edit',则返回false;否则返回undefined
  265. */
  266. const editFlowGroup = async (type, item, idx) => {
  267. if (type === 'edit') {
  268. categoryList.value[idx].editor = true
  269. return false
  270. }
  271. try {
  272. categoryList.value[idx].editor = false
  273. const param = {
  274. id: categoryList.value[idx].categoryId,
  275. name: categoryList.value[idx].categoryName
  276. // sort: categoryList.value[idx].categorySort
  277. }
  278. await flowGroup.flowGroupAddOrEditSaveApi(param)
  279. flowGroupListAll()
  280. } catch (e) {
  281. console.log(e)
  282. }
  283. }
  284. // 删除
  285. const stopEv = (ele: any) => {
  286. ElMessageBox.confirm(`确认删除「${ele.processName}」流程?`, '提示', {
  287. confirmButtonText: '确认',
  288. cancelButtonText: '取消',
  289. type: 'error'
  290. }).then(async () => {
  291. await process.progressDeleteApi({ id: ele.processId })
  292. flowGroupListAll()
  293. })
  294. }
  295. /**
  296. * 启用/禁用事件处理函数
  297. *
  298. * @param id 要启用/禁用的元素的ID
  299. * @param state 启用/禁用的状态,1表示启用,0表示禁用
  300. * @returns 无返回值,执行异步操作
  301. */
  302. const enabledEv = async (ele: any, state: number) => {
  303. ElMessageBox.confirm(`您即将${state === 1 ? '启用' : '禁用'}「${ele.processName}」流程,是否确认?`, '提示', {
  304. confirmButtonText: '确认',
  305. cancelButtonText: '取消',
  306. type: 'warning'
  307. }).then(async () => {
  308. await process.processUpdateStateApi({ id: ele.processId, state: state })
  309. ElMessage.success('操作成功')
  310. flowGroupListAll()
  311. })
  312. }
  313. /**
  314. * 复制事件处理函数
  315. *
  316. * @returns 无返回值,执行异步操作
  317. */
  318. const copyEv = (ele: any) => {
  319. ElMessageBox.confirm(`您即将复制「${ele.processName}」流程,是否确认?`, '提示', {
  320. confirmButtonText: '确认',
  321. cancelButtonText: '取消',
  322. type: 'info'
  323. }).then(async () => {
  324. await process.progressCloneApi({ id: ele.processId })
  325. flowGroupListAll()
  326. })
  327. }
  328. // 修改
  329. const updateEv = async (element: any) => {
  330. const jumpRouterUrl = '/flow_create/' + (element.processType === 'main' ? 'index' : element.processType)
  331. router.push(`${jumpRouterUrl}?id=${element.processId}`)
  332. }
  333. // 打开当前流程的历史版本
  334. const historyEv = (element: any) => {
  335. currentProcess.value = element
  336. visibleHistory.value = !visibleHistory.value
  337. }
  338. // keep-alive缓存树,防止创建完流程过后,回到当前窗口未刷新
  339. onActivated(() => {
  340. flowGroupListAll()
  341. })
  342. /**
  343. * 当组件被挂载到 DOM 后,执行回调函数
  344. */
  345. onMounted(() => {
  346. nextTick(() => {
  347. flowGroupListAll()
  348. })
  349. })
  350. // 父组件使用的话需要导出
  351. defineExpose({
  352. showAddInput,
  353. searchProcessEv: flowGroupListAll
  354. })
  355. </script>
  356. <style scoped lang="scss">
  357. .list_group_container {
  358. // 同sortGroup一样的样式
  359. .group_list {
  360. border-radius: 4px;
  361. margin-bottom: 16px;
  362. box-shadow: var(--el-box-shadow-lighter);
  363. background-color: var(--el-bg-color);
  364. .group_header {
  365. height: 54px;
  366. //padding: 0px 16px 0px 12px;
  367. font-size: 16px;
  368. line-height: 24px;
  369. background-color: var(--el-fill-color-light);
  370. &_title {
  371. position: relative;
  372. font-family: PingFangSC-Medium;
  373. display: flex;
  374. align-items: center;
  375. &.disabled {
  376. color: var(--el-text-color-disabled);
  377. }
  378. .edit_line {
  379. display: inline-block;
  380. width: 200px;
  381. border: 1px solid var(--el-text-color-primary);
  382. visibility: hidden;
  383. height: 36px;
  384. position: absolute;
  385. left: -5px;
  386. }
  387. .edit_icon {
  388. right: -170px;
  389. visibility: hidden;
  390. }
  391. &:hover {
  392. .edit_line,
  393. .edit_icon {
  394. visibility: visible;
  395. }
  396. }
  397. }
  398. &_nameOperate {
  399. cursor: pointer;
  400. }
  401. }
  402. .group_body,
  403. .group_list_ul {
  404. box-sizing: border-box;
  405. padding: 0;
  406. //width: 1000px;
  407. }
  408. .group_item {
  409. padding: 8px 12px;
  410. &:not(.group_item-disabled):hover {
  411. background-color: var(--el-fill-color-lighter);
  412. }
  413. .group_itemIcon {
  414. flex-shrink: 0;
  415. margin-right: 8px;
  416. //height: 42px;
  417. //width: 42px;
  418. border-radius: 50%;
  419. overflow: hidden;
  420. //font-size: 42px;
  421. /*img {
  422. width: 100%;
  423. height: 100%;
  424. object-fit: cover;
  425. }*/
  426. }
  427. .group_itemLeft {
  428. padding-right: 8px;
  429. }
  430. .group_itemOperations {
  431. cursor: pointer;
  432. .le-icon {
  433. flex-shrink: 0;
  434. }
  435. }
  436. .group_itemIntro {
  437. font-size: 14px;
  438. text-overflow: ellipsis;
  439. overflow: hidden;
  440. white-space: nowrap;
  441. line-height: 17px;
  442. font-family: PingFangSC-Regular;
  443. }
  444. .group_itemNameWrapper {
  445. justify-content: flex-start;
  446. }
  447. .group_itemName {
  448. max-width: 418px;
  449. font-size: 14px;
  450. text-overflow: ellipsis;
  451. overflow: hidden;
  452. white-space: nowrap;
  453. line-height: 16px;
  454. font-weight: 500;
  455. font-family: PingFangSC-Medium;
  456. }
  457. }
  458. }
  459. .ghost {
  460. opacity: 0.5;
  461. box-shadow: 1px 1px 5px 2px rgba(0, 0, 0, 0.15);
  462. }
  463. }
  464. </style>