From 656ea3139db54b8dc9a29b8cb239d0f0df9a6c05 Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期二, 27 六月 2023 18:19:42 +0800
Subject: [PATCH] 2023-06-27

---
 src/menu/components/chart/antv-X6/index.jsx |  953 ++++++++++++++++++++++++++++++-----------------------------
 1 files changed, 483 insertions(+), 470 deletions(-)

diff --git a/src/menu/components/chart/antv-X6/index.jsx b/src/menu/components/chart/antv-X6/index.jsx
index 815cdf0..0cfcbf6 100644
--- a/src/menu/components/chart/antv-X6/index.jsx
+++ b/src/menu/components/chart/antv-X6/index.jsx
@@ -1,27 +1,305 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Popover } from 'antd'
-import { ToolOutlined, DeleteOutlined, FontColorsOutlined } from '@ant-design/icons'
+import { Popover, Tooltip } from 'antd'
+import { ToolOutlined, DeleteOutlined, FontColorsOutlined, VerticalAlignTopOutlined, VerticalAlignBottomOutlined, SaveOutlined, ZoomInOutlined, ZoomOutOutlined, OneToOneOutlined, DownloadOutlined } from '@ant-design/icons'
 import { Graph, Shape } from '@antv/x6'
-import { Stencil } from '@antv/x6-plugin-stencil'
-import { Transform } from '@antv/x6-plugin-transform'
-import { Selection } from '@antv/x6-plugin-selection'
-import { Snapline } from '@antv/x6-plugin-snapline'
-import { Keyboard } from '@antv/x6-plugin-keyboard'
-import { Clipboard } from '@antv/x6-plugin-clipboard'
-import { History } from '@antv/x6-plugin-history'
 
 import MKEmitter from '@/utils/events.js'
 import asyncComponent from '@/utils/asyncComponent'
 import asyncIconComponent from '@/utils/asyncIconComponent'
 import { resetStyle, getTables, checkComponent } from '@/utils/utils-custom.js'
