Browse Source

feat: add flowChart Component (#488)

啝裳 3 năm trước cách đây
mục cha
commit
2576735ade
33 tập tin đã thay đổi với 1062 bổ sung0 xóa
  1. 3 0
      package.json
  2. 22 0
      src/components/FlowChart/index.ts
  3. 150 0
      src/components/FlowChart/src/Control.vue
  4. 18 0
      src/components/FlowChart/src/DataDialog.vue
  5. 145 0
      src/components/FlowChart/src/NodePanel.vue
  6. 160 0
      src/components/FlowChart/src/adpterForTurbo.ts
  7. BIN
      src/components/FlowChart/src/assets/background/bpmn-end.png
  8. BIN
      src/components/FlowChart/src/assets/background/bpmn-exclusiveGateway.png
  9. BIN
      src/components/FlowChart/src/assets/background/bpmn-start.png
  10. BIN
      src/components/FlowChart/src/assets/background/bpmn-user.png
  11. BIN
      src/components/FlowChart/src/assets/background/click.png
  12. BIN
      src/components/FlowChart/src/assets/background/download.png
  13. BIN
      src/components/FlowChart/src/assets/background/end.png
  14. BIN
      src/components/FlowChart/src/assets/background/push.png
  15. BIN
      src/components/FlowChart/src/assets/background/start.png
  16. BIN
      src/components/FlowChart/src/assets/background/time.png
  17. BIN
      src/components/FlowChart/src/assets/background/user.png
  18. 5 0
      src/components/FlowChart/src/assets/iconfont/iconfont.css
  19. BIN
      src/components/FlowChart/src/assets/iconfont/iconfont.eot
  20. 8 0
      src/components/FlowChart/src/assets/iconfont/iconfont.js
  21. 58 0
      src/components/FlowChart/src/assets/iconfont/iconfont.json
  22. 25 0
      src/components/FlowChart/src/assets/iconfont/iconfont.svg
  23. BIN
      src/components/FlowChart/src/assets/iconfont/iconfont.ttf
  24. BIN
      src/components/FlowChart/src/assets/iconfont/iconfont.woff
  25. BIN
      src/components/FlowChart/src/assets/iconfont/iconfont.woff2
  26. 55 0
      src/components/FlowChart/src/config.ts
  27. 1 0
      src/locales/lang/en/routes/demo/comp.ts
  28. 1 0
      src/locales/lang/zh_CN/routes/demo/comp.ts
  29. 7 0
      src/router/menus/modules/demo/comp.ts
  30. 8 0
      src/router/routes/modules/demo/comp.ts
  31. 240 0
      src/views/demo/comp/flow-chart/dataTurbo.json
  32. 133 0
      src/views/demo/comp/flow-chart/index.vue
  33. 23 0
      yarn.lock

+ 3 - 0
package.json

@@ -33,6 +33,8 @@
   },
   "dependencies": {
     "@iconify/iconify": "^2.0.0-rc.6",
+    "@logicflow/core": "^0.3.0",
+    "@logicflow/extension": "^0.3.0",
     "@vueuse/core": "^4.8.1",
     "@zxcvbn-ts/core": "^0.3.0",
     "ant-design-vue": "^2.1.2",
@@ -52,6 +54,7 @@
     "vditor": "^3.8.4",
     "vue": "3.0.11",
     "vue-i18n": "9.0.0",
+    "vue-json-pretty": "^2.0.2",
     "vue-router": "^4.0.6",
     "vue-types": "^3.0.2",
     "xlsx": "^0.16.9"

+ 22 - 0
src/components/FlowChart/index.ts

@@ -0,0 +1,22 @@
+import { App } from 'vue';
+import control from './src/Control.vue';
+import nodePanel from './src/NodePanel.vue';
+import dataDialog from './src/DataDialog.vue';
+
+export const Control = Object.assign(control, {
+  install(app: App) {
+    app.component(control.name, control);
+  },
+});
+
+export const NodePanel = Object.assign(nodePanel, {
+  install(app: App) {
+    app.component(nodePanel.name, nodePanel);
+  },
+});
+
+export const DataDialog = Object.assign(dataDialog, {
+  install(app: App) {
+    app.component(dataDialog.name, dataDialog);
+  },
+});

+ 150 - 0
src/components/FlowChart/src/Control.vue

@@ -0,0 +1,150 @@
+<template>
+  <div class="control-container">
+    <!-- 功能按钮 -->
+    <ul>
+      <li
+        v-for="(item, key) in titleLists"
+        :key="key"
+        :title="item.text"
+        @mouseenter.prevent="onEnter(key)"
+        @mouseleave.prevent="focusIndex = -1"
+      >
+        <a-button
+          :disabled="item.disabled"
+          :style="{ cursor: item.disabled === false ? 'pointer' : 'not-allowed' }"
+          @click="onControl(item, key)"
+        >
+          <span :class="'iconfont ' + item.icon"></span>
+          <p>{{ item.text }}</p>
+        </a-button>
+      </li>
+    </ul>
+  </div>
+</template>
+
+<script lang="ts">
+  import { defineComponent, ref, unref, onMounted } from 'vue';
+
+  export default defineComponent({
+    name: 'Control',
+    props: {
+      lf: Object || String,
+      catTurboData: Boolean,
+    },
+    emits: ['catData'],
+    setup(props, { emit }) {
+      let focusIndex = ref(-1);
+      let titleLists = ref([
+        {
+          icon: 'icon-zoom-out-hs',
+          text: '缩小',
+          disabled: false,
+        },
+        {
+          icon: 'icon-enlarge-hs',
+          text: '放大',
+          disabled: false,
+        },
+        {
+          icon: 'icon-full-screen-hs',
+          text: '适应',
+          disabled: false,
+        },
+        {
+          icon: 'icon-previous-hs',
+          text: '上一步',
+          disabled: true,
+        },
+        {
+          icon: 'icon-next-step-hs',
+          text: '下一步',
+          disabled: true,
+        },
+        {
+          icon: 'icon-download-hs',
+          text: '下载图片',
+          disabled: false,
+        },
+        {
+          icon: 'icon-watch-hs',
+          text: '查看数据',
+          disabled: false,
+        },
+      ]);
+
+      const onControl = (item, key) => {
+        ['zoom', 'zoom', 'resetZoom', 'undo', 'redo', 'getSnapshot'].forEach((v, i) => {
+          let domControl = props.lf;
+          if (key === 1) {
+            domControl.zoom(true);
+          }
+          if (key === 6) {
+            emit('catData');
+          }
+          if (key === i) {
+            domControl[v]();
+          }
+        });
+      };
+
+      const onEnter = (key) => {
+        focusIndex.value = key;
+      };
+
+      onMounted(() => {
+        props.lf.on('history:change', ({ data: { undoAble, redoAble } }) => {
+          unref(titleLists)[3].disabled = !undoAble;
+          unref(titleLists)[4].disabled = !redoAble;
+        });
+      });
+
+      return {
+        focusIndex,
+        titleLists,
+        onControl,
+        onEnter,
+      };
+    },
+  });
+</script>
+
+<style scoped>
+  @import './assets/iconfont/iconfont.css';
+
+  .control-container {
+    position: absolute;
+    right: 20px;
+    background: hsla(0, 0%, 100%, 0.8);
+    box-shadow: 0 1px 4px rgb(0 0 0 / 30%);
+  }
+
+  .iconfont {
+    font-size: 25px;
+  }
+
+  .control-container p {
+    margin: 0;
+    font-size: 12px;
+  }
+
+  .control-container ul {
+    display: flex;
+    justify-content: space-around;
+    align-items: center;
+    margin: 2px;
+  }
+
+  .control-container ul li {
+    width: 60px;
+    text-align: center;
+  }
+
+  .control-container ul li button {
+    width: 100%;
+    height: 60px;
+    padding: 0;
+    background-color: transparent;
+    border: none;
+    outline: none;
+  }
+</style>

+ 18 - 0
src/components/FlowChart/src/DataDialog.vue

@@ -0,0 +1,18 @@
+<template>
+  <vue-json-pretty :path="'res'" :deep="3" :showLength="true" :data="graphData" />
+</template>
+
+<script lang="ts">
+  import VueJsonPretty from 'vue-json-pretty';
+  import 'vue-json-pretty/lib/styles.css';
+  import { defineComponent } from 'vue';
+  export default defineComponent({
+    name: 'DataDialog',
+    components: {
+      VueJsonPretty,
+    },
+    props: {
+      graphData: Object,
+    },
+  });
+</script>

+ 145 - 0
src/components/FlowChart/src/NodePanel.vue

@@ -0,0 +1,145 @@
+<template>
+  <!-- 左侧bpmn元素选择器 -->
+  <div class="node-panel">
+    <div
+      class="node-item"
+      v-for="item in nodeList"
+      :key="item.text"
+      @mousedown="nodeDragNode(item)"
+    >
+      <div class="node-item-icon" :class="item.class">
+        <div v-if="item.type === 'user' || item.type === 'time'" class="shape"></div>
+      </div>
+      <span class="node-label">{{ item.text }}</span>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+  import { defineComponent, ref, unref } from 'vue';
+  export default defineComponent({
+    name: 'NodePanel',
+    props: {
+      lf: Object,
+      nodeList: Array,
+    },
+    setup(props) {
+      let node = ref({
+        type: 'rect',
+        property: {
+          a: 'efrwe',
+          b: 'wewe',
+        },
+      });
+      let properties = ref({
+        a: 'efrwe',
+        b: 'wewe',
+      });
+
+      const nodeDragNode = (item) => {
+        props.lf.dnd.startDrag({
+          type: item.type,
+          properties: unref(properties),
+        });
+      };
+
+      return {
+        node,
+        properties,
+        nodeDragNode,
+      };
+    },
+  });
+</script>
+
+<style scoped>
+  .node-panel {
+    position: absolute;
+    top: 100px;
+    left: 50px;
+    z-index: 101;
+    width: 70px;
+    padding: 20px 10px;
+    text-align: center;
+    background-color: white;
+    border-radius: 6px;
+    box-shadow: 0 0 10px 1px rgb(228, 224, 219);
+  }
+
+  .node-item {
+    margin-bottom: 20px;
+  }
+
+  .node-item-icon {
+    display: flex;
+    height: 30px;
+    background-size: cover;
+    flex-wrap: wrap;
+    justify-content: center;
+  }
+
+  .node-label {
+    margin-top: 5px;
+    font-size: 12px;
+    user-select: none;
+  }
+
+  .node-start {
+    background: url('./background/start.png') no-repeat;
+    background-size: cover;
+  }
+
+  .node-rect {
+    border: 1px solid black;
+  }
+
+  .node-user {
+    background: url('./background/user.png') no-repeat;
+    background-size: cover;
+  }
+
+  .node-time {
+    background: url('./background/time.png') no-repeat;
+    background-size: cover;
+  }
+
+  .node-push {
+    background: url('./background/push.png') no-repeat;
+    background-size: cover;
+  }
+
+  .node-download {
+    background: url('./background/download.png') no-repeat;
+    background-size: cover;
+  }
+
+  .node-click {
+    background: url('./background/click.png') no-repeat;
+    background-size: cover;
+  }
+
+  .node-end {
+    background: url('./background/end.png') no-repeat;
+    background-size: cover;
+  }
+
+  .bpmn-start {
+    cursor: grab;
+    background: url('./assets/background/bpmn-start.png') center center no-repeat;
+  }
+
+  .bpmn-end {
+    cursor: grab;
+    background: url('./assets/background/bpmn-end.png') center center no-repeat;
+  }
+
+  .bpmn-user {
+    cursor: grab;
+    background: url('./assets/background/bpmn-user.png') center center no-repeat;
+  }
+
+  .bpmn-exclusiveGateway {
+    cursor: grab;
+    background: url('./assets/background/bpmn-exclusiveGateway.png') center center no-repeat;
+  }
+</style>

+ 160 - 0
src/components/FlowChart/src/adpterForTurbo.ts

@@ -0,0 +1,160 @@
+const TurboType = {
+  SEQUENCE_FLOW: 1,
+  START_EVENT: 2,
+  END_EVENT: 3,
+  USER_TASK: 4,
+  SERVICE_TASK: 5,
+  EXCLUSIVE_GATEWAY: 6,
+};
+
+function getTurboType(type) {
+  switch (type) {
+    case 'bpmn:sequenceFlow':
+      return TurboType.SEQUENCE_FLOW;
+    case 'bpmn:startEvent':
+      return TurboType.START_EVENT;
+    case 'bpmn:endEvent':
+      return TurboType.END_EVENT;
+    case 'bpmn:userTask':
+      return TurboType.USER_TASK;
+    case 'bpmn:serviceTask':
+      return TurboType.SERVICE_TASK;
+    case 'bpmn:exclusiveGateway':
+      return TurboType.EXCLUSIVE_GATEWAY;
+    default:
+      return type;
+  }
+}
+
+function convertNodeToTurboElement(node) {
+  const { id, type, x, y, text = '', properties } = node;
+  return {
+    incoming: [],
+    outgoing: [],
+    dockers: [],
+    type: getTurboType(node.type),
+    properties: {
+      ...properties,
+      name: (text && text.value) || '',
+      x: x,
+      y: y,
+      text,
+      logicFlowType: type,
+    },
+    key: id,
+  };
+}
+
+function convertEdgeToTurboElement(edge) {
+  const {
+    id,
+    type,
+    sourceNodeId,
+    targetNodeId,
+    startPoint,
+    endPoint,
+    pointsList,
+    text = '',
+    properties,
+  } = edge;
+  return {
+    incoming: [sourceNodeId],
+    outgoing: [targetNodeId],
+    type: getTurboType(type),
+    dockers: [],
+    properties: {
+      ...properties,
+      name: (text && text.value) || '',
+      text,
+      startPoint,
+      endPoint,
+      pointsList,
+      logicFlowType: type,
+    },
+    key: id,
+  };
+}
+
+export function toTurboData(data) {
+  const nodeMap = new Map();
+  const turboData = {
+    flowElementList: [],
+  };
+  data.nodes.forEach((node) => {
+    const flowElement = convertNodeToTurboElement(node);
+    turboData.flowElementList.push(flowElement);
+    nodeMap.set(node.id, flowElement);
+  });
+  data.edges.forEach((edge) => {
+    const flowElement = convertEdgeToTurboElement(edge);
+    const sourceElement = nodeMap.get(edge.sourceNodeId);
+    sourceElement.outgoing.push(flowElement.key);
+    const targetElement = nodeMap.get(edge.targetNodeId);
+    targetElement.incoming.push(flowElement.key);
+    turboData.flowElementList.push(flowElement);
+  });
+  return turboData;
+}
+
+function convertFlowElementToEdge(element) {
+  const { incoming, outgoing, properties, key } = element;
+  const { text, startPoint, endPoint, pointsList, logicFlowType } = properties;
+  const edge = {
+    id: key,
+    type: logicFlowType,
+    sourceNodeId: incoming[0],
+    targetNodeId: outgoing[0],
+    text,
+    startPoint,
+    endPoint,
+    pointsList,
+    properties: {},
+  };
+  const excludeProperties = ['startPoint', 'endPoint', 'pointsList', 'text', 'logicFlowType'];
+  Object.keys(element.properties).forEach((property) => {
+    if (excludeProperties.indexOf(property) === -1) {
+      edge.properties[property] = element.properties[property];
+    }
+  });
+  return edge;
+}
+
+function convertFlowElementToNode(element) {
+  const { properties, key } = element;
+  const { x, y, text, logicFlowType } = properties;
+  const node = {
+    id: key,
+    type: logicFlowType,
+    x,
+    y,
+    text,
+    properties: {},
+  };
+  const excludeProperties = ['x', 'y', 'text', 'logicFlowType'];
+  Object.keys(element.properties).forEach((property) => {
+    if (excludeProperties.indexOf(property) === -1) {
+      node.properties[property] = element.properties[property];
+    }
+  });
+  return node;
+}
+
+export function toLogicflowData(data) {
+  const lfData = {
+    nodes: [],
+    edges: [],
+  };
+  const list = data.flowElementList;
+  list &&
+    list.length > 0 &&
+    list.forEach((element) => {
+      if (element.type === TurboType.SEQUENCE_FLOW) {
+        const edge = convertFlowElementToEdge(element);
+        lfData.edges.push(edge);
+      } else {
+        const node = convertFlowElementToNode(element);
+        lfData.nodes.push(node);
+      }
+    });
+  return lfData;
+}

BIN
src/components/FlowChart/src/assets/background/bpmn-end.png


BIN
src/components/FlowChart/src/assets/background/bpmn-exclusiveGateway.png


BIN
src/components/FlowChart/src/assets/background/bpmn-start.png


BIN
src/components/FlowChart/src/assets/background/bpmn-user.png


BIN
src/components/FlowChart/src/assets/background/click.png


BIN
src/components/FlowChart/src/assets/background/download.png


BIN
src/components/FlowChart/src/assets/background/end.png


BIN
src/components/FlowChart/src/assets/background/push.png


BIN
src/components/FlowChart/src/assets/background/start.png


BIN
src/components/FlowChart/src/assets/background/time.png


BIN
src/components/FlowChart/src/assets/background/user.png


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 5 - 0
src/components/FlowChart/src/assets/iconfont/iconfont.css


BIN
src/components/FlowChart/src/assets/iconfont/iconfont.eot


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 8 - 0
src/components/FlowChart/src/assets/iconfont/iconfont.js


+ 58 - 0
src/components/FlowChart/src/assets/iconfont/iconfont.json

@@ -0,0 +1,58 @@
+{
+  "id": "2491438",
+  "name": "liu'c'tu",
+  "font_family": "iconfont",
+  "css_prefix_text": "icon-",
+  "description": "",
+  "glyphs": [
+    {
+      "icon_id": "755619",
+      "name": "自适应图标",
+      "font_class": "full-screen-hs",
+      "unicode": "e656",
+      "unicode_decimal": 58966
+    },
+    {
+      "icon_id": "14445801",
+      "name": "查看",
+      "font_class": "watch-hs",
+      "unicode": "e766",
+      "unicode_decimal": 59238
+    },
+    {
+      "icon_id": "9712640",
+      "name": "下载",
+      "font_class": "download-hs",
+      "unicode": "e6af",
+      "unicode_decimal": 59055
+    },
+    {
+      "icon_id": "1029099",
+      "name": "放大",
+      "font_class": "enlarge-hs",
+      "unicode": "e765",
+      "unicode_decimal": 59237
+    },
+    {
+      "icon_id": "20017362",
+      "name": "上一步",
+      "font_class": "previous-hs",
+      "unicode": "e84c",
+      "unicode_decimal": 59468
+    },
+    {
+      "icon_id": "1010015",
+      "name": "缩小",
+      "font_class": "zoom-out-hs",
+      "unicode": "e744",
+      "unicode_decimal": 59204
+    },
+    {
+      "icon_id": "20017363",
+      "name": "下一步",
+      "font_class": "next-step-hs",
+      "unicode": "e84b",
+      "unicode_decimal": 59467
+    }
+  ]
+}

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 25 - 0
src/components/FlowChart/src/assets/iconfont/iconfont.svg


BIN
src/components/FlowChart/src/assets/iconfont/iconfont.ttf


BIN
src/components/FlowChart/src/assets/iconfont/iconfont.woff


BIN
src/components/FlowChart/src/assets/iconfont/iconfont.woff2


+ 55 - 0
src/components/FlowChart/src/config.ts

@@ -0,0 +1,55 @@
+export const nodeList = [
+  {
+    text: '开始',
+    type: 'start',
+    class: 'node-start',
+  },
+  {
+    text: '矩形',
+    type: 'rect',
+    class: 'node-rect',
+  },
+  {
+    type: 'user',
+    text: '用户',
+    class: 'node-user',
+  },
+  {
+    type: 'push',
+    text: '推送',
+    class: 'node-push',
+  },
+  {
+    type: 'download',
+    text: '位置',
+    class: 'node-download',
+  },
+  {
+    type: 'end',
+    text: '结束',
+    class: 'node-end',
+  },
+];
+
+export const BpmnNode = [
+  {
+    type: 'bpmn:startEvent',
+    text: '开始',
+    class: 'bpmn-start',
+  },
+  {
+    type: 'bpmn:endEvent',
+    text: '结束',
+    class: 'bpmn-end',
+  },
+  {
+    type: 'bpmn:exclusiveGateway',
+    text: '网关',
+    class: 'bpmn-exclusiveGateway',
+  },
+  {
+    type: 'bpmn:userTask',
+    text: '用户',
+    class: 'bpmn-user',
+  },
+];

+ 1 - 0
src/locales/lang/en/routes/demo/comp.ts

@@ -35,4 +35,5 @@ export default {
 
   time: 'Relative Time',
   cropperImage: 'Cropper Image',
+  flowChart: 'Flow Chart',
 };

+ 1 - 0
src/locales/lang/zh_CN/routes/demo/comp.ts

@@ -34,4 +34,5 @@ export default {
 
   time: '相对时间',
   cropperImage: '图片裁剪',
+  flowChart: '流程图',
 };

+ 7 - 0
src/router/menus/modules/demo/comp.ts

@@ -124,6 +124,13 @@ const menu: MenuModule = {
         },
       },
       {
+        path: 'flowChart',
+        name: t('routes.demo.comp.flowChart'),
+        tag: {
+          content: 'new',
+        },
+      },
+      {
         path: 'countTo',
         name: t('routes.demo.comp.countTo'),
       },

