Browse Source

feat: add flowChart components

xiaoxian521 4 years ago
parent
commit
c7f6ff6514

+ 22 - 3
package-lock.json

@@ -390,9 +390,18 @@
       }
     },
     "@logicflow/core": {
-      "version": "0.2.9",
-      "resolved": "https://registry.npm.taobao.org/@logicflow/core/download/@logicflow/core-0.2.9.tgz",
-      "integrity": "sha1-wTU8PV0P58QNm7jPq+PMbVmF5PE="
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/@logicflow/core/-/core-0.3.0.tgz",
+      "integrity": "sha512-FPRTuj0y6Yny+YDZ+faTzA8pZyouEWX1Vr6rH91wJR0J3NOHgb7pV/TJoHSosavFuyyw87nLw9UsyUUgHKVV+A=="
+    },
+    "@logicflow/extension": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/@logicflow/extension/-/extension-0.3.0.tgz",
+      "integrity": "sha512-vMmYT8H53oFhOpNftCYQMbNYbTiXqQUxOOKlPcrKkZb0FsXSiEZ/MUKBF3mAarvFlzdMaB5xJjakMfy07/bdvw==",
+      "requires": {
+        "@logicflow/core": "^0.3.0",
+        "ids": "^1.0.0"
+      }
     },
     "@nodelib/fs.scandir": {
       "version": "2.1.4",
@@ -1235,6 +1244,11 @@
       "integrity": "sha1-xr5oWKvQE9do6YNmrkfiXViHsa4=",
       "dev": true
     },
+    "ids": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/ids/-/ids-1.0.0.tgz",
+      "integrity": "sha512-Zvtq1xUto4LttpstyOlFum8lKx+i1OmRfg+6A9drFS9iSZsDPMHG4Sof/qwNR4kCU7jBeWFPrY2ocHxiz7cCRw=="
+    },
     "indexes-of": {
       "version": "1.0.1",
       "resolved": "https://registry.npm.taobao.org/indexes-of/download/indexes-of-1.0.1.tgz",
@@ -1958,6 +1972,11 @@
         "@vue/devtools-api": "^6.0.0-beta.7"
       }
     },
