<template>
  <div class="rightX6" ref="rightX6">
    <!-- 顶部标题 -->
    <div class="topLabel">
      <span>{{ rootNodeName }}</span>
    </div>
    <!-- X6 -->
    <div id="rightX6"></div>

    <!-- 模态框 Start -->
    <DragModal
        :visible="modalVisible"
        :title="title"
        :width="1000"
        :destroyOnClose="true"
        :maskClosable="false"
        @cancel="modalVisible = false"
        :footer="null">
      <!-- 添加结构 -->
      <AddStructure
          ref='addStructure'
          v-if="actionName === 'addStructure'"
          :checkedNode='checkedNode'
          @closeModal='closeModal'
          @initLeftTree='initLeftTree'
          @initRightX6='initRightX6'
          @addRightX6Node='addNode'
      />
      <!-- 添加接口 -->
      <AddInterface
          ref='addInterface'
          v-if="actionName === 'addInterface'"
          :checkedNode='checkedNode'
          @closeModal='closeModal'
          @initLeftTree='initLeftTree'
          @initRightX6='initRightX6'
          @addRightX6Node='addNode'
      />
      <!-- 添加评审意见 -->
      <AddReviewComments
          ref='addReviewComments'
          v-if="actionName === 'addReviewComments'"
          :checkedNode='checkedNode'
          @closeModal='closeModal'
          @initLeftTree='initLeftTree'
      />
    </DragModal>
    <!-- 模态框 End -->
    <!-- 连线属性 Start -->
    <PropertyEdgeModal
        ref="propertyEdgeModal"
        @setCheckedNode="setCheckedNode"
        @refreshLineShowHide="refreshLineShowHide"
        @handleEdgesCurve="handleEdgesCurve"
    />
    <!-- 连线属性 End -->
    <!-- 节点属性 Start -->
    <PropertyNodeModal
        ref="propertyNodeModal"
        @initLeftTree='initLeftTree'
        @refreshPosition='refreshPosition'
    />
    <!-- 节点属性 End -->
  </div>
</template>
<script>

import {Graph, Rectangle, Line, Path, Curve} from "@antv/x6";
import {Selection} from "@antv/x6-plugin-selection";
import {Clipboard} from "@antv/x6-plugin-clipboard";
import {Transform} from "@antv/x6-plugin-transform";
import {Snapline} from "@antv/x6-plugin-snapline";
import { Export } from '@antv/x6-plugin-export'
import {config, showHidePorts} from '../../assets/topology';
import AddReviewComments from "../AddModal/AddReviewComments.vue";
import PropertyStructure from "../PropertyModal/PropertyStructure.vue";
import PropertyEdgeModal from "../PropertyModal/PropertyEdgeModal.vue"
import PropertyNodeModal from "../PropertyModal/PropertyNodeModal.vue"
import AddStructure from "../AddModal/AddStructurel.vue";
import AddInterface from "../AddModal/AddInterface.vue";
import AntSpinDot from "@/components/AntSpinDot/index.vue";

window.rightX6Graph = undefined;

