antv- X6----vue 组件循环渲染

antv- X6----vue 组件循环渲染

公司需求需要做一个思维导图类似于飞书后台管理的okr思维导图,也是第一次接触,但是我感觉好像canvas也能画,我太菜了不会,就去找了好多的案例框架啥的,一开始用的SuperFlow 这个思维导图,但是没研究出来偶然看见了antv–x6这个框架,其中也设计到vue2使用这个框架,但是因为项目是vue3所以做了一些改变

第一步:

先创建一个画布,这个ID是你的画布ID
antv- X6----vue 组件循环渲染_第1张图片
antv- X6----vue 组件循环渲染_第2张图片

下面是数据结构…一定要给结构里面添加X跟Y的值,这样你的画布在渲染这个地方的时候才能显示的出来,如果是从后台拿数据,那么就循环添加一下做一个x跟y的计算就好
antv- X6----vue 组件循环渲染_第3张图片
接下来就是创建画布了,通过ID去绑定,其他的东西官网上面都有,一搜就出来,这里我就不多做解释了
antv- X6----vue 组件循环渲染_第4张图片

这里就是创建节点了,节点一定是先创建之后才能再绘画
antv- X6----vue 组件循环渲染_第5张图片

graph.addNode就是创建节点,给它放在循环里面,这样就可以循环树状的节点
antv- X6----vue 组件循环渲染_第6张图片

子组件关键内容已经给标记出来了,不懂直接复制代码去试一下就懂了

antv- X6----vue 组件循环渲染_第7张图片
下面是父组件

<template>
  <div class="wrap">
    <div class="home">
      <div class="menu-bar">
        <!-- 画布部分 -->
          <div class="canvas-card">
            <div id="container" ></div>
          </div>
        </div>
      </div>
    </div>
</template>

<script  lang="ts">
import { Graph,Shape } from '@antv/x6'
import { defineComponent,onMounted, reactive } from 'vue'
import '@antv/x6-vue-shape'
import Cyndi from "./Cyndi.vue";
import { OkrView } from '@/apis/okr'
import { useStore } from '@/store'
import ColleagueOkrVue from '../MyOKR/ColleagueOkr.vue';

export default defineComponent({
  setup() {
    const state = reactive({
      moduleList: [
        {
          id: 1,
          name: '测试1',
          o: '12312312',
          kr: [
            '123123123',
            '321321312'
          ],
          krCount: 2,
          proess: 0,
          x: 200,
          y:300,
          children: [
            {
              id: 2,
              name: '测试子集',
              o: '12312312',
              kr: [
                '123123123',
                '321321312'
              ],
              krCount: 2,
              proess: 0,
              x: 600,
              y:220
            },
            {
              id: 3,
              name: '测试子集2',
              o: '12312312',
              kr: [
                '123123123',
                '321321312'
              ],
              krCount: 2,
              proess: 0,
              x: 600,
              y:350
            }
          ],
        }
      ], // 列表可拖动模块
    })

  const getData = () => {
    const params = {
      employeeNum:'10015',
      period:''
    }
    state.moduleList = res.result
      res.result.map((item, index) => {
        item.isShow = false
        if (res.result.length > 1) {
          item.x = 200
          item.y = 300 * index
        } else {
          item.x = 200
          item.y = 300
        }
        if (item.children.length !== 0) {
           item.map((row, rowIndex) => {
              if (item.children.length > 1) {
                row.x = 600
                row.y = 150 * rowIndex
              } else {
                row.x = 600
                row.y = 150
              }
          })
        }
      })
      state.moduleList = res.result
      if (state.moduleList !== []) {
        restGraph()
      }
  }
    let graph: Graph;
    const restGraph = () => {
      let container = document.getElementById('container') as HTMLElement
      graph = new Graph({
        container:container, // 画布容器  创建画布
        height: document.body.offsetHeight,
        background: {
          color: "#f2f1f6", // 设置画布背景颜色
        },
        panning: {
          enabled: true  //支持拖拽
        },
        mousewheel: {
          enabled: true // 支持滚动放大缩小
        },
        grid: {
          type: 'mesh',
          size: 5, // 网格大小 10px
          visible: true, // 渲染网格背景
          args: {
            color: '#f0f2f5', // 网格线/点颜色
            thickness: 1 // 网格线宽度/网格点大小
          }
        },
        rotating: {
          enabled: true,
        },
        connecting: {
          allowBlank: false,
          connector: 'smooth',
        },

      })

      // 第二种封装抽离写法
      Graph.registerNode("cyndi-shape", {
        inherit: "vue-shape",
        x: 200,
        y: 150,


        component: {
          template: `
          
        `,
          components: {
            Cyndi
          },
        },
      });

      state.moduleList.map((item:any) => {
          graph.addNode({  //这个addNode就是创建节点
            id:item.id,
            shape: "cyndi-shape",
            x: item.x,
            y: item.y,
            data:item
          })
        if (item.children) {
          item.children.map(row => {
            console.log(row, '123');

            graph.addNode({  //这个addNode就是创建节点
              id:row.id,
              shape: "cyndi-shape",
              x: row.x,
              y: row.y,
              data:row
            })
            graph.addEdge({
              source: {
                cell: item.id,

                anchor: {
                  name: 'right',
                  args: {
                    dx:300,
                    dy: 70,
                  },
                },
              },
              target: {
                cell: row.id,
                attrs: {
                  line: {
                    strokt: '#ff3040',
                    strokeWidth: 2,
                    targetMarker: null,
                  }
                },
                anchor: {
                  name: 'left',
                  args: {
                    dy: 70,
                  },
                },
              }
            })
          })
        }
      })

    }

    onMounted(() => {
      getData()
    })

     return {
      getData,
      state
    };
  }
})