+    "vue-json-pretty": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/vue-json-pretty/-/vue-json-pretty-2.0.2.tgz",
+      "integrity": "sha512-Vn7SX3XR9cfvGRNoTDNID89GmvVUMb7/fLUX3C3n0Qptga0N7hp7Zwspui1I1XN5pE+PeoVghCSYty+bi8KnjA=="
+    },
     "vue-router": {
       "version": "4.0.6",
       "resolved": "https://registry.npm.taobao.org/vue-router/download/vue-router-4.0.6.tgz",

+ 3 - 1
package.json

@@ -8,7 +8,8 @@
   },
   "dependencies": {
     "@amap/amap-jsapi-loader": "^1.0.1",
-    "@logicflow/core": "^0.2.9",
+    "@logicflow/core": "^0.3.0",
+    "@logicflow/extension": "^0.3.0",
     "@vueuse/core": "^4.8.1",
     "await-to-js": "^2.1.1",
     "axios": "^0.21.1",
@@ -27,6 +28,7 @@
     "v-contextmenu": "^3.0.0-alpha.4",
     "vue": "^3.0.11",
     "vue-i18n": "^9.1.2",
+    "vue-json-pretty": "^2.0.2",
     "vue-router": "^4.0.6",
     "vue-types": "^3.0.2",
     "vuedraggable": "^4.0.1",

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


+ 115 - 0
src/components/FlowChart/src/AddPanel.vue

@@ -0,0 +1,115 @@
+<template>
+  <el-tabs tab-position="left">
+    <el-tab-pane label="添加动作">
+      <div v-for="item in nodeList" :key="item.type">
+        <el-button
+          class="add-node-btn"
+          type="primary"
+          size="mini"
+          @click="$_addNode(item)"
+        >{{item.label}}</el-button>
+      </div>
+    </el-tab-pane>
+    <el-tab-pane label="添加组">
+      <el-button class="add-node-btn" type="primary" size="mini" @click="$_addTempalte">模板</el-button>
+    </el-tab-pane>
+  </el-tabs>
+</template>
+
+<script>
+export default {
+  name: 'AddPanel',
+  props: {
+    nodeData: Object,
+    lf: Object || String
+  },
+  data() {
+    return {
+      nodeList: [
+        {
+          type: 'user',
+          label: '用户'
+        },
+        {
+          type: 'push',
+          label: '推送'
+        }
+      ]
+    }
+  },
+  methods: {
+    $_addNode(item) {
+      const { lf, nodeData } = this.$props
+      const { id, x, y } = nodeData
+      const nextNode = lf.addNode({
+        type: item.type,
+        x: x + 150,
+        y: y + 150
+      })
+      const nextId = nextNode.id
+      lf.createEdge({ sourceNodeId: id, targetNodeId: nextId })
+      this.$emit('addNodeFinish')
+    },
+    $_addTempalte() {
+      const { lf, nodeData } = this.$props
+      const { id, x, y } = nodeData
+      const timeNode = lf.addNode({
+        type: 'download',
+        x,
+        y: y + 150
+      })
+      const userNode = lf.addNode({
+        type: 'user',
+        x: x + 150,
+        y: y + 150
+      })
+      const pushNode = lf.addNode({
+        type: 'push',
+        x: x + 150,
+        y: y + 300,
+        properties: {}
+      })
+      const endNode = lf.addNode({
+        type: 'end',
+        x: x + 300,
+        y: y + 150
+      })
+      const endNode2 = lf.addNode({
+        type: 'end',
+        x: x + 300,
+        y: y + 300
+      })
+      lf.createEdge({ sourceNodeId: id, targetNodeId: timeNode.id })
+      lf.createEdge({ sourceNodeId: timeNode.id, targetNodeId: userNode.id })
+      lf.createEdge({
+        sourceNodeId: userNode.id,
+        targetNodeId: endNode.id,
+        endPoint: { x: x + 280, y: y + 150 },
+        text: {
+          value: 'Y',
+          x: x + 230,
+          y: y + 140
+        }
+      })
+      lf.createEdge({
+        sourceNodeId: userNode.id,
+        targetNodeId: pushNode.id,
+        text: {
+          value: 'N',
+          x: x + 160,
+          y: y + 230
+        }
+      })
+      lf.createEdge({ sourceNodeId: pushNode.id, targetNodeId: endNode2.id, endPoint: { x: x + 280, y: y + 300 } })
+      this.$emit('addNodeFinish')
+    }
+  }
+}
+</script>
+
+<style scoped>
+.add-node-btn {
+  margin-bottom: 10px;
+  margin-right: 20px;
+}
+</style>

+ 0 - 0
src/components/FlowChart/src/Bpmn.vue


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

@@ -0,0 +1,74 @@
+<template>
+  <div>
+    <el-button-group>
+      <el-button size="small" @click="$_zoomIn">放大</el-button>
+      <el-button size="small" @click="$_zoomOut">缩小</el-button>
+      <el-button size="small" @click="$_zoomReset">大小适应</el-button>
+      <el-button size="small" @click="$_translateRest">定位还原</el-button>
+      <el-button size="small" @click="$_reset">还原(大小&定位)</el-button>
+      <el-button size="small" @click="$_undo" :disabled="undoDisable">上一步(ctrl+z)</el-button>
+      <el-button size="small" @click="$_redo" :disabled="redoDisable">下一步(ctrl+y)</el-button>
+      <el-button size="small" @click="$_download">下载图片</el-button>
+      <el-button size="small" @click="$_catData">查看数据</el-button>
+      <el-button v-if="catTurboData" size="small" @click="$_catTurboData">查看turbo数据</el-button>
+    </el-button-group>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Control',
+  props: {
+    lf: Object || String,
+    catTurboData: Boolean
+  },
+  data() {
+    return {
+      undoDisable: true,
+      redoDisable: true,
+    }
+  },
+  mounted() {
+    this.$props.lf.on('history:change', ({ data: { undoAble, redoAble } }) => {
+      this.$data.undoDisable = !undoAble
+      this.$data.redoDisable = !redoAble
+    })
+  },
+  methods: {
+    $_zoomIn() {
+      this.$props.lf.zoom(true)
+    },
+    $_zoomOut() {
+      this.$props.lf.zoom(false)
+    },
+    $_zoomReset() {
+      this.$props.lf.resetZoom()
+    },
+    $_translateRest() {
+      this.$props.lf.resetTranslate()
+    },
+    $_reset() {
+      this.$props.lf.resetZoom()
+      this.$props.lf.resetTranslate()
+    },
+    $_undo() {
+      this.$props.lf.undo()
+    },
+    $_redo() {
+      this.$props.lf.redo()
+    },
+    $_download() {
+      this.$props.lf.getSnapshot()
+    },
+    $_catData() {
+      this.$emit('catData')
+    },
+    $_catTurboData() {
+      this.$emit('catTurboData')
+    }
+  }
+}
+</script>
+
+<style scoped>
+</style>

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

