Web开发day14:Antv X6 + Vue(Chapter1)

  • Post author:
  • Post category:vue



目录


一、快速上手


(1)HelloWorld


(2)demo2:平移、放大与缩小


(3)demo3:节点和边的配置


二、画布


(1)Graph、Panning、MouseWheel


(2)Transform


三、基类Cell


(1)addNode和addEdge、markup和选择器


(2)自定义节点添加图片、自定义节点定制Css


(3)添加文本节点、通过控制面板控制属性


(4)拖拽式页面基本案例


(5)设置默认的condig、使用propHooks


一、快速上手

(1)HelloWorld

  • HelloWorld.vue
<template>
  <div id="container"></div>
</template>

<script>
import {Graph} from '@antv/x6'
import {onMounted} from "vue";

const data = {
  nodes: [
    {
      id: 'node1',
      x: 0,
      y: 0,
      width: 80,
      height: 40,
      label: 'hello'
    },
    {
      id: 'node2',
      x: 200,
      y: 200,
      width: 80,
      height: 40,
      label: 'world'
    }
  ],
  edges: [
    {
      source: 'node1',
      target: 'node2',
    }
  ]
}

export default {
  name: 'HelloWorld',
  setup() {
    let graph = {}
    onMounted(() => {
      graph = new Graph({
        container: document.getElementById('container'),
        width: 2000,
        height: 400,
        grid: true,
      })
      graph.fromJSON(data);
    })
  }
}
</script>

<style scoped>
</style>

(2)demo2:平移、放大与缩小

  • demo2.vue
<template>
  <div>
    <!-- element-ui组件,需要在main.js中引入,方法见chapter 2-4 -->
    <el-button @click="pingyi">平移</el-button>
    <el-button @click="suoxiao">缩小0.5倍</el-button>
    <el-button @click="fangda">放大0.5倍</el-button>
    <div id="container"></div>
  </div>
</template>

<script>
import {Graph} from '@antv/x6'
import {onMounted} from "vue";

const data = {
  nodes: [
    {
      id: 'node1',
      x: 0,
      y: 0,
      width: 80,
      height: 40,
      label: 'hello'
    },
    {
      id: 'node2',
      x: 200,
      y: 200,
      width: 80,
      height: 40,
      label: 'world'
    }
  ],
  edges: [
    {
      source: 'node1',
      target: 'node2',
    }
  ]
}

export default {
  name: 'demo2',
  setup() {
    let graph = {}
    onMounted(() => {
      graph = new Graph({
        container: document.getElementById('container'),
        width: 2000,
        height: 400,
        grid: {
          // 默认是10px
          size: 10,
          visible: true,
        },
        background: {
          color: '#fffbe6',
        }
      })
      graph.fromJSON(data);
    });

    // 定义平移方法
    const pingyi = ()=>{
      graph.translate(80, 40)
    }

    // 定义缩小方法
    const suoxiao = ()=>{
      graph.zoom(-0.5)
    }

    // 定义放大方法
    const fangda = ()=>{
      graph.zoom(0.5)
    }

    return {
      pingyi,
      suoxiao,
      fangda
    }
  }

}
</script>

<style scoped>
</style>

(3)demo3:节点和边的配置

  • demo3.vue
<template>
  <div id="container"></div>
</template>

<script>
import {Graph, Shape} from '@antv/x6'
import {onMounted} from "vue"

const node11 = new Shape.HTML({
  id: 'node11',
  x: 1300,
  y: 400,
  width: 80,
  height: 40,
  attrs:{
    body:{
      stroke: '#1890ff',
    },
    label: {
      text: "html",
      fontSize: 25,
    },
  },
})

const edge7_11 = new Shape.Edge({
  source: 'node7',
  target: 'node11',
  attrs: {
    line: {
      stroke: '#1890ff',
      strokeDasharray: 5,
      targetMarker: 'classic',
    }
  },
})

