Просмотр исходного кода

add 增加logicflow流程图预览

gssong 10 месяцев назад
Родитель
Сommit
3019701856

+ 1 - 0
package.json

@@ -22,6 +22,7 @@
   "dependencies": {
     "@element-plus/icons-vue": "2.3.1",
     "@highlightjs/vue-plugin": "2.1.0",
+    "@logicflow/core": "^2.0.13",
     "@vueup/vue-quill": "1.2.0",
     "@vueuse/core": "13.1.0",
     "animate.css": "4.1.1",

+ 4 - 155
src/components/Process/approvalRecord.vue

@@ -3,21 +3,7 @@
     <el-dialog v-model="visible" draggable title="审批记录" :width="props.width" :height="props.height" :close-on-click-modal="false">
       <el-tabs v-model="tabActiveName" class="demo-tabs">
         <el-tab-pane v-loading="loading" label="流程图" name="image" style="height: 68vh">
-          <div
-            ref="imageWrapperRef"
-            class="image-wrapper"
-            @wheel="handleMouseWheel"
-            @mousedown="handleMouseDown"
-            @mousemove="handleMouseMove"
-            @mouseup="handleMouseUp"
-            @mouseleave="handleMouseLeave"
-            @dblclick="resetTransform"
-            :style="transformStyle"
-          >
-            <el-card class="box-card">
-              <el-image :src="imgUrl" class="scalable-image" />
-            </el-card>
-          </div>
+          <flowChart :defJson="defJson" v-if="defJson != ''" />
         </el-tab-pane>
         <el-tab-pane v-loading="loading" label="审批信息" name="info">
           <div>
@@ -76,7 +62,7 @@
 import { flowImage } from '@/api/workflow/instance';
 import { propTypes } from '@/utils/propTypes';
 import { listByIds } from '@/api/system/oss';
-
+import FlowChart from '@/components/Process/flowChart.vue';
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { wf_task_status } = toRefs<any>(proxy?.useDict('wf_task_status'));
 const props = defineProps({
@@ -88,6 +74,7 @@ const visible = ref(false);
 const historyList = ref<Array<any>>([]);
 const tabActiveName = ref('image');
 const imgUrl = ref('');
+const defJson = ref<any>('');
 
 //初始化查询审批记录
 const init = async (businessId: string | number) => {
@@ -99,6 +86,7 @@ const init = async (businessId: string | number) => {
     if (resp.data) {
       historyList.value = resp.data.list;
       imgUrl.value = 'data:image/gif;base64,' + resp.data.image;
+      defJson.value = resp.data.defChart.defJson;
       if (historyList.value.length > 0) {
         historyList.value.forEach((item) => {
           if (item.ext) {
@@ -124,109 +112,6 @@ const handleDownload = (ossId: string) => {
   proxy?.$download.oss(ossId);
 };
 
-const imageWrapperRef = ref<HTMLElement | null>(null);
-const scale = ref(1); // 初始缩放比例
-const maxScale = 3; // 最大缩放比例
-const minScale = 0.5; // 最小缩放比例
-
-let isDragging = false;
-let startX = 0;
-let startY = 0;
-let currentTranslateX = 0;
-let currentTranslateY = 0;
-
-const handleMouseWheel = (event: WheelEvent) => {
-  event.preventDefault();
-  let newScale = scale.value - event.deltaY / 1000;
-  newScale = Math.max(minScale, Math.min(newScale, maxScale));
-  if (newScale !== scale.value) {
-    scale.value = newScale;
-    resetDragPosition(); // 重置拖拽位置,使图片居中
-  }
-};
-
-const handleMouseDown = (event: MouseEvent) => {
-  if (scale.value > 1) {
-    event.preventDefault(); // 阻止默认行为,防止拖拽
-    isDragging = true;
-    startX = event.clientX;
-    startY = event.clientY;
-  }
-};
-
-const handleMouseMove = (event: MouseEvent) => {
-  if (!isDragging || !imageWrapperRef.value) return;
-
-  const deltaX = event.clientX - startX;
-  const deltaY = event.clientY - startY;
-  startX = event.clientX;
-  startY = event.clientY;
-
-  currentTranslateX += deltaX;
-  currentTranslateY += deltaY;
-
-  // 边界检测,防止图片被拖出容器
-  const bounds = getBounds();
-  if (currentTranslateX > bounds.maxTranslateX) {
-    currentTranslateX = bounds.maxTranslateX;
-  } else if (currentTranslateX < bounds.minTranslateX) {
-    currentTranslateX = bounds.minTranslateX;
-  }
-
-  if (currentTranslateY > bounds.maxTranslateY) {
-    currentTranslateY = bounds.maxTranslateY;
-  } else if (currentTranslateY < bounds.minTranslateY) {
-    currentTranslateY = bounds.minTranslateY;
-  }
-
-  applyTransform();
-};
-
-const handleMouseUp = () => {
-  isDragging = false;
-};
-
-const handleMouseLeave = () => {
-  isDragging = false;
-};
-
-const resetTransform = () => {
-  scale.value = 1;
-  currentTranslateX = 0;
-  currentTranslateY = 0;
-  applyTransform();
-};
-
-const resetDragPosition = () => {
-  currentTranslateX = 0;
-  currentTranslateY = 0;
-  applyTransform();
-};
-
-const applyTransform = () => {
-  if (imageWrapperRef.value) {
-    imageWrapperRef.value.style.transform = `translate(${currentTranslateX}px, ${currentTranslateY}px) scale(${scale.value})`;
-  }
-};
-
-const getBounds = () => {
-  if (!imageWrapperRef.value) return { minTranslateX: 0, maxTranslateX: 0, minTranslateY: 0, maxTranslateY: 0 };
-
-  const imgRect = imageWrapperRef.value.getBoundingClientRect();
-  const containerRect = imageWrapperRef.value.parentElement?.getBoundingClientRect() ?? imgRect;
-
-  const minTranslateX = (containerRect.width - imgRect.width * scale.value) / 2;
-  const maxTranslateX = -(containerRect.width - imgRect.width * scale.value) / 2;
-  const minTranslateY = (containerRect.height - imgRect.height * scale.value) / 2;
-  const maxTranslateY = -(containerRect.height - imgRect.height * scale.value) / 2;
-
-  return { minTranslateX, maxTranslateX, minTranslateY, maxTranslateY };
-};
-
-const transformStyle = computed(() => ({
-  transition: isDragging ? 'none' : 'transform 0.2s ease'
-}));
-
 /**
  * 对外暴露子组件方法
  */
@@ -235,46 +120,10 @@ defineExpose({
 });
 </script>
 <style lang="scss" scoped>
-.triangle {
-  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
-  border-radius: 6px;
-}
-
-.triangle::after {
-  content: ' ';
-  position: absolute;
-  top: 8em;
-  right: 215px;
-  border: 15px solid;
-  border-color: transparent #fff transparent transparent;
-}
-
 .container {
   :deep(.el-dialog .el-dialog__body) {
     max-height: calc(100vh - 170px) !important;
     min-height: calc(100vh - 170px) !important;
   }
 }
-
-.image-wrapper {
-  width: 100%;
-  overflow: hidden;
-  position: relative;
-  margin: 0 auto;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  user-select: none; /* 禁用文本选择 */
-  cursor: grab; /* 设置初始鼠标指针为可拖动 */
-}
-
-.image-wrapper:active {
-  cursor: grabbing; /* 当正在拖动时改变鼠标指针 */
-}
-
-.scalable-image {
-  object-fit: contain;
-  width: 100%;
-  padding: 15px;
-}
 </style>

+ 84 - 0
src/components/Process/flowChart.vue

@@ -0,0 +1,84 @@
+<template>
+  <div>
+    <el-header style="border-bottom: 1px solid rgb(218 218 218); height: auto">
+      <div style="display: flex; padding: 10px 0px; justify-content: space-between">
+        <div>
+          <el-tooltip effect="dark" content="自适应屏幕" placement="bottom">
+            <el-button size="small" icon="Rank" @click="zoomViewport(1)">自适应屏幕</el-button>
+          </el-tooltip>
+          <el-tooltip effect="dark" content="放大" placement="bottom">
+            <el-button size="small" icon="ZoomIn" @click="zoomViewport(true)">放大</el-button>
+          </el-tooltip>
+          <el-tooltip effect="dark" content="缩小" placement="bottom">
+            <el-button size="small" icon="ZoomOut" @click="zoomViewport(false)">缩小</el-button>
+          </el-tooltip>
+        </div>
+        <div>
+          <el-button size="small" style="border: 1px solid #000">未完成</el-button>
+          <el-button size="small" style="background-color: #fff8dc; border: 1px solid #ffcd17">进行中</el-button>
+          <el-button size="small" style="background-color: #f0ffd9; border: 1px solid #9dff00">已完成</el-button>
+        </div>
+      </div>
+    </el-header>
+    <div class="container" ref="container"></div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import LogicFlow from '@logicflow/core';
+import '@logicflow/core/lib/style/index.css';
+import Start from './js/start.js';
+import Between from './js/between.js';
+import Serial from './js/serial.js';
+import Parallel from './js/parallel.js';
+import End from './js/end.js';
+import Skip from './js/skip.js';
+import { json2LogicFlowJson } from './js/tool.js';
+
+// Props 定义方式变化
+const props = defineProps({
+  defJson: {
+    type: Object,
+    default: () => ({})
+  }
+});
+
+const container = ref(null);
+const lf = ref(null);
+const register = () => {
+  lf.value.register(Start);
+  lf.value.register(Between);
+  lf.value.register(Serial);
+  lf.value.register(Parallel);
+  lf.value.register(End);
+  lf.value.register(Skip);
+};
+const zoomViewport = async (zoom) => {
+  lf.value.zoom(zoom);
+  // 将内容平移至画布中心
+  lf.value.translateCenter();
+};
+
+onMounted(async () => {
+  if (props.defJson) {
+    const data = json2LogicFlowJson(props.defJson);
+    lf.value = new LogicFlow({
+      container: container.value,
+      grid: false,
+      isSilentMode: true,
+      textEdit: false
+    });
+    register();
+    lf.value.render(data);
+    lf.value.translateCenter();
+  }
+});
+</script>
+
+<style scoped>
+/* 样式部分保持不变 */
+.container {
+  width: 100%;
+  height: 500px;
+}
+</style>

+ 154 - 0
src/components/Process/flowChartImg.vue

@@ -0,0 +1,154 @@
+<template>
+  <div
+    ref="imageWrapperRef"
+    class="image-wrapper"
+    @wheel="handleMouseWheel"
+    @mousedown="handleMouseDown"
+    @mousemove="handleMouseMove"
+    @mouseup="handleMouseUp"
+    @mouseleave="handleMouseLeave"
+    @dblclick="resetTransform"
+    :style="transformStyle"
+  >
+    <el-card class="box-card">
+      <el-image :src="props.imgUrl" class="scalable-image" />
+    </el-card>
+  </div>
+</template>
+
+<script setup lang="ts">
+// Props 定义方式变化
+const props = defineProps({
+  imgUrl: {
+    type: String,
+    default: () => ''
+  }
+});
+
+const imageWrapperRef = ref<HTMLElement | null>(null);
+const scale = ref(1); // 初始缩放比例
+const maxScale = 3; // 最大缩放比例
+const minScale = 0.5; // 最小缩放比例
+
+let isDragging = false;
+let startX = 0;
+let startY = 0;
+let currentTranslateX = 0;
+let currentTranslateY = 0;
+
+const handleMouseWheel = (event: WheelEvent) => {
+  event.preventDefault();
+  let newScale = scale.value - event.deltaY / 1000;
+  newScale = Math.max(minScale, Math.min(newScale, maxScale));
+  if (newScale !== scale.value) {
+    scale.value = newScale;
+    resetDragPosition(); // 重置拖拽位置,使图片居中
+  }
+};
+
+const handleMouseDown = (event: MouseEvent) => {
+  if (scale.value > 1) {
+    event.preventDefault(); // 阻止默认行为,防止拖拽
+    isDragging = true;
+    startX = event.clientX;
+    startY = event.clientY;
+  }
+};
+
+const handleMouseMove = (event: MouseEvent) => {
+  if (!isDragging || !imageWrapperRef.value) return;
+
+  const deltaX = event.clientX - startX;
+  const deltaY = event.clientY - startY;
+  startX = event.clientX;
+  startY = event.clientY;
+
+  currentTranslateX += deltaX;
+  currentTranslateY += deltaY;
+
+  // 边界检测,防止图片被拖出容器
+  const bounds = getBounds();
+  if (currentTranslateX > bounds.maxTranslateX) {
+    currentTranslateX = bounds.maxTranslateX;
+  } else if (currentTranslateX < bounds.minTranslateX) {
+    currentTranslateX = bounds.minTranslateX;
+  }
+
+  if (currentTranslateY > bounds.maxTranslateY) {
+    currentTranslateY = bounds.maxTranslateY;
+  } else if (currentTranslateY < bounds.minTranslateY) {
+    currentTranslateY = bounds.minTranslateY;
+  }
+
+  applyTransform();
+};
+
+const handleMouseUp = () => {
+  isDragging = false;
+};
+
+const handleMouseLeave = () => {
+  isDragging = false;
+};
+
+const resetTransform = () => {
+  scale.value = 1;
+  currentTranslateX = 0;
+  currentTranslateY = 0;
+  applyTransform();
+};
+
+const resetDragPosition = () => {
+  currentTranslateX = 0;
+  currentTranslateY = 0;
+  applyTransform();
+};
+
+const applyTransform = () => {
+  if (imageWrapperRef.value) {
+    imageWrapperRef.value.style.transform = `translate(${currentTranslateX}px, ${currentTranslateY}px) scale(${scale.value})`;
+  }
+};
+
+const getBounds = () => {
+  if (!imageWrapperRef.value) return { minTranslateX: 0, maxTranslateX: 0, minTranslateY: 0, maxTranslateY: 0 };
+
+  const imgRect = imageWrapperRef.value.getBoundingClientRect();
+  const containerRect = imageWrapperRef.value.parentElement?.getBoundingClientRect() ?? imgRect;
+
+  const minTranslateX = (containerRect.width - imgRect.width * scale.value) / 2;
+  const maxTranslateX = -(containerRect.width - imgRect.width * scale.value) / 2;
+  const minTranslateY = (containerRect.height - imgRect.height * scale.value) / 2;
+  const maxTranslateY = -(containerRect.height - imgRect.height * scale.value) / 2;
+
+  return { minTranslateX, maxTranslateX, minTranslateY, maxTranslateY };
+};
+
+const transformStyle = computed(() => ({
+  transition: isDragging ? 'none' : 'transform 0.2s ease'
+}));
+</script>
+
+<style scoped>
+.image-wrapper {
+  width: 100%;
+  overflow: hidden;
+  position: relative;
+  margin: 0 auto;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  user-select: none; /* 禁用文本选择 */
+  cursor: grab; /* 设置初始鼠标指针为可拖动 */
+}
+
+.image-wrapper:active {
+  cursor: grabbing; /* 当正在拖动时改变鼠标指针 */
+}
+
+.scalable-image {
+  object-fit: contain;
+  width: 100%;
+  padding: 15px;
+}
+</style>

+ 21 - 0
src/components/Process/js/between.js

@@ -0,0 +1,21 @@
+import { RectNode, RectNodeModel } from '@logicflow/core';
+
+class BetweenModel extends RectNodeModel {
+  initNodeData(data) {
+    super.initNodeData(data);
+    this.width = 100;
+    this.height = 80;
+    this.radius = 5;
+  }
+  getNodeStyle() {
+    return super.getNodeStyle();
+  }
+}
+
+class BetweenView extends RectNode {}
+
+export default {
+  type: 'between',
+  model: BetweenModel,
+  view: BetweenView
+};

+ 16 - 0
src/components/Process/js/end.js

@@ -0,0 +1,16 @@
+import { CircleNode, CircleNodeModel } from '@logicflow/core';
+
+class endModel extends CircleNodeModel {
+  initNodeData(data) {
+    super.initNodeData(data);
+    this.r = 20;
+  }
+}
+
+class endView extends CircleNode {}
+
+export default {
+  type: 'end',
+  model: endModel,
+  view: endView
+};

+ 55 - 0
src/components/Process/js/parallel.js

@@ -0,0 +1,55 @@
+import { h, PolygonNode, PolygonNodeModel } from '@logicflow/core';
+
+class ParallelModel extends PolygonNodeModel {
+  static extendKey = 'ParallelModel';
+  constructor(data, graphModel) {
+    if (!data.text) {
+      data.text = '';
+    }
+    if (data.text && typeof data.text === 'string') {
+      data.text = {
+        value: data.text,
+        x: data.x,
+        y: data.y + 40
+      };
+    }
+    super(data, graphModel);
+    this.points = [
+      [25, 0],
+      [50, 25],
+      [25, 50],
+      [0, 25]
+    ];
+  }
+}
+
+class ParallelView extends PolygonNode {
+  static extendKey = 'ParallelNode';
+  getShape() {
+    const { model } = this.props;
+    const { x, y, width, height, points } = model;
+    const style = model.getNodeStyle();
+    return h(
+      'g',
+      {
+        transform: `matrix(1 0 0 1 ${x - width / 2} ${y - height / 2})`
+      },
+      h('polygon', {
+        ...style,
+        x,
+        y,
+        points
+      }),
+      h('path', {
+        d: 'm 23,10 0,12.5 -12.5,0 0,5 12.5,0 0,12.5 5,0 0,-12.5 12.5,0 0,-5 -12.5,0 0,-12.5 -5,0 z',
+        ...style
+      })
+    );
+  }
+}
+
+export default {
+  type: 'parallel',
+  view: ParallelView,
+  model: ParallelModel
+};

+ 55 - 0
src/components/Process/js/serial.js

@@ -0,0 +1,55 @@
+import { h, PolygonNode, PolygonNodeModel } from '@logicflow/core';
+
+class SerialModel extends PolygonNodeModel {
+  static extendKey = 'SerialModel';
+  constructor(data, graphModel) {
+    if (!data.text) {
+      data.text = '';
+    }
+    if (data.text && typeof data.text === 'string') {
+      data.text = {
+        value: data.text,
+        x: data.x,
+        y: data.y + 40
+      };
+    }
+    super(data, graphModel);
+    this.points = [
+      [25, 0],
+      [50, 25],
+      [25, 50],
+      [0, 25]
+    ];
+  }
+}
+
+class SerialView extends PolygonNode {
+  static extendKey = 'SerialNode';
+  getShape() {
+    const { model } = this.props;
+    const { x, y, width, height, points } = model;
+    const style = model.getNodeStyle();
+    return h(
+      'g',
+      {
+        transform: `matrix(1 0 0 1 ${x - width / 2} ${y - height / 2})`
+      },
+      h('polygon', {
+        ...style,
+        x,
+        y,
+        points
+      }),
+      h('path', {
+        d: 'm 16,15 7.42857142857143,9.714285714285715 -7.42857142857143,9.714285714285715 3.428571428571429,0 5.714285714285715,-7.464228571428572 5.714285714285715,7.464228571428572 3.428571428571429,0 -7.42857142857143,-9.714285714285715 7.42857142857143,-9.714285714285715 -3.428571428571429,0 -5.714285714285715,7.464228571428572 -5.714285714285715,-7.464228571428572 -3.428571428571429,0 z',
+        ...style
+      })
+    );
+  }
+}
+
+export default {
+  type: 'serial',
+  view: SerialView,
+  model: SerialModel
+};

+ 32 - 0
src/components/Process/js/skip.js

@@ -0,0 +1,32 @@
+import { PolylineEdge, PolylineEdgeModel } from '@logicflow/core';
+
+class SkipModel extends PolylineEdgeModel {
+  setAttributes() {
+    this.offset = 20;
+  }
+
+  getEdgeStyle() {
+    const style = super.getEdgeStyle();
+    const { properties } = this;
+    if (properties.isActived) {
+      style.strokeDasharray = '4 4';
+    }
+    return style;
+  }
+
+  /**
+   * 重写此方法,使保存数据是能带上锚点数据。
+   */
+  getData() {
+    const data = super.getData();
+    data.sourceAnchorId = this.sourceAnchorId;
+    data.targetAnchorId = this.targetAnchorId;
+    return data;
+  }
+}
+
+export default {
+  type: 'skip',
+  view: PolylineEdge,
+  model: SkipModel
+};

+ 16 - 0
src/components/Process/js/start.js

@@ -0,0 +1,16 @@
+import { CircleNode, CircleNodeModel } from '@logicflow/core';
+
+class StartModel extends CircleNodeModel {
+  initNodeData(data) {
+    super.initNodeData(data);
+    this.r = 20;
+  }
+}
+
+class StartView extends CircleNode {}
+
+export default {
+  type: 'start',
+  model: StartModel,
+  view: StartView
+};

+ 237 - 0
src/components/Process/js/tool.js

@@ -0,0 +1,237 @@
+const NODE_TYPE_MAP = { 0: 'start', 1: 'between', 2: 'end', 3: 'serial', 4: 'parallel' };
+
+/**
+ * 将warm-flow的定义json数据转成LogicFlow支持的数据格式
+ * @param {*} json
+ * @returns LogicFlow的数据
+ */
+export const json2LogicFlowJson = (definition) => {
+  const graphData = {
+    nodes: [],
+    edges: []
+  };
+  // 解析definition属性
+  graphData.flowCode = definition.flowCode;
+  graphData.flowName = definition.flowName;
+  graphData.version = definition.version;
+  graphData.fromCustom = definition.fromCustom;
+  graphData.fromPath = definition.fromPath;
+  // 解析节点
+  const allSkips = definition.nodeList.reduce((acc, node) => {
+    if (node.skipList && Array.isArray(node.skipList)) {
+      acc.push(...node.skipList);
+    }
+    return acc;
+  }, []);
+  const allNodes = definition.nodeList;
+  // 解析节点
+  if (allNodes.length) {
+    for (var i = 0, len = allNodes.length; i < len; i++) {
+      let node = allNodes[i];
+      let lfNode = {
+        text: {},
+        properties: {}
+      };
+      // 处理节点
+      lfNode.type = NODE_TYPE_MAP[node.nodeType];
+      lfNode.id = node.nodeCode;
+      let coordinate = node.coordinate;
+      if (coordinate) {
+        const attr = coordinate.split('|');
+        const nodeXy = attr[0].split(',');
+        lfNode.x = parseInt(nodeXy[0]);
+        lfNode.y = parseInt(nodeXy[1]);
+        if (attr.length === 2) {
+          const textXy = attr[1].split(',');
+          lfNode.text.x = parseInt(textXy[0]);
+          lfNode.text.y = parseInt(textXy[1]);
+        }
+      }
+      lfNode.text.value = node.nodeName;
+      lfNode.properties.nodeRatio = node.nodeRatio.toString();
+      lfNode.properties.permissionFlag = node.permissionFlag;
+      lfNode.properties.anyNodeSkip = node.anyNodeSkip;
+      lfNode.properties.listenerType = node.listenerType;
+      lfNode.properties.listenerPath = node.listenerPath;
+      lfNode.properties.formCustom = node.formCustom;
+      lfNode.properties.formPath = node.formPath;
+      lfNode.properties.ext = {};
+      if (node.ext && typeof node.ext === 'string') {
+        try {
+          node.ext = JSON.parse(node.ext);
+          node.ext.forEach((e) => {
+            lfNode.properties.ext[e.code] = String(e.value).includes(',') ? e.value.split(',') : String(e.value);
+          });
+        } catch (error) {
+          console.error('Error parsing JSON:', error);
+        }
+      }
+      lfNode.properties.style = {};
+      if (node.status === 2) {
+        lfNode.properties.style.fill = '#F0FFD9';
+        lfNode.properties.style.stroke = '#9DFF00';
+      }
+      if (node.status === 1) {
+        lfNode.properties.style.fill = '#FFF8DC';
+        lfNode.properties.style.stroke = '#FFCD17';
+      }
+      graphData.nodes.push(lfNode);
+    }
+  }
+  if (allSkips.length) {
+    // 处理边
+    let skipEle = null;
+    let edge = {};
+    for (var j = 0, lenn = allSkips.length; j < lenn; j++) {
+      skipEle = allSkips[j];
+      edge = {
+        text: {},
+        properties: {}
+      };
+      edge.id = skipEle.id;
+      edge.type = 'skip';
+      edge.sourceNodeId = skipEle.nowNodeCode;
+      edge.targetNodeId = skipEle.nextNodeCode;
+      edge.text = { value: skipEle.skipName };
+      edge.properties.skipCondition = skipEle.skipCondition;
+      edge.properties.skipName = skipEle.skipName;
+      edge.properties.skipType = skipEle.skipType;
+      const expr = skipEle.expr;
+      if (expr) {
+        edge.properties.expr = skipEle.expr;
+      }
+      const coordinate = skipEle.coordinate;
+      if (coordinate) {
+        const coordinateXy = coordinate.split('|');
+        edge.pointsList = [];
+        coordinateXy[0].split(';').forEach((item) => {
+          const pointArr = item.split(',');
+          edge.pointsList.push({
+            x: parseInt(pointArr[0]),
+            y: parseInt(pointArr[1])
+          });
+        });
+        edge.startPoint = edge.pointsList[0];
+        edge.endPoint = edge.pointsList[edge.pointsList.length - 1];
+        if (coordinateXy.length > 1) {
+          let textXy = coordinateXy[1].split(',');
+          edge.text.x = parseInt(textXy[0]);
+          edge.text.y = parseInt(textXy[1]);
+        }
+      }
+      graphData.edges.push(edge);
+    }
+  }
+  console.log(graphData);
+  return graphData;
+};
+
+/**
+ * 将LogicFlow的数据转成warm-flow的json定义文件
+ * @param {*} data(...definitionInfo,nodes,edges)
+ * @returns
+ */
+export const logicFlowJsonToWarmFlow = (data) => {
+  // 先构建成流程对象
+  const definition = {
+    nodeList: []
+  };
+
+  /**
+   * 根据节点的类型值,获取key
+   * @param {*} mapValue 节点类型映射
+   * @returns
+   */
+  const getNodeTypeValue = (mapValue) => {
+    for (const key in NODE_TYPE_MAP) {
+      if (NODE_TYPE_MAP[key] === mapValue) {
+        return key;
+      }
+    }
+  };
+  /**
+   * 根据节点的编码,获取节点的类型
+   * @param {*} nodeCode 当前节点名称
+   * @returns
+   */
+  const getNodeType = (nodeCode) => {
+    for (const node of definition.nodeList) {
+      if (nodeCode === node.nodeCode) {
+        return node.nodeType;
+      }
+    }
+  };
+  /**
+   * 拼接skip坐标
+   * @param {*} edge logicFlow的edge
+   * @returns
+   */
+  const getCoordinate = (edge) => {
+    let coordinate = '';
+    for (let i = 0; i < edge.pointsList.length; i++) {
+      coordinate = coordinate + parseInt(edge.pointsList[i].x) + ',' + parseInt(edge.pointsList[i].y);
+      if (i !== edge.pointsList.length - 1) {
+        coordinate = coordinate + ';';
+      }
+    }
+    if (edge.text) {
+      coordinate = coordinate + '|' + parseInt(edge.text.x) + ',' + parseInt(edge.text.y);
+    }
+    return coordinate;
+  };
+  // 流程定义
+  definition.id = data.id;
+  definition.flowCode = data.flowCode;
+  definition.flowName = data.flowName;
+  definition.version = data.version;
+  definition.fromCustom = data.fromCustom;
+  definition.fromPath = data.fromPath;
+  // 流程节点
+  data.nodes.forEach((anyNode) => {
+    let node = {};
+    node.nodeType = getNodeTypeValue(anyNode.type);
+    node.nodeCode = anyNode.id;
+    if (anyNode.text) {
+      node.nodeName = anyNode.text.value;
+    }
+    node.permissionFlag = anyNode.properties.permissionFlag;
+    node.nodeRatio = anyNode.properties.nodeRatio;
+    node.anyNodeSkip = anyNode.properties.anyNodeSkip;
+    node.listenerType = anyNode.properties.listenerType;
+    node.listenerPath = anyNode.properties.listenerPath;
+    node.formCustom = anyNode.properties.formCustom;
+    node.formPath = anyNode.properties.formPath;
+    node.ext = [];
+    for (const key in anyNode.properties.ext) {
+      if (Object.prototype.hasOwnProperty.call(anyNode.properties.ext, key)) {
+        let e = anyNode.properties.ext[key];
+        node.ext.push({ code: key, value: Array.isArray(e) ? e.join(',') : e });
+      }
+    }
+    node.ext = JSON.stringify(node.ext);
+    node.coordinate = anyNode.x + ',' + anyNode.y;
+    if (anyNode.text && anyNode.text.x && anyNode.text.y) {
+      node.coordinate = node.coordinate + '|' + anyNode.text.x + ',' + anyNode.text.y;
+    }
+    node.handlerType = anyNode.properties.handlerType;
+    node.handlerPath = anyNode.properties.handlerPath;
+    node.version = definition.version;
+    node.skipList = [];
+    data.edges.forEach((anyEdge) => {
+      if (anyEdge.sourceNodeId === anyNode.id) {
+        let skip = {};
+        skip.skipType = anyEdge.properties.skipType;
+        skip.skipCondition = anyEdge.properties.skipCondition;
+        skip.skipName = anyEdge?.text?.value || anyEdge.properties.skipName;
+        skip.nowNodeCode = anyEdge.sourceNodeId;
+        skip.nowNodeType = getNodeType(skip.nowNodeCode);
+        skip.nextNodeCode = anyEdge.targetNodeId;
+        skip.nextNodeType = getNodeType(skip.nextNodeCode);
+        skip.coordinate = getCoordinate(anyEdge);
+        node.skipList.push(skip);
+      }
+    });
+    definition.nodeList.push(node);
+  });
+  return JSON.stringify(definition);
+};