vueFlow.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991
  1. <template>
  2. <div class="vueFlow">
  3. <div id="container"></div>
  4. <div id="stencil"></div>
  5. <div id="graph-container"></div>
  6. <div id="minimap"></div>
  7. </div>
  8. <el-button @click="submitAll" type="primary">保存</el-button>
  9. <el-dialog
  10. title="节点信息配置"
  11. v-model="dialogVisible"
  12. width="500"
  13. v-loading="loading"
  14. >
  15. <byForm
  16. :formConfig="formType === 'handle-btn' ? formConfig : branchBtnConfig"
  17. :formOption="formOption"
  18. v-model="formData.data"
  19. :rules="rules"
  20. ref="byform"
  21. >
  22. </byForm>
  23. <template #footer>
  24. <el-button @click="dialogVisible = false" size="large"
  25. >取 消</el-button
  26. >
  27. <el-button
  28. type="danger"
  29. @click="deleteFlowDefinitionNodeObj()"
  30. size="large"
  31. :loading="submitLoading"
  32. >
  33. 删 除
  34. </el-button>
  35. <el-button
  36. type="primary"
  37. @click="submitForm('byform')"
  38. size="large"
  39. :loading="submitLoading"
  40. >
  41. 确 定
  42. </el-button>
  43. </template>
  44. </el-dialog>
  45. <el-dialog
  46. title="节点信息配置"
  47. v-model="startModalType"
  48. width="500"
  49. v-loading="loading"
  50. >
  51. <div>
  52. 节点后执行方法
  53. <el-input style="margin-top:10px" v-model="handlingMethod"></el-input>
  54. </div>
  55. <template #footer>
  56. <el-button @click="startModalType = false" size="large">取 消</el-button>
  57. <el-button
  58. type="primary"
  59. @click="startModalType = false"
  60. size="large"
  61. :loading="submitLoading"
  62. >
  63. 确 定
  64. </el-button>
  65. </template>
  66. </el-dialog>
  67. </template>
  68. <script lang="ts" setup>
  69. import {
  70. ref,
  71. onMounted,
  72. reactive,
  73. computed,
  74. getCurrentInstance,
  75. onDeactivated,
  76. onActivated,
  77. } from "vue";
  78. import byForm from "@/components/byForm/index";
  79. import { Graph, Shape } from "@antv/x6";
  80. import { Stencil } from "@antv/x6-plugin-stencil";
  81. import { Transform } from "@antv/x6-plugin-transform";
  82. import { Selection } from "@antv/x6-plugin-selection";
  83. import { Snapline } from "@antv/x6-plugin-snapline";
  84. import { Keyboard } from "@antv/x6-plugin-keyboard";
  85. import { Clipboard } from "@antv/x6-plugin-clipboard";
  86. import { register } from "@antv/x6-vue-shape";
  87. import { History } from "@antv/x6-plugin-history";
  88. import Cookies from "js-cookie";
  89. import { ElMessage, ElMessageBox } from "element-plus";
  90. import startBtn from "./startBtn.vue";
  91. import endBtn from "./endBtn.vue";
  92. import handleBtn from "./handleBtn.vue";
  93. import branchBtn from "./branchBtn.vue";
  94. import { MiniMap } from "@antv/x6-plugin-minimap";
  95. import useTagsViewStore from "@/store/modules/tagsView";
  96. defineProps({
  97. title: {
  98. type: Object,
  99. default: "",
  100. },
  101. });
  102. const { proxy } = getCurrentInstance();
  103. const internalInstance = getCurrentInstance();
  104. const dialogVisible = ref(false);
  105. const modalType = ref("add");
  106. const loading = ref(false);
  107. const submitLoading = ref(false);
  108. let formType = ref(1); //1办理 2分支
  109. const formData = reactive({
  110. data: {
  111. userName: "",
  112. password: "",
  113. },
  114. });
  115. const startModalType = ref(false);
  116. const handlingMethod = ref("");
  117. const byform = ref(null);
  118. const flowDefinitionNodeObj = ref({});
  119. const rules = reactive({
  120. nodeName: [
  121. {
  122. required: true,
  123. message: "请输入节点名称",
  124. trigger: "blur",
  125. },
  126. ],
  127. handleObjectType: [
  128. {
  129. required: true,
  130. message: "办理人类型不能为空",
  131. trigger: "blur",
  132. },
  133. ],
  134. handleObjectId: [
  135. {
  136. required: true,
  137. message: "办理人不能为空",
  138. trigger: "blur",
  139. },
  140. ],
  141. });
  142. const branchBtnConfig = computed(() => {
  143. return [
  144. {
  145. type: "input",
  146. prop: "nodeName",
  147. label: "节点名称",
  148. required: true,
  149. itemType: "text",
  150. },
  151. ];
  152. });
  153. const formConfig = computed(() => {
  154. return [
  155. {
  156. type: "input",
  157. prop: "nodeName",
  158. label: "节点名称",
  159. required: true,
  160. itemType: "text",
  161. },
  162. {
  163. type: "select",
  164. prop: "handleObjectType",
  165. label: "办理人",
  166. placeholder: "请选择办理人类型",
  167. required: true,
  168. itemWidth: 30,
  169. fn: (e) => {
  170. gethandleObjectList(e);
  171. },
  172. //1用户 2部门负责人 3部门总监 4岗位 5角色
  173. data: [
  174. {
  175. label: "用户",
  176. value: 1,
  177. },
  178. {
  179. label: "部门负责人",
  180. value: 2,
  181. },
  182. {
  183. label: "部门总监",
  184. value: 3,
  185. },
  186. {
  187. label: "岗位",
  188. value: 4,
  189. },
  190. {
  191. label: "角色",
  192. value: 5,
  193. },
  194. ],
  195. },
  196. // {
  197. // type: "treeSelect",
  198. // prop: "handleObjectId",
  199. // label: "请选择办理人",
  200. // itemWidth: 30,
  201. // data: [],
  202. // },
  203. {
  204. type: "select",
  205. label: " ",
  206. itemWidth: 30,
  207. prop: "handleObjectId",
  208. placeholder: "请选择办理人",
  209. data: [],
  210. },
  211. {
  212. type: "input",
  213. prop: "handlingMethod",
  214. label: "节点后置执行方法",
  215. required: true,
  216. itemType: "text",
  217. },
  218. {
  219. type: "input",
  220. prop: "jumpCondition",
  221. label: "条件表达式",
  222. required: true,
  223. itemType: "text",
  224. },
  225. {
  226. type: "checkbox",
  227. prop: "nodeButtonSet",
  228. label: "节点按钮",
  229. //1通过 2驳回 3返回上一步 4退回到发起人
  230. data: [
  231. {
  232. label: "通过",
  233. value: 1,
  234. disabled: true,
  235. },
  236. {
  237. label: "驳回",
  238. value: 2,
  239. },
  240. {
  241. label: "返回上一步",
  242. value: 3,
  243. },
  244. {
  245. label: "退回到发起人",
  246. value: 4,
  247. },
  248. ],
  249. },
  250. {
  251. type: "radio",
  252. prop: "jobNumber11",
  253. label: "审批意见必填",
  254. data: [
  255. {
  256. label: "是",
  257. value: 1,
  258. },
  259. {
  260. label: "否",
  261. value: 0,
  262. },
  263. ],
  264. },
  265. ];
  266. });
  267. const formOption = reactive({
  268. inline: true,
  269. labelWidth: 100,
  270. itemWidth: 100,
  271. });
  272. let graph;
  273. const submitForm = () => {
  274. byform.value.handleSubmit((valid) => {
  275. flowDefinitionNodeObj.value[formData.data.id] = formData.data;
  276. dialogVisible.value = false;
  277. formData.data.cell.setData({
  278. title: formData.data.nodeName,
  279. });
  280. });
  281. };
  282. const submitFormData = {
  283. flowInfoId: null,
  284. titleTemplate: null,
  285. tenantId: Cookies.get("tenantId"),
  286. nodeObject: "",
  287. lineObject: "",
  288. flowDefinitionNodeList: [],
  289. };
  290. const submitAll = () => {
  291. if (proxy.title == "") {
  292. ElMessage({
  293. message: "请输入流程标题",
  294. type: "warning",
  295. });
  296. return;
  297. }
  298. submitFormData.titleTemplate = proxy.title;
  299. const nodeList = graph.toJSON().cells;
  300. submitFormData.nodeObject = JSON.stringify(nodeList);
  301. submitFormData.lineObject = JSON.stringify(flowDefinitionNodeObj.value);
  302. const isStart = false;
  303. for (let i = 0; i < nodeList.length; i++) {
  304. const element = nodeList[i];
  305. //是办理节点
  306. if (
  307. element.shape != "start-btn" &&
  308. element.shape != "edge" &&
  309. element.shape != "end-btn"
  310. ) {
  311. if (!flowDefinitionNodeObj.value[element.id]) {
  312. ElMessage({
  313. message: "有节点未配置,请检查节点",
  314. type: "warning",
  315. });
  316. return;
  317. }
  318. submitFormData.flowDefinitionNodeList.push({
  319. ...flowDefinitionNodeObj.value[element.id],
  320. nodeType: element.shape == "branch-btn" ? 3 : 2,
  321. });
  322. }
  323. if (element.shape == "end-btn") {
  324. submitFormData.flowDefinitionNodeList.push({
  325. ...flowDefinitionNodeObj.value[element.id],
  326. });
  327. }
  328. if (element.shape == "start-btn") {
  329. submitFormData.flowDefinitionNodeList.push({
  330. nodeName: "开始",
  331. nodeType: 1,
  332. id: 1,
  333. parentId: 0,
  334. nodeButtonSet: [1],
  335. handlingMethod: handlingMethod.value,
  336. });
  337. }
  338. //说明是线
  339. if (element.shape == "edge") {
  340. if (!flowDefinitionNodeObj.value[element.target.cell]) {
  341. ElMessage({
  342. message: "有节点未配置,请检查节点",
  343. type: "warning",
  344. });
  345. return;
  346. }
  347. flowDefinitionNodeObj.value[element.target.cell].id = element.target.cell;
  348. flowDefinitionNodeObj.value[element.target.cell].parentId =
  349. element.source.cell;
  350. submitFormData.flowDefinitionNodeList = [];
  351. }
  352. }
  353. addVersion();
  354. };
  355. //选取一个随机不重复的正整数id
  356. const randomId = () => {
  357. const id = Math.floor(Math.random() * 100000000000000000);
  358. if (flowDefinitionNodeObj.value[id]) {
  359. randomId();
  360. } else {
  361. return id;
  362. }
  363. };
  364. const addVersion = () => {
  365. const idObg = {};
  366. for (let i = 0; i < submitFormData.flowDefinitionNodeList.length; i++) {
  367. const element = submitFormData.flowDefinitionNodeList[i];
  368. if (element.parentId == null && element.nodeName == "结束") {
  369. ElMessage({
  370. message: "有结束节点未连线,请配置",
  371. type: "warning",
  372. });
  373. return;
  374. }
  375. if (isNaN(element.id)) {
  376. if (idObg[element.id]) {
  377. element.id = idObg[element.id];
  378. } else {
  379. const id = randomId();
  380. idObg[element.id] = id;
  381. element.id = id;
  382. }
  383. }
  384. if (isNaN(element.parentId) && element.nodeName != "开始") {
  385. if (idObg[element.parentId]) {
  386. element.parentId = idObg[element.parentId];
  387. } else {
  388. const id = randomId();
  389. idObg[element.parentId] = id;
  390. element.parentId = id;
  391. }
  392. }
  393. //nodeButtonSet转成字符串类型,用逗号隔开
  394. if (element.nodeButtonSet) {
  395. element.nodeButtonSet = element.nodeButtonSet.join(",");
  396. }
  397. }
  398. proxy.post("/flowDefinition/addVersion", submitFormData).then((res) => {
  399. ElMessage({
  400. message: "保存成功",
  401. type: "success",
  402. });
  403. useTagsViewStore().delView(router.currentRoute.value);
  404. history.go(-1);
  405. });
  406. };
  407. //将组数里的id和parentId转换成整正整数类型
  408. const changeId = (arr) => {
  409. for (let i = 0; i < arr.length; i++) {
  410. const element = arr[i];
  411. element.id = parseInt(element.id);
  412. element.parentId = parseInt(element.parentId);
  413. }
  414. };
  415. const deleteFlowDefinitionNodeObj = (id) => {
  416. graph.removeNode(formData.data.id);
  417. delete flowDefinitionNodeObj.value[id];
  418. dialogVisible.value = false;
  419. };
  420. const gethandleObjectList = (e) => {
  421. formData.data.handleObjectId = "";
  422. if (e === 1) {
  423. proxy
  424. .get(
  425. "/tenantUser/list?pageNum=1&pageSize=1000&tenantId=" +
  426. submitFormData.tenantId,
  427. {}
  428. )
  429. .then((res) => {
  430. formConfig.value[2].data = res.rows.map((item) => {
  431. return {
  432. label: item.nickName,
  433. value: item.userId,
  434. };
  435. });
  436. });
  437. }
  438. if (e === 3 || e === 2) {
  439. proxy
  440. .get(
  441. "/tenantDept/list?pageNum=1&pageSize=1000&tenantId=" +
  442. submitFormData.tenantId,
  443. {}
  444. )
  445. .then((res) => {
  446. formConfig.value[2].data = res.data.map((item) => {
  447. return {
  448. label: item.deptName,
  449. value: item.deptId,
  450. };
  451. });
  452. });
  453. }
  454. if (e === 4) {
  455. }
  456. if (e === 5) {
  457. proxy
  458. .get(
  459. "/tenantRole/list?pageNum=1&pageSize=1000&tenantId=" +
  460. submitFormData.tenantId,
  461. {}
  462. )
  463. .then((res) => {
  464. formConfig.value[2].data = res.rows.map((item) => {
  465. return {
  466. label: item.roleName,
  467. value: item.roleId,
  468. };
  469. });
  470. });
  471. }
  472. };
  473. const getTenantDept = () => {};
  474. getTenantDept();
  475. const recursive = (data) => {
  476. data.map((item) => {
  477. item.label = item.deptName;
  478. item.id = item.deptId;
  479. if (item.children) {
  480. recursive(item.children);
  481. } else {
  482. item.children = [];
  483. }
  484. });
  485. };
  486. const pushRoom = (port: any) => {
  487. if (port.node.shape == "end-btn") {
  488. flowDefinitionNodeObj.value[port.node.id] = {
  489. nodeName: "结束",
  490. nodeType: 99,
  491. id: port.id,
  492. nodeButtonSet: "",
  493. parentId: null,
  494. };
  495. }
  496. };
  497. //用于存储流程定义节点数据
  498. const antvInit = (data) => {
  499. graph = new Graph({
  500. height: 600,
  501. container: document.getElementById("graph-container")!,
  502. grid: true,
  503. onPortRendered: pushRoom,
  504. mousewheel: {
  505. enabled: true,
  506. zoomAtMousePosition: true,
  507. modifiers: "ctrl",
  508. minScale: 0.5,
  509. maxScale: 3,
  510. },
  511. connecting: {
  512. allowLoop: false,
  513. // router: 'manhattan',
  514. connector: {
  515. name: "rounded",
  516. args: {
  517. radius: 8,
  518. },
  519. },
  520. anchor: "center",
  521. connectionPoint: "anchor",
  522. allowBlank: false,
  523. snap: {
  524. radius: 20,
  525. },
  526. createEdge() {
  527. return new Shape.Edge({
  528. attrs: {
  529. line: {
  530. stroke: "#A2B1C3",
  531. strokeWidth: 2,
  532. targetMarker: {
  533. name: "block",
  534. width: 12,
  535. height: 8,
  536. },
  537. },
  538. },
  539. zIndex: 0,
  540. });
  541. },
  542. validateConnection({ targetMagnet }) {
  543. return !!targetMagnet;
  544. },
  545. },
  546. highlighting: {
  547. magnetAdsorbed: {
  548. name: "stroke",
  549. args: {
  550. attrs: {
  551. fill: "#5F95FF",
  552. stroke: "#5F95FF",
  553. },
  554. },
  555. },
  556. },
  557. });
  558. graph.use(
  559. new MiniMap({
  560. container: document.getElementById("minimap"),
  561. })
  562. );
  563. const stencil = new Stencil({
  564. title: "流程图",
  565. target: graph,
  566. stencilGraphWidth: 360,
  567. stencilGraphHeight: 280,
  568. collapsable: true,
  569. groups: [
  570. {
  571. title: "基础流程图",
  572. name: "group1",
  573. },
  574. ],
  575. layoutOptions: {
  576. columns: 2,
  577. columnWidth: 170,
  578. rowHeight: 100,
  579. },
  580. });
  581. document.getElementById("stencil")!.appendChild(stencil.container);
  582. // #region 使用插件
  583. graph
  584. .use(
  585. new Transform({
  586. resizing: true,
  587. rotating: true,
  588. })
  589. )
  590. .use(
  591. new Selection({
  592. enabled: true,
  593. rubberband: true,
  594. showNodeSelectionBox: true,
  595. })
  596. )
  597. .use(
  598. new Snapline({
  599. enabled: true,
  600. })
  601. )
  602. .use(
  603. new Keyboard({
  604. enabled: true,
  605. })
  606. )
  607. .use(
  608. new Clipboard({
  609. enabled: true,
  610. })
  611. )
  612. .use(
  613. new History({
  614. enabled: true,
  615. })
  616. );
  617. // 控制连接桩显示/隐藏
  618. const showPorts = (ports: NodeListOf<SVGElement>, show: boolean) => {
  619. for (let i = 0, len = ports.length; i < len; i += 1) {
  620. ports[i].style.visibility = show ? "visible" : "hidden";
  621. }
  622. };
  623. graph.on("node:mouseenter", () => {
  624. const container = document.getElementById("graph-container")!;
  625. const ports = container.querySelectorAll(
  626. ".x6-port-body"
  627. ) as NodeListOf<SVGElement>;
  628. showPorts(ports, true);
  629. });
  630. graph.on("node:mouseleave", () => {
  631. const container = document.getElementById("graph-container")!;
  632. const ports = container.querySelectorAll(
  633. ".x6-port-body"
  634. ) as NodeListOf<SVGElement>;
  635. showPorts(ports, false);
  636. });
  637. // #endregion
  638. graph.on("cell:click", ({ e, x, y, cell, view }) => {
  639. if (cell.shape === "start-btn") {
  640. startModalType.value = true;
  641. return;
  642. }
  643. if (cell.shape === "end-btn" || cell.shape === "edge") {
  644. ElMessageBox.confirm("是否删除", "提示", {
  645. confirmButtonText: "确定",
  646. cancelButtonText: "取消",
  647. type: "warning",
  648. }).then(() => {
  649. graph.removeNode(cell.id);
  650. // delete flowDefinitionNodeObj.value[id]
  651. });
  652. return;
  653. }
  654. formType.value = cell.shape;
  655. if (flowDefinitionNodeObj.value[cell.id]) {
  656. formData.data = flowDefinitionNodeObj.value[cell.id];
  657. formData.data.cell = cell;
  658. } else {
  659. formData.data = {
  660. id: cell.id,
  661. cell: cell,
  662. nodeButtonSet: [1],
  663. };
  664. }
  665. dialogVisible.value = true;
  666. });
  667. // #region 初始化图形
  668. const ports = {
  669. groups: {
  670. top: {
  671. position: "top",
  672. attrs: {
  673. circle: {
  674. r: 4,
  675. magnet: true,
  676. stroke: "#5F95FF",
  677. strokeWidth: 1,
  678. fill: "#fff",
  679. style: {
  680. visibility: "hidden",
  681. },
  682. },
  683. },
  684. },
  685. right: {
  686. position: "right",
  687. attrs: {
  688. circle: {
  689. r: 4,
  690. magnet: true,
  691. stroke: "#5F95FF",
  692. strokeWidth: 1,
  693. fill: "#fff",
  694. style: {
  695. visibility: "hidden",
  696. },
  697. },
  698. },
  699. },
  700. bottom: {
  701. position: "bottom",
  702. attrs: {
  703. circle: {
  704. r: 4,
  705. magnet: true,
  706. stroke: "#5F95FF",
  707. strokeWidth: 1,
  708. fill: "#fff",
  709. style: {
  710. visibility: "hidden",
  711. },
  712. },
  713. },
  714. },
  715. left: {
  716. position: "left",
  717. attrs: {
  718. circle: {
  719. r: 4,
  720. magnet: true,
  721. stroke: "#5F95FF",
  722. strokeWidth: 1,
  723. fill: "#fff",
  724. style: {
  725. visibility: "hidden",
  726. },
  727. },
  728. },
  729. },
  730. },
  731. items: [
  732. {
  733. group: "top",
  734. },
  735. {
  736. group: "right",
  737. },
  738. {
  739. group: "bottom",
  740. },
  741. {
  742. group: "left",
  743. },
  744. ],
  745. };
  746. Graph.registerNode(
  747. "custom-rect",
  748. {
  749. inherit: "rect",
  750. width: 66,
  751. height: 36,
  752. attrs: {
  753. body: {
  754. strokeWidth: 1,
  755. stroke: "#5F95FF",
  756. fill: "#EFF4FF",
  757. },
  758. text: {
  759. fontSize: 12,
  760. fill: "#262626",
  761. },
  762. },
  763. ports: { ...ports },
  764. },
  765. true
  766. );
  767. register({
  768. shape: "start-btn",
  769. width: 150,
  770. height: 90,
  771. component: startBtn,
  772. effect: ["title"],
  773. ports: { ...ports },
  774. data: {
  775. title: 80,
  776. },
  777. });
  778. register({
  779. shape: "handle-btn",
  780. width: 150,
  781. height: 90,
  782. effect: ["title"],
  783. component: handleBtn,
  784. ports: { ...ports },
  785. });
  786. register({
  787. shape: "branch-btn",
  788. width: 150,
  789. height: 90,
  790. effect: ["title"],
  791. component: branchBtn,
  792. ports: { ...ports },
  793. });
  794. register({
  795. shape: "end-btn",
  796. width: 150,
  797. height: 90,
  798. effect: ["title"],
  799. component: endBtn,
  800. ports: { ...ports },
  801. });
  802. // const r1 = graph.createNode({
  803. // shape: 'start-btn',
  804. // label: '开始',
  805. // zIndex: 100,
  806. // attrs: {
  807. // body: {
  808. // rx: 20,
  809. // ry: 26,
  810. // },
  811. // },
  812. // data: {
  813. // title: 80,
  814. // },
  815. // })
  816. const r2 = graph.createNode({
  817. shape: "handle-btn",
  818. label: "办理",
  819. zIndex: 100,
  820. attrs: {
  821. body: {
  822. rx: 40,
  823. ry: 46,
  824. },
  825. },
  826. });
  827. const r3 = graph.createNode({
  828. shape: "branch-btn",
  829. label: "分支",
  830. zIndex: 100,
  831. attrs: {
  832. body: {
  833. rx: 40,
  834. ry: 46,
  835. },
  836. },
  837. });
  838. const r4 = graph.createNode({
  839. shape: "end-btn",
  840. label: "结束",
  841. zIndex: 100,
  842. attrs: {
  843. body: {
  844. rx: 20,
  845. ry: 26,
  846. },
  847. },
  848. });
  849. stencil.load([r2, r3, r4], "group1");
  850. // const startNode = graph.addNode({
  851. // shape: 'custom-rect',
  852. // label: '开始',
  853. // id: 1,
  854. // x: 500,
  855. // y: 100,
  856. // })
  857. if (data) {
  858. graph.fromJSON(data);
  859. } else {
  860. graph.addNode({
  861. shape: "start-btn",
  862. x: 500,
  863. y: 20,
  864. label: "开始",
  865. id: 1,
  866. attrs: {},
  867. });
  868. }
  869. };
  870. const getFlowInfo = () => {
  871. proxy
  872. .post("/flowDefinition/getDetails", { id: submitFormData.id })
  873. .then((res) => {
  874. if (res.lineObject) {
  875. flowDefinitionNodeObj.value = JSON.parse(res.lineObject);
  876. for (const key in flowDefinitionNodeObj.value) {
  877. if (flowDefinitionNodeObj.value[key].nodeButtonSet) {
  878. flowDefinitionNodeObj.value[key].nodeButtonSet =
  879. flowDefinitionNodeObj.value[key].nodeButtonSet.map((item) => {
  880. return item * 1;
  881. });
  882. }
  883. }
  884. }
  885. if (res.nodeObject) {
  886. antvInit(JSON.parse(res.nodeObject));
  887. } else {
  888. antvInit();
  889. }
  890. for (const key in flowDefinitionNodeObj.value) {
  891. //延迟等待dom渲染完成
  892. setTimeout(() => {
  893. if (
  894. flowDefinitionNodeObj.value[key].nodeName != "结束" &&
  895. flowDefinitionNodeObj.value[key].cell != "开始"
  896. ) {
  897. let htmlNode = document.querySelector(
  898. "g[data-cell-id='" + key + "']"
  899. );
  900. //获取htmlNode节点下的title,修改title的内容
  901. htmlNode.getElementsByClassName("title")[0].innerHTML =
  902. flowDefinitionNodeObj.value[key].nodeName;
  903. }
  904. }, 2000);
  905. }
  906. dialogVisible.value = false;
  907. });
  908. };
  909. const router = useRouter();
  910. onActivated(() => {});
  911. onDeactivated(() => {
  912. if (window.document.getElementById("minimap").children.length > 1) {
  913. window.document.getElementById("minimap").children[0].remove();
  914. }
  915. });
  916. onMounted(() => {
  917. //获取url router参数
  918. submitFormData.flowInfoId = router.currentRoute.value.query.flowInfoId;
  919. submitFormData.id = router.currentRoute.value.query.id;
  920. submitFormData.tenantId = router.currentRoute.value.query.tenantId;
  921. if (submitFormData.flowInfoId) {
  922. getFlowInfo();
  923. }
  924. setTimeout(() => {
  925. if (window.document.getElementById("minimap").children.length > 1) {
  926. window.document.getElementById("minimap").children[0].remove();
  927. }
  928. }, 500);
  929. });
  930. </script>
  931. <style lang="scss">
  932. #minimap .x6-widget-minimap {
  933. border: 1px solid #dcdcdc;
  934. }
  935. .x6-widget-stencil-group-title {
  936. display: none !important;
  937. }
  938. .x6-widget-stencil-title {
  939. display: none;
  940. }
  941. .x6-widget-stencil-content {
  942. top: 0 !important;
  943. }
  944. .vueFlow {
  945. position: relative;
  946. display: flex;
  947. justify-content: space-between;
  948. overflow: hidden;
  949. height: 600px;
  950. .x6-graph {
  951. width: 100% !important;
  952. }
  953. #stencil {
  954. position: fixed;
  955. top: 250px;
  956. left: 40px;
  957. z-index: 100;
  958. width: 360px;
  959. height: 500px;
  960. background: #fff;
  961. overflow: hidden;
  962. background: #eee;
  963. border-radius: 20px;
  964. }
  965. #container {
  966. }
  967. #graph-container {
  968. width: 100%;
  969. position: absolute;
  970. right: 0;
  971. top: 0;
  972. }
  973. }
  974. </style>