const data = {
  nodes: [
    {
      id: 'node1',
      shape: 'rect',
      x: 100,
      y: 200,
      width: 80,
      height: 40,
      label: '矩形'
    },
    {
      id: 'node2',
      shape: 'circle',
      x: 300,
      y: 200,
      width: 80,
      height: 40,
      label: '圆形'
    },
    {
      id: 'node3',
      shape: 'ellipse',
      x: 500,
      y: 200,
      width: 80,
      height: 40,
      label: '椭圆'
    },
    {
      id: 'node4',
      shape: 'polygon',
      x: 700,
      y: 200,
      width: 80,
      height: 40,
      label: '多边形',
      attrs: {
        body: {
          // 填充色
          fill: '#efdbff',
          // 边框色
          stroke: '#9254de',
          // 设置了4组,四边形,代表四个点的偏移,与width、height、x、y一起看
          // 点之间都连接
          refPoints: '0,10 10,0 20,10 10,20',
        }
      }
    },
    {
      id: 'node5',
      shape: 'polyline',
      x: 900,
      y: 200,
      width: 80,
      height: 40,
      label: '折线',
      attrs: {
        body: {
          fill: '#efdbff',
          stroke: '#9254de',
          // 点之间只连两两,最后一个和第一个不连
          refPoints: '0,0 0,10 10,10 10,0'
        }
      }
    },
    {
      id: 'node6',
      shape: 'path',
      x: 1100,
      y: 200,
      width: 80,
      height: 40,
      label: '路径',
      // svg格式的path格式
      // path: 'M 0 5 10 0 C 20 0 20 20 10 20 L 0 15 Z',
      path: 'm468.21502,252.08332l-72.97137,0l-22.52531,-66.80479l-3.16285,9.33589l-19.38904,57.4817l-72.95144,-0.01281l59.01764,41.32638l-22.56518,66.84961l59.03093,-41.32638l59.04422,41.34559l-22.57182,-66.86882l59.04422,-41.32638zm-87.69588,56.4892l-7.80745,-5.46195l-7.80745,5.46835l-25.92736,18.14672l9.90051,-29.35236l2.99009,-8.84925l-7.82073,-5.47475l-25.91407,-18.14672l41.69508,0l2.99009,-8.84285l9.89386,-29.35236l9.89386,29.33315l2.98344,8.84285l41.73495,0l-25.95394,18.15953l-7.82738,5.46835l2.98344,8.86206l9.92044,29.35236l-25.92736,-18.15313z',
      attrs: {
        body: {
          fill: '#efdbff',
          stroke: '#9254de',
        }
      }
    },
    {
      id: 'node7',
      shape: 'image',
      x: 1300,
      y: 200,
      width: 80,
      height: 40,
      imageUrl: 'https://gw.alipayobjects.com/os/s/prod/antv/assets/image/logo-with-text-73b8a.svg'
    },
    // {
    //   id: 'node8',
    //   shape: "image-bordered",
    //   x: 1300,
    //   y: 400,
    //   width: 80,
    //   height: 40,
    //   imageUrl: 'https://gw.alipayobjects.com/os/s/prod/antv/assets/image/logo-with-text-73b8a.svg'
    // },
    // {
    //   id: 'node9',
    //   shape: 'image-embedded',
    //   x: 1100,
    //   y: 400,
    //   width: 80,
    //   height: 40,
    //   imageUrl: 'https://img2-bs.511doc.com/mark/00/00/37/42/32e7b926810f3bd87e6e46a3226a3bdf.jpg%21/quality/90/unsharp/true/compress/true/fw/640/clip/640x500a0a500'
    // },
    // {
    //   id: 'node10',
    //   shape: 'image-inscribed',
    //   x: 900,
    //   y: 400,
    //   width: 80,
    //   height: 40,
    //   imageUrl: 'https://img2-bs.511doc.com/mark/00/00/37/42/32e7b926810f3bd87e6e46a3226a3bdf.jpg%21/quality/90/unsharp/true/compress/true/fw/640/clip/640x500a0a500'
    // },
  ],
  edges: [
    {
      source: 'node1',
      target: 'node2',
    },
    {
      source: 'node2',
      target: 'node3',
    },
    {
      shape: 'edge',
      source: 'node3',
      target: 'node4',
    },
    {
      shape: 'edge',
      source: 'node4',
      target: 'node5',
    },
    {
      shape: 'edge',
      source: 'node5',
      target: 'node6',
    },
    {
      shape: 'edge',
      source: 'node6',
      target: 'node7',
    },
    // {
    //   shape: 'edge',
    //   source: 'node7',
    //   target: 'node11',
    // },
  ]
}