@@ -0,0 +1,22 @@
+<template>
+  <div>
+    <vue-json-pretty :path="'res'" :deep="3" :showLength="true" :data="graphData"></vue-json-pretty>
+  </div>
+</template>
+
+<script>
+import VueJsonPretty from 'vue-json-pretty'
+import 'vue-json-pretty/lib/styles.css'
+
+export default {
+  props: {
+    graphData: Object
+  },
+  components: {
+    VueJsonPretty,
+  },
+};
+</script>
+
+<style scoped>
+</style>

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

@@ -0,0 +1,124 @@
+<template>
+  <div class="node-panel">
+    <div class="node-item" v-for="item in nodeList" :key="item.text" @mousedown="$_dragNode(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>
+export default {
+  name: 'NodePanel',
+  props: {
+    lf: Object,
+    nodeList: Array,
+  },
+  data() {
+    return {
+      node: {
+        type: 'rect',
+        property: {
+          a: 'efrwe',
+          b: 'wewe'
+        }
+      },
+      properties: {
+        a: 'efrwe',
+        b: 'wewe'
+      }
+    }
+  },
+  methods: {
+    $_dragNode(item) {
+      this.$props.lf.dnd.startDrag({
+        type: item.type,
+        properties: this.$data.properties
+      })
+    }
+  }
+}
+</script>
+
+<style>
+.node-panel {
+  position: absolute;
+  top: 100px;
+  left: 50px;
+  width: 70px;
+  padding: 20px 10px;
+  background-color: white;
+  box-shadow: 0 0 10px 1px rgb(228, 224, 219);
+  border-radius: 6px;
+  text-align: center;
+  z-index: 101;
+}
+.node-item {
+  margin-bottom: 20px;
+}
+.node-item-icon {
+  height: 30px;
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: center;
+  background-size: cover;
+}
+.node-label {
+  font-size: 12px;
+  margin-top: 5px;
+  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 {
+  background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAnBJREFUOBGdVL1rU1EcPfdGBddmaZLiEhdx1MHZQXApraCzQ7GKLgoRBxMfcRELuihWKcXFRcEWF8HBf0DdDCKYRZpnl7p0svLe9Zzbd29eQhTbC8nv+9zf130AT63jvooOGS8Vf9Nt5zxba7sXQwODfkWpkbjTQfCGUd9gIp3uuPP8bZ946g56dYQvnBg+b1HB8VIQmMFrazKcKSvFW2dQTxJnJdQ77urmXWOMBCmXM2Rke4S7UAW+/8ywwFoewmBps2tu7mbTdp8VMOkIRAkKfrVawalJTtIliclFbaOBqa0M2xImHeVIfd/nKAfVq/LGnPss5Kh00VEdSzfwnBXPUpmykNss4lUI9C1ga+8PNrBD5YeqRY2Zz8PhjooIbfJXjowvQJBqkmEkVnktWhwu2SM7SMx7Cj0N9IC0oQXRo8xwAGzQms+xrB/nNSUWVveI48ayrFGyC2+E2C+aWrZHXvOuz+CiV6iycWe1Rd1Q6+QUG07nb5SbPrL4426d+9E1axKjY3AoRrlEeSQo2Eu0T6BWAAr6COhTcWjRaYfKG5csnvytvUr/WY4rrPMB53Uo7jZRjXaG6/CFfNMaXEu75nG47X+oepU7PKJvvzGDY1YLSKHJrK7vFUwXKkaxwhCW3u+sDFMVrIju54RYYbFKpALZAo7sB6wcKyyrd+aBMryMT2gPyD6GsQoRFkGHr14TthZni9ck0z+Pnmee460mHXbRAypKNy3nuMdrWgVKj8YVV8E7PSzp1BZ9SJnJAsXdryw/h5ctboUVi4AFiCd+lQaYMw5z3LGTBKjLQOeUF35k89f58Vv/tGh+l+PE/wG0rgfIUbZK5AAAAABJRU5ErkJggg==)
+    center center no-repeat;
+  cursor: grab;
+}
+.bpmn-end {
+  background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAA1BJREFUOBFtVE1IVUEYPXOf+tq40Y3vPcmFIdSjIorWoRG0ERWUgnb5FwVhYQSl72oUoZAboxKNFtWiwKRN0M+jpfSzqJAQclHo001tKkjl3emc8V69igP3znzfnO/M9zcDcKT67azmjYWTwl9Vn7Vumeqzj1DVb6cleQY4oAVnIOPb+mKAGxQmKI5CWNJ2aLPatxWa3aB9K7/fB+/Z0jUF6TmMlFLQqrkECWQzOZxYGjTlOl8eeKaIY5yHnFn486xBustDjWT6dG7pmjHOJd+33t0iitTPkK6tEvjxq4h2MozQ6WFSX/LkDUGfFwfhEZj1Auz/U4pyAi5Sznd7uKzznXeVHlI/Aywmk6j7fsUsEuCGADrWARXXwjxWQsUbIupDHJI7kF5dRktg0eN81IbiZXiTESic50iwS+t1oJgL83jAiBupLDCQqwziaWSoAFSeIR3P5Xv5az00wyIn35QRYTwdSYbz8pH8fxUUAtxnFvYmEmgI0wYXUXcCCSpeEVpXlsRhBnCEATxWylL9+EKCAYhe1NGstUa6356kS9NVvt3DU2fd+Wtbm/+lSbylJqsqkSm9CRhvoJVlvKPvF1RKY/FcPn5j4UfIMLn8D4UYb54BNsilTDXKnF4CfTobA0FpoW/LSp306wkXM+XaOJhZaFkcNM82ASNAWMrhrUbRfmyeI1FvRBTpN06WKxa9BK0o2E4Pd3zfBBEwPsv9sQBnmLVbLEIZ/Xe9LYwJu/Er17W6HYVBc7vmuk0xUQ+pqxdom5Fnp55SiytXLPYoMXNM4u4SNSCFWnrVIzKG3EGyMXo6n/BQOe+bX3FClY4PwydVhthOZ9NnS+ntiLh0fxtlUJHAuGaFoVmttpVMeum0p3WEXbcll94l1wM/gZ0Ccczop77VvN2I7TlsZCsuXf1WHvWEhjO8DPtyOVg2/mvK9QqboEth+7pD6NUQC1HN/TwvydGBARi9MZSzLE4b8Ru3XhX2PBxf8E1er2A6516o0w4sIA+lwURhAON82Kwe2iDAC1Watq4XHaGQ7skLcFOtI5lDxuM2gZe6WFIotPAhbaeYlU4to5cuarF1QrcZ/lwrLaCJl66JBocYZnrNlvm2+MBCTmUymPrYZVbjdlr/BxlMjmNmNI3SAAAAAElFTkSuQmCC)
+    center center no-repeat;
+  cursor: grab;
+}
+.bpmn-user {
+  background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==)
+    center center no-repeat;
+  cursor: grab;
+}
+.bpmn-exclusiveGateway {
+  background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAAHeEJUAAAAABGdBTUEAALGPC/xhBQAAAvVJREFUOBGNVEFrE0EU/mY3bQoiFlOkaUJrQUQoWMGePLX24EH0IIoHKQiCV0G8iE1covgLiqA/QTzVm1JPogc9tIJYFaQtlhQxqYjSpunu+L7JvmUTU3AgmTfvffPNN++9WSA1DO182f6xwILzD5btfAoQmwL5KJEwiQyVbSVZ0IgRyV6PTpIJ81E5ZvqfHQR0HUOBHW4L5Et2kQ6Zf7iAOhTFAA8s0pEP7AXO1uAA52SbqGk6h/6J45LaLhO64ByfcUzM39V7ZiAdS2yCePPEIQYvTUHqM/n7dgQNfBKWPjpF4ISk8q3J4nB11qw6X8l+FsF3EhlkEMfrjIer3wJTLwS2aCNcj4DbGxXTw00JmAuO+Ni6bBxVUCvS5d9aa04+so4pHW5jLTywuXAL7jJ+D06sl82Sgl2JuVBQn498zkc2bGKxULHjCnSMadBKYDYYHAtsby1EQ5lNGrQd4Y3v4Zo0XdGEmDno46yCM9Tk+RiJmUYHS/aXHPNTcjxcbTFna000PFJHIVZ5lFRqRpJWk9/+QtlOUYJj9HG5pVFEU7zqIYDVsw2s+AJaD8wTd2umgSCCyUxgGsS1Y6TBwXQQTFuZaHcd8gAGioE90hlsY+wMcs30RduYtxanjMGal8H5dMW67dmT1JFtYUEe8LiQLRsPZ6IIc7A4J5tqco3T0pnv/4u0kyzrYUq7gASuEyI8VXKvB9Odytv6jS/PNaZBln0nioJG/AVQRZvApOdhjj3Jt8QC8Im09SafwdBdvIpztpxWxpeKCC+EsFdS8DCyuCn2munFpL7ctHKp+Xc5cMybeIyMAN33SPL3ZR9QV1XVwLyzHm6Iv0/yeUuUb7PPlZC4D4HZkeu6dpF4v9j9MreGtMbxMMRLIcjJic9yHi7WQ3yVKzZVWUr5UrViJvn1FfUlwe/KYVfYyWRLSGNu16hR01U9IacajXPei0wx/5BqgInvJN+MMNtNme7ReU9SBbgntovn0kKHpFg7UogZvaZiOue/q1SBo9ktHzQAAAAASUVORK5CYII=)
+    center center no-repeat;
+  cursor: grab;
+}
+</style>

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