+ 8 - 0
src/router/routes/modules/demo/comp.ts

@@ -241,6 +241,14 @@ const comp: AppRouteModule = {
       },
     },
     {
+      path: 'flowChart',
+      name: 'flowChartDemo',
+      component: () => import('/@/views/demo/comp/flow-chart/index.vue'),
+      meta: {
+        title: t('routes.demo.comp.flowChart'),
+      },
+    },
+    {
       path: 'timestamp',
       name: 'TimeDemo',
       component: () => import('/@/views/demo/comp/time/index.vue'),

+ 240 - 0
src/views/demo/comp/flow-chart/dataTurbo.json

@@ -0,0 +1,240 @@
+{
+  "flowElementList": [
+    {
+      "incoming": [],
+      "outgoing": ["Flow_33inf2k"],
+      "dockers": [],
+      "type": 2,
+      "properties": {
+        "a": "efrwe",
+        "b": "wewe",
+        "name": "开始",
+        "x": 280,
+        "y": 200,
+        "text": {
+          "x": 280,
+          "y": 200,
+          "value": "开始"
+        },
+        "logicFlowType": "bpmn:startEvent"
+      },
+      "key": "Event_1d42u4p"
+    },
+    {
+      "incoming": ["Flow_379e0o9"],
+      "outgoing": [],
+      "dockers": [],
+      "type": 3,
+      "properties": {
+        "a": "efrwe",
+        "b": "wewe",
+        "name": "结束",
+        "x": 920,
+        "y": 200,
+        "text": {
+          "x": 920,
+          "y": 200,
+          "value": "结束"
+        },
+        "logicFlowType": "bpmn:endEvent"
+      },
+      "key": "Event_08p8i6q"
+    },
+    {
+      "incoming": ["Flow_0pfouf0"],
+      "outgoing": ["Flow_3918lhh"],
+      "dockers": [],
+      "type": 6,
+      "properties": {
+        "a": "efrwe",
+        "b": "wewe",
+        "name": "网关",
+        "x": 580,
+        "y": 200,
+        "text": {
+          "x": 580,
+          "y": 200,
+          "value": "网关"
+        },
+        "logicFlowType": "bpmn:exclusiveGateway"
+      },
+      "key": "Gateway_1fngqgj"
+    },
+    {
+      "incoming": ["Flow_33inf2k"],
+      "outgoing": ["Flow_0pfouf0"],
+      "dockers": [],
+      "type": 4,
+      "properties": {
+        "a": "efrwe",
+        "b": "wewe",
+        "name": "用户",
+        "x": 420,
+        "y": 200,
+        "text": {
+          "x": 420,
+          "y": 200,
+          "value": "用户"
+        },
+        "logicFlowType": "bpmn:userTask"
+      },
+      "key": "Activity_2mgtaia"
+    },
+    {
+      "incoming": ["Flow_3918lhh"],
+      "outgoing": ["Flow_379e0o9"],
+      "dockers": [],
+      "type": 5,
+      "properties": {
+        "a": "efrwe",
+        "b": "wewe",
+        "name": "服务",
+        "x": 760,
+        "y": 200,
+        "text": {
+          "x": 760,
+          "y": 200,
+          "value": "服务"
+        },
+        "logicFlowType": "bpmn:serviceTask"
+      },
+      "key": "Activity_1sp8qc8"
+    },
+    {
+      "incoming": ["Event_1d42u4p"],
+      "outgoing": ["Activity_2mgtaia"],
+      "type": 1,
+      "dockers": [],
+      "properties": {
+        "name": "边",
+        "text": {
+          "x": 331,
+          "y": 200,
+          "value": "边"
+        },
+        "startPoint": {
+          "x": 298,
+          "y": 200
+        },
+        "endPoint": {
+          "x": 370,
+          "y": 200
+        },
+        "pointsList": [
+          {
+            "x": 298,
+            "y": 200
+          },
+          {
+            "x": 370,
+            "y": 200
+          }
+        ],
+        "logicFlowType": "bpmn:sequenceFlow"
+      },
+      "key": "Flow_33inf2k"
+    },
+    {
+      "incoming": ["Activity_2mgtaia"],
+      "outgoing": ["Gateway_1fngqgj"],
+      "type": 1,
+      "dockers": [],
+      "properties": {
+        "name": "边2",
+        "text": {
+          "x": 507,
+          "y": 200,
+          "value": "边2"
+        },
+        "startPoint": {
+          "x": 470,
+          "y": 200
+        },
+        "endPoint": {
+          "x": 555,
+          "y": 200
+        },
+        "pointsList": [
+          {
+            "x": 470,
+            "y": 200
+          },
+          {
+            "x": 555,
+            "y": 200
+          }
+        ],
+        "logicFlowType": "bpmn:sequenceFlow"
+      },
+      "key": "Flow_0pfouf0"
+    },
+    {
+      "incoming": ["Gateway_1fngqgj"],
+      "outgoing": ["Activity_1sp8qc8"],
+      "type": 1,
+      "dockers": [],
+      "properties": {
+        "name": "边3",
+        "text": {
+          "x": 664,
+          "y": 200,
+          "value": "边3"
+        },
+        "startPoint": {
+          "x": 605,
+          "y": 200
+        },
+        "endPoint": {
+          "x": 710,
+          "y": 200
+        },
+        "pointsList": [
+          {
+            "x": 605,
+            "y": 200
+          },
+          {
+            "x": 710,
+            "y": 200
+          }
+        ],
+        "logicFlowType": "bpmn:sequenceFlow"
+      },
+      "key": "Flow_3918lhh"
+    },
+    {
+      "incoming": ["Activity_1sp8qc8"],
+      "outgoing": ["Event_08p8i6q"],
+      "type": 1,
+      "dockers": [],
+      "properties": {
+        "name": "边4",
+        "text": {
+          "x": 871,
+          "y": 200,
+          "value": "边4"
+        },
+        "startPoint": {
+          "x": 810,
+          "y": 200
+        },
+        "endPoint": {
+          "x": 902,
+          "y": 200
+        },
+        "pointsList": [
+          {
+            "x": 810,
+            "y": 200
+          },
+          {
+            "x": 902,
+            "y": 200
+          }
+        ],
+        "logicFlowType": "bpmn:sequenceFlow"
+      },
+      "key": "Flow_379e0o9"
+    }
+  ]
+}

+ 133 - 0
src/views/demo/comp/flow-chart/index.vue

@@ -0,0 +1,133 @@
+<template>
+  <div class="logic-flow-view">
+    <!-- 辅助工具栏 -->
+    <Control class="demo-control" v-if="lf" :lf="lf" :catTurboData="false" @catData="catData" />
+    <!-- 节点面板 -->
+    <NodePanel :lf="lf" :nodeList="nodeList" />
+    <!-- 画布 -->
+    <div id="LF-Turbo"></div>
+    <!-- 数据查看面板 -->
+    <BasicModal @register="register" title="数据">
+      <DataDialog :graphData="graphData" />
+    </BasicModal>
+  </div>
+</template>
+
+<script lang="ts">
+  import { ref, unref, onMounted } from 'vue';
+  import LogicFlow from '@logicflow/core';
+  import { Snapshot, BpmnElement, Menu } from '@logicflow/extension';
+  import '@logicflow/core/dist/style/index.css';
+  import '@logicflow/extension/lib/style/index.css';
+  import { Control, NodePanel, DataDialog } from '/@/components/FlowChart';
+
+  import { toLogicflowData } from '/@/components/FlowChart/src/adpterForTurbo';
+  import { BpmnNode } from '/@/components/FlowChart/src/config';
+  import demoData from './dataTurbo.json';
+
+  import { BasicModal, useModal } from '/@/components/Modal';
+  export default {
+    components: { NodePanel, Control, DataDialog, BasicModal },
+    setup() {
+      let lf = ref(null);
+      let graphData = ref(null);
+      let config = ref({
+        grid: true,
+        background: {
+          color: '#f7f9ff',
+        },
+        keyboard: {
+          enabled: true,
+        },
+      });
+      let nodeList = BpmnNode;
+
+      const [register, { openModal }] = useModal();
+
+      function initLf() {
+        // 画布配置
+        LogicFlow.use(Snapshot);
+        // 使用bpmn插件,引入bpmn元素,这些元素可以在turbo中转换后使用
+        LogicFlow.use(BpmnElement);
+        // 启动右键菜单
+        LogicFlow.use(Menu);
+        const domLf = new LogicFlow({
+          ...unref(config),
+          container: document.querySelector('#LF-Turbo'),
+        });
+        lf.value = domLf;
+        // 设置边类型bpmn:sequenceFlow为默认类型
+        unref(lf).setDefaultEdgeType('bpmn:sequenceFlow');
+        onRender();
+      }
+
+      function onRender() {
+        // Turbo数据转换为LogicFlow内部识别的数据结构
+        const lFData = toLogicflowData(demoData);
+        lf.value.render(lFData);
+      }
+
+      function catData() {
+        graphData.value = unref(lf).getGraphData();
+        openModal();
+      }
+
+      onMounted(() => {
+        initLf();
+      });
+
+      return {
+        lf,
+        graphData,
+        config,
+        nodeList,
+        catData,
+        register,
+        openModal,
+      };
+    },
+  };
+</script>
+
+<style scoped>
+  #LF-Turbo {
+    width: 100vw;
+    height: 85%;
+    outline: none;
+  }
+
+  .logic-flow-view {
+    position: relative;
+    height: 100%;
+  }
+
+  .demo-title {
+    margin: 20px;
+    text-align: center;
+  }
+
+  .demo-control {
+    position: absolute;
+    top: 10px;
+    right: 20px;
+    z-index: 2;
+  }
+
+  .time-plus {
+    cursor: pointer;
+  }
+
+  .add-panel {
+    position: absolute;
+    z-index: 11;
+    padding: 10px 5px;
+    background-color: white;
+  }
+
+  .el-drawer__body {
+    z-index: 3;
+    height: 80%;
+    margin-top: -30px;
+    overflow: auto;
+  }
+</style>

