<template>
  <div class="blank-page page-view w-100 h-100">
    <div class="header pa-15">
      <div class="flex-row-sb-c">
        <div class="flex">
          <div class="header-title mr-20">指标关联： 你独立吗？（属性指标）</div>
          <el-button class="mapTypeBtn" type="primary" :plain="mapType === 'MultiToSingle'" @click="handleChangeMapType('SingleToMulti')">
            <div class="flex flex-gap-4">
              <div :class="['iconType', 'flex-row-c', { active: mapType === 'MultiToSingle' }]">
                <div class="single"></div>
              </div>
              一推多
            </div>
          </el-button>
          <el-button class="mapTypeBtn" type="primary" :plain="mapType === 'SingleToMulti'" @click="handleChangeMapType('MultiToSingle')">
            <div class="flex flex-gap-4">
              <div :class="['iconType', 'flex-row-c', { active: mapType === 'SingleToMulti' }]">
                <div class="multi"></div>
              </div>
              多推一
            </div>
          </el-button>
        </div>
        <div class="flex">
          <!-- <el-button type="primary" icon="el-icon-picture" @click="getMindData">获取数据</el-button> -->

          <el-button type="primary" :plain="viewMode === 'table'" icon="el-icon-connection" @click="handleViewMode('mind')">思维导图</el-button>
          <el-button type="primary" :plain="viewMode === 'mind'" icon="el-icon-document" @click="handleViewMode('table')">表格视图</el-button>
          <el-button type="primary" plain icon="el-icon-download" @click="exportImage">导出</el-button>
          <el-button type="success" icon="el-icon-setting" @click="toggleSideEditor()" :disabled="viewMode === 'table'">工作面板</el-button>
        </div>
      </div>
    </div>
    <div class="flex-1 ma-15 overflow-h">
      <div class="jsmind-map flex-col" v-show="viewMode === 'mind'">
        <div class="flex-1 overflow-h flex-row-s-c flex-gap-10">
          <div
            v-if="mapType === 'SingleToMulti'"
            id="jsmind_container"
            @contextmenu.prevent.stop="nodePreventClick"
            @click="nodeClick"
            ref="container"
            :class="['jsmind_layout', 'bg-white border-radius-5', 'h-100', showSideEditor ? 'short-layout' : 'wild-layout']"
          ></div>
          <div
            v-if="mapType === 'MultiToSingle'"
            id="jsmind_container"
            @contextmenu.prevent.stop="nodePreventClick"
            @click="nodeClick"
            ref="container"
            :class="['jsmind_layout', 'bg-white border-radius-5', 'h-100', showSideEditor ? 'short-layout' : 'wild-layout']"
          ></div>
          <div :class="['side-editor', 'bg-white', 'border-radius-5', 'h-100', 'flex-1', showSideEditor ? 'w-p-370' : 'w-p-0']">
            <div class="tab-wrapper" v-if="showSideEditor">
              <div class="h-98 tab-content-wrapper-single" v-if="currentNode && currentNode.data.level === 2 && rootNode.id !== currentNode.id">
                <el-form ref="form" :model="currentNode" label-width="80px" label-position="top">
                  <span class="tag-label fs-16" v-if="mapType === 'SingleToMulti'"
                    >{{ currentNode.topic }} <i class="el-icon-right"></i> {{ rootNode.topic }}</span
                  >
                  <span class="tag-label fs-16" v-if="mapType === 'MultiToSingle'"
                    >{{ rootNode.topic }} <i class="el-icon-right"></i> {{ currentNode.topic }}</span
                  >
                  <el-descriptions direction="vertical" :column="1" border class="mt-16">
                    <el-descriptions-item>
                      <template slot="label">
                        <span class="flex-row-sb-c">
                          <span class="header-text fs-16"> 关系描述 </span>
                        </span>
                      </template>
                      <span> {{ currentNode.data.memo || '暂无' }}</span>
                    </el-descriptions-item>
                    <el-descriptions-item>
                      <template slot="label">
                        <span class="flex-row-sb-c">
                          <span class="header-text fs-16"> 计算说明 </span>
                        </span>
                      </template>
                      <span> {{ currentNode.data.formula || '暂无' }}</span>
                    </el-descriptions-item>
                  </el-descriptions>
                </el-form>
              </div>
              <div class="flex-row-c h-100" v-else>
                <el-empty :image="require('@/assets/img/components/mindMap/empty.svg')" :image-size="240" description="请选择子节点"></el-empty>
              </div>
            </div>
          </div>
        </div>

        <div
          class="common-tip"
          v-show="showPopover"
          :style="{
            left: tipStyle.left,
            top: tipStyle.top,
            bottom: tipStyle.bottom,
            right: tipStyle.right,
          }"
        >
          <div>关系说明：</div>
          <div>{{ currentMemo }}</div>
        </div>

        <div
          class="common-formula"
          v-show="showFormulaPopover"
          :style="{
            left: tipStyle.left,
            top: tipStyle.top,
            bottom: tipStyle.bottom,
            right: tipStyle.right,
          }"
        >
          <div>计算公式：</div>
          <div>{{ currentFormula }}</div>
        </div>
      </div>
      <div class="table-view flex-col" v-show="viewMode === 'table'">
        <div class="flex-1 overflow-h flex-gap-10 border-radius-5 bg-white pa-15">
          <div class="flex-1 overflow-h">
            <el-table :data="tableData" :border="true" height="100%" :style="{ width: '100%' }">
              <el-table-column :label="`结果指标：【孔雀型】${mapType === 'SingleToMulti' ? '（一推多）' : '（多推一）'}`" align="center">
                <el-table-column
                  resizable
                  align="center"
                  show-overflow-tooltip
                  v-for="column in columns"
                  :key="column.prop"
                  :prop="column.prop"
                  :label="column.label"
                  :width="column.width"
                ></el-table-column>
              </el-table-column>
            </el-table>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import domtoimage from 'dom-to-image';
