<template>
  <div>
    <div class="tools-bar">
      <el-popover
        placement="bottom"
        width="200"
        class="el-po"
        trigger="click">
        <div ref="stencilContainer" class="x6-stencil"></div>
        <i class="el-icon-s-operation opt-btn" slot="reference" ></i>
      </el-popover>
    </div>
    <div class="content" :style="{ height: contentHeight }">
      <!-- <div ref="stencilContainer" class="x6-stencil"></div> -->
      <div class="middle-box">
        <div ref="container" class="x6-content" :style="{ height: contentHeight }"></div>
        <!-- <div @click="click" style="width:600px;height:600px; position: absolute;z-index: 3;background:red;pointer-events;none;">这个是上层</div> -->
        <!-- <div @click="click2" style="width:800px;height:600px; position: absolute;z-index: 2;background:yellow;pointer-events;none;">这个是中层</div> -->
        <!-- 中间地图 -->
        <baidu-map 
          class="mp-view" 
          :style="{ height: mapHeight }" 
          :center="center" 
          :zoom="zoom" 
          @ready="handler"></baidu-map>
      </div>
    </div>
  </div>
</template>

<script>
import { Graph, Shape, Addon,Path } from '@antv/x6';
const { Stencil } = Addon
const { Rect, Circle } = Shape
export default {
  data() {
    return {
      contentHeight:'0px', //容器高度，x6高度
      mapHeight: "200px", //地图高度
      center: {lng: 0, lat: 0},//地图中心点变量
      zoom: 3,//地图缩放变量

      graph:undefined,//x6画布
      stencil:undefined,//x6拖拽

      projection:undefined,//地图projection参数
      data : {
        // 节点
        nodes: [
          {
            id: 'node1', // String，可选，节点的唯一标识
            x: 0,       // Number，必选，节点位置的 x 值
            y: 0,       // Number，必选，节点位置的 y 值
            width: 80,   // Number，可选，节点大小的 width 值
            height: 40,  // Number，可选，节点大小的 height 值
            label: 'hello', // String，节点标签
          },
          {
            id: 'node2', // String，节点的唯一标识
            x: 160,      // Number，必选，节点位置的 x 值
            y: 180,      // Number，必选，节点位置的 y 值
            width: 80,   // Number，可选，节点大小的 width 值
            height: 40,  // Number，可选，节点大小的 height 值
            label: 'world', // String，节点标签
          },
        ],
        // 边
        edges: [
          {
            source: 'node1', // String，必须，起始节点 id
            target: 'node2', // String，必须，目标节点 id
          },
        ],
      }
    }
  },
  mounted () {
    this.init()
    this.initCell()
    this.initGraph();
    this.initStencil()
  },
  methods:{
    init() {
      this.contentHeight = document.documentElement.clientHeight -45 + "px";
      this.mapHeight = document.documentElement.clientHeight -45 + "px";
    },
    //初始化地图设置数据
    handler ({BMap, map}) {
      console.log(BMap, map)
      this.center.lng = 116.404
      this.center.lat = 39.915
      this.zoom = 14
      this.projection = map
    },
    //初始化图形
    initCell(){
      //桩
      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,
      )
    },
    //初始化画布
    initGraph(){
      let mapZoom = this.zoom
      this.graph = new Graph({
        container: this.$refs.container,
        background: {//背景
          color: 'transport', // 设置画布背景颜色
        },
        grid: {//网格
          size: 10,      // 网格大小 10px
          visible: true, // 渲染网格背景
        },
        // panning: {//画布是否可以拖动
        //   enabled: true, //开启支持拖拽平移
        //   modifiers: 'shift', //按下修饰键并点击鼠标才能触发画布拖拽
        // },
        mousewheel: {//滚轮缩放画布
          enabled: true,
          modifiers: ['ctrl'],
          guard(e){//判断一个滚轮事件是否应该被处理
            
            return true
          }
        },
        snapline: true,//对齐线
        selecting: {//点选/框选
          enabled: true,//开启点选/框选
          rubberband: true,//启用框选
          showNodeSelectionBox: true,//是否显示节点的选择框
        },
        resizing:true,//缩放节点
        highlighting: {//高亮选项，指定触发某种交互时的高亮样式
          // 当链接桩可以被链接时，在链接桩外围渲染一个蓝色
          magnetAdsorbed: {
            name: 'stroke',
            args: {
              attrs: {
                //fill: '#5F95FF',
                stroke: '#5F95FF',
              },
            },
          },
          //当可以当父节点时，高亮
          embedding: {
            name: 'stroke',
            args: {
              padding: -1,
              attrs: {
                stroke: '#73d13d',
              },
            },
          },
        },
        connecting:{//连线配置
          //设置连接桩自动吸附，吸附距离为20
          snap: {
            radius: 20,
          },
          allowBlank:false,//是否允许连接到画布空白位置的点，设置为否
          allowNode:false,//是否允许边链接到节点（非节点上的链接桩）
          allowEdge:false,//是否允许边链接到另一个边
          anchor: 'center',//当连接到节点时，通过 anchor 来指定被连接的节点的锚点
          connectionPoint: 'anchor',//指定连接点
          router: {//路由将边的路径点 vertices 做进一步转换处理
            name: 'manhattan',
            args: {
              padding: 1,
            },
          },
          connector: {//连接器
            name: 'rounded',
            args: {
              radius: 8,
            },
          },
          createEdge() {//创建新的边
            return new Shape.Edge({
              attrs: {
                line: {
                  //stroke: '#A2B1C3',
                  stroke: 'green',
                  strokeWidth: 3,
                  targetMarker: {
                    name: 'block',
                    width: 12,
                    height: 8,
                  },
                },
              },
              zIndex: 20,
            })
          },
          validateConnection({ targetMagnet }) {//在移动边的时候判断连接是否有效
            return !!targetMagnet
          },
        },
        //节点嵌套
        embedding:{
          enabled:true,//开启节点嵌套
          findParent(node){//在节点被移动时通过 findParent 指定的方法返回父节点
            const bbox = node.node.getBBox()
            return this.getNodes().filter((node) => {
              // 只有 data.parent 为 true 的节点才是父节点
              const data = node.getData()
              if (data && data.canBeParent) {
                const targetBBox = node.getBBox()
                return bbox.isIntersectWithRect(targetBBox)
              }
              return false
            })
          }
        },
        //限制子节点的移动范围
        translating: {
          restrict(view) {
            if(view){
              const cell = view.cell
              if (cell.isNode()) {
                const parent = cell.getParent()
                if (parent) {
                  return parent.getBBox()
                }
              }
            }
            return null
          },
        },
      });
      //this.graph.fromJSON(this.data)

      //事件注册
      //鼠标移动到节点上，显示桩、给当前节点添加删除按钮
      this.graph.on('node:mouseenter', ( e ) => {
        const container = this.$refs.container
        const ports = container.querySelectorAll(
          '.x6-port-body',
        ) 
        this.showPorts(ports, true)
        this.showNodeTool(e.node,true)
      })
      //鼠标移出节点，隐藏桩、删除当前节点删除按钮
      this.graph.on('node:mouseleave', ( e) => {
        const container = this.$refs.container
        const ports = container.querySelectorAll(
          '.x6-port-body',
        ) 
        this.showPorts(ports, false)
        this.showNodeTool(e.node,false)
      })
      //鼠标移动到线上，给当前线添加删除按钮
      this.graph.on('edge:mouseenter',(e)=>{
        this.showEdgeTool(e.edge,true)
      })
      //鼠标移出线，删除当前线的删除按钮
      this.graph.on('edge:mouseleave',(e)=>{
        this.showEdgeTool(e.edge,false)
      })
      //移入父节点，父节点改变后
      this.graph.on('node:change:parent', ({ node }) => {
        node.attr({
          body:{
            stroke: 'none',
            fill: '#47C769',
          },
          label: {
            text: '节点-过程',
          },
        })
      })
      //节点添加到画布中
      this.graph.on('node:added',(args)=>{
        //将在容器的位置坐标转换为地图的地理坐标
        this.pixelToPoint(args.node,args.node.store.data.position)
      })
      //节点在画布上移动
      this.graph.on('node:change:position',(args)=>{
        //将在容器的位置坐标转换为地图的地理坐标
        this.pixelToPoint(args.node,args.current)
      })
      //画布平移
      // this.graph.on('translate', ({ tx, ty }) => {
      //   console.log("平移tx",tx)
      //   //console.log("平移ty",ty)
      //   this.mapScrollBy(tx,ty)
      // })
    },
    //初始化拖拽Stencil
    initStencil(){
      this.stencil = new Stencil({
        target: this.graph,//设置画布
        stencilGraphWidth: 200,//模板画布宽度
        stencilGraphHeight: 80,//模板画布高度
        groups: [
          {
            name: 'group1',//分组名称
            title: '元素',//分组标题
            collapsable: false,//分组是否可折叠，默认为 true
          },
        ],
      })
      //将Stencil挂载到页面上
      this.$refs.stencilContainer.appendChild(this.stencil.container)
      //定义模板节点
      const process = this.graph.createNode({
        shape: 'custom-rect',
        label: '过程',
        zIndex: 10,
        attrs: {
          body: {
            stroke: 'none',
            fill: '#3199FF',
          },
        },
      })
      const node = this.graph.createNode({
        shape: 'custom-rect',
        label: '节点',
        zIndex: 1,
        data: {
          canBeParent: true,
        },
      })
      //装载模板节点
      this.stencil.load([process,node], 'group1')
    },
    //链接桩显示/隐藏事件
    showPorts(ports,show){
      ports.forEach(port => {
        port.style.visibility = show?'visible' : 'hidden'
      });
    },  
    //节点上的删除按钮添加/删除事件
    showNodeTool(node,show){
      if(show){
        node.addTools({
          name: 'button-remove',
          args: {
            x: '100%',
            y: 0,
            offset: { x: -3, y: 3 },
          },
        })
      }else{
        node.removeTools()
      }
    },
    //线上的删除按钮添加/删除事件
    showEdgeTool(edge,show){
      if(show){
        edge.addTools({
          name: 'button-remove',
          args: { distance: -40 },
        })
      }else{
        edge.removeTools()
      }
    },
    //将像素坐标转换为地理坐标
    pixelToPoint(node,position){
      let ePoint = this.projection.pixelToPoint({x:position.x,y:position.y})
      //将数据记录到node中
      node.store.data.mapPoint = ePoint
    },
    //地图平移
    mapScrollBy(tx,ty){
      this.projection.panBy(tx,ty)
    },
  }

}
</script>

<style lang="scss" scoped>
.tools-bar{
  height: 45px;
  line-height: 45px;
  background: #fff;
  display: flex;
  
  .opt-btn{
    margin-top: 4px;
    font-size: 35px;
  }
}
.content {
  display: flex;
  .x6-stencil{
    width: 250px;
  }
  .middle-box{
    width: 100%;
    position: relative;
    .x6-content {
      width: 100%;
      position: absolute;
      z-index: 2;
    }
    .mp-view{
      width: 100%;
      position:absolute;
      z-index: 1;
    }
  }
}
.el-popover{
  height: 500px; 
  overflow: auto;
}
</style>
<style>
.el-popover{
  height: 120px; 
  overflow: auto;
}
</style>