AntV G6 组织图使用(后端渲染数据)

一、业务场景:
点击按钮,跳转页面并显示该数据的组织架构图(类似于粒子效果)

二、问题描述:
初始写死的数据能显示,但是从接口请求到的数据赋上值 渲染不了

三、具体实现步骤:
(1)下载

  npm install --save @antv/g6

(2)在需要使用的文件里面单独引入

  import G6 from '@antv/g6'

(3)所有逻辑处理写在初始化函数里面

 initComponent (arrList,edgesList) {
		// arrList是后台返回的数组数据,拿到后调用初始化函数把值传过来
		// edgesList 是用粒子连接 arrList数组里面数据的(以arrList数组里的第一项为中心)
        this.data = {
          nodes: arrList,
          edges:edgesList
        }
        /**
         * 遍历节点数据,给节点添加图片
         */
        for (let i = 0, len = this.data.nodes.length; i < len; i++) {
          // eslint-disable-next-line eqeqeq
          if (this.data.nodes[i].id !== 'node1') { // 'offlineAnomaly'
            this.data.nodes[i].img = '/labelshow.jpg'
          }
          // eslint-disable-next-line eqeqeq
          if (this.data.nodes[i].status == 1) { // 'onlineAuth'
            // this.data.nodes[i].img = 'https://yyb.gtimg.com/aiplat/page/product/visionimgidy/img/demo6-16a47e5d31.jpg?max_age=31536000'
          }
          // eslint-disable-next-line eqeqeq
          if (this.data.nodes[i].status == 2) { // 'onlineAuth'
            // this.data.nodes[i].img = 'https://img0.baidu.com/it/u=3927459320,2138990686&fm=26&fmt=auto'
          }
        }
        // todo 设置鼠标悬停显示详情操作
        const tooltip = new G6.Tooltip({
          offsetX: 70,
          offsetY: 20,
          getContent (e) {
            const outDiv = document.createElement('div')
            outDiv.style.width = '180px'
            // e.item._cfg.id== 'node1'?<li>人员ID: ${e.item.getModel().id}</li>: <li>标签ID: ${e.item.getModel().id}</li>
            if(e.item._cfg.id== 'node1'){
              outDiv.innerHTML = `
            <ul id="nodeDetails">
              <li>人员名称: ${e.item.getModel().label}</li>
<!--              <li>IP: ${e.item.getModel().ip}</li>-->
<!--               <li>人员ID: ${e.item.getModel().id}</li>-->
<!--              <li>status: ${e.item.getModel().status}</li>-->
            </ul>`
              return outDiv
            }else {
              outDiv.innerHTML = `
            <ul id="nodeDetails">
              <li>标签名称: ${e.item.getModel().label}</li>
               <li>标签ID: ${e.item.getModel().id}</li>
            </ul>`
              return outDiv
            }

          },
          itemTypes: ['node']
        })
        // todo 初始化画布宽高为div parentContent 的宽度和高度
        this.canvasWidth = this.$refs.parentContent.clientWidth
        this.canvasHeight = this.$refs.parentContent.clientHeight
        this.graph = new G6.Graph({
          container: 'container',
          width: this.canvasWidth,
          height: this.canvasHeight,
          linkCenter: true,
          plugins: [tooltip], // 配置 Tooltip 插件
          modes: {
            default: ['drag-canvas', 'zoom-canvas', 'drag-node', 'activate-relations'] // 允许拖拽画布、放缩画布、拖拽节点、设置高亮
          },
          layout: {
            type: 'force',
            preventOverlap: true, // 防止节点重叠
            // 防碰撞必须设置nodeSize或size,否则不生效,由于节点的size设置了40,虽然节点不碰撞了,但是节点之间的距离很近,label几乎都挤在一起,所以又重新设置了大一点的nodeSize,这样效果会好很多
            nodeSize: 120,
            linkDistance: 90 // 指定边距离为150
          },
          defaultNode: { // 节点样式修改
            type: 'image', // 设置节点为图片
            size: [40, 40], // 节点大小
            labelCfg: { // 修改节点label样式
              style: {
                fill: '#5B8FF9', // 字体颜色
                fontSize: 14 // 字体大小
              }
            }
          },
          defaultEdge: {
            style: {
              lineWidth: 2, // 线宽
              stroke: '#778899', // 线的颜色
              endArrow: { // 设置终点箭头
                path: G6.Arrow.vee(5, 20, 15), // 使用内置箭头路径函数,参数为箭头的 宽度、长度、偏移量(默认为 0,与 d 对应)
                d: 15
              }
            }
          }
          // 设置节点高亮样式
          // edgeStateStyles: {
          //   highlight: {
          //     stroke: '#32dadd'
          //   }
          // }
        })

        /**
         * 鼠标移出时,删除节点连线高亮
         * */
        // this.graph.on('node:mouseleave', this.clearAllStats)

        this.graph.on('node:click', (e) => {
          const nodeItem = e.item // 获取被点击的节点元素对象
          console.log(nodeItem)
          console.log('单击', nodeItem._cfg)
          // 如果点击的是人员就开始添加
          if(nodeItem._cfg.id== 'node1'){
            this.visible = true
          }
        if(nodeItem._cfg.id !== 'node1'){
          this.remove(nodeItem._cfg.id)
        }
        })
        // 接收数据并渲染
        this.graph.data(this.data)
        this.graph.render()
        
        const el = this.graph.findById(this.data.nodes[0]?.id)
        console.log('model', el)
         el._cfg.model.label = '张三'
        el._cfg.model.img = 'https://img2.baidu.com/it/u=617857580,3197175121&fm=253&fmt=auto&app=138&f=JPEG?w=260&h=260'
        this.graph.refreshItem(el)

        // 双击节点
        this.graph.on('node:dblclick', (e) => {
          // 先将所有当前是 click 状态的节点置为非 click 状态
          const clickNodes = this.graph.findAllByState('node', 'dblclick')
          clickNodes.forEach((cn) => {
            this.graph.setItemState(cn, 'dblclick', false)
          })
          const nodeItem = e.item // 获取被点击的节点元素对象
          console.log('双击', nodeItem._cfg)
          this.graph.setItemState(nodeItem, 'dblclick', true) // 设置当前节点的 click 状态为 true
        })
      },