+ 23 - 0
yarn.lock

@@ -1151,6 +1151,19 @@
   resolved "https://registry.npmjs.org/@intlify/shared/-/shared-9.0.0.tgz#d85b3b5f9033f377c5cf2202cf2459aa49948f36"
   integrity sha512-0r4v7dnY8g/Jfx2swUWy2GyfH/WvIpWvkU4OIupvxDTWiE8RhcpbOCVvqpVh/xGi0proHQ/r2Dhc0QSItUsfDQ==
 
+"@logicflow/core@^0.3.0":
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/@logicflow/core/-/core-0.3.0.tgz#910ca7865487fbe6c45a450d13627875c6965bf4"
+  integrity sha512-FPRTuj0y6Yny+YDZ+faTzA8pZyouEWX1Vr6rH91wJR0J3NOHgb7pV/TJoHSosavFuyyw87nLw9UsyUUgHKVV+A==
+
+"@logicflow/extension@^0.3.0":
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/@logicflow/extension/-/extension-0.3.0.tgz#cea4470de3a8e4b7da69b17d7507b2d8edd76b50"
+  integrity sha512-vMmYT8H53oFhOpNftCYQMbNYbTiXqQUxOOKlPcrKkZb0FsXSiEZ/MUKBF3mAarvFlzdMaB5xJjakMfy07/bdvw==
+  dependencies:
+    "@logicflow/core" "^0.3.0"
+    ids "^1.0.0"
+
 "@nodelib/fs.scandir@2.1.4":
   version "2.1.4"
   resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69"