</script>

下面是子组件

<template>
<div class="count_main">
  <div class="title">
      <div class="img">
        <img src="@/assets/images/myOkr/okrNotice.png" alt="">
      </div>
      <div class="name">{{data.name || ''}} 
      </div>
          <!-- :color="rateStatus[item.rateStatus]" -->
      <div class="num">
         <el-progress
          type="circle"
          stroke-width="20"
          :percentage="data.process"
        />
         {{data.process || ''}}
      </div>
  </div> 
  <div class="count_main_list">
    <div class="main_target">
        <span class="main_target_sort">0{{data.id}}</span>
        <span class="main_target_text">{{data.o}}</span>
    </div>
    <div class="main_okr_list" v-show="data.isShow == true">
      <ul>
        <li v-for="(item,index) in data.kr" :key="index">
          <span>KR{{index + 1 }}</span>
          <span class="text">{{item}}</span>
        </li>
      </ul>
    </div>
  </div>
  <div class="count_suspension" @click="handleShow(data)">{{data.krCount}}</div> 
</div>
</template>

<script lang="ts">
import { Node } from "@antv/x6";
import { defineComponent, onMounted, ref, inject } from "vue";
 import { ElProgress } from 'element-plus'
export default defineComponent({
  components: { ElProgress },
  name: "Cyndi",
  setup() {
    const rateStatus = {
      1: '#5dc928',
      99: '#ff6b6c',
      55: '#ffb900'
    }
    const data = ref({
      process: null,
      name:'',
      isShow: false,
      kr: [],
      krCount:''
      });
    onMounted(() => {
      const getNode: Function | undefined = inject<Function>("getNode");
      const node: Node = getNode?.();
      data.value = node?.data;
      console.log(data.value,11111111111111111);
      // 监听数据改变事件
      node?.on("change:data", ({ current }) => {
        data.value = current.title;
      });
    });

   const handleShow = (item) =>{
      return item.isShow = !item.isShow
    }
    return {
      data,
      rateStatus,
      handleShow
    };
  },
});
</script>

<style lang="scss">
.count_main{
  width: 300px;
  background: #fff;
  margin-top:20px;
  border-radius: 5px;
  box-shadow: 1px 1px 1px 1px #eef0f4;
  position: relative;
.title{
  display:flex;
  border-top: 2px solid green;
  line-height: 30px;
  border-bottom: 1px solid #f3f3f3;
  .img{
    img{
      width: 20px;
      height: 20px;
      border-radius: 50%;
      vertical-align: middle;
    }
  }
  .name{
    flex:3;
    padding-left: 10px;
    box-sizing: border-box;
    line-height: 33px;
    color:#5d6b82;
  }

  .num{
    width:50px
  }
}
.count_main_list{
  height: 100%;
  .main_target{
    // height: 30px;
    // line-height: 30px;
    padding: 10px;
    box-sizing: border-box;
    .main_target_sort{
      display: inline-block;
      background-color: #0052cc;
      color: #fff;
      padding:2px 3px;
      box-sizing: border-box;
      border-radius: 5px;
    }
    .main_target_text{
      display: inline-block;
      color: #5d6b82;
      margin-left: 5px;
    }
  }
}
.count_suspension{
  position: absolute;
  top:60%;
  right: -20px;
  font-size: 12px;
  border-radius: 50%;
  background: #fff;
  padding: 2px 4px;
  box-sizing: border-box;
}
.count_suspension:hover{
  background: #0052cc;
  color: #fff;
  cursor: pointer;
}

.main_okr_list{
  padding:0px 20px;
  box-sizing: border-box;
  li{
    padding: 5px 0;
    box-sizing: border-box;
    span{
      background: #e5f4ff;
      color: #3175d7;
      padding: 2px 4px;
      box-sizing: border-box;
      border-radius: 5px;
    }
    .text{
      color: #7b879a;
      background: none;
    }
  }
}
}
</style>
<style lang="scss" scoped>
::v-deep .el-progress-circle {
  width: 20px !important;
  height: 20px !important;
}

::v-deep .el-progress--circle, .el-progress--dashboard{
    padding-top: 4px;
    display: inline-block;
    box-sizing: border-box;
    span{
      display: inline-block;
      padding-left: 20px;
      box-sizing:border-box;
      line-height: 33px;
    }
}

</style>

写的不好请见谅,讲解的比较粗糙,如果有不懂的欢迎随时私信

你可能感兴趣的:(思维导图,vue.js)