(4)在任何地方要重新渲染的话,都需要调用初始化函数,两个值都必须传,必须确保是最新的

         this.initComponent(this.data.nodes,this.data.edges)

四、完整代码

<template>
  <!--设置parentContent的宽高为浏览器大小-->
  <div class="parentContent" ref="parentContent">
    <div id="container" ref="container"></div>

    <!--新增弹框-->
    <a-modal
      :title="labelTitle==0?'新增标签':'编辑标签'"
      :visible="visible"
      width="600px"
      @cancel="handleCancel"
    >
      <template slot="footer">
        <a-button @click="handleCancel">取消</a-button>
        <a-button type="primary" @click="handleOk">确定</a-button>
      </template>
      <a-form-model ref="ruleForm" :model="form" :rules="rules" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
        <a-form-model-item label="标签名称" prop="labelname">
          <a-input v-model="form.labelname" placeholder="请输入"/>
        </a-form-model-item>

        <a-form-model-item label="标签颜色" prop="color">
          <a-input placeholder="请选择" type="color" v-model="form.color"/>
        </a-form-model-item>

        <a-form-item label="标签图标" prop="tb">
          <a-input
            v-model="form.tb"
            ref="iconInput"
            @click="iconselect()"
            enterButton="选择图标"
            placeholder="选择图标"
          >
            <a-icon slot="prefix" :type="icon"/>
            <a-icon slot="suffix" type="close-circle" @click="emitEmpty"/>
          </a-input>
        </a-form-item>

        <a-form-model-item label="代码" prop="code" v-if="form.labelshape=='0'" v-hasPermi="['label:ifSystemLabel']">
          <a-input v-model="form.code" placeholder="请输入"/>
        </a-form-model-item>
      </a-form-model>
      <iconSelector-modal ref="modal" @ok="setIcon" :icon="icon"/>
    </a-modal>
  </div>