export default {
  name: 'demo3',
  setup() {
    onMounted(() => {
      let graph = new Graph({
        container: document.getElementById('container'),
        width: 2000,
        height: 600,
        grid: true,
      });
      graph.fromJSON(data);
      graph.addNode(node11);
      graph.addEdge(edge7_11)
    })
  }
}
</script>

<style scoped>
</style>

二、画布

(1)Graph、Panning、MouseWheel

<template>
  <el-button @click="ifPannable">测试画布是否可平移</el-button>
  <el-button @click="startPanning">启用画布平移</el-button>
  <el-button @click="endPanning">禁止画布平移</el-button>
  <el-button @click="changePanning">切换画布平移状态</el-button>
  <div id="container"></div>
</template>

<script>
import {Graph} from '@antv/x6'
import {onMounted, defineComponent} from "vue";

const data = {
  // 定义节点和边
  nodes: [
    {
      id: 'node1',
      x: 0,
      y: 0,
      width: 80,
      height: 40,
      label: 'hello'
    },
    {
      id: 'node2',
      x: 200,
      y: 200,
      width: 80,
      height: 40,
      label: 'world'
    }
  ],
  edges: [
    {
      source: 'node1',
      target: 'node2',
    }
  ]
}

export default defineComponent({
  name: 'huaBu',
  setup() {
    let graph = {}
    onMounted(() => {
      graph = new Graph({
        container: document.getElementById('container'),
        width: 2000,
        height: 400,
        grid: {
          size: 10,
          visible: true,
        },
        background: {
          color: '#fdfb01'
        },

        // 画布平移
        // panning: true,
        panning: {
          enabled: true,
          modifiers: 'shift',  // 按下shift+左键才会移动,防止冲突
          eventTypes: ['leftMouseDown', 'rightMouseDown'],  // 或单选之一,还可选mouseWheel
        },

        // 滚轮配合画布实现缩放
        scroller: {
          enabled: true,
          pannable: true,
          pageVisible: true,
          pageBreak: false,
        },
        mousewheel: {
          enabled: true,
          factor: 1.2,
          modifiers: ["alt", "shift"],
          // 判断滚轮事件是否被处理,当按下shift滚动滑轮时,不缩放
          // g是graph当前画布,e是wheelEvent滚轮事件
          guard(g, e){
            if(e.altKey){
              return false
            }
            return true
          }
        },
      })

      graph.fromJSON(data);
    })

    // 画布是否可平移
    function ifPannable(){
      console.log(graph.isPannable())
    }

    // 启用画布平移
    function startPanning(){
      graph.enablePanning()
      console.log('已启用画布平移')
    }

    // 禁止画布平移
    function endPanning(){
      graph.disablePanning()
      console.log('已禁用画布平移')
    }

    // 切换画布平移状态
    function changePanning(){
      graph.togglePanning()
      console.log('已切换画布平移状态')
    }

    return {
      ifPannable,
      startPanning,
      endPanning,
      changePanning,
    }
  }
})
</script>

<style scoped>
</style>

(2)Transform