export default {
  name: 'RightX6',
  components: {
    AntSpinDot,
    AddStructure,
    AddInterface,
    PropertyStructure,
    PropertyEdgeModal,
    PropertyNodeModal,
    AddReviewComments,
  },
  created() {
  },
  data() {
    return {
      /** 删除弹出框 */
      deleteModal: undefined,
      /** 属性节点ID */
      attrStruId: '',
      /** x6图形 */
      rightX6Graph: undefined,
      /** x6图形配置 */
      rightX6Config: undefined,
      /** 模态框状态 */
      modalVisible: false,
      /** 模态框标题 */
      title: "",
      /** 模态框，选项卡Key */
      tabKey: "",
      /** 添加类型 */
      addType: '',
      /** 模态框唯一Key */
      actionName: "",
      /** 选中节点 */
      checkedNode: undefined,
      /** 根节点名称 */
      rootNodeName: undefined,
      /** 鼠标点击位置 */
      x6ClickPosition: {},
      /** 加载对象 */
      loader: undefined,
      /** 连线move标识 */
      edgeMoveFlag: false,
      /** 将要挪动的节点 */
      moveNodeId: '',
      /** 连接目标节点 */
      connectTargetNode: undefined,
    }
  },
  watch: {
    'rightX6Graph'(val) {
      window.rightX6Graph = val;
    },
  },
  mounted() {
    this.initRightX6Config();
    this.initRightX6();
  },
  methods: {

    /******************************************* x6 Start **************************************************/
    /**
     * 初始化X6
     */
    initRightX6() {
      this.initRightX6Data();
    },
    /**
     * 初始化X6配置
     */
    initRightX6Config() {
      let _that = this;
      _that.rightX6Graph = new Graph({
        container: document.getElementById("rightX6"),
        autoResize: true,
        virtual: false, // 是否开启可视区域渲染能力，默认值为 false
        background: {
          color: 'rgba(255, 255, 255)' // 背景颜色
        },
        // 缩放级别
        scaling: {
          min: 0.1, // 默认值为 0.01
          max: 5, // 默认值为 16
        },
        // 网格
        grid: {
          visible: true,
          type: 'doubleMesh',
          args: [
            {
              color: '#eee', // 主网格线颜色
              thickness: 1, // 主网格线宽度
            },
            {
              color: '#ddd', // 次网格线颜色
              thickness: 1, // 次网格线宽度
              factor: 4, // 主次网格线间隔
            },
          ],
        },
        // 嵌套节点，默认禁用
        embedding: {enabled: false},
        // 拖拽平移
        panning: {enabled: true},
        // 鼠标滚轮缩放
        mousewheel: {enabled: true},
        // 配置连线规则
        connecting: {
          snap: true, // 自动吸附
          allowBlank: false, // 是否允许连接到画布空白位置的点
          allowMulti: true, // 是否允许在相同的起始节点和终止之间创建多条边
          allowLoop: false, // 是否允许创建循环连线，即边的起始节点和终止节点为同一节点
          highlight: true, // 拖动边时，是否高亮显示所有可用的节点
          allowNode: false, // 是否允许边连接到节点
          allowEdge: false, // 是否允许边连接到另一个边
          allowPort: this.$api.isLook !== '1', // 是否允许边链接到链接桩，默认为 true
          validateEdge({edge, type, previous}) {
            return true
          },
        },
        // 定制节点和边的交互行为
        interacting: {
          // 禁止节点移动
          nodeMovable: this.$api.isLook !== '1',
          // 禁止连线移动
          edgeMovable: this.$api.isLook !== '1',
        }
      });
      // 绑定事件
      _that.bindingEvent();
      // 注册插件
      _that.registerPlugin();
    },
    /**
     * 初始化X6数据
     */
    initRightX6Data() {
      let _that = this;
      // 开启加载...
      _that.handleShowHideLoading(true);
      _that.$api.getBoundaryData({
        fmeaId: _that.$store.getters.rfmeaId
      }).then(res => {
        if (res.data && res.data.length > 0) {
          let rightX6Data = _that.handleRightX6Data(res.data);
          // 构建基本数据
          _that.rightX6Graph.fromJSON(rightX6Data);
          // 处理连线曲线
          _that.handleEdgesCurve();
          // 刷新父子节点的位置
          _that.refreshPosition();
        }
      }).finally(() => {
        setTimeout(() => {
          // 默认全屏
          _that.rightX6Graph.zoomToFit();
          // 关闭加载...
          _that.handleShowHideLoading(false);
        }, 50);
      })
    },
    /**
     * 刷新父子节点的位置
     */
    refreshPosition() {
      this.rightX6Graph.getNodes().forEach(node => {
        this.parentPosition(node);
      });
    },
    /**
     * 计算父节点位置
     * @param node
     */
    parentPosition(node) {
      let embedPadding = 20;
      const parent = node.getParent();
      if (parent && parent.isNode()) {
        let originPosition = parent.prop("originPosition");
        if (!originPosition) {
          originPosition = parent.getPosition();
          parent.prop("originPosition", originPosition);
        }
        let originSize = parent.prop("originSize");
        if (!originSize) {
          originSize = parent.getSize();
          parent.prop("originSize", originSize);
        }
        let originRec = new Rectangle(
            originPosition.x,
            originPosition.y,
            originSize.width,
            originSize.height
        );
        const children = parent.getChildren();
        const bboxs = children?.filter((item) => item.isNode()).map((child) => child.getBBox());
        if (!bboxs || bboxs.length === 0) return;

        let x0 = Math.min(...bboxs.map((bbox) => bbox.left)) - embedPadding;
        let y0 = Math.min(...bboxs.map((bbox) => bbox.top)) - embedPadding * 2;
        let x1 = Math.max(...bboxs.map((bbox) => bbox.right)) - x0 + embedPadding;
        let y1 = Math.max(...bboxs.map((bbox) => bbox.bottom)) - y0 + embedPadding;

        let currentRec = new Rectangle(x0, y0, x1, y1);
        if (!currentRec.equals(originRec)) {
          parent.setPosition(currentRec.topLeft, {
            skipParentHandler: true,
          });
          parent.resize(currentRec.width, currentRec.height, {
            skipParentHandler: true,
          });
        }
      }
      parent && this.parentPosition(parent);
    },
    /**
     * 绑定事件
     */
    bindingEvent() {
      let _that = this;
      // 滚轮缩放
      _that.rightX6Graph.on("scale", ({sx, sy, ox, oy}) => {
        _that.handleCloseLabelEditor();
      });
      // 调整节点大小
      _that.rightX6Graph.on("node:resized", ({node}) => {
        _that.updateNode(node);
      });
      // 旋转节点
      _that.rightX6Graph.on("node:rotated", ({node}) => {
        _that.updateNode(node);
      });
      // 空白出单击
      _that.rightX6Graph.on('blank:click', ({}) => {
        let nodes = _that.rightX6Graph.getNodes();
        let node = nodes.filter(node => node?.store.data.parent === '00000000000000000000000000000000')[0];
        _that.setCheckedNode(node);
      });
      // 节点单击
      _that.rightX6Graph.on('node:click', ({node}) => {
        _that.setCheckedNode(node.toJSON());
        // 增加修改名称输入框 查看状态下不允许修改
        if (this.$api.isLook !== '1') {
          if (!node.hasTool('node-editor')) {
            node.addTools(['node-editor']);
          }
        }
      });
      // 连线单击
      _that.rightX6Graph.on('edge:click', ({edge}) => {
        _that.setCheckedNode(edge.toJSON());
      });
      // 连线双击
      _that.rightX6Graph.on('edge:dblclick', ({edge}) => {
        _that.setCheckedNode(edge.toJSON());
        let checkedNode = this.$store.getters.lastCheckedX6Node;
        this.checkedNode = {
          id: checkedNode.id,
          chartFlag: 1,
          x6Node: {
            x: this.x6ClickPosition.x,
            y: this.x6ClickPosition.y,
            zIndex: ++checkedNode.zIndex,
          },
        }
        // 清除节点高亮
        this.clearHighLightCopyCutNode();
        // 连线属性
        if (checkedNode.shape && checkedNode.shape === 'edge') {
          this.$refs.propertyEdgeModal.addLineFlag = false;
          this.$refs.propertyEdgeModal.linkId = checkedNode.id;
          this.$refs.propertyEdgeModal.visible = true;
        } else {
          this.$refs.propertyNodeModal.attrId = checkedNode.id;
          this.$refs.propertyNodeModal.visible = true;
        }
      });
      // 连线拖动
      _that.rightX6Graph.on('edge:change:vertices', ({edge}) => {
        _that.edgeMoveFlag = true;
      });
      // 连线鼠标抬起
      _that.rightX6Graph.on('edge:mouseup', ({edge}) => {
        if (_that.edgeMoveFlag) {
          let color = edge.attrs.line.color;
          // 设置连线颜色、箭头
          edge.prop({
            attrs: {
              line: {
                color: color, // 连线颜色
                stroke: color, // 连线颜色
              }
            }
          })
          this.updateEdge(edge);
        }
      });
      // 空白右键菜单
      _that.rightX6Graph.on("blank:contextmenu", ({e}) => {
        let nodes = _that.rightX6Graph.getNodes();
        let node = nodes.filter(node => node?.store.data.parent === '00000000000000000000000000000000')[0];
        node.store.data.struTypeCode = '00';
        _that.setCheckedNode(node);
        let menuBtns = _that.getMenuBtn(node);
        _that.x6ClickPosition = _that.rightX6Graph.clientToLocal(e.clientX, e.clientY)
        _that.handleBankContextMenu(menuBtns);
      });
      // 节点右键菜单
      _that.rightX6Graph.on("node:contextmenu", ({e, node}) => {
        _that.setCheckedNode(node.toJSON());
        let menuBtns = _that.getMenuBtn(node);
        _that.x6ClickPosition = _that.rightX6Graph.clientToLocal(e.clientX, e.clientY)
        _that.handleContextMenu(node, menuBtns);
      });
      // 连线右键菜单
      _that.rightX6Graph.on("edge:contextmenu", ({e, edge}) => {
        _that.setCheckedNode(edge.toJSON());
        let menuEdgeBtns = _that.getEdgeMenuBtn(edge);
        _that.handleContextMenu(edge, menuEdgeBtns);
      })
      // 移动节点后触发
      _that.rightX6Graph.on("node:moved", ({node}) => {
        _that.updateNode(node);
      });
      // 移动节点前触发
      _that.rightX6Graph.on("node:move", ({node, options}) => {
        _that.moveNodeId = node.id;
      });
      // 节点移动
      _that.rightX6Graph.on("node:change:position", ({node, options}) => {
        if (options.skipParentHandler) return;
        const children = node.getChildren();
        if (children && children.length > 0) {
          node.prop("originPosition", node.getPosition());
        }
        // 只需要计算正在拖拽的节点即可
        if (node.id === _that.moveNodeId) {
          _that.parentPosition(node);
        }
      });
      // 连线连接节点监听
      _that.rightX6Graph.on("edge:connected", ({isNew, edge}) => {
        if (isNew) {
          let sourceNode = edge.getSourceNode();
          let targetNode = edge.getTargetNode();
          if (targetNode.isDescendantOf(sourceNode) || targetNode.isAncestorOf(sourceNode)) {
            _that.rightX6Graph.removeEdge(edge);
            return;
          }
          // 接口之间不能相互连接!
          if (sourceNode?.store.data.struTypeCode === '99' && targetNode?.store.data.struTypeCode === '99') {
            _that.rightX6Graph.removeEdge(edge);
            _that.$message.warning(this.$t('structural-analysis.top-btn.interfaces-cannot-added'))
            return;
          }
          showHidePorts(targetNode, false);
          _that.addEdge(edge);
        }
      });
      // 鼠标移入节点
      _that.rightX6Graph.on('node:mouseenter', ({node}) => {
        showHidePorts(node, true);
      });
      // 鼠标移出节点
      _that.rightX6Graph.on('node:mouseleave', ({node}) => {
        showHidePorts(node, false);
      });
      // 连线移动
      _that.rightX6Graph.on('edge:mousemove', ({view}) => {
        const sid = view.sourceView?.cell.id;
        const tid = view.targetView?.cell.id;
        _that.connectTargetNode ? showHidePorts(_that.connectTargetNode, false) : '';
        if (sid && tid && sid !== tid) {
          let cell = _that.rightX6Graph.getCellById(tid);
          if (cell) {
            _that.connectTargetNode = cell;
            showHidePorts(cell, true);
          }
        }
      });
      // 监听节点属性变化
      // current, 当前值
      // previous, 改变之前的值
      // options, 透传的 options
      _that.rightX6Graph.on("node:change:attrs", ({cell, node, key, current, previous, options,}) => {
        if (options && options.propertyPath === 'attrs/text/text' && current.text.text && current.text.text !== node.attrs.label.title) {
          if (current.text.text.length > 500){
            current.text.text = node.attrs.label.title
            _that.$message.warn(_that.$t('global-message.warn.node-name-length'));
            return false
          }
          node.prop({
            attrs: {
              label: {
                title: current.text.text,
              }
            }
          })
          let nodeJson = node.toJSON();
          let param = {};
          param.struId = nodeJson.id;
          param.struName = current.text.text;
          param.fmeaId = _that.$store.getters.rfmeaId
          param.processNodes = [{
            w: node.store.data.size.width,
            h: node.store.data.size.height,
            c: node.attrs.body.fill,
            x: node.store.data.position.x,
            y: node.store.data.position.y,
            struId: nodeJson.id,
            struName: current.text.text,
          }];
          param.options = JSON.stringify(node);
          _that.$api.updateBoundaryNodeDetail(param).then((res) => {
            if (res.flag) {
              node.label = current.text.text;
              // 刷新左侧树
              _that.initLeftTree();
            } else {
              _that.$message.error(res.message, 1)
            }
          });
        } else if (!current.text || !current.text.text && previous.text.text) {
          node.setLabel(previous.text.text);
        }
      });
    },
    /**
     * 注册插件
     */
    registerPlugin() {
      let _that = this;
      // 框选插件
      _that.rightX6Graph.use(
          new Selection({
            enabled: true, // 是否启用框选，默认为 true。
            multiple: true, // 是否启用点击多选，默认为 true
            rubberband: true, // 是否启用框选，默认为 false
            movable: true, // 选中的节点是否可以被移动，设置为 true 时，拖动选框触发节点移动，默认为 true
            showNodeSelectionBox: true, // 是否显示节点的选择框，默认为 false
            pointerEvents: 'none', // 如果打开 showNodeSelectionBox 时，会在节点上方盖一层元素，导致节点的事件无法响应，此时可以配置 pointerEvents: none 来解决，默认值是 auto
            modifiers: ['ctrl', 'meta'], // 用于设置上面框选配套的修饰键
            multipleSelectionModifiers: ['ctrl', 'meta'] // 用于设置上面点击多选配套的修饰键
          }),
      );
      // 复制
      _that.rightX6Graph.use(
          new Clipboard({
            enabled: true, // 是否启用复制，默认为 true。
          })
      );
      // 放大缩小
      _that.rightX6Graph.use(
          new Transform({
            // 调整节点大小时触发
            resizing: {
              enabled: _that.$api.isLook !== '1', // 是否支持调整节点大小，默认为 false。
              minWidth: 50, // 最小的调整宽度
              minHeight: 25, // 最小的调整高度
              allowReverse: false, // 到达最小宽度或者高度时是否允许控制点反向拖动
            },
            // 旋转节点时触发
            // rotating: {
            //   enabled: _that.$api.isLook !== '1', // 是否支持调整节点角度，默认为 false
            //   grid: 15, // 每次旋转的角度
            // },
          })
      );
      // 辅助线
      _that.rightX6Graph.use(
          new Snapline({
            enabled: true, // 是否启用复制，默认为 true。
            className: 'my-snapline',
            tolerance: 1,
          })
      );
      // 曲线
      Graph.registerConnector(
          'multi-smooth',
          (
              sourcePoint,
              targetPoint,
              routePoints,
              options,
          ) => {
            const {index = 0, total = 1, gap = 20} = options;
            const line = new Line(sourcePoint, targetPoint);
            const centerIndex = (total - 1) / 2;
            const dist = index - centerIndex;
            const diff = Math.abs(dist);
            const factor = diff === 0 ? 1 : diff / dist;
            const vertice = line
                .pointAtLength(line.length() / 2 + gap * factor * Math.ceil(diff))
                .rotate(90, line.getCenter());

            const points = [sourcePoint, vertice, targetPoint];
            const curves = Curve.throughPoints(points);
            const path = new Path(curves);
            return options.raw ? path : path.serialize();
          },
          true,
      );
    },
    /**
     * 处理x6数据
     * @param data
     * @returns {{nodes: *[], edges: *[]}}
     */
    handleRightX6Data(data) {
      let x6Data = {
        nodes: this.getRightX6Nodes(data),
        edges: this.getRightX6Edges(data),
      };
      // 处理父子关系
      x6Data.nodes.forEach(node => {
        node.children = x6Data.nodes.filter(cur => cur.parent === node.id).map(cur => cur.id);
      });
      return x6Data;
    },
    /**
     * 处理连线曲线
     */
    handleEdgesCurve() {
      let _that = this;
      let edges = _that.rightX6Graph.getEdges();
      if (edges.length === 0) return;
      const map = new Map();
      const connectorType = ["multi-smooth", "normal"];
      edges.forEach(edge => {
        if (connectorType.indexOf(edge.connector?.name) !== -1) {
          let k = edge.source.cell + '_' + edge.source.port + '_' + edge.target.cell + '_' + edge.target.port;
          if (!map.has(k)) {
            map.set(k, []);
          }
          let v = map.get(k);
          v.push(edge.id)
          map.set(k, v);
        }
      });
      edges.forEach(edge => {
        if (connectorType.indexOf(edge.connector?.name) !== -1) {
          let k = edge.source.cell + '_' + edge.source.port + '_' + edge.target.cell + '_' + edge.target.port;
          if (map.has(k)) {
            let v = map.get(k);
            edge.prop({
              connector: {
                name: 'multi-smooth',
                args: {
                  total: v.length,
                  index: v.findIndex(i => i === edge.id),
                },
              }
            });
          }
        }
      });
    },
    /**
     * 获取x6节点
     * @param data
     * @returns {*[]}
     */
    getRightX6Nodes(data) {
      let nodes = [];
      for (let i = 0; i < data.length; i++) {
        let item = data[i];
        if (item.group === 'nodes' && item.chartFlag === 1) { // 节点 「chartFlag 1：显示，0：隐藏」
          let node;
          // 自定义布局使用X6数据
          if (item.options) {
            node = JSON.parse(item.options);
            node.zIndex = item.deep
          } else {
            node = {
              id: item.data.id, // ID
              parent: item.data.parentId, // 父节点
              x: Number(item.position.x), // 位置：x轴
              y: Number(item.position.y), // 位置：y轴
              width: Number(item.data.w), // 宽
              height: Number(item.data.h), // 高
              label: item.data.stru_name, // 名称
              shape: item.struTypeCode === '01' ? 'rect' : 'ellipse', // 结构：矩形，接口：椭圆
              struTypeCode: item.struTypeCode, // 节点类型
              zIndex: item.deep,
              // 属性
              attrs: {
                label: {
                  fill: '#000000', // 文字
                  refX: 0.5, // 文字距离左方比例
                  refY: 8, // 文字距离上方比例
                  textAnchor: 'middle',
                  textVerticalAnchor: 'top',
                  textWrap: {
                    width: -10, // 宽度减少 10px
                    height: 40, // 高度为参照元素高度的一半
                    ellipsis: true, // 文本超出显示范围时，自动添加省略号
                    breakWord: true, // 是否截断单词
                  },
                  title: item.data.stru_name, // 标题
                },
                body: {
                  stroke: '#8f8f8f', // 边框颜色
                  strokeWidth: 1, // 边框宽度
                  fill: item.data ? item.data.c : config.defaultParams.c, // 填充颜色
                  rx: 6,
                  ry: 6,
                },
              },
              ports: {
                groups: {
                  defaultGroup: {
                    attrs: {
                      circle: {
                        r: 4,
                        magnet: true, // 当 magnet 属性为 true 时，表示该元素可以被链接，即在连线过程中可以被当做连线的起点或终点，与连接桩类似。
                        stroke: '#31d0c6',
                        fill: '#fff',
                        strokeWidth: 2,
                      },
                    },
                    position: {
                      name: 'ellipseSpread', // 沿椭圆均匀分布的连接桩
                      args: {
                        compensateRotate: true, // 是否沿圆弧修正连接桩的旋转角度。
                      },
                    },
                  },
                },
                items: [
                  {id: 'port1', group: 'defaultGroup'},
                  {id: 'port2', group: 'defaultGroup'},
                  {id: 'port3', group: 'defaultGroup'},
                  {id: 'port4', group: 'defaultGroup'}
                ],
              }
            };
          }
          // 处理根节点，根节点默认不显示
          if (item.data.parentId === '00000000000000000000000000000000') { // 根节点
            this.rootNodeName = item.data.stru_name;
            node.visible = false;
          }
          // 查看不允许双击修改名称
          if (this.$api.isLook === '1') {
            delete node.tools;
          }
          nodes.push(node);
        }
      }
      return nodes;
    },
    /**
     * 获取x6连线
     * @param data
     * @returns {*[]}
     */
    getRightX6Edges(data) {
      let nodes = data.map(item => item.data.id);
      let edges = [];
      data.forEach(item => {
        if (item.group === 'edges') { // 连线
          // 节点不存在过滤掉
          if (nodes.indexOf(item.data.source) !== -1 && nodes.indexOf(item.data.target) !== -1) {
            let edge;
            // 自定义布局使用X6数据
            if (item.options) {
              edge = JSON.parse(item.options);
            } else {
              edge = {
                id: item.data.id, // ID
                source: item.data.source,
                target: item.data.target,
                shape: 'edge', // 形状 edge：连线
                attrs: {
                  line: {
                    stroke: item.data.color, // 连线颜色
                    strokeWidth: 1, // 连线宽度
                    linkType: item.data.linkType, // 连接类型
                    color: item.data.color, // 颜色
                    sourceMarker: "classic",
                    strokeDasharray: '', // 线条：虚线/实线
                  }
                },
                labels: [{
                  attrs: {
                    label: {
                      text: item.data.linkName,
                    }
                  }
                }],
                connector: {
                  name: "normal" // 连线：弧线/直线
                },
                zIndex: 99
              };
            }
            edge.lineRealStruId = item.lineRealStruId;
            edges.push(edge);
          }
        }
      });
      return edges;
    },
    /**
     * 获取所有子节点
     */
    getAllChildren(id, children) {
      let cell = this.rightX6Graph.getCellById(id);
      let child = (cell.children || []).map(item => item.id);
      if (cell && child.length > 0) {
        children.push(...child);
        child.forEach(childId => {
          this.getAllChildren(childId, children);
        });
      }
    },
    /**
     * 关闭双击修改名称的输入框
     */
    handleCloseLabelEditor() {
      let selectedCells = this.rightX6Graph.getSelectedCells();
      if (selectedCells && selectedCells.length > 0) {
        selectedCells.forEach(cell => {
          if (cell.hasTool('node-editor')) {
            cell.removeTool('node-editor');
          }
        });
      }
    },
    /**
     * 添加节点
     * @param node
     */
    addNode(node) {
      let _that = this;
      _that.rightX6Graph.addNode(node);
      let parentX6Node = window.rightX6Graph.getCellById(node.parent);
      let curX6Node = window.rightX6Graph.getCellById(node.id);
      if (parentX6Node) {
        parentX6Node.addChild(curX6Node);
      }
      _that.parentPosition(curX6Node);
    },
    /**
     * 更新节点
     * @param node
     */
    updateNode(node) {
      let _that = this;
      let children = [];
      _that.getAllChildren(node.id, children);
      // 包含自己
      children.push(node.id);
      let cells = _that.rightX6Graph.getCells();
      let x6Options = [];
      cells.forEach(cell => {
        if (children.indexOf(cell.id) !== -1) {
          showHidePorts(cell, false);
          x6Options.push(cell);
        }
      });
      _that.$api.moveBoundaryData({
        fmeaId: _that.$store.getters.rfmeaId,
        struId: node.id,
        chartMessageJson: JSON.stringify(x6Options)
      }).then((res) => {
        if (!res.flag) {
          _that.$message.error(res.message, 1)
        }
      });
    },
    /**
     * 更新连线
     * @param edge
     */
    updateEdge(edge) {
      let _that = this;
      // 移除线段
      // if (edge.hasTool('segments')) {
      //   edge.removeTool('segments');
      // }
      _that.$api.saveBoundaryLinkData({
        fmeaId: _that.$store.getters.rfmeaId,
        struId: edge.id,
        fromStruId: edge.store.data.source.cell,
        toStructureId: edge.store.data.target.cell,
        color: edge.store.data.attrs.line.color,
        options: JSON.stringify(edge),
      }).then((res) => {
        if (res.flag) {
          // 刷新选中状态
          _that.setCheckedNode();
          _that.handleEdgesCurve();
        } else {
          _that.$message.error(res.message);
        }
      }).finally(() => {
        _that.edgeMoveFlag = false;
      })
    },
    /**
     * 新建连线
     * @param edge
     * @returns {Promise<void>}
     */
    addEdge(edge) {
      let _that = this;
      _that.$api.saveBoundaryLinkData({
        fmeaId: _that.$store.getters.rfmeaId,
        fromStruId: edge.store.data.source.cell,
        toStructureId: edge.store.data.target.cell,
        struName: '功能描述',
        linkDirect: 2, // 默认双向
        color: '#8f8f8f',
      }).then((resp) => {
        if (resp.flag) {
          let linkId = resp.data.connectId;
          _that.rightX6Graph.updateCellId(edge, linkId);
          // connectId返回空说明没有连线保存，关闭弹窗不用走删除连线接口
          if (resp.data.connectId === '') {
            _that.$refs.propertyEdgeModal.addLineFlag = false;
          } else {
            _that.$refs.propertyEdgeModal.addLineFlag = true;
          }
          _that.$refs.propertyEdgeModal.linkId = linkId;
          _that.$refs.propertyEdgeModal.visible = true;
          _that.initRightX6Data();
        } else {
          _that.$message({
            type: 'error',
            message: resp.message,
          })
        }
      });
    },
    /**
     * 删除连线(自动生成的连线删除后刷新页面会自动恢复,仅可以删除用户手动创建的连线)
     */
    deleteEdge(node) {
      let _that = this;
      if (_that.deleteModal) {
        // 销毁
        _that.deleteModal.destroy();
      }
      let deleteContent = undefined;
      let deleteNumber = undefined;
      let data = {
        rfmeaId: _that.$store.getters.rfmeaId,
        struId: node.lineRealStruId
      };
      _that.$api.getDeleteNodeNumber(data).then(res => {
        if (res.flag) {
          deleteNumber = res.data;
          deleteContent = _that.$t('global-message.warn.interfacie.connexio.delete', [deleteNumber]);
          _that.deleteModal = _that.$confirm({
            title: _that.$t('global-message.warn'),
            content: deleteContent,
            okText: _that.$t('global-btn.ok'),
            cancelText: _that.$t('global-btn.cancel'),
            onOk() {
              _that.showHideLoading(true);
              _that.$api.deleteBoundary({
                fmeaId: _that.$store.getters.rfmeaId,
                struId: node.id,
                connectId: node.id,
                delFlag: 1,
              }).then((res) => {
                if (res.flag) {
                  _that.initLeftTree();
                  let edge = _that.rightX6Graph.getCellById(node.id);
                  if (edge) {
                    _that.rightX6Graph.removeEdge(edge);
                    _that.setCheckedNode();
                    _that.handleEdgesCurve();
                  }
                } else {
                  _that.$message.error(res.message, 1);
                }
              }).finally(() => {
                _that.showHideLoading(false);
              });
            },
            onCancel() {}
          });
        }
      });
    },
    // 隐藏边界图
    hideBoundaryMap(node) {
      let _that = this;
      let data = {
        fmeaId: _that.$store.getters.rfmeaId,
        struId: node.id
      }
      _that.$confirm({
        title: _that.$t('global-message.warn'),
        content: _that.$t('global-message.warn.-hide-the-boundary-map'),
        okText: _that.$t('global-btn.ok'),
        cancelText: _that.$t('global-btn.cancel'),
        onOk() {
          _that.showHideLoading(true);
          _that.$api.hideBoundary(data).then((res) => {
            if (res.flag) {
              _that.initLeftTree();
              _that.initRightX6Data();
            } else {
              _that.$message.error(res.message, 1);
            }
          }).finally(() => {
            _that.showHideLoading(false);
          });
        },
        onCancel() {}
      });
    },
    /**
     * 显示、隐藏 -- 加载Loading
     * @param flag
     */
    handleShowHideLoading(flag) {
      if (flag) {
        if (this.loader) this.loader.hide();
        this.loader = this.$loading.show({
          container: this.$refs.rightX6,
          color: '#1890FF',
        }, {
          // 自定义加载动画组件
          default: this.$createElement('ant-spin-dot'),
        });
      } else {
        this.loader.hide();
      }
    },
    /**
     * 导出图片
     */
    exportImages() {
      let _that = this;
      _that.rightX6Graph.use(new Export());
      // 图片固定为png格式
      let pictureName = _that.rootNodeName + ".png";
      _that.rightX6Graph.exportPNG(pictureName, {padding: 100});
      // 导出成功以后 调用父组件的方法重置背景颜色弹窗状态
      _that.$nextTick(() => {
        _that.$emit('changePickerHiddenFlag');
      });
    },
    /******************************************* x6 End **************************************************/


    /******************************************* 右键菜单 Start **************************************************/
    /**
     * 取得当前节点的右键菜单项
     * @param checkNode 选中节点
     */
    getCurrentNodeMenus(checkNode) {
      let nodeTypeDict = Object.values(this.$store.getters.nodeTypeDict.structureAnalysis).filter((item) => {
        return item.key === checkNode.store.data.struTypeCode;
      })[0];
      // 1：只读，2：编制
      let readOnly = this.$api.isLook;
      // 只读
      if (readOnly === '1') {
        return nodeTypeDict.rightMenuReadOnly;
      } else {
        return nodeTypeDict.rightMenu;
      }
    },
    /**
     * 取得连线的右键菜单项
     * @param checkNode 选中节点
     */
    getCurrentEdgeMenus(checkNode) {
      let nodeTypeDict = Object.values(this.$store.getters.nodeTypeDict.structureAnalysis).filter((item) => {
        return item.key === checkNode.store.data.shape;
      })[0];
      // 1：只读，2：编制
      let readOnly = this.$api.isLook;
      // 只读
      if (readOnly === '1') {
        return nodeTypeDict.rightMenuReadOnly;
      } else {
        return nodeTypeDict.rightMenu;
      }
    },
    /**
     * 获取连线右键菜单
     */
    getEdgeMenuBtn(edge) {
      let cNodeMenus = this.getCurrentEdgeMenus(edge);
      let menuBtn = {};
      this.$store.getters.struRightX6Menus.forEach((item) => {
        if (cNodeMenus.indexOf(item.key) !== -1) {
          switch (item.key) {
            case "delete": // 删除
              break;
            default:
              break;
          }
          menuBtn['' + item.key + ''] = {name: this.$t('' + item.label + ''), icon: "fas iconfont " + item.icon};
        }
      });
      return menuBtn;
    },
    /**
     * 获取右键菜单项
     * @param node
     */
    getMenuBtn(node) {
      // 取得当前节点的右键菜单项
      let cNodeMenus = this.getCurrentNodeMenus(node);
      let menuBtn = {};
      this.$store.getters.struRightX6Menus.forEach((item) => {
        if (cNodeMenus.indexOf(item.key) !== -1) {
          switch (item.key) {
            case "addStructure": // 添加结构
              break;
            case "addInterface": // 添加接口
              break;
            case "hide": // 隐藏
              break;
            case "delete": // 删除
              break;
            case "property": // 属性
              break;
            case "addReviewComments": // 评审意见
              break;
            default:
              break;
          }
          menuBtn['' + item.key + ''] = {name: this.$t('' + item.label + ''), icon: "fas iconfont " + item.icon};
        }
      });
      // 处理右键菜单的分割线
      menuBtn = this.handleContextMenuSep(menuBtn);
      return menuBtn;
    },
    /**
     * 处理右键菜单的分割线
     * @param menuBtn
     */
    handleContextMenuSep(menuBtn) {
      let afterFilter = Object.keys(menuBtn);
      let operationTypes = this.$store.getters.nodeTypeDict.operationType;
      let sepIndex = 1;
      for (let i = 0; i < operationTypes.length; i++) {
        let innerJoinArray = afterFilter.filter((v) => operationTypes[i].includes(v));
        if (innerJoinArray && innerJoinArray.length > 0) {
          let index = afterFilter.findIndex(val => val === innerJoinArray[innerJoinArray.length - 1]);
          // 最后一组分割线不显示
          if (i < operationTypes.length - 1 && afterFilter.length > 1) {
            afterFilter.splice(index + 1, 0, 'sep' + sepIndex++);
          }
        }
      }
      let newMenuBtn = {};
      for (let i = 0; i < afterFilter.length; i++) {
        for (let j in menuBtn) {
          if (afterFilter[i] === j) {
            newMenuBtn['' + afterFilter[i] + ''] = menuBtn[j];
            break;
          } else {
            newMenuBtn['' + afterFilter[i] + ''] = "---------";
          }
        }
      }
      return newMenuBtn;
    },
    /**
     * 处理空白右键菜单
     *
     * @param items
     */
    handleBankContextMenu(items) {
      if ($.isEmptyObject(items)) return;
      let _that = this;
      // 隐藏上次菜单
      let selector = '#rightX6';
      jQuery.contextMenu('destroy', selector);
      // 隐藏上次菜单
      jQuery.contextMenu({
        selector: selector,
        autoHide: true,
        zIndex: 10,
        items: items,
        callback: function (itemKey, opt) {
          let arr = itemKey.split("_");
          let key, label;
          key = arr[0];
          if (arr.length > 1 && key === 'convert') {
            label = arr[1];
          }
          let actionInfo = {key: key, label: label};
          _that.menuAction(actionInfo);
        },
      });
    },
    /**
     * 处理右键菜单
     *
     * @param node
     * @param items
     */
    handleContextMenu(node, items) {
      if ($.isEmptyObject(items)) return;
      let _that = this;
      // 隐藏上次菜单
      let selector = "g[data-cell-id='" + node.id + "']"
      jQuery.contextMenu('destroy', selector);
      // 隐藏上次菜单
      jQuery.contextMenu({
        selector: selector,
        autoHide: true,
        zIndex: 10,
        items: items,
        callback: function (itemKey, opt) {
          let arr = itemKey.split("_");
          let key, label;
          key = arr[0];
          if (arr.length > 1 && key === 'convert') {
            label = arr[1];
          }
          let actionInfo = {key: key, label: label};
          _that.menuAction(actionInfo);
        },
      });
    },
    /**
     * 右键菜单事件
     * @param actionInfo 菜单项信息
     */
    menuAction(actionInfo) {
      let checkedNode = this.$store.getters.lastCheckedX6Node;
      this.checkedNode = {
        id: checkedNode.id,
        chartFlag: 1,
        x6Node: {
          x: this.x6ClickPosition.x,
          y: this.x6ClickPosition.y,
          zIndex: ++checkedNode.zIndex,
        },
      }
      this.actionName = actionInfo.key;
      switch (actionInfo.key) {
        case "addStructure": // 结构
          this.addType = 'struTree';
          this.clearHighLightCopyCutNode();
          this.handleAddModal(checkedNode, actionInfo.key);
          this.modalVisible = true;
          break;
        case "addInterface": // 接口
          this.addType = 'outStru';
          this.clearHighLightCopyCutNode();
          this.handleAddModal(checkedNode, actionInfo.key);
          this.modalVisible = true;
          break;
        case "delete": // 删除
          this.clearHighLightCopyCutNode();
          if (checkedNode.shape && checkedNode.shape === 'edge') {
            this.deleteEdge(checkedNode);
          } else {
            this.deleteNode(checkedNode);
          }
          break;
        case "hide": // 隐藏
          this.clearHighLightCopyCutNode();
          this.hideBoundaryMap(checkedNode);
          break;
        case "property": // 属性
          this.clearHighLightCopyCutNode();
          if (checkedNode.shape && checkedNode.shape === 'edge') {
            this.$refs.propertyEdgeModal.addLineFlag = false;
            this.$refs.propertyEdgeModal.linkId = checkedNode.id;
            this.$refs.propertyEdgeModal.visible = true;
          } else {
            this.$refs.propertyNodeModal.attrId = checkedNode.id;
            this.$refs.propertyNodeModal.visible = true;
          }
          break;
        case "addReviewComments": // 添加评审意见
          this.clearHighLightCopyCutNode();
          this.handleReviewCommentsModal(checkedNode);
          this.modalVisible = true;
          break;
      }
    },
    /**
     * 处理添加
     * @param node 节点
     * @param menuKey 菜单标识
     */
    handleAddModal(node, menuKey) {
      let name = node.attrs.text.text;
      let adds = this.$store.getters.nodeTypeDict.addModals;
      for (let i = 0, len = adds.length; i < len; i++) {
        if (adds[i].menuKey === menuKey) {
          this.title = this.$t('' + adds[i].title + '') + name;
          this.tab = this.$t('' + adds[i].label + '');
          return;
        }
      }
    },
    /**
     * 处理属性Modal逻辑
     * @param node 节点
     * @param tabKey 选项卡Key
     */
    handlePropertyModal(node, tabKey = "") {
      this.attrStruId = node.id;
      let name = "14,07".indexOf(node.struTypeCode) !== -1 ? "" : node.attrs.text.text;
      let merges = this.$store.getters.nodeTypeDict.propertyModals;
      let merge = {};
      for (let i = 0; i < merges.length; i++) {
        if ("09,10".indexOf(node.struTypeCode) !== -1
            && merges[i].key === node.curOptTypeCode) {
          merge = merges[i];
          break;
        }
        if (merges[i].key === node.struTypeCode) {
          merge = merges[i];
          break;
        }
      }
      this.actionName = merge.action;
      this.title = this.$t('' + merge.title + '') + name;
      this.tabKey = tabKey;
    },
    /**
     * 处理评审意见Modal逻辑
     * @param node 节点
     */
    handleReviewCommentsModal(node) {
      let name = "14,07".indexOf(node.struTypeCode) !== -1 ? "" : node.attrs.text.text;
      let reviewComments = this.$store.getters.nodeTypeDict.reviewCommentsModals;
      for (let i = 0, len = reviewComments.length; i < len; i++) {
        if ("09,10".indexOf(node.struTypeCode) !== -1
            && reviewComments[i].key === node.curOptTypeCode) {
          this.title = this.$t('' + reviewComments[i].title + '') + name;
          return;
        }
        if (reviewComments[i].key === node.struTypeCode) {
          this.title = this.$t('' + reviewComments[i].title + '') + name;
          return;
        }
      }
    },
    /******************************************* 右键菜单 End **************************************************/


    /******************************************* 父组件方法 Start **************************************************/
    /**
     * 存储当前节点信息
     */
    setCheckedNode(node) {
      this.$emit("setCheckedNode", node, "rightX6");
    },
    /**
     * 刷新连线显示隐藏
     */
    refreshLineShowHide() {
      this.$emit("refreshLineShowHide");
    },
    /**
     * 初始化边界图
     */
    initLeftTree() {
      this.$emit('initLeftTree');
    },
    /**
     * 清除：复制/剪切时，节点高亮
     */
    clearHighLightCopyCutNode() {
      this.$emit("clearHighLightCopyCutNode", "leftTree");
    },
    /**
     * 删除节点
     */
    deleteNode(node) {
      this.$emit("deleteNode", node);
    },
    /**
     * 显示、隐藏主界面 -- 加载Loading
     */
    showHideLoading(flag) {
      this.$emit("showHideLoading", flag);
    },
    /******************************************* 父组件方法 End **************************************************/

    /******************************************* 子组件方法 Start **************************************************/
    /**
     * 关闭模态框
     */
    closeModal() {
      this.modalVisible = false;
    },
    /******************************************* 子组件方法 End **************************************************/
  }
}
</script>

<style lang='scss' scoped>
.rightX6 {

  .topLabel {
    width: 100%;
    z-index: 999;
    position: absolute;
    text-align: center;
    font-size: 30px;
  }

  // 框选颜色
  //::v-deep .x6-widget-selection-box {
  //  border: 2px dashed #1890FF;
  //}
}

</style>