import utils from '@/utils.js';
import 'jsmind/style/jsmind.css';
import 'jsmind/es6/jsmind.draggable-node.js';
import 'jsmind/es6/jsmind.screenshot.js';
import jsMind from 'jsmind';
export default {
  name: 'mindMap',
  data() {
    return {
      // 配置信息
      showSideEditor: false,

      addTarget: {
        topic: '',
        code: '',
        type: 1,
        desc: '',
        source: 1,
      },

      defaultProps: {
        value: 'id',
        label: 'name',
      },
      versionData: [
        {
          versionNum: '1.2',
          time: '2025-01-10 10:55:22',
        },

        {
          versionNum: '1.1',
          time: '2025-01-11 11:55:22',
        },
        {
          versionNum: '1.0',
          time: '2025-01-10 10:55:22',
        },
      ],
      mapType: 'SingleToMulti', //MultiToSingle  SingleToMulti
      // 脑图信息
      // 脑图数据
      jm: null,
      editor: null,
      mindData: {
        meta: {
          name: '指标关联思维导图',
          author: 'artfess',
          version: '0.0.1',
        },
        format: 'node_tree',
        data: {
          id: 'root',
          topic: '指标：你独立吗?',
          'background-color': '#409eff',
          level: 0,
          children: [
            {
              id: utils.uuid(), // [必选] ID, 所有节点的ID不应有重复，否则ID重复的结节将被忽略
              topic: '任务:PDP算法模型', // [必选] 节点上显示的内容
              direction: 'right', // [可选] 节点的方向，此数据仅在第一层节点上有效，目前仅支持 left 和 right 两种，默认为 right
              expanded: true, // [可选] 该节点是否是展开状态，默认为 true
              'background-color': '#67c23a',
              level: 1,
              children: [
                { id: utils.uuid(), topic: '指标：猫头鹰型', 'background-color': '#e6a23c', level: 2 },
                { id: utils.uuid(), topic: '指标：你外向吗？', 'background-color': '#e6a23c', level: 2 },
                {
                  id: utils.uuid(),
                  topic: '指标：你爱说话吗？',
                  'background-color': '#e6a23c',
                  level: 2,
                  memo: '这是关系描述', // 关系描述
                  formula: '这是计算公式', // 计算公式说明
                },
              ],
            },
            {
              id: utils.uuid(),
              topic: '任务:9型人格算法',
              direction: 'right',
              expanded: true,
              'background-color': '#67c23a',
              level: 1,
              children: [
                { id: utils.uuid(), 'background-color': '#e6a23c', topic: '猫头鹰型', level: 2 },
                { id: utils.uuid(), 'background-color': '#e6a23c', topic: '老虎型', level: 2 },
                {
                  id: utils.uuid(),
                  'background-color': '#e6a23c',
                  memo: '这是关系描述', // 关系描述
                  formula: '这是计算公式', // 计算公式说明
                  topic: '变色龙型',
                  level: 2,
                },
              ],
            },
            {
              id: utils.uuid(),
              topic: '任务:16型人格算法', // 标题
              direction: 'right', // 方向   right  left
              'font-weight': 'normal', // 字体粗细 bold  normal
              'font-style': 'normal', // 字体样式 italic  normal
              'font-size': 18, // 字体大小
              'foreground-color': '#fff', // 文字颜色
              'background-color': '#67c23a', // 背景颜色
              level: 1, // 层级
              children: [
                // 子节点
                { id: utils.uuid(), 'background-color': '#e6a23c', topic: '老虎型', level: 2 },
                { id: utils.uuid(), 'background-color': '#e6a23c', topic: '孔雀型', level: 2 },
                { id: utils.uuid(), 'background-color': '#e6a23c', topic: '考拉型', level: 2 },
              ],
            },
          ],
        },
      },
      // 脑图配置
      options: {
        container: 'jsmind_container', // [必选] 容器的ID
        editable: true, // [可选] 是否启用编辑
        theme: 'primary', // [可选] 主题
        support_html: true,
        log_level: 'debug', // 设置日志级别为 debug
        view: {
          engine: 'canvas', // 思维导图各节点之间线条的绘制引擎
          enable_device_pixel_ratio: true, // 按设备像素比例绘制高清思维导图
          hmargin: 100, // 思维导图距容器外框的最小水平距离
          vmargin: 50, // 思维导图距容器外框的最小垂直距离
          line_width: 2, // 思维导图线条的粗细
          line_color: '#666', // 思维导图线条的颜色
          draggable: true, // 是否允许拖动画布
          hide_scrollbars_when_draggable: true,
          node_overflow: 'hidden',
          zoom: {
            // 缩放配置
            min: 0.1, // 最小缩放比例
            max: 2.1, // 最大缩放比例
            step: 0.1, // 缩放比例步长
          },
          expander_style: 'number',
          line_style: 'curved',
          //   custom_node_render: function (jm, element, node) {
          //     element.innerHTML = `${node.topic}<div class='tip-icon${node.data.memo ? ' showTip' : ''}' data-memo='${
          //       node.data.memo || ''
          //     }'><i class="el-icon-chat-dot-square"></i></div><div class='tip-icon${node.data.formula ? ' showFormula' : ''}' data-formula='${
          //       node.data.formula || ''
          //     }'><i class="el-icon-connection"></i></div>`;
          //     return true; // 表示已经渲染此节点
          //   },
        },
        layout: {
          hspace: 100, // 节点之间的水平间距
          vspace: 20, // 节点之间的垂直间距
        },
        shortcut: {
          // 禁用快捷键
          enable: false,
        },
      },

      structure: {
        active: 'right',
      },

      createType: '', // 添加平级or子级
      currentNode: null, // 选中节点信息
      showMenu: false, // 是否显示右键菜单栏
      isroot: false,
      menuStyle: {
        top: '',
        bottom: '',
        left: '',
        right: '',
      },
      tipStyle: { top: '', bottom: '', left: '', right: '' },
      showPopover: false,
      currentMemo: '',
      showFormulaPopover: false,
      currentFormula: '',
      viewMode: 'mind',
      tableData: [],

      // 表格
      columns: [],
      columnsSingleToMulti: [
        {
          prop: 'type',
          label: '指标类型',
          width: '150',
        },
        {
          prop: 'name',
          label: '指标名称',
        },
        {
          prop: 'inferenceType',
          label: '推理类型',
          width: '150',
        },
        {
          prop: 'taskName',
          label: '算法推理任务名称',
        },
        {
          prop: 'subTargetName',
          label: '关联指标',
        },
        {
          prop: 'subTargetType',
          label: '关联指标类型',
          width: '150',
        },
        {
          prop: 'targetRelation',
          label: '关联描述',
          width: '200',
        },
        {
          prop: 'targetFormula',
          label: '计算说明',
          width: '200',
        },
      ],
      columnsMultiToSingle: [
        {
          prop: 'subTargetName',
          label: '关联指标',
          width: '200',
        },
        {
          prop: 'subTargetType',
          label: '关联指标类型',
          width: '150',
        },
        {
          prop: 'targetRelation',
          label: '关联描述',
          width: '200',
        },
        {
          prop: 'targetFormula',
          label: '计算说明',
          width: '200',
        },
        {
          prop: 'taskName',
          label: '算法推理任务名称',
        },
        {
          prop: 'inferenceType',
          label: '推理类型',
          width: '150',
        },
        {
          prop: 'name',
          label: '指标名称',
        },
        {
          prop: 'type',
          label: '指标类型',
          width: '150',
        },
      ],
    };
  },
  mounted() {
    var that = this;
    this.loadMind();
  },
  methods: {
    handleViewMode(viewMode) {
      this.viewMode = viewMode;
      if (this.mapType === 'SingleToMulti') {
        this.columns = this.columnsSingleToMulti;
      } else {
        this.columns = this.columnsMultiToSingle;
      }
      if (this.viewMode === 'mind') {
        setTimeout(() => {
          this.reset();
        }, 10);
      }
    },
    loadMind() {
      this.jm = null;
      this.jm = new jsMind(this.options);
      this.jm.show(this.mindData);
      this.editor = this.jm.view.e_editor;
      this.init();
      this.mouseWheel();
      this.mouseDrag();
      this.rootNode = this.jm.get_root();
    },

    // 类型切换
    changeMapType(type) {
      this.mapType = type;
      this.reloadMind();
    },
    // tab切换
    handleClickTab() {},
    // 配置编辑显示隐藏
    toggleSideEditor(type) {
      this.showSideEditor = type === undefined ? !this.showSideEditor : type;
      setTimeout(() => {
        this.reset();
      }, 200);
    },
    // 缩小
    zoomOut() {
      this.jm.view.zoom_out();
    },
    // 放大
    zoomIn() {
      this.jm.view.zoom_in();
    },

    // 重置视图
    reset() {
      this.jm.resize();
    },

    exportImage() {
      let jm = jsMind.current;
      jm.shoot();
    },

    // 循环树结构
    loopTreeData(list, callback) {
      (function doOneFloor(list) {
        if (Array.isArray(list)) {
          for (let i = 0; i < list.length; i++) {
            const item = list[i];
            callback(item, i);
            if (item.children && item.children.length > 0) {
              doOneFloor(item.children);
            }
          }
        }
      })(list);
    },

    // 编辑节点

    // 禁用双击编辑
    stopDBclick() {
      const jmnodes = document.getElementsByTagName('jmnode');
      Object.values(jmnodes).forEach((jmnode) => {
        jmnode.ondblclick = function (e) {
          e.preventDefault();
          e.stopPropagation();
        };
      });
    },

    showTip(event) {
      if (this.showPopover) {
        return;
      }
      this.currentMemo = event?.fromElement?.children[0]?.dataset?.memo || '';
      const el = document.querySelector('.common-tip');
      const width = parseFloat(window.getComputedStyle(el).width);
      const height = parseFloat(window.getComputedStyle(el).height) * 3 + 12;
      const windowHeight = window.innerHeight;
      const windowWidth = window.innerWidth;
      // 极限位置 避免越界
      if (event.clientY + height > windowHeight) {
        this.tipStyle.left = event.clientX + 'px';
        this.tipStyle.top = 'unset';
        this.tipStyle.bottom = 0;
      } else if (event.clientX + width > windowWidth) {
        this.tipStyle.top = event.clientY + 'px';
        this.tipStyle.left = 'unset';
        this.tipStyle.right = 0;
      } else {
        this.tipStyle.left = event.clientX + 'px';
        this.tipStyle.top = event.clientY + 'px';
        this.tipStyle.bottom = 'unset';
      }
      if (this.currentMemo) {
        this.showPopover = true;
      }
    },

    hideTip() {
      this.currentMemo = '';
      this.showPopover = false;
    },

    showFormula(event) {
      if (this.showFormulaPopover) {
        return;
      }
      this.currentFormula = event?.fromElement?.children[1]?.dataset?.formula || '';
      const el = document.querySelector('.common-formula');
      const width = parseFloat(window.getComputedStyle(el).width);
      const height = parseFloat(window.getComputedStyle(el).height) * 3 + 12;
      const windowHeight = window.innerHeight;
      const windowWidth = window.innerWidth;
      // 极限位置 避免越界
      if (event.clientY + height > windowHeight) {
        this.tipStyle.left = event.clientX + 'px';
        this.tipStyle.top = 'unset';
        this.tipStyle.bottom = 0;
      } else if (event.clientX + width > windowWidth) {
        this.tipStyle.top = event.clientY + 'px';
        this.tipStyle.left = 'unset';
        this.tipStyle.right = 0;
      } else {
        this.tipStyle.left = event.clientX + 'px';
        this.tipStyle.top = event.clientY + 'px';
        this.tipStyle.bottom = 'unset';
      }
      if (this.currentFormula) {
        this.showFormulaPopover = true;
      }
    },

    hideFormula() {
      this.currentFormula = '';
      this.showFormulaPopover = false;
    },

    // 备注绑定点击事件
    tipsClick() {
      const tips = document.getElementsByClassName('tip-icon showTip');
      Object.values(tips).forEach((tip) => {
        tip.removeEventListener('mouseover', this.showTip);
        tip.addEventListener('mouseover', this.showTip);
      });
      Object.values(tips).forEach((tip) => {
        tip.removeEventListener('mouseleave', this.hideTip);
        tip.addEventListener('mouseleave', this.hideTip);
      });

      const Formula = document.getElementsByClassName('tip-icon showFormula');
      Object.values(Formula).forEach((formula) => {
        formula.removeEventListener('mouseover', this.showFormula);
        formula.addEventListener('mouseover', this.showFormula);
      });
      Object.values(Formula).forEach((formula) => {
        formula.removeEventListener('mouseleave', this.hideFormula);
        formula.addEventListener('mouseleave', this.hideFormula);
      });
    },

    // 初始化配置
    init() {
      this.jm.expand_all();

      this.stopDBclick();

      this.tipsClick();
    },
    // 获取选中标签的 ID
    get_selected_nodeid() {
      const selectedNode = this.jm.get_selected_node();
      if (selectedNode) {
        return selectedNode.id;
      } else {
        return null;
      }
    },

    // 切换思维导图结构
    toggleStucture(type) {
      if (this.structure.active === type) return;
      this.structure.active = type;
      switch (type) {
        case 'side':
          // 两边分布
          this.loopTreeData(this.mindData.data.children, (item, i) => {
            item.direction = i % 2 ? 'left' : 'right';
          });
          break;

        case 'MultiToSingle':
          // 向左分布
          this.loopTreeData(this.mindData.data.children, (item) => {
            item.direction = 'left';
          });
          break;

        case 'SingleToMulti':
          // 向右分布
          this.loopTreeData(this.mindData.data.children, (item) => {
            item.direction = 'right';
          });
          break;

        default:
          break;
      }

      this.jm.show(this.mindData);
      this.init();
    },

    handleChangeMapType(type) {
      this.toggleStucture(type);
      this.mapType = type;
      if (this.mapType === 'SingleToMulti') {
        this.columns = this.columnsSingleToMulti;
      } else {
        this.columns = this.columnsMultiToSingle;
      }
    },

    // 拖拽
    handleDrop(draggingNode, dropNode) {
      // 前一个兄弟节点
      const prevNode = this.jm.find_node_before(dropNode);
      // 获取移动后的node
      const dragForm = {
        modelId: '',
        treeNum: !prevNode ? draggingNode : prevNode.id,
        thisTreeNum: dropNode,
      };
      console.log('dragForm', dragForm);

      // TODO 调接口
    },
    // 单击重置选中背景颜色
    nodeClick() {
      this.showMenu = false;
      const selectedNode = this.jm.get_selected_node();
      console.log(selectedNode, 'selectedNode');
      if (selectedNode) {
        this.currentNode = selectedNode;
        this.addTarget.topic = this.currentNode.topic || '';
        if (selectedNode.data.level === 2 && this.rootNode.id !== this.currentNode.id) {
          this.toggleSideEditor(true);
        } else {
          this.toggleSideEditor(false);
        }
      }
    },
    // 阻止冒泡
    nodePreventClick() {},

    // 获取数据
    getMindData() {
      this.mindData = this.jm.get_data();
      console.log(this.mindData, 'this.dataUpdata');
    },

    // 鼠标滚轮放大缩小
    mouseWheel() {
      if (document.addEventListener) {
        document.addEventListener('domMouseScroll', this.scrollFunc, false);
      }
      this.$refs.container.onmousewheel = this.scrollFunc;
    },

    // 滚轮缩放
    scrollFunc(e) {
      this.showMenu = false;
      e = e || window.event;
      if (e.wheelDelta) {
        if (e.wheelDelta > 0) {
          this.zoomIn();
        } else {
          this.zoomOut();
        }
      } else if (e.detail) {
        if (e.detail > 0) {
          this.zoomIn();
        } else {
          this.zoomOut();
        }
      }
      e.preventDefault();

      this.jm.resize();
    },

    // 鼠标拖拽
    mouseDrag() {
      // 里层
      const el = document.querySelector('.jsmind-inner');
      // 选中节点
      let selected;

      el.onmousedown = (ev) => {
        // 选中节点
        selected = this.jm.get_selected_node();
        // 标识 是否拖拽节点 避免冲突
        this.dragNodeFlag = !!selected;

        const disX = ev.clientX;
        const disY = ev.clientY;
        const originalScrollLeft = el.scrollLeft;
        const originalScrollTop = el.scrollTop;
        const originalScrollBehavior = el.style['scroll-behavior'];
        const originalPointerEvents = el.style['pointer-events'];
        // auto: 默认值，表示滚动框立即滚动到指定位置。
        el.style['scroll-behavior'] = 'auto';
        // 鼠标移动事件是监听的整个document，这样可以使鼠标能够在元素外部移动的时候也能实现拖动
        document.onmousemove = (ev) => {
          if (this.dragNodeFlag) return;
          this.drag = false;
          ev.preventDefault();
          // 计算拖拽的偏移距离
          const distanceX = ev.clientX - disX;
          const distanceY = ev.clientY - disY;

          el.scrollTo(originalScrollLeft - distanceX, originalScrollTop - distanceY);

          // 在鼠标拖动的时候将点击事件屏蔽掉
          el.style['pointer-events'] = 'none';
          el.style.cursor = 'grabbing';
        };
        document.onmouseup = () => {
          if (!this.dragNodeFlag) {
            el.style['scroll-behavior'] = originalScrollBehavior;
            el.style['pointer-events'] = originalPointerEvents;
            el.style.cursor = 'grab';
          }
          document.onmousemove = document.onmouseup = null;
        };
      };
    },
  },

  beforeDestroy() {
    document.removeEventListener('domMouseScroll', this.scrollFunc, false);
  },
};
</script>

<style lang="scss" scoped>
@import '@/components/mindMap/mindMapStyle.scss';
</style>

<style lang="scss">
.save-dropdown-menu.el-dropdown-menu--small {
  .save-dropdown-item.el-dropdown-menu__item {
    line-height: 220% !important;
    font-size: 16px !important;
  }
}
</style>