<template>
  <el-button @click="reSize">设置容器大小</el-button>
  <el-button @click="getAndSetPercent">获取缩放比例并设置(scale版)</el-button>
  <el-button @click="getAndSetPercentZoom">获取缩放比例并设置(zoom版)</el-button>
  <el-button @click="contentToCenter">画布内容居中</el-button>
  <div id="container"></div>
</template>

<script>
import {Graph} from '@antv/x6'
import {onMounted, defineComponent} from "vue";

const data = {
  // 定义节点和边
  nodes: [
    {
      id: 'node1',
      x: 0,
      y: 0,
      width: 80,
      height: 40,
      label: 'hello'
    },
    {
      id: 'node2',
      x: 200,
      y: 200,
      width: 80,
      height: 40,
      label: 'world'
    }
  ],
  edges: [
    {
      source: 'node1',
      target: 'node2',
    }
  ]
}

export default defineComponent({
  name: 'huaBu2',
  setup() {
    let graph = {}
    onMounted(() => {
      graph = new Graph({
        container: document.getElementById('container'),
        width: 2000,
        height: 400,
        grid: {
          size: 10,
          visible: true,
        },
        background: {
          color: '#fdfb01'
        },

        // 配置画布最大最小缩放比
        scaling: {
          min: 0.05,  // 默认0.01
          max: 12,  // 默认16
        }
      })

      graph.fromJSON(data);
    })

    // 设置容器大小
    function reSize(){
      graph.resize(700, 400)
    }

    // 获取当前画布缩放比例,并设置,scale版(1表示不缩放,无负数)
    function getAndSetPercent(){
      console.log(graph.scale())
      setTimeout(()=>{
        graph.scale(0.5, 0.5, 0, 0)  // x轴缩放比例、y轴缩放比例、缩放中心x坐标、缩放中心y坐标
      }, 2000)
    }

    // 获取当前画布缩放比例,并设置,Zoom版(0表示不缩放,大于0放大小于0缩小)
    function getAndSetPercentZoom(){
      console.log(graph.zoom())
      setTimeout(()=> {
        graph.zoom(0.1, false)  // 缩放比例、是否绝对缩放
      }, 2000)
    }

    // 画布内容居中
    function contentToCenter(){
      graph.centerContent()
    }

    return {
      reSize,
      getAndSetPercent,
      getAndSetPercentZoom,
      contentToCenter,
    }
  }
})
</script>

<style scoped>
</style>

三、基类Cell

(1)addNode和addEdge、markup和选择器

  • cellDemo1.vue
<template>
  <el-button @click="addNodeAndEdge">addNode、addEdge添加点和边</el-button>
  <el-button @click="addCustomNode">添加自定义节点测试markup</el-button>
  <div id="container"></div>
</template>

<script>
import {Graph, Shape, Node} from '@antv/x6'
import {onMounted, defineComponent} from "vue";