@@ -4895,6 +4908,11 @@ icss-utils@^5.0.0:
   resolved "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
   integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
 
+ids@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/ids/-/ids-1.0.0.tgz#df67f2d37b81d7c2effc87e03d17ebff95a58c05"
+  integrity sha512-Zvtq1xUto4LttpstyOlFum8lKx+i1OmRfg+6A9drFS9iSZsDPMHG4Sof/qwNR4kCU7jBeWFPrY2ocHxiz7cCRw==
+
 ieee754@^1.1.13:
   version "1.2.1"
   resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
@@ -9284,6 +9302,11 @@ vue-i18n@9.0.0:
     "@intlify/shared" "9.0.0"
     "@vue/devtools-api" "^6.0.0-beta.5"
 
+vue-json-pretty@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/vue-json-pretty/-/vue-json-pretty-2.0.2.tgz#cb8f559af15ea3a2ee53b2742672c7791826d6a3"
+  integrity sha512-Vn7SX3XR9cfvGRNoTDNID89GmvVUMb7/fLUX3C3n0Qptga0N7hp7Zwspui1I1XN5pE+PeoVghCSYty+bi8KnjA==
+
 vue-router@^4.0.6:
   version "4.0.6"
   resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.0.6.tgz#91750db507d26642f225b0ec6064568e5fe448d6"

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác