editFlowDialog.vue 6.2 KB

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