export default defineComponent({
  name: 'cellDemo1',
  setup() {
    let graph = {}
    onMounted(() => {
      graph = new Graph({
        container: document.getElementById('container'),
        width: 2000,
        height: 400,
        grid: true,
        background:{
          color: '#e0e07c'
        }
      })
    })

    // addNode和addEdge
    function addNodeAndEdge() {
      const rect = new Shape.Rect({
        id: "node1",
        x: 100,
        y: 200,
        width: 80,
        height: 40,
        label: "rect",
        attrs: {
          body: {
            stroke: 'blue',
            fill: 'red'
          },
          label: {
            text: 'rect',
            fill: '#333'
          }
        },
      });

      const circle = new Shape.Circle({
        id: "node2",
        x: 280,
        y: 200,
        width: 60,
        height: 60,
        label: "circle",
      });
      const edge = new Shape.Edge({
        source: rect,
        target: circle,
      });
      graph.addNode(rect)
      graph.addNode(circle)
      graph.addEdge(edge)
    }

    // 自定义节点,演示markup属性
    function addCustomNode(){
      const customNode = new Node({
        x: 100,
        y: 100,
        width: 80,
        height: 40,
        // markup搭配attrs使用,markup会覆盖
        markup: [
          {
            tagName: 'rect',
            selector: 'body111',
          },
          {
            tagName: 'text',
            selector: 'label111',
          },
        ],
        attrs: {
          // 此处可以写rect/body111,text/label111
          body111: {
            stroke: '#000',
            fill: '#fff',
            ref: 'label111',  // 文本要在矩形里边
            rx: 3,
            ry: 3,
            refWidth: 100,
            refHeight: 100,
            refX: -50,
            refY: -50,
          },
          text: {
            fontSize: 14,
            textAnchor: 'middle',
            textVerticalAnchor: 'middle'
          },
          label111: {
            text: '自定义node'
          }
        }
      })
      console.log('正在添加自定义节点')
      graph.addNode(customNode)
      console.log('添加完成')
    }

    // 组选择器groupSelector,文字label和文字content的公共部分放textGroup
    // markup: [
    //   {
    //     tagName: 'rect',
    //     selector: 'body',
    //   },
    //   {
    //     tagName: 'text',
    //     selector: 'label',
    //     groupSelector: 'textGroup'
    //   },
    //   {
    //     tagName: 'text',
    //     selector: 'content',
    //     groupSelector: 'textGroup'
    //   }
    // ]

    // 优先级:selector > groupSelector > tagName

    return {
      addNodeAndEdge,
      addCustomNode,
    }
  }
})
</script>

<style scoped>
</style>

(2)自定义节点添加图片、自定义节点定制Css

  • cellDemo2.vue
<template>
  <el-button @click="addCustomNode">添加自定义节点image</el-button>
  <el-button @click="addNodeCss">添加自定义节点测试Css</el-button>
  <div id="container"></div>
</template>

<script>
import {Graph, Shape} from '@antv/x6'
import {onMounted, defineComponent} from "vue";

export default defineComponent({
  name: 'cellDemo2',
  setup() {
    let graph = {}
    onMounted(() => {
      graph = new Graph({
        container: document.getElementById('container'),
        width: 2000,
        height: 400,
        grid: true,
        background:{
          color: '#e0e07c'
        }
      })
    })

    // 自定义节点,加图片
    function addCustomNode(){
      const customNode = new Shape.Rect({
        x: 100,
        y: 100,
        width: 200,
        height: 100,
        markup: [
          {
            tagName: 'rect',
            selector: 'body',
          },
          {
            tagName: 'text',
            selector: 'label1',
          },
          {
            tagName: 'text',
            selector: 'label2',
          },
          {
            tagName: 'image',
            selector: 'image',
          }
        ],
        attrs: {
          body: {
            stroke: '#5f95ff',
            strokeWidth: 1,
            fill: '#afafaf'
          },
          image: {
            'xlink:href': 'https://gw.alipayobjects.com/os/s/prod/antv/assets/image/logo-with-text-73b8a.svg',
            width: 40,
            height: 40,
            x: 10,
            y: 10,
          },
          label1: {
            text: 'label1:Node',
            refX: 50,
            refY: 50,
            fontSize: 10,
            fill: '#000',
            'text-anchor': 'start',  // middle、end,决定文字的哪一端位于refX、refY偏移后的位置
          },
          label2: {
            text: 'label2:Welcome cufeuzi',
            refX: 50,
            refY: 70,
            fontSize: 10,
            fill: '#000',
            'text-anchor': 'start'
          }
        }
      })
      graph.addNode(customNode)
    }

    // 自定义节点,定制css
    function addNodeCss(){
        const nodeCss = new Shape.Circle({
          x: 400,
          y: 100,
          width: 200,
          height: 100,
          markup: [
            {
              tagName: 'circle',
              selector: 'body',
            },
            {
              tagName: 'text',
              selector: 'label',
            }
          ],
          attrs: {
            body: {
              fill: '#fff',
              class: 'bodyCss',
              stroke: '#000',
            },
            label: {
              text: 'nodeCss',
              fill: 'blue',
            }
          }
        })
      graph.addNode(nodeCss)
    }

    return {
      addCustomNode,
      addNodeCss,
    }
  }
})
</script>