+import lanes from './lane.json'
+import xflows from './xflow.json'
 import './index.scss'
 
 const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
 const ChartCompileForm = asyncIconComponent(() => import('./chartcompile'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
+
+// #region 鍒濆鍖栧浘褰�
+const groups = {
+  top: {
+    position: 'top',
+    attrs: {
+      circle: {
+        r: 4,
+        magnet: true,
+        stroke: 'var(--mk-sys-color)',
+        strokeWidth: 1,
+        fill: '#fff',
+        style: {
+          visibility: 'hidden'
+        }
+      }
+    }
+  },
+  right: {
+    position: 'right',
+    attrs: {
+      circle: {
+        r: 4,
+        magnet: true,
+        stroke: 'var(--mk-sys-color)',
+        strokeWidth: 1,
+        fill: '#fff',
+        style: {
+          visibility: 'hidden'
+        }
+      }
+    }
+  },
+  bottom: {
+    position: 'bottom',
+    attrs: {
+      circle: {
+        r: 4,
+        magnet: true,
+        stroke: 'var(--mk-sys-color)',
+        strokeWidth: 1,
+        fill: '#fff',
+        style: {
+          visibility: 'hidden'
+        }
+      }
+    }
+  },
+  left: {
+    position: 'left',
+    attrs: {
+      circle: {
+        r: 4,
+        magnet: true,
+        stroke: 'var(--mk-sys-color)',
+        strokeWidth: 1,
+        fill: '#fff',
+        style: {
+          visibility: 'hidden'
+        }
+      }
+    }
+  }
+}
+
+Graph.registerNode(
+  'lane',
+  {
+    inherit: 'rect',
+    markup: [
+      {
+        tagName: 'rect',
+        selector: 'body',
+      },
+      {
+        tagName: 'rect',
+        selector: 'name-rect',
+      },
+      {
+        tagName: 'text',
+        selector: 'name-text',
+      },
+    ],
+    attrs: {
+      body: {
+        fill: '#FFF',
+        stroke: '#5F95FF',
+        strokeWidth: 1,
+      },
+      'name-rect': {
+        width: 200,
+        height: 36,
+        fill: '#5F95FF',
+        stroke: '#fff',
+        strokeWidth: 1,
+        x: -1,
+      },
+      'name-text': {
+        ref: 'name-rect',
+        refY: 0.5,
+        refX: 0.5,
+        textAnchor: 'middle',
+        fontWeight: 'bold',
+        fill: '#fff',
+        fontSize: 14,
+      },
+    },
+    zIndex: 0
+  },
+  true,
+)
+
+Graph.registerNode(
+  'mk-rect',
+  {
+    inherit: 'rect',
+    width: 66,
+    height: 36,
+    attrs: {
+      body: {
+        strokeWidth: 1,
+        stroke: '#000000',
+        fill: '#FFFFFF'
+      },
+      text: {
+        fontSize: 12,
+        fill: '#262626'
+      }
+    },
+    ports: {
+      groups,
+      items: [
+        { group: 'top' },
+        { group: 'right' },
+        { group: 'bottom' },
+        { group: 'left' }
+      ]
+    }
+  },
+  true
+)
+
+Graph.registerNode(
+  'mk-polygon',
+  {
+    inherit: 'polygon',
+    width: 66,
+    height: 36,
+    attrs: {
+      body: {
+        strokeWidth: 1,
+        stroke: '#000000',
+        fill: '#FFFFFF'
+      },
+      text: {
+        fontSize: 12,
+        fill: '#262626'
+      }
+    },
+    ports: {
+      groups,
+      items: [
+        { group: 'top' },
+        { group: 'right' },
+        { group: 'bottom' },
+        { group: 'left' }
+      ]
+    }
+  },
+  true
+)
+
+Graph.registerNode(
+  'mk-circle',
+  {
+    inherit: 'circle',
+    width: 36,
+    height: 36,
+    attrs: {
+      body: {
+        strokeWidth: 1,
+        stroke: '#000000',
+        fill: '#FFFFFF'
+      },
+      text: {
+        fontSize: 12,
+        fill: '#262626'
+      }
+    },
+    ports: {
+      groups,
+      items: [
+        { group: 'top' },
+        { group: 'right' },
+        { group: 'bottom' },
+        { group: 'left' }
+      ]
+    }
+  },
+  true
+)
+
+Graph.registerNode(
+  'mk-ellipse',
+  {
+    inherit: 'ellipse',
+    width: 66,
+    height: 36,
+    attrs: {
+      body: {
+        strokeWidth: 1,
+        stroke: '#000000',
+        fill: '#FFFFFF'
+      },
+      text: {
+        fontSize: 12,
+        fill: '#262626'
+      }
+    },
+    ports: {
+      groups,
+      items: [
+        { group: 'top' },
+        { group: 'right' },
+        { group: 'bottom' },
+        { group: 'left' }
+      ]
+    }
+  },
+  true
+)
+
+Graph.registerNode(
+  'mk-star',
+  {
+    inherit: 'polygon',
+    width: 36,
+    height: 36,
+    points: '100,10 40,198 190,78 10,78 160,198',
+    attrs: {
+      body: {
+        fill: '#FFFFFF',
+        stroke: '#000000',
+        strokeWidth: 1,
+        fillRule: 'nonzero'
+      },
+      text: {
+        fontSize: 12,
+        fill: '#262626'
+      }
+    },
+    ports: {
+      groups,
+      items: [
+        { group: 'top' },
+        { group: 'right' },
+        { group: 'bottom' },
+        { group: 'left' }
+      ]
+    }
+  },
+  true
+)
+
+Graph.registerNode(
+  'mk-text',
+  {
+    inherit: 'rect',
+    width: 66,
+    height: 36,
+    attrs: {
+      body: {
+        strokeWidth: 0,
+        fill: 'transparent'
+      },
+      text: {
+        fontSize: 12,
+        fill: '#262626'
+      }
+    }
+  },
+  true
+)
 
 class antvX6Chart extends Component {
   static propTpyes = {
@@ -31,8 +309,7 @@
   }
 
   state = {
-    card: null,
-    eventListener: null
+    card: null
   }
 
   UNSAFE_componentWillMount () {
@@ -41,9 +318,19 @@
     if (card.isNew) {
       let _plot = {
         width: card.width || 24,
-        height: 400,
+        height: 500,
         subtype: card.subtype,
-        name: card.name
+        name: card.name,
+        grid: {
+          visible: true,
+          type: 'dot',
+          args: {
+            color: '#a0a0a0',
+            thickness: 1
+          }
+        },
+        gridType: 'dot',
+        export: 'png'
       }
 
       let _card = {
@@ -95,65 +382,89 @@
     }, 1000)
   }
 
-  viewrender = () => {
-    // const { card } = this.state
-
-    // if (card.plot.subtype === 'mindmap') {
-    //   this.ponitrender()
-    // } else if (card.plot.subtype === 'indentTree') {
-    //   this.indentrender()
-    // } else if (card.plot.subtype === 'kapmap') {
-      this.kapmaprender()
-    // }
-  }
-
-  kapmaprender = () => {
+  plotchange = (res) => {
     const { card } = this.state
 
-    // #region 鍒濆鍖栫敾甯�
+    if (
+      card.plot.subtype !== res.plot.subtype ||
+      (res.plot.gridType && card.plot.gridType !== res.plot.gridType) ||
+      (res.plot.gridType && card.plot.backgroundColor !== res.plot.backgroundColor)
+    ) {
+      let _element = document.getElementById(card.uuid + 'container')
+      if (_element) {
+        _element.innerHTML = ''
+      }
+      setTimeout(() => {
+        this.viewrender()
+      }, 50)
+    }
+
+    this.updateComponent(res)
+  }
+
+  viewrender = () => {
+    const { card } = this.state
+
+    if (card.plot.subtype === 'xflow') {
+      this.xflowrender()
+    } else if (card.plot.subtype === 'lane') {
+      this.lanerender()
+    }
+  }
+
+  xflowrender = () => {
+    const { card } = this.state
+
     const graph = new Graph({
       container: document.getElementById(card.uuid + 'container'),
-      grid: true,
+      grid: card.plot.grid,
+      scaling: {
+        min: 0.5,
+        max: 2
+      },
+      autoResize: true,
+      panning: true,
+      background: {
+        color: card.plot.backgroundColor || 'transparent'
+      },
       mousewheel: {
         enabled: true,
         zoomAtMousePosition: true,
-        modifiers: 'ctrl',
-        minScale: 0.5,
-        maxScale: 3,
+        modifiers: 'ctrl'
       },
       connecting: {
         router: 'manhattan',
         connector: {
           name: 'rounded',
           args: {
-            radius: 8,
-          },
+            radius: 8
+          }
         },
         anchor: 'center',
         connectionPoint: 'anchor',
         allowBlank: false,
         snap: {
-          radius: 20,
+          radius: 20
         },
         createEdge() {
           return new Shape.Edge({
             attrs: {
               line: {
-                stroke: '#A2B1C3',
-                strokeWidth: 2,
+                stroke: '#000000',
+                strokeWidth: 1,
                 targetMarker: {
                   name: 'block',
                   width: 12,
-                  height: 8,
-                },
-              },
+                  height: 8
+                }
+              }
             },
-            zIndex: 0,
+            zIndex: 0
           })
         },
         validateConnection({ targetMagnet }) {
           return !!targetMagnet
-        },
+        }
       },
       highlighting: {
         magnetAdsorbed: {
@@ -161,442 +472,118 @@
           args: {
             attrs: {
               fill: '#5F95FF',
-              stroke: '#5F95FF',
+              stroke: '#5F95FF'
+            }
+          }
+        }
+      }
+    })
+
+    let cells = []
+    xflows.forEach((item) => {
+      if (item.shape === 'edge') {
+        cells.push(graph.createEdge(item))
+      } else {
+        cells.push(graph.createNode(item))
+      }
+    })
+    graph.resetCells(cells)
+    graph.positionContent('center')
+  }
+
+  lanerender = () => {
+    const { card } = this.state
+
+    const graph = new Graph({
+      container: document.getElementById(card.uuid + 'container'),
+      scaling: {
+        min: 0.5,
+        max: 2
+      },
+      autoResize: true,
+      panning: true,
+      background: { color: '#ffffff' },
+      mousewheel: {
+        enabled: true,
+        zoomAtMousePosition: true,
+        modifiers: 'ctrl'
+      },
+      connecting: {
+        router: 'manhattan',
+        connector: {
+          name: 'rounded',
+          args: {
+            radius: 8
+          }
+        },
+        anchor: 'center',
+        connectionPoint: 'anchor',
+        allowBlank: false,
+        snap: {
+          radius: 20
+        },
+        createEdge() {
+          return new Shape.Edge({
+            attrs: {
+              line: {
+                stroke: '#000000',
+                strokeWidth: 1,
+                targetMarker: {
+                  name: 'block',
+                  width: 12,
+                  height: 8
+                }
+              }
             },
-          },
+            zIndex: 2
+          })
         },
+        validateConnection({ targetMagnet }) {
+          return !!targetMagnet
+        }
       },
-    })
-    // #endregion
-
-    // #region 浣跨敤鎻掍欢
-    graph
-      .use(
-        new Transform({
-          resizing: true,
-          rotating: true,
-        }),
-      )
-      .use(
-        new Selection({
-          rubberband: true,
-          showNodeSelectionBox: true,
-        }),
-      )
-      .use(new Snapline())
-      .use(new Keyboard())
-      .use(new Clipboard())
-      .use(new History())
-    // #endregion
-
-    // #region 鍒濆鍖� stencil
-    const stencil = new Stencil({
-      title: '娴佺▼鍥�',
-      target: graph,
-      stencilGraphWidth: 200,
-      stencilGraphHeight: 180,
-      // collapsable: true,
-      groups: [
-        {
-          title: '閫氱敤鑺傜偣',
-          name: 'group1',
-        },
-        {
-          title: '绯荤粺璁捐鍥�',
-          name: 'group2',
-          graphHeight: 250,
-          layoutOptions: {
-            rowHeight: 70,
-          },
-        },
-      ],
-      layoutOptions: {
-        columns: 2,
-        columnWidth: 80,
-        rowHeight: 55,
+      highlighting: {
+        magnetAdsorbed: {
+          name: 'stroke',
+          args: {
+            attrs: {
+              fill: '#5F95FF',
+              stroke: '#5F95FF'
+            }
+          }
+        }
       },
-    })
-    document.getElementById(card.uuid + 'stencil').appendChild(stencil.container)
-    // #endregion
+      translating: {
+        restrict(cellView) {
+          const cell = cellView.cell
+          const parentId = cell.prop('parent')
 
-    // #region 蹇嵎閿笌浜嬩欢
-    graph.bindKey(['meta+c', 'ctrl+c'], () => {
-      const cells = graph.getSelectedCells()
-      if (cells.length) {
-        graph.copy(cells)
-      }
-      return false
-    })
-    graph.bindKey(['meta+x', 'ctrl+x'], () => {
-      const cells = graph.getSelectedCells()
-      if (cells.length) {
-        graph.cut(cells)
-      }
-      return false
-    })
-    graph.bindKey(['meta+v', 'ctrl+v'], () => {
-      if (!graph.isClipboardEmpty()) {
-        const cells = graph.paste({ offset: 32 })
-        graph.cleanSelection()
-        graph.select(cells)
-      }
-      return false
-    })
-
-    // undo redo
-    graph.bindKey(['meta+z', 'ctrl+z'], () => {
-      if (graph.canUndo()) {
-        graph.undo()
-      }
-      return false
-    })
-    graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {
-      if (graph.canRedo()) {
-        graph.redo()
-      }
-      return false
-    })
-
-    // select all
-    graph.bindKey(['meta+a', 'ctrl+a'], () => {
-      const nodes = graph.getNodes()
-      if (nodes) {
-        graph.select(nodes)
+          if (parentId) {
+            const parentNode = graph.getCellById(parentId)
+            if (parentNode) {
+              return parentNode.getBBox().moveAndExpand({
+                x: 0,
+                y: 36,
+                width: 0,
+                height: -36,
+              })
+            }
+          }
+          return cell.getBBox()
+        }
       }
     })
 
-    // delete
-    graph.bindKey('backspace', () => {
-      const cells = graph.getSelectedCells()
-      if (cells.length) {
-        graph.removeCells(cells)
+    let cells = []
+    lanes.forEach((item) => {
+      if (item.shape === 'edge') {
+        cells.push(graph.createEdge(item))
+      } else {
+        cells.push(graph.createNode(item))
       }
     })
-
-    // zoom
-    graph.bindKey(['ctrl+1', 'meta+1'], () => {
-      const zoom = graph.zoom()
-      if (zoom < 1.5) {
-        graph.zoom(0.1)
-      }
-    })
-    graph.bindKey(['ctrl+2', 'meta+2'], () => {
-      const zoom = graph.zoom()
-      if (zoom > 0.5) {
-        graph.zoom(-0.1)
-      }
-    })
-
-    // 鎺у埗杩炴帴妗╂樉绀�/闅愯棌
-    const showPorts = (ports, show) => {
-      for (let i = 0, len = ports.length; i < len; i += 1) {
-        ports[i].style.visibility = show ? 'visible' : 'hidden'
-      }
-    }
-    graph.on('node:mouseenter', () => {
-      const container = document.getElementById(card.uuid + 'container')
-      const ports = container.querySelectorAll('.x6-port-body')
-      showPorts(ports, true)
-    })
-    graph.on('node:mouseleave', () => {
-      const container = document.getElementById(card.uuid + 'container')
-      const ports = container.querySelectorAll('.x6-port-body')
-      showPorts(ports, false)
-    })
-    // #endregion
-
-    // #region 鍒濆鍖栧浘褰�
-    const ports = {
-      groups: {
-        top: {
-          position: 'top',
-          attrs: {
-            circle: {
-              r: 4,
-              magnet: true,
-              stroke: '#5F95FF',
-              strokeWidth: 1,
-              fill: '#fff',
-              style: {
-                visibility: 'hidden',
-              },
-            },
-          },
-        },
-        right: {
-          position: 'right',
-          attrs: {
-            circle: {
-              r: 4,
-              magnet: true,
-              stroke: '#5F95FF',
-              strokeWidth: 1,
-              fill: '#fff',
-              style: {
-                visibility: 'hidden',
-              },
-            },
-          },
-        },
-        bottom: {
-          position: 'bottom',
-          attrs: {
-            circle: {
-              r: 4,
-              magnet: true,
-              stroke: '#5F95FF',
-              strokeWidth: 1,
-              fill: '#fff',
-              style: {
-                visibility: 'hidden',
-              },
-            },
-          },
-        },
-        left: {
-          position: 'left',
-          attrs: {
-            circle: {
-              r: 4,
-              magnet: true,
-              stroke: '#5F95FF',
-              strokeWidth: 1,
-              fill: '#fff',
-              style: {
-                visibility: 'hidden',
-              },
-            },
-          },
-        },
-      },
-      items: [
-        {
-          group: 'top',
-        },
-        {
-          group: 'right',
-        },
-        {
-          group: 'bottom',
-        },
-        {
-          group: 'left',
-        },
-      ],
-    }
-
-    Graph.registerNode(
-      'custom-rect',
-      {
-        inherit: 'rect',
-        width: 66,
-        height: 36,
-        attrs: {
-          body: {
-            strokeWidth: 1,
-            stroke: '#5F95FF',
-            fill: '#EFF4FF',
-          },
-          text: {
-            fontSize: 12,
-            fill: '#262626',
-          },
-        },
-        ports: { ...ports },
-      },
-      true,
-    )
-
-    Graph.registerNode(
-      'custom-polygon',
-      {
-        inherit: 'polygon',
-        width: 66,
-        height: 36,
-        attrs: {
-          body: {
-            strokeWidth: 1,
-            stroke: '#5F95FF',
-            fill: '#EFF4FF',
-          },
-          text: {
-            fontSize: 12,
-            fill: '#262626',
-          },
-        },
-        ports: {
-          ...ports,
-          items: [
-            {
-              group: 'top',
-            },
-            {
-              group: 'bottom',
-            },
-          ],
-        },
-      },
-      true,
-    )
-
-    Graph.registerNode(
-      'custom-circle',
-      {
-        inherit: 'circle',
-        width: 45,
-        height: 45,
-        attrs: {
-          body: {
-            strokeWidth: 1,
-            stroke: '#5F95FF',
-            fill: '#EFF4FF',
-          },
-          text: {
-            fontSize: 12,
-            fill: '#262626',
-          },
-        },
-        ports: { ...ports },
-      },
-      true,
-    )
-
-    Graph.registerNode(
-      'custom-image',
-      {
-        inherit: 'rect',
-        width: 52,
-        height: 52,
-        markup: [
-          {
-            tagName: 'rect',
-            selector: 'body',
-          },
-          {
-            tagName: 'image',
-          },
-          {
-            tagName: 'text',
-            selector: 'label',
-          },
-        ],
-        attrs: {
-          body: {
-            stroke: '#5F95FF',
-            fill: '#5F95FF',
-          },
-          image: {
-            width: 26,
-            height: 26,
-            refX: 13,
-            refY: 16,
-          },
-          label: {
-            refX: 3,
-            refY: 2,
-            textAnchor: 'left',
-            textVerticalAnchor: 'top',
-            fontSize: 12,
-            fill: '#fff',
-          },
-        },
-        ports: { ...ports },
-      },
-      true,
-    )
-
-    const r1 = graph.createNode({
-      shape: 'custom-rect',
-      label: '寮�濮�',
-      attrs: {
-        body: {
-          rx: 20,
-          ry: 26,
-        },
-      },
-    })
-    const r2 = graph.createNode({
-      shape: 'custom-rect',
-      label: '杩囩▼',
-    })
-    const r3 = graph.createNode({
-      shape: 'custom-rect',
-      attrs: {
-        body: {
-          rx: 6,
-          ry: 6,
-        },
-      },
-      label: '鍙�夎繃绋�',
-    })
-    const r4 = graph.createNode({
-      shape: 'custom-polygon',
-      attrs: {
-        body: {
-          refPoints: '0,10 10,0 20,10 10,20',
-        },
-      },
-      label: '鍐崇瓥',
-    })
-    const r5 = graph.createNode({
-      shape: 'custom-polygon',
-      attrs: {
-        body: {
-          refPoints: '10,0 40,0 30,20 0,20',
-        },
-      },
-      label: '鏁版嵁',
-    })
-    const r6 = graph.createNode({
-      shape: 'custom-circle',
-      label: '杩炴帴',
-    })
-    stencil.load([r1, r2, r3, r4, r5, r6], 'group1')
-
-    const imageShapes = [
-      {
-        label: 'Client',
-        image:
-          'https://gw.alipayobjects.com/zos/bmw-prod/687b6cb9-4b97-42a6-96d0-34b3099133ac.svg',
-      },
-      {
-        label: 'Http',
-        image:
-          'https://gw.alipayobjects.com/zos/bmw-prod/dc1ced06-417d-466f-927b-b4a4d3265791.svg',
-      },
-      {
-        label: 'Api',
-        image:
-          'https://gw.alipayobjects.com/zos/bmw-prod/c55d7ae1-8d20-4585-bd8f-ca23653a4489.svg',
-      },
-      {
-        label: 'Sql',
-        image:
-          'https://gw.alipayobjects.com/zos/bmw-prod/6eb71764-18ed-4149-b868-53ad1542c405.svg',
-      },
-      {
-        label: 'Clound',
-        image:
-          'https://gw.alipayobjects.com/zos/bmw-prod/c36fe7cb-dc24-4854-aeb5-88d8dc36d52e.svg',
-      },
-      {
-        label: 'Mq',
-        image:
-          'https://gw.alipayobjects.com/zos/bmw-prod/2010ac9f-40e7-49d4-8c4a-4fcf2f83033b.svg',
-      },
-    ]
-    const imageNodes = imageShapes.map((item) =>
-      graph.createNode({
-        shape: 'custom-image',
-        label: item.label,
-        attrs: {
-          image: {
-            'xlink:href': item.image,
-          },
-        },
-      }),
-    )
-    stencil.load(imageNodes, 'group2')
+    graph.resetCells(cells)
+    graph.positionContent('top')
   }
 
   updateComponent = (card) => {
@@ -646,10 +633,10 @@
     let _style = resetStyle(card.style)
 
     return (
-      <div className="menu-scatter-chart-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
+      <div className="menu-x6-chart-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
-            <ChartCompileForm config={card} plotchange={this.updateComponent}/>
+            <ChartCompileForm config={card} plotchange={this.plotchange}/>
             <CopyComponent type="antvG6" card={card}/>
             <FontColorsOutlined className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle}/>
             <DeleteOutlined className="close" title="delete" onClick={() => this.props.deletecomponent(card.uuid)}/>
@@ -659,8 +646,34 @@
           <ToolOutlined/>
         </Popover>
         <NormalHeader config={card} updateComponent={this.updateComponent}/>
+        <div className="mk-toolbar">
+          <div className="left-tool">
+            {card.plot.subtype === 'xflow' ? <Tooltip title="缃墠">
+              <VerticalAlignTopOutlined/>
+            </Tooltip> : null}
+            {card.plot.subtype === 'xflow' ? <Tooltip title="缃悗">
+              <VerticalAlignBottomOutlined/>
+            </Tooltip> : null}
+            <Tooltip title="淇濆瓨">
+              <SaveOutlined/>
+            </Tooltip>
+            {card.plot.export === 'png' ? <Tooltip title="瀵煎嚭鍥剧墖">
+              <DownloadOutlined/>
+            </Tooltip> : null}
+          </div>
+          <div className="right-tool">
+            <Tooltip title="鏀惧ぇ">
+              <ZoomInOutlined/>
+            </Tooltip>
+            <Tooltip title="缂╁皬">
+              <ZoomOutOutlined/>
+            </Tooltip>
+            <Tooltip title="1:1">
+              <OneToOneOutlined/>
+            </Tooltip>
+          </div>
+        </div>
         <div className="canvas" style={{width: '100%', minHeight: card.plot.height, height: card.plot.height}} id={card.uuid + 'canvas'}>
-          <div id={card.uuid + 'stencil'} className="mk-stencil"></div>
           <div id={card.uuid + 'container'} className="mk-container"></div>
         </div>
         <div className="component-name">

--
Gitblit v1.8.0