</template>
<script>
  import G6 from '@antv/g6'
  import { getLabelLists, delUserLabel, addLabels,addUserLabel, editLabels,showUserLabel } from '@/api/system/label'
  import IconSelectorModal from '@/views/system/modules/IconSelectorModal'
  export default {
    name: 'BqtuDiago',
    components: {
      IconSelectorModal
    },
   async mounted () {
      this.ryId = this.$route.query.ryId
      this.ryname = this.$route.query.name
      console.log(this.ryname)
     this.initSize()
     await this.getUserLabel()
    },
    watch:{
      'this.data.nodes'(val, oldVal) {
        console.log(val,oldVal)
        if(val) {
          const that = this
          val.forEach(function(value, index, array) {
            const el = that.graph.findById(value.id)
            console.log('model', el._cfg.model)
            console.log('value', value)
            el._cfg.model.id = value.id
            el._cfg.model.label = value.label
            el._cfg.model.ip= value.ip
            el._cfg.model.status= value.status
            if (value.status == 1) {
              el._cfg.model.img = '/labelshow.jpg'
            }
            if (value.status == 0) {
              el._cfg.model.img = '.....'
            }
            that.graph.refreshItem(el)
          })
        }
      }
    },
    data () {
      return {
        ryId:'',
        ryname:'',
        labelTitle: 0,
        icon: 'smile',
        visible: false,
        pagination: {
          current: 1,
          pageSize: 10,
          total: 0
        },
        form: {
          labelname: '',
          labeltype: '',
          labelshape: '',
          labelid: '',
          code: '',
          tb: '',
          color: ''
        },
        rules: {
          labelname: [
            { required: true, message: '请输入', trigger: 'blur' }
            // { min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
          ],
          labelshape: [
            { required: true, message: '请选择', trigger: 'blur' }
            // { min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
          ],
          labeltype: [
            { required: true, message: '请选择', trigger: 'blur' }
            // { min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
          ],
          tb: [
            { required: true, message: '请选择', trigger: 'blur' }
            // { min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
          ],
          code: [
            { required: true, message: '请输入', trigger: 'blur' }
            // { min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
          ]

        },

        data: {
          nodes:[],
          edges:[],
        }, // 拓扑图数据
        graph: undefined, // new G6
        canvasWidth: 0, // 画布宽度
        canvasHeight: 0 // 画布高度
      }
    },
    methods: {

      getUserLabel() {
        console.log(this.ryId)
        showUserLabel({
          ryId: this.ryId,
          pageNum: this.pagination.current,
          pageSize: this.pagination.pageSize
        })
          .then(res => {
            console.log(res)
            const { data,code, msg } = res || {}
            this.data.nodes = data?.sysBqRequests
            console.log(this.data.nodes)

            this.data.nodes.forEach((item)=>{
              this.data.edges.push({
                source: 'node1',
                target: item.id
              })
            })
            console.log(this.data.edges)
            this.data.nodes.unshift({
                  id: 'node1',
                  label: '人员',
                  img:'/portrait.png',
                  status:'0'
            })
            console.log(this.data.nodes)
            this.initComponent(this.data.nodes,this.data.edges)
          }).catch((err) => {
          console.log(err)
        })
      },

// 删除
      remove(id) {
        // e.preventDefault();//阻止默认行为
        console.log(id)
        // this.selectedRowKeys.push(id)
        // console.log(this.selectedRowKeys)
        this.$confirm({
          content: '您确定要删除此标签吗?',
          okText: '确定',
          centered: true,//页面居中位置
          okType: 'danger',
          cancelText: '取消',
          onOk: () => {
            delUserLabel({
              bqId: id
            }).then(res => {
              console.log(res)
              let { msg } = res
              if (msg == 'success') {
                this.$message.success('操作成功')
                // 销毁画布在新增上去
                this.graph.destroy();
                this.getUserLabel()
                // 删除以后清空要删除的数组
                // this.selectedRowKeys = []
              } else {
                // this.$message.error(errorMessage)
              }
            }).catch(err => {
              console.log(err)
            })
          },
          onCancel: () => {
            console.log('Cancel')
          }
        })
      },
      iconselect() {
        this.$refs.modal.show()
      },
      setIcon(icon) {
        console.log(icon)
        this.icon = icon
        this.form.tb = icon
        // this.form.setFieldsValue({ icon: icon })
      },
      emitEmpty() {
        this.$refs.iconInput.focus()
        // this.form.setFieldsValue({ icon: '' })
        this.form.tb = ''
      },
      handleCancel() {
        if(this.labelTitle ==0){
          this.form = {}
        }
        this.visible = false
      },
      // 新增和编辑标签
      handleOk() {
        this.$refs.ruleForm.validate(valid => {
          if (valid) {
            if (this.labelTitle == 0) {
              console.log(this.ryId)
              addUserLabel({
                mc: this.form.labelname,
                // code:this.form.code,
                dm: this.form.code,
                // sfxt: this.form.labelshape,
                ys: this.form.color,
                lx: '1',
                ryId:this.ryId,
                tb: this.form.tb
              })
                .then(res => {
                  console.log(res)
                  let { msg } = res
                  if (msg == 'success') {
                    this.visible = false
                    this.$message.success('操作成功')
                    // 销毁画布在新增上去
                    this.graph.destroy();
                    this.getUserLabel()
                    // this.form = {}
                  } else {
                    this.$message.error(msg)
                  }
                }).catch((err) => {
                console.log(err)
              })
            } else {
              editLabels({
                mc: this.form.labelname,
                dm: this.form.code,
                ys: this.form.color,
                sfxt: this.form.labelshape,
                lx: this.form.labeltype,
                tb: this.form.tb,
                id: this.form.labelid
              })
                .then(res => {
                  console.log(res)
                  let { msg } = res
                  if (msg == 'success') {
                    this.visible = false
                    this.$message.success('操作成功')

                  }
                }).catch((err) => {
                console.log(err)
              })
            }
          }
        })
      },




      /**
       * 设置画布大小自适应
       */
      initSize () {
        const self = this // 因为箭头函数会改变this指向,指向windows。所以先把this保存
        setTimeout(() => {
          // todo 浏览器窗口发生变化时
          window.onresize = function () {
            // todo 获取div parentContent 的宽度和高度
            this.canvasWidth = self.$refs?.parentContent?.clientWidth
            this.canvasHeight = self.$refs?.parentContent?.clientHeight
            // todo 修改画布的大小
            self.graph.changeSize(this.canvasWidth, this.canvasHeight)
            // todo 将图移动到画布中心位置
            self.graph.fitCenter()
          }
        }, 20)
      },
      /**
       * 创建G6,并对G6的一些设置
       * */

      initComponent (arrList,edgesList) {
        console.log(arrList)
        this.data = {
          nodes: arrList,
          //   nodes:[
          //   {
          //     id: 'node1',
          //     label: '采集服务器',
          //     // ip: '192.168.1.1',
          //     // status: 0
          //     // 此处的key值一定不要出现type,如果出现type,图片修改无效
          //   },
          //   {
          //     id: 'node2',
          //     label: '标签1',
          //   },
          //   {
          //     id: 'node3',
          //     label: '标签2',
          //   },
          //   {
          //     id: 'node4',
          //     label: '标签3',
          //   },
          //   {
          //     id: 'node5',
          //     label: '标签4',
          //   },
          //   {
          //     id: 'node6',
          //     label: '标签5',
          //   },
          // ],
          edges:edgesList

            // edges[
            // {
            //   source: 'node1',
            //   target: '188'
            // },
            // {
            //   source: 'node1',
            //   target: '189'
            // },
            // {
            //   source: 'node1',
            //   target: '201'
            // },

          // ]
        }
        /**
         * 遍历节点数据,给节点添加图片
         */
        console.log(this.data.nodes)
        for (let i = 0, len = this.data.nodes.length; i < len; i++) {
          // eslint-disable-next-line eqeqeq
          if (this.data.nodes[i].id !== 'node1') { // 'offlineAnomaly'
            this.data.nodes[i].img = '/labelshow.jpg'
          }
          // eslint-disable-next-line eqeqeq
          if (this.data.nodes[i].status == 1) { // 'onlineAuth'
            // this.data.nodes[i].img = 'https://yyb.gtimg.com/aiplat/page/product/visionimgidy/img/demo6-16a47e5d31.jpg?max_age=31536000'
          }
          // eslint-disable-next-line eqeqeq
          if (this.data.nodes[i].status == 2) { // 'onlineAuth'
            // this.data.nodes[i].img = 'https://img0.baidu.com/it/u=3927459320,2138990686&fm=26&fmt=auto'
          }
        }
        // todo 设置鼠标悬停显示详情操作
        const tooltip = new G6.Tooltip({
          offsetX: 70,
          offsetY: 20,
          getContent (e) {
            const outDiv = document.createElement('div')
            outDiv.style.width = '180px'
            // e.item._cfg.id== 'node1'?<li>人员ID: ${e.item.getModel().id}</li>: <li>标签ID: ${e.item.getModel().id}</li>
            if(e.item._cfg.id== 'node1'){
              outDiv.innerHTML = `
            <ul id="nodeDetails">
              <li>人员名称: ${e.item.getModel().label}</li>
<!--              <li>IP: ${e.item.getModel().ip}</li>-->
<!--               <li>人员ID: ${e.item.getModel().id}</li>-->
<!--              <li>status: ${e.item.getModel().status}</li>-->
            </ul>`
              return outDiv
            }else {
              outDiv.innerHTML = `
            <ul id="nodeDetails">
              <li>标签名称: ${e.item.getModel().label}</li>
               <li>标签ID: ${e.item.getModel().id}</li>
            </ul>`
              return outDiv
            }

          },
          itemTypes: ['node']
        })
        // todo 初始化画布宽高为div parentContent 的宽度和高度
        this.canvasWidth = this.$refs.parentContent.clientWidth
        this.canvasHeight = this.$refs.parentContent.clientHeight
        this.graph = new G6.Graph({
          container: 'container',
          width: this.canvasWidth,
          height: this.canvasHeight,
          linkCenter: true,
          plugins: [tooltip], // 配置 Tooltip 插件
          modes: {
            default: ['drag-canvas', 'zoom-canvas', 'drag-node', 'activate-relations'] // 允许拖拽画布、放缩画布、拖拽节点、设置高亮
          },
          layout: {
            type: 'force',
            preventOverlap: true, // 防止节点重叠
            // 防碰撞必须设置nodeSize或size,否则不生效,由于节点的size设置了40,虽然节点不碰撞了,但是节点之间的距离很近,label几乎都挤在一起,所以又重新设置了大一点的nodeSize,这样效果会好很多
            nodeSize: 120,
            linkDistance: 90 // 指定边距离为150
          },
          defaultNode: { // 节点样式修改
            type: 'image', // 设置节点为图片
            size: [40, 40], // 节点大小
            labelCfg: { // 修改节点label样式
              style: {
                fill: '#5B8FF9', // 字体颜色
                fontSize: 14 // 字体大小
              }
            }
          },
          defaultEdge: {
            style: {
              lineWidth: 2, // 线宽
              stroke: '#778899', // 线的颜色
              endArrow: { // 设置终点箭头
                path: G6.Arrow.vee(5, 20, 15), // 使用内置箭头路径函数,参数为箭头的 宽度、长度、偏移量(默认为 0,与 d 对应)
                d: 15
              }
            }
          }
          // 设置节点高亮样式
          // edgeStateStyles: {
          //   highlight: {
          //     stroke: '#32dadd'
          //   }
          // }
        })
        /**
         * 鼠标移入时,设置节点连线高亮
         * */
        // const that = this // 改变this指向
        // this.graph.on('node:mouseenter', function (e) {
        //   let item = e.item
        //   that.graph.setAutoPaint(false)
        //   that.graph.getNodes().forEach(function (node) {
        //     that.graph.clearItemStates(node)
        //     that.graph.setItemState(node, 'dark', true)
        //   })
        //   that.graph.setItemState(item, 'dark', false)
        //   that.graph.setItemState(item, 'highlight', true)
        //   that.graph.getEdges().forEach(function (edge) {
        //     if (edge.getSource() === item) {
        //       that.graph.setItemState(edge.getTarget(), 'dark', false)
        //       that.graph.setItemState(edge.getTarget(), 'highlight', true)
        //       that.graph.setItemState(edge, 'highlight', true)
        //       edge.toFront()
        //     } else if (edge.getTarget() === item) {
        //       that.graph.setItemState(edge.getSource(), 'dark', false)
        //       that.graph.setItemState(edge.getSource(), 'highlight', true)
        //       that.graph.setItemState(edge, 'highlight', true)
        //       edge.toFront()
        //     } else {
        //       that.graph.setItemState(edge, 'highlight', false)
        //     }
        //   })
        //   that.graph.paint()
        //   that.graph.setAutoPaint(true)
        // })
        /**
         * 鼠标移出时,删除节点连线高亮
         * */
        // this.graph.on('node:mouseleave', this.clearAllStats)

        this.graph.on('node:click', (e) => {
          const nodeItem = e.item // 获取被点击的节点元素对象
          console.log(nodeItem)
          console.log('单击', nodeItem._cfg)
          // 如果点击的是人员就开始添加
          if(nodeItem._cfg.id== 'node1'){
            this.visible = true
          }
        if(nodeItem._cfg.id !== 'node1'){
          this.remove(nodeItem._cfg.id)
        }
        })

        // 接收数据并渲染
        this.graph.data(this.data)
        this.graph.render()



        console.log(this.data.nodes)
        console.log(this.data.nodes[0])
        const el = this.graph.findById(this.data.nodes[0]?.id)
        console.log('model', el)
        // // el._cfg.model.label = '张三'
        el._cfg.model.label = this.ryname
        // el._cfg.model.ip = '192.168.....'
        // el._cfg.model.status = 4
        el._cfg.model.img = '/portrait.png'
        // el._cfg.model.img = 'https://img2.baidu.com/it/u=617857580,3197175121&fm=253&fmt=auto&app=138&f=JPEG?w=260&h=260'
        this.graph.refreshItem(el)


        // 双击节点
        this.graph.on('node:dblclick', (e) => {
          // 先将所有当前是 click 状态的节点置为非 click 状态
          const clickNodes = this.graph.findAllByState('node', 'dblclick')
          clickNodes.forEach((cn) => {
            this.graph.setItemState(cn, 'dblclick', false)
          })
          const nodeItem = e.item // 获取被点击的节点元素对象
          console.log('双击', nodeItem._cfg)
          this.graph.setItemState(nodeItem, 'dblclick', true) // 设置当前节点的 click 状态为 true
        })
      },
      /**
       * 清除节点连线高亮状态
       */
      clearAllStats () {
        const that = this
        this.graph.setAutoPaint(false)
        this.graph.getNodes().forEach(function (node) {
          that.graph.clearItemStates(node)
        })
        this.graph.getEdges().forEach(function (edge) {
          that.graph.clearItemStates(edge)
        })
        this.graph.paint()
        this.graph.setAutoPaint(true)
      }
    }
  }
</script>

<style>
  .parentContent {
    width: 100%;
    height: 100%;
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
  }
  #nodeDetails {
    list-style: none;
  }
  #nodeDetails>li {
    padding:5px 0;
    text-align: left;
  }
</style>

五、效果展示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

你已经成功了,撒花。
今天的分享到此结束,欢迎小伙伴们一起交流

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值