<style scoped>
#container :deep(.bodyCss) {
  fill: #00fd35;
}
</style>

(3)添加文本节点、通过控制面板控制属性

  • cellDemo3.vue
<template>
  <a-row :gutter="[8, 8]">
    <el-button @click="addTextNode">添加文本节点</el-button>
    <el-button @click="controlChange">通过控制面板修改属性</el-button>
    <el-button @click="toJson">toJSON</el-button>
  </a-row>
  <a-row :gutter="[16, 8]" style="padding-top: 10px">
    <a-col :span="20">
      <div id="container"></div>
    </a-col>
    <a-col :span="4">
      <!-- :model可以父组件传给子组件,但不能反向 -->
      <a-form :model="formState">
        <a-form-item label="标题" v-show="formState.title !== null">
          <!-- 此处必须v-model:value=,不能v-model=,暂不知原理 -->
          <a-input v-model:value="formState.title" @change="onTitleChange"></a-input>
        </a-form-item>

        <a-form-item label="用户" v-show="formState.userId !== null">
          <a-select v-model:value="formState.userId" placeholder="请选择用户" @change="onUserIdChange">
            <a-select-option :value=1>张三</a-select-option>
            <a-select-option :value=2>李四</a-select-option>
          </a-select>
        </a-form-item>

        <a-form-item label="角色" v-show="formState.roleId !== null">
          <a-select v-model:value="formState.roleId" placeholder="请选择角色" @change="onRoleIdChange">
            <a-select-option :value=1>管理员</a-select-option>
            <a-select-option :value=2>普通用户</a-select-option>
          </a-select>
        </a-form-item>
      </a-form>
    </a-col>
  </a-row>
</template>

<script>
import {Graph, Shape, Cell} from '@antv/x6'
import {onMounted, reactive, defineComponent, nextTick} from "vue";

export default defineComponent({
  name: 'cellDemo3',
  setup() {
    // 双向数据绑定
    const formState = reactive({
      title: null,
      userId: null,
      roleId: null,
    })
    let graph = {}
    let curCel = new Cell()
    onMounted(() => {
      graph = new Graph({
        container: document.getElementById('container'),
        height: 400,
        grid: true,
        background:{
          color: '#e0e07c'
        }
      })
    })

    // 点击鼠标时
    nextTick(()=>{
      // 当去点击的cell
      graph.on('cell:click', ({cell})=>{
        // console.log(cell.getAttrs(), cell.getData())
        // 原cell边框变黑
        curCel.attr('body/stroke', null)
        curCel = cell
        // 当前cell边框变红
        curCel.attr('body/stroke', 'red')
        let labelText = null;
        if(cell.getAttrs().text.text) labelText = cell.getAttrs().text.text
        if(cell.getAttrs().label.text) labelText = cell.getAttrs().label.text
        formState.title = labelText
        formState.userId = cell.getData()? cell.getData().userId : undefined
        formState.roleId = cell.getData()? cell.getData().roleId : undefined
        // console.log(formState)
      })
    })

    function onTitleChange(){
      curCel.attr('label/text', formState.title)
    }

    function onUserIdChange(){
      curCel.setData({
        userId: formState.userId
      })
    }

    function onRoleIdChange(){
      curCel.setData({
        roleId: formState.roleId
      })
    }

    // 自定义节点,加图片
    function addTextNode(){
      const textNode = new Shape.TextBlock({
        x: 200,
        y: 40,
        width: 300,
        height: 120,
        text: '我是cufeuzi你是谁啊',
        attrs: {
          label: {
            contenteditable: 'true',
          },
          body: {
            fill: '#efdbff',
            stroke: '#9254de',
            rx: 4,
            ry: 4,
          }
        }
      })
      graph.addNode(textNode)
    }

    // 控制面板修改属性
    function controlChange(){
      const node = new Shape.Rect({
        x: 300,
        y: 200,
        width: 200,
        height: 60,
        attrs: {
          label: {
            text: 'rect',
            fill: 'blue',
          }
        },
        data: {
          userId: 1,
          roleId: 1,
        }
      })
      graph.addNode(node)
    }

    // 控制台输出画布的JSON格式数据
    function toJson(){
      console.log(graph.toJSON())
    }

    return {
      addTextNode,
      controlChange,
      toJson,
      formState,
      onTitleChange,
      onUserIdChange,
      onRoleIdChange,
    }
  }
})
</script>