@@ -0,0 +1,166 @@
+
+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/background/click.png


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


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


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


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


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


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


+ 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'
+  },
+]

+ 32 - 9
src/router/index.ts

@@ -131,25 +131,24 @@ const routes: Array<RouteRecordRaw> = [
     }
   },
   {
-    path: '/user',
-    name: 'user',
+    path: '/flowChart',
+    name: 'flowChart',
     component: Layout,
-    redirect: '/user/base',
+    redirect: '/flowChart/index',
     children: [
       {
-        path: '/user/base',
-        component: () => import(/* webpackChunkName: "user" */ '../views/user.vue'),
+        path: '/flowChart/index',
+        component: () => import(/* webpackChunkName: "user" */ '../views/flow-chart/index.vue'),
         meta: {
-          // icon: 'el-icon-user',
-          title: 'baseinfo',
+          title: 'flowChart',
           showLink: false,
           savedPosition: true
         }
       },
     ],
     meta: {
-      icon: 'el-icon-user',
-      title: 'usermanagement',
+      icon: 'el-icon-set-up',
+      title: 'flowChart',
       showLink: true,
       savedPosition: true
     }
@@ -178,6 +177,30 @@ const routes: Array<RouteRecordRaw> = [
       savedPosition: true
     }
   },
+  {
+    path: '/user',
+    name: 'user',
+    component: Layout,
+    redirect: '/user/base',
+    children: [
+      {
+        path: '/user/base',
+        component: () => import(/* webpackChunkName: "user" */ '../views/user.vue'),
+        meta: {
+          // icon: 'el-icon-user',
+          title: 'baseinfo',
+          showLink: false,
+          savedPosition: true
+        }
+      },
+    ],
+    meta: {
+      icon: 'el-icon-user',
+      title: 'usermanagement',
+      showLink: true,
+      savedPosition: true
+    }
+  },
   {
     path: '/error',
     name: 'error',

+ 12 - 4
src/views/components/seamless-scroll/index.vue

@@ -4,10 +4,18 @@
       <template #header>
         <div class="card-header">
           <span>无缝滚动示例</span>
-          <el-button class="button" type="text" @click="changeDirection('top')">向上滚动</el-button>
-          <el-button class="button" type="text" @click="changeDirection('bottom')">向下滚动</el-button>
-          <el-button class="button" type="text" @click="changeDirection('left')">向左滚动</el-button>
-          <el-button class="button" type="text" @click="changeDirection('right')">向右滚动</el-button>
+          <el-button class="button" type="text" @click="changeDirection('top')">
+            <span :style="{color: classOption.direction === 'top' ? 'red' : ''}">向上滚动</span>
+          </el-button>
+          <el-button class="button" type="text" @click="changeDirection('bottom')">
+            <span :style="{color: classOption.direction === 'bottom' ? 'red' : ''}">向下滚动</span>
+          </el-button>
+          <el-button class="button" type="text" @click="changeDirection('left')">
+            <span :style="{color: classOption.direction === 'left' ? 'red' : ''}">向左滚动</span>
+          </el-button>
+          <el-button class="button" type="text" @click="changeDirection('right')">
+            <span :style="{color: classOption.direction === 'right' ? 'red' : ''}">向右滚动</span>
+          </el-button>
         </div>
       </template>
       <SeamlessScroll ref="scroll" :data="listData" :class-option="classOption" class="warp">

+ 272 - 0
src/views/flow-chart/dataTurbo.json

@@ -0,0 +1,272 @@
+{
+  "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"
+    }
+  ]
+}

