design.vue 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. <template>
  2. <div class="FL-container getting-started">
  3. <!-- 辅助工具栏 -->
  4. <Control class="demo-control" v-if="lf.container" :lf="lf as LogicFlow" @catData="$_catData" @saveModel="$_saveModel"></Control>
  5. <!-- 画布 -->
  6. <div class="app-content" ref="logicFlowContainerRef"></div>
  7. <!-- 属性面板 -->
  8. <el-drawer :title="t('message.flowModel.setNodeProps')" v-model="dialogVisible" direction="rtl" size="900px" :before-close="closeDialog">
  9. <PropertyDialog v-if="dialogVisible" :nodeData="clickNode" :lf="lf as LogicFlow" @setPropertiesFinish="closeDialog"></PropertyDialog>
  10. </el-drawer>
  11. <!-- 数据查看面板 -->
  12. <el-dialog :title="t('message.flowModel.data')" v-model="dataVisible" width="50%">
  13. <DataDialog :graphData="graphData"></DataDialog>
  14. </el-dialog>
  15. </div>
  16. </template>
  17. <script setup lang="ts">
  18. import LogicFlow from '@logicflow/core'
  19. import { DndPanel, Menu, MiniMap, SelectionSelect, Snapshot } from '@logicflow/extension'
  20. import '@logicflow/core/lib/style/index.css'
  21. import '@logicflow/extension/lib/style/index.css'
  22. import { onMounted, ref, watch } from 'vue'
  23. import Control from '/@/components/gFlow/Control.vue'
  24. import { setPatternItems, setTheme } from '/@/components/gFlow/config'
  25. import {
  26. registerConcurrent,
  27. registerCondition,
  28. registerEnd,
  29. registerPush,
  30. registerStart,
  31. registerUserTask,
  32. registerWebHook,
  33. } from '/@/components/gFlow/registerNode'
  34. import PropertyDialog from '/@/components/gFlow/propertySetting/PropertyDialog.vue'
  35. import DataDialog from '/@/components/gFlow/DataDialog.vue'
  36. import { useRoute } from 'vue-router'
  37. import { ElMessage } from 'element-plus'
  38. import { useI18n } from 'vue-i18n'
  39. import { getNodeData, saveModeNode } from '/@/api/flow/flowModel'
  40. import {
  41. FlowCheckRuleDept,
  42. FlowCheckRuleDeptLeader,
  43. FlowCheckRulePost,
  44. FlowCheckRuleRole,
  45. FlowCheckRuleUser
  46. } from '/@/components/gFlow/consts'
  47. import GraphConfigData = LogicFlow.GraphConfigData
  48. const route = useRoute()
  49. const { t,locale } = useI18n()
  50. const modelId = ref(0)
  51. const logicFlowContainerRef = ref()
  52. const lf = ref<LogicFlow>({} as LogicFlow)
  53. const dataVisible = ref(false)
  54. const graphData = ref<GraphConfigData>()
  55. const addPanelStyle = ref({
  56. top: '0',
  57. left: '0',
  58. })
  59. const showAddPanel = ref(false)
  60. const addClickNode = ref<any>()
  61. const clickNode = ref({})
  62. const dialogVisible = ref(false)
  63. const moveData = ref({})
  64. onMounted(() => {
  65. if (route.query.id) {
  66. modelId.value = parseInt(route.query.id as string)
  67. } else {
  68. ElMessage.error(t('message.flowModel.paramError'))
  69. return
  70. }
  71. lf.value = new LogicFlow({
  72. container: logicFlowContainerRef.value,
  73. grid: true,
  74. plugins: [DndPanel, SelectionSelect, Snapshot, Menu, MiniMap],
  75. pluginsOptions: {
  76. miniMap: {
  77. isShowHeader: false,
  78. isShowCloseIcon: true,
  79. headerTitle: t('message.flowModel.miniMap'),
  80. },
  81. },
  82. })
  83. registerNode()
  84. initLF()
  85. })
  86. //注册节点信息
  87. const registerNode = () => {
  88. const logicFlow = lf.value as LogicFlow
  89. registerStart(logicFlow)
  90. registerEnd(logicFlow)
  91. registerConcurrent(logicFlow)
  92. registerPush(logicFlow)
  93. registerCondition(logicFlow)
  94. registerUserTask(logicFlow)
  95. registerWebHook(logicFlow)
  96. }
  97. const initLF = () => {
  98. const logicFlow = lf.value as LogicFlow
  99. setPatternItems(logicFlow,t)
  100. setTheme(logicFlow)
  101. $_render()
  102. $_LfEvent()
  103. }
  104. watch(locale,()=> {
  105. setPatternItems(lf.value as LogicFlow,t)
  106. })
  107. const $_render = () => {
  108. //获取节点数据
  109. getNodeData(modelId.value).then((res: any) => {
  110. const nodes = (res.nodes ?? []).map((item: any) => {
  111. const node = {
  112. id: item.nodeId,
  113. type: item.nodeType,
  114. x: item.nodePosition.x,
  115. y: item.nodePosition.y,
  116. text: item.nodeText ?? {},
  117. properties: {
  118. text: item.nodeText?.value,
  119. actionRule: item.nodeActionRule,
  120. approveRule: item.nodeRule,
  121. notice: item.notice ?? [],
  122. rollback: item.rollback,
  123. nodeExt: item.nodeExt ?? [],
  124. deptProv: item.deptProv ?? 1,
  125. // WebHook 配置 - 从后端数据构建 webhook 对象
  126. webhook: JSON.parse(item.ext ?? '{}'),
  127. // 动态判断配置
  128. dynamicJudge: JSON.parse(item.ext ?? '{}')?.userId,
  129. },
  130. }
  131. setReceiver(node, item.nodeReceiver)
  132. return node
  133. })
  134. const edges = (res.edges ?? []).map((item: any) => {
  135. return {
  136. id: item.id,
  137. type: item.lineType,
  138. sourceNodeId: item.sourceNodeId,
  139. targetNodeId: item.targetNodeId,
  140. startPoint: item.startPoint,
  141. endPoint: item.endPoint,
  142. properties: item.properties,
  143. pointsList: item.pointsList,
  144. text: item.text,
  145. }
  146. })
  147. lf.value.render({ nodes, edges })
  148. $_LfEvent()
  149. })
  150. }
  151. const setReceiver = (node: any, receivers: Array<string>) => {
  152. switch (node.properties.actionRule) {
  153. case FlowCheckRuleRole: //角色
  154. node.properties.roleIds = receivers
  155. break
  156. case FlowCheckRuleDept: //部门
  157. case FlowCheckRuleDeptLeader: //部门负责人
  158. node.properties.deptIds = receivers
  159. break
  160. case FlowCheckRuleUser: //指定人员
  161. node.properties.userIds = receivers
  162. break
  163. case FlowCheckRulePost: //岗位
  164. node.properties.postIds = receivers
  165. }
  166. }
  167. const $_catData = () => {
  168. graphData.value = lf.value.getGraphData() as GraphConfigData
  169. dataVisible.value = true
  170. }
  171. const $_saveModel = () => {
  172. const data = lf.value.getGraphData() as any
  173. const nodes = data.nodes.map((item: any) => {
  174. return {
  175. nodeId: item.id,
  176. modelId: modelId.value,
  177. nodeType: item.type,
  178. nodePosition: { x: item.x, y: item.y },
  179. nodeText: item.text,
  180. nodeActionRule: item.properties?.actionRule,
  181. nodeReceiver: getNodeReceiver(item.properties?.actionRule, item),
  182. nodeRule: item.properties?.approveRule,
  183. notice: item.properties?.notice,
  184. rollback: item.properties?.rollback,
  185. nodeExt: item.properties?.nodeExt,
  186. deptProv: item.properties?.deptProv,
  187. ext: JSON.stringify({
  188. // WebHook 相关配置 - 整合到 webhook 对象中
  189. ...item.properties?.webhook,
  190. userId: item.properties?.dynamicJudge,
  191. }),
  192. }
  193. })
  194. const edges = data.edges.map((item: any) => {
  195. return {
  196. modelId: modelId.value,
  197. lineType: item.type,
  198. sourceNodeId: item.sourceNodeId,
  199. targetNodeId: item.targetNodeId,
  200. startPoint: item.startPoint,
  201. endPoint: item.endPoint,
  202. properties: item.properties,
  203. text: item.text,
  204. pointsList: item.pointsList,
  205. }
  206. })
  207. const postData = {
  208. modelId: modelId.value,
  209. nodes: nodes,
  210. edges: edges,
  211. }
  212. saveModeNode(postData)
  213. .then(() => {
  214. ElMessage.success(t('message.flowModel.saveSuccess'))
  215. })
  216. .catch(() => ElMessage.error(t('message.flowModel.exportFailed')))
  217. }
  218. const $_LfEvent = () => {
  219. const logicFlow = lf.value as LogicFlow
  220. logicFlow.on('node:click', (data) => {
  221. console.log('node:click', data)
  222. clickNode.value = data.data
  223. dialogVisible.value = true
  224. })
  225. logicFlow.on('edge:click', ({ data }) => {
  226. console.log('edge:click', data)
  227. //this.$data.clickNode = data;
  228. //this.$data.dialogVisible = true;
  229. })
  230. logicFlow.on('element:click', () => {
  231. hideAddPanel()
  232. })
  233. logicFlow.on('edge:add', ({ data }) => {
  234. console.log('edge:add', data)
  235. })
  236. logicFlow.on('node:mousemove', ({ data }) => {
  237. console.log('node:mousemove')
  238. moveData.value = data
  239. })
  240. logicFlow.on('blank:click', () => {
  241. hideAddPanel()
  242. })
  243. logicFlow.on('connection:not-allowed', (data) => {
  244. console.log('connection:not-allowed', data)
  245. })
  246. logicFlow.on('node:mousemove', () => {
  247. console.log('on mousemove')
  248. })
  249. }
  250. const hideAddPanel = () => {
  251. showAddPanel.value = false
  252. addPanelStyle.value.top = '0'
  253. addPanelStyle.value.left = '0'
  254. addClickNode.value = null
  255. }
  256. const closeDialog = () => {
  257. dialogVisible.value = false
  258. }
  259. const getNodeReceiver = (rule: number, item: any) => {
  260. switch (rule) {
  261. case FlowCheckRuleRole: //角色
  262. return item.properties.roleIds
  263. case FlowCheckRuleDept: //部门
  264. case FlowCheckRuleDeptLeader: //部门负责人
  265. return item.properties.deptIds
  266. case FlowCheckRuleUser: //指定人员
  267. return item.properties.userIds
  268. case FlowCheckRulePost: //岗位
  269. return item.properties.postIds
  270. }
  271. return []
  272. }
  273. </script>
  274. <style scoped lang="scss">
  275. .FL-container {
  276. width: 100%;
  277. overflow: hidden;
  278. .app-content {
  279. height: calc(100vh - 120px);
  280. }
  281. }
  282. .demo-control {
  283. position: absolute;
  284. top: 20px;
  285. right: 50px;
  286. z-index: 2;
  287. }
  288. </style>