<style scoped>
</style>

(4)拖拽式页面基本案例

  • cellDemo4.vue
<template>
  <a-row :gutter="[8, 8]">
<!--  gutter表示水平(col和col之间)和垂直(row和row之间)间隔,span表示每个col在水平方向所占大小(总共24)  -->
    <a-col :span="5">
      <div id="stencil"></div>
    </a-col>
    <a-col :span="14">
      <div id="container"></div>
    </a-col>
    <a-col :span="5">
      <a-form :model="formData">
        <a-form-item label="标题" v-show="formData.id !== null">
          <a-select v-model:value="formData.id" placeholder="请选择数据" @change="onIdChange">
            <a-select-option
              v-for="item in dropdownData.tableData"
              :key="item.id"
              :value="item.id">
              {{ item.title }}
            </a-select-option>
          </a-select>
        </a-form-item>

        <a-form-item label="内容" v-show="formData.content !== null">
          <a-input v-model:value="formData.content" @change="onContentChange"/>
        </a-form-item>

        <a-button @click="toJson">toJson</a-button>
      </a-form>
    </a-col>
  </a-row>

</template>

<script>
import {Graph, Shape} from "@antv/x6";
import {Stencil} from "@antv/x6-plugin-stencil";
import {onMounted, reactive} from "vue";

const tableData = [
  {
    id: 1,
    title: '标题1',
    content: '内容1',
  },
  {
    id: 2,
    title: '标题2',
    content: '内容2',
  },
  {
    id: 3,
    title: '标题3',
    content: '内容3',
  }
]