+ 132 - 0
src/views/flow-chart/index.vue

@@ -0,0 +1,132 @@
+<template>
+  <div class="logic-flow-view">
+    <!-- 辅助工具栏 -->
+    <Control
+      class="demo-control"
+      v-if="lf"
+      :lf="lf"
+      :catTurboData="false"
+      @catData="$_catData"
+      @catTurboData="$_catTurboData"
+    ></Control>
+    <!-- 节点面板 -->
+    <NodePanel :lf="lf" :nodeList="nodeList"></NodePanel>
+    <!-- 画布 -->
+    <div id="LF-Turbo"></div>
+    <!-- 数据查看面板 -->
+    <el-dialog title="数据" v-model="dataVisible" width="50%">
+      <DataDialog :graphData="graphData"></DataDialog>
+    </el-dialog>
+  </div>
+</template>
+<script>
+import LogicFlow from '@logicflow/core'
+import { Snapshot, BpmnElement } from '@logicflow/extension'
+import '@logicflow/core/dist/style/index.css'
+import '@logicflow/extension/lib/style/index.css'
+import NodePanel from '/@/components/FlowChart/src/NodePanel.vue'
+import Control from '/@/components/FlowChart/src/Control.vue'
+import DataDialog from '/@/components/FlowChart/src/DataDialog.vue'
+import { toTurboData, toLogicflowData } from '/@/components/FlowChart/src/adpterForTurbo.ts'
+import { BpmnNode } from '/@/components/FlowChart/src/config.ts'
+import demoData from './dataTurbo.json'
+
+export default {
+  name: 'LF',
+  components: { NodePanel, Control, DataDialog },
+  data() {
+    return {
+      lf: null,
+      dialogVisible: false,
+      graphData: null,
+      dataVisible: false,
+      config: {
+        grid: true,
+        background: {
+          color: '#f7f9ff'
+        },
+        keyboard: {
+          enabled: true
+        },
+      },
+      nodeList: BpmnNode,
+    }
+  },
+  mounted() {
+    this.$_initLf()
+  },
+  methods: {
+    $_initLf() {
+      // 画布配置
+      LogicFlow.use(Snapshot)
+      // 使用bpmn插件,引入bpmn元素,这些元素可以在turbo中转换后使用
+      LogicFlow.use(BpmnElement)
+      const lf = new LogicFlow({ ...this.config, container: document.querySelector('#LF-Turbo') })
+      this.lf = lf
+      // 设置边类型bpmn:sequenceFlow为默认类型
+      lf.setDefaultEdgeType('bpmn:sequenceFlow')
+      this.$_render()
+    },
+    $_render() {
+      // Turbo数据转换为LogicFlow内部识别的数据结构
+      const lFData = toLogicflowData(demoData)
+      this.lf.render(lFData)
+    },
+    closeDialog() {
+      this.$data.dialogVisible = false
+    },
+    $_catData() {
+      this.$data.graphData = this.$data.lf.getGraphData()
+      this.$data.dataVisible = true
+    },
+    $_catTurboData() {
+      debugger
+      const graphData = this.$data.lf.getGraphData()
+      // 数据转化为Turbo识别的数据结构
+      this.$data.graphData = toTurboData(graphData)
+      this.$data.dataVisible = true
+    },
+    goto() {
+      this.$router.push('/')
+    }
+  }
+}
+</script>
+<style>
+.logic-flow-view {
+  height: 100vh;
+  position: relative;
+}
+.demo-title {
+  text-align: center;
+  margin: 20px;
+}
+.demo-control {
+  position: absolute;
+  top: 50px;
+  right: 100px;
+  z-index: 2;
+}
+#LF-Turbo {
+  width: 100vw;
+  height: 85%;
+  outline: none;
+  margin: 10px 0 0 10px;
+}
+.time-plus {
+  cursor: pointer;
+}
+.add-panel {
+  position: absolute;
+  z-index: 11;
+  background-color: white;
+  padding: 10px 5px;
+}
+.el-drawer__body {
+  height: 80%;
+  overflow: auto;
+  margin-top: -30px;
+  z-index: 3;
+}
+</style>
+