editFlowDialog.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. <template>
  2. <div class="flow-container">
  3. <el-dialog v-model="state.isShowDialog" draggable :close-on-click-modal="false" fullscreen>
  4. <template #header>
  5. <div style="color: #fff">
  6. <span>{{ props.title }}</span>
  7. </div>
  8. </template>
  9. <div class="f-content">
  10. <div class="f-container">
  11. <div class="f-switch">
  12. <el-switch v-model="state.value2" @change="change" class="mb-2" active-text="打开框选" inactive-text="关闭框选" />
  13. </div>
  14. <PanelControl v-if="lf" :lf="lf" @catData="getData"></PanelControl>
  15. <div class="f-container-c" ref="container" id="container"></div>
  16. <PanelNode v-if="lf" :lf="lf"></PanelNode>
  17. <el-drawer title="属性" v-model="drawer" :direction="direction" size="500px" :before-close="handleClose">
  18. <PropertyDialog v-if="drawer" :nodeData="state.nodeData" :lf="lf" @setPropertiesFinish="handleClose"></PropertyDialog>
  19. </el-drawer>
  20. <el-dialog
  21. title="数据"
  22. v-model="dataVisible"
  23. width="50%">
  24. <PanelDataDialog :graphData="state.graphData"></PanelDataDialog>
  25. </el-dialog>
  26. </div>
  27. </div>
  28. <template #footer>
  29. <span class="dialog-footer">
  30. <el-button @click="cancel">取 消</el-button>
  31. <el-button type="primary" @click="submit">确 定</el-button>
  32. </span>
  33. </template>
  34. </el-dialog>
  35. </div>
  36. </template>
  37. <script setup lang="ts">
  38. import { reactive, ref, nextTick } from 'vue';
  39. import LogicFlow from '@logicflow/core';
  40. import '@logicflow/core/dist/style/index.css';
  41. import { BpmnElement, InsertNodeInPolyline, Menu, MiniMap, SelectionSelect, Snapshot } from '@logicflow/extension';
  42. import '@logicflow/extension/lib/style/index.css';
  43. import { ElMessageBox } from 'element-plus';
  44. import RegisterEdge from './LogicFlow/Register/RegisterEdge';
  45. import RegisterNode from './LogicFlow/Register/RegisterNode';
  46. import PanelNode from './LogicFlow/Panel/PanelNode.vue';
  47. import PanelControl from './LogicFlow/Panel/PanelControl.vue';
  48. import PanelDataDialog from './LogicFlow/Panel/PanelDataDialog.vue';
  49. import PropertyDialog from './LogicFlow/Property/PropertyDialog.vue';
  50. // 接口
  51. import { getAPI } from '/@/utils/axios-utils';
  52. import { ApprovalFlowApi } from '/@/api-services/api';
  53. import { ApprovalFlowOutput, UpdateApprovalFlowInput } from '/@/api-services/models';
  54. var props = defineProps({
  55. title: {
  56. type: String,
  57. default: '',
  58. },
  59. });
  60. const emit = defineEmits(['reloadTable', 'updateFlow']);
  61. const flowData = ref({});
  62. const lf = ref<InstanceType<typeof LogicFlow>>();
  63. const drawer = ref(false);
  64. const direction = ref('rtl');
  65. const dataVisible = ref(false);
  66. const state = reactive({
  67. loading: false,
  68. isShowDialog: false,
  69. ruleSource: {} as UpdateApprovalFlowInput,
  70. nodeData: {},
  71. graphData: {},
  72. });
  73. const openDialog = (row: ApprovalFlowOutput) => {
  74. state.ruleSource = row as UpdateApprovalFlowInput;
  75. // 初始化数据
  76. if (state.ruleSource.flowJson) {
  77. flowData.value = JSON.parse(state.ruleSource.flowJson);
  78. } else {
  79. flowData.value = {
  80. nodes: [],
  81. edges: [],
  82. };
  83. }
  84. state.isShowDialog = true;
  85. nextTick(() => {
  86. // 初始化画布
  87. initGraph();
  88. });
  89. console.log('open');
  90. };
  91. const closeDialog = () => {
  92. emit('reloadTable');
  93. state.isShowDialog = false;
  94. console.log('close');
  95. };
  96. const cancel = () => {
  97. state.isShowDialog = false;
  98. console.log('cancel');
  99. };
  100. // 保存流程设计
  101. const submit = async () => {
  102. flowData.value = lf.value?.getGraphData();
  103. state.ruleSource.flowJson = JSON.stringify(flowData.value);
  104. await getAPI(ApprovalFlowApi).apiApprovalFlowUpdatePost(state.ruleSource);
  105. emit('updateFlow', flowData.value);
  106. closeDialog();
  107. };
  108. const initGraph = () => {
  109. // 初始化画布
  110. const container: HTMLElement = document.querySelector('#container')!;
  111. // 配置项
  112. const config = {
  113. stopScrollGraph: true, // 禁止鼠标滚动移动画布
  114. stopZoomGraph: true, // 禁止缩放
  115. metaKeyMultipleSelected: true,
  116. // 背景网格大小
  117. grid: {
  118. size: 10,
  119. type: 'dot',
  120. },
  121. // 快捷键
  122. keyboard: {
  123. enabled: true,
  124. },
  125. // 辅助线
  126. snapline: true,
  127. };
  128. lf.value = new LogicFlow({
  129. ...config,
  130. plugins: [
  131. BpmnElement,
  132. // 作栋节点自动插入边
  133. InsertNodeInPolyline,
  134. // 右键菜单
  135. Menu,
  136. // 迷你图
  137. MiniMap,
  138. // 框选
  139. SelectionSelect,
  140. // 快照
  141. Snapshot
  142. ],
  143. container: container,
  144. width: container.clientWidth,
  145. height: container.clientHeight,
  146. });
  147. // 设置主题
  148. lf.value.setTheme({
  149. snapline: {
  150. stroke: '#1E90FF', // 对齐线颜色
  151. strokeWidth: 1, // 对齐线宽度
  152. },
  153. });
  154. // 注册自定义节点
  155. RegisterNode.Register(lf.value);
  156. // 注册自定义边
  157. RegisterEdge.Register(lf.value);
  158. // 监听节点点击事件
  159. lf.value.on('node:click', ({ data }) => {
  160. state.nodeData = data;
  161. drawer.value = true;
  162. });
  163. // 监听边点击事件
  164. lf.value.on('edge:click', ({ data }) => {
  165. state.nodeData = data;
  166. drawer.value = true;
  167. });
  168. // 渲染数据
  169. lf.value.render(flowData.value);
  170. // 画布居中
  171. lf.value.focusOn({ coordinate: { x: 300, y: 300 } });
  172. };
  173. // 框选
  174. const change = (val: boolean) => {
  175. if (val) {
  176. lf.value?.extension.selectionSelect.openSelectionSelect();
  177. } else {
  178. lf.value?.extension.selectionSelect.closeSelectionSelect();
  179. }
  180. };
  181. // 获取数据
  182. const getData = () => {
  183. var data = lf.value?.getGraphData();
  184. state.graphData = data;
  185. dataVisible.value = true;
  186. };
  187. // 关闭属性界面提醒
  188. const handleClose = (done: () => void) => {
  189. ElMessageBox.confirm('确认要关闭当前属性编辑?')
  190. .then(() => {
  191. done();
  192. })
  193. .catch(() => {
  194. // catch error
  195. });
  196. };
  197. defineExpose({ openDialog });
  198. </script>
  199. <style scoped lang="scss">
  200. :deep(.el-tabs__nav-scroll) {
  201. width: 70%;
  202. margin: 0 auto;
  203. }
  204. .flow-container {
  205. :deep(.el-dialog) {
  206. .el-dialog__header {
  207. display: none !important;
  208. }
  209. .el-dialog__body {
  210. max-height: calc(100vh - 45px) !important;
  211. }
  212. }
  213. }
  214. .f-content {
  215. display: flex;
  216. flex-grow: 1;
  217. z-index: 1;
  218. margin: -2px -19px -20px -19px;
  219. height: calc(100vh - 100px) !important;
  220. .f-container {
  221. flex-grow: 1;
  222. position: relative;
  223. .f-switch {
  224. position: absolute;
  225. z-index: 2;
  226. top: -22px;
  227. left: 5px;
  228. .el-switch {
  229. margin-right: 10px;
  230. }
  231. }
  232. .el-drawer {
  233. height: 80%;
  234. overflow: auto;
  235. margin-top: -30px;
  236. z-index: !important;
  237. }
  238. .f-container-c {
  239. position: absolute;
  240. width: 100%;
  241. height: 100%;
  242. }
  243. }
  244. }
  245. </style>