export default {
  name: "cellDemo4",
  setup(){
    let graph = null;
    let stencil = null;
    let curCel = null;

    // 构建当前画布
    function buildGraph(){
      graph = new Graph({
        container: document.getElementById('container'),
        height: 600,
        background: {
          color: '#fffbe6',
        },
        grid: {
          size: 10,
          visible: true,
        }
      })
    }

    // 构建左侧菜单栏
    function buildStencil(){
      stencil = new Stencil({
        target: graph,
        stencilGraphWidth: 290,
        // 是否可折叠
        collapsable: true,
        groups: [
          {
            name: 'basic',
            title: '基础节点',
            graphHeight: 180,
          },
          {
            name: 'combination',
            title: '组合节点',
            layoutOptions: {
              columns: 1,
              marginX: 80,
            },
            graphHeight: 260,
          }
        ]
      })
      document.querySelector('#stencil').appendChild(stencil.container)
    }

    // 左侧菜单栏节点
    function stencilLoadNode(){
      const bizNode1 = new Shape.Rect({
        width: 100,
        height: 50,
        attrs: {
          label: {
            fontSize: 12,
            text: '业务节点1',
          }
        }
      })
      const bizNode2 = new Shape.Rect({
        width: 100,
        height: 50,
        attrs: {
          label: {
            fontSize: 12,
            text: '业务节点2',
          }
        }
      })
      const bizNode3 = new Shape.Rect({
        width: 100,
        height: 50,
        attrs: {
          label: {
            fontSize: 12,
            text: '业务节点3',
          }
        }
      })
      const bizNode4 = new Shape.Rect({
        width: 120,
        height: 50,
        attrs: {
          label: {
            fontSize: 12,
            text: '组合业务节点1',
          }
        }
      })
      stencil.load([bizNode1, bizNode2, bizNode3], 'basic')
      stencil.load([bizNode4], 'combination')
    }

    // 表单数据定义
    let formData = reactive({
      id: null,
      title: null,
      content: null,
    })

    // 节点点击事件
    function initEvent(){
      graph.on('cell:click', ({cell})=>{
        console.log(cell.getAttrs())
        if(curCel != null)
          curCel.attr('body/stroke', null)
        curCel = cell
        curCel.attr('body/stroke', 'red')
        formData.id = cell.getData()? cell.getData().id: undefined

        if(formData.id){
          setTimeout(()=>{
            const tableDataRow = tableData.filter(item => item.id === formData.id)[0]
            formData.title = tableDataRow.title
            formData.content = tableDataRow.content
          }, 100)
        }else{
          formData.title = null
          formData.content = null
        }
      })
    }

    // 定义下拉数据
    const dropdownData = reactive({
      tableData: [],
    })

    onMounted(()=>{
      buildGraph();
      buildStencil();
      stencilLoadNode();
      initEvent();
      setTimeout(()=>{
        dropdownData.tableData = tableData;
      }, 1000);
    })

    function onIdChange(){
      setTimeout(()=>{
        const tableDataRow = tableData.filter(item => item.id === formData.id)[0]
        formData.title = tableDataRow.title
        formData.content = tableDataRow.content

        curCel.setData({
          id: formData.id,
          content: formData.content
        })
        curCel.attr('label/text', formData.title)
      }, 100)
    }

    function onContentChange(){
      curCel.data.content = formData.content
    }

    function toJson(){
      console.log(graph.toJSON())
    }

    return{
      formData,
      dropdownData,
      onIdChange,
      onContentChange,
      toJson,
    }
  }
}
</script>

<style scoped>

</style>

(5)设置默认的condig、使用propHooks

  • cellDemo5.vue
<template>
  <a-button @click="addRect">addRect</a-button>
  <a-button @click="rectReConfig">rectReConfig</a-button>
  <a-button @click="toJson">toJson</a-button>
  <div id="container"></div>
</template>

<script>
import {defineComponent, onMounted} from "vue";
import {Graph, Shape, Node, ObjectExt} from "@antv/x6";

export default defineComponent({
  name: "cellDemo5",
  setup(){
    let graph;
    onMounted(()=>{
      graph = new Graph({
        container: document.getElementById('container'),
        height: 600,
        background: {
          color: '#fffbe6',
        },
        grid: {
          size: 10,
          visible: true,
        }
      })
    })
    function addRect(){
      const rect = new Shape.Rect({
        x: 10,
        y: 40,
        width: 200,
        height: 100,
        attrs: {
          label: {
            text: 'attrs/label'
          }
        }
      })
      graph.addNode(rect)
    }
    function rectReConfig(){
      Shape.Rect.config({
        // 如果一开始设置了,此处设置不作数
        // 如果一开始没设置(addRect中),则使用此处设置的
        // 最终实际效果为,如果addRect有的,以addRect为主,没有的以config为主(深度融合)
        width: 80,
        height: 40,
        attrs: {
          label: {
            fill: 'red'
          }
        },
        data: {
          tableId: 1
        },
        // 钩子函数打印两次,第一次打印config中的设置,第二次打印addRect中的设置
        // 如果使用ObjectExt,则最终效果包含钩子中内容(覆盖之前)
        propHooks(metadata){
          console.log(metadata)
          const { data, ...others } = metadata
          if(data){
            ObjectExt.setByPath(others, 'data/tableId', 2)
          }
          return others
        }
      })
    }
    function toJson(){
      console.log(graph.toJSON())
    }
    return{
      addRect,
      rectReConfig,
      toJson
    }
  }
})
</script>

<style scoped>

</style>



版权声明:本文为m0_56681374原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。