<!--
Copyright 2019 Huawei Technologies Co., Ltd.All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<template>
  <!--cl-graph-manage -->
  <div class="cl-graph-manage">
    <div class="graph-p32">
      <div class="guide-content"
           v-if="guide.show">
        <el-popover placement="top-start"
                    ref="popover"
                    :style="{ position: 'absolute', top: guide.top, left: guide.left }"
                    :title="guide.title"
                    width="370"
                    v-model="guide.show">
          <i class="el-icon-close"
             @click="closeUserGuide"></i>
          <div v-for="item in guide.content"
               :key="item"
               class="guide-span">{{ item }}</div>
          <div class="step-pic">
            <img :src="require(`@/assets/images/graph-stepimg${guide.step}.svg`)" />
          </div>

          <el-button type="primary"
                     @click="guideNext">{{
            guide.step === 3 ? $t('graph.finish') : $t('graph.next')
          }}</el-button>
        </el-popover>
        <div class="step"
             v-show="guide.step == 1">
          <img :src="require(`@/assets/images/graph-step1${language === 'en-us' ? '-en' : ''}.svg`)"
               alt="" />
        </div>
        <div class="step"
             v-show="guide.step == 2">
          <img :src="require(`@/assets/images/graph-step2${language === 'en-us' ? '-en' : ''}.svg`)"
               alt="" />
        </div>
        <div class="step"
             v-show="guide.step == 3">
          <img :src="require(`@/assets/images/graph-step3${language === 'en-us' ? '-en' : ''}.svg`)"
               alt="" />
        </div>
      </div>
      <div class="cl-title cl-graph-title">
        <div class="cl-title-left">
          {{ $t('graph.titleText') }}
          <div class="path-message">
            <span>{{$t('symbols.leftbracket')}}</span>
            <span>{{$t('trainingDashboard.summaryDirPath')}}</span>
            <span>{{summaryPath}}</span>
            <span>{{$t('symbols.rightbracket')}}</span>
          </div>
          <span @click="showUserGuide"
                class="guide">
            <i class="guide-icon"></i>
            {{$t('graph.guide')}}
          </span>
        </div>
        <div class="cl-title-right">
          <div class="cl-close-btn"
               @click="jumpToTrainDashboard">
            <img src="@/assets/images/close-page.png" />
          </div>
        </div>
      </div>
      <div class="cl-content">
        <div id="graphs">
          <div class="cl-graph"
               :class="fullScreen ? 'full-screen' : ''">
            <!-- graph -->
            <div class="graph-container"
                 :class="rightShow ? '' : 'all'">
              <!-- No data is displayed. -->
              <div class="image-noData"
                   v-if="!loading.show && !Object.keys(allGraphData).length">
                <div>
                  <img :src="require('@/assets/images/nodata.png')"
                       alt="" />
                </div>
                <div class="noData-text">{{ $t('public.noData') }}</div>
              </div>
              <!-- Operation button column -->
              <div class="operate-button-list">
                <!-- Download button. -->
                <div :title="$t('graph.fullScreen')"
                     class="full-screen-button"
                     @click="toggleScreen"></div>
                <div :title="$t('graph.downloadPic')"
                     class="download-button"
                     @click="downLoadSVG"></div>
              </div>
              <div id="graph"
                   class="graph"
                   v-loading.fullscreen.lock="loading.show"
                   element-loading-background="rgba(0, 0, 0, 0.3)"
                   :element-loading-text="loading.info"></div>
            </div>
            <!-- Right column -->
            <div id="sidebar"
                 :class="rightShow ? '' : 'right-hide'">
              <div class="toggle-right"
                   @click="toggleRight">
                <i :class="rightShow ? 'icon-toggle' : 'icon-toggle icon-left'"></i>
              </div>
              <!-- Search box -->
              <div class="sidebar-tooltip">
                <el-tooltip placement="top"
                            effect="light">
                  <div slot="content"
                       class="tooltip-container">
                    <div class="cl-graph-sidebar-tip">
                      {{$t('graph.sidebarTip')}}
                    </div>
                  </div>
                  <i class="el-icon-info"></i>
                </el-tooltip>
              </div>
              <el-select @change="fileChange"
                         @visible-change="getSelectList"
                         :popper-append-to-body="false"
                         class="search"
                         v-model="fileSearchBox.value">
                <el-option v-for="item in fileSearchBox.suggestions"
                           :key="item.value"
                           :title="item.value"
                           :label="item.value"
                           :value="item.value">
                </el-option>
              </el-select>
              <!-- Search box -->
              <div class="search-wrap">
                <el-input class="search"
                          :placeholder="$t('graph.inputNodeName')"
                          v-model="searchBox.value"
                          @input="filterChange"
                          @keyup.enter.native="searchNodesNames"
                          clearable>
                  <el-button slot="append"
                             @click="treeWrapFlag=!treeWrapFlag"
                             class="collapse_i">
                    <i class="el-icon-caret-bottom"
                       v-if="!treeWrapFlag"></i>
                    <i class="el-icon-caret-top"
                       v-else></i>
                  </el-button>
                </el-input>
                <div class="tree-wrap"
                     v-show="treeWrapFlag">
                  <el-tree v-show="treeFlag"
                           :props="props"
                           :load="loadNode"
                           @node-collapse="nodeCollapse"
                           @node-click="handleNodeClick"
                           node-key="name"
                           :expand-on-click-node="false"
                           :lazy="true"
                           :highlight-current="true"
                           ref="tree">
                    <span class="custom-tree-node"
                          slot-scope="{ node ,data }">
                      <span>
                        <img v-if="data.type ==='name_scope'"
                             :src="require('@/assets/images/name-scope.svg')"
                             class="image-type" />
                        <img v-else-if="data.type ==='Const'"
                             :src="require('@/assets/images/constant-node.svg')"
                             class="image-type" />
                        <img v-else-if="data.type ==='aggregation_scope'"
                             :src="require('@/assets/images/polymetric.svg')"
                             class="image-type" />
                        <img v-else
                             :src="require('@/assets/images/operator-node.svg')"
                             class="image-type" />
                      </span>
                      <span class="custom-tree-node">{{ node.label }}</span>
                    </span>
                  </el-tree>
                  <el-tree v-show="!treeFlag"
                           :props="defaultProps"
                           :load="loadSearchNode"
                           :lazy="true"
                           node-key="name"
                           @node-click="handleNodeClick"
                           :expand-on-click-node="false"
                           ref="searchTree">
                    <span class="custom-tree-node"
                          slot-scope="{ node ,data }">
                      <span>
                        <img v-if="data.type ==='name_scope'"
                             :src="require('@/assets/images/name-scope.svg')"
                             class="image-type" />
                        <img v-else-if="data.type ==='Const'"
                             :src="require('@/assets/images/constant-node.svg')"
                             class="image-type" />
                        <img v-else-if="data.type ==='aggregation_scope'"
                             :src="require('@/assets/images/polymetric.svg')"
                             class="image-type" />
                        <img v-else
                             :src="require('@/assets/images/operator-node.svg')"
                             class="image-type" />
                      </span>
                      <span class="custom-tree-node">{{ node.label }}</span>
                    </span>
                  </el-tree>
                </div>
              </div>
              <!-- Functional Area -->
              <div id="small-container">
                <div id="small-resize">
                  <div id="small-map"></div>
                  <div id="inside-box"></div>
                </div>
              </div>
              <!-- Node information -->
              <div :class="
                  showLegend
                    ? 'node-info-con node-info-container'
                    : 'node-info-con node-info-container-long'
                ">
                <div class="title">
                  {{ $t('graph.nodeInfo') }}
                  <img :src="require('@/assets/images/all-drop-down.png')"
                       fun="fold"
                       hidden
                       alt="" />
                  <img :src="require('@/assets/images/all-uptake.png')"
                       fun="fold"
                       hidden
                       alt="" />
                </div>
                <div class="node-info"
                     v-show="selectedNode.show">
                  <div class="items">
                    <div class="label item">{{ $t('graph.name') }}</div>
                    <div class="value">
                      <span class="cl-display-block"
                            @dblclick="nodeNameClick">{{ selectedNode.title }}</span>
                    </div>
                  </div>
                  <div class="items"
                       v-if="selectedNode.countShow">
                    <div class="label item">{{ $t('graph.count') }}</div>
                    <div class="value items-over">{{ selectedNode.count }}</div>
                  </div>
                  <div class="items"
                       v-if="!selectedNode.countShow">
                    <div class="label item">{{ $t('graph.type') }}</div>
                    <div class="value items-over">{{ selectedNode.type }}</div>
                  </div>
                  <div class="items itemHeight"
                       v-if="!selectedNode.countShow">
                    <div class="item">
                      {{ $t('graph.attr') }} ({{
                        selectedNode.info.attributes.length
                      }})
                    </div>
                  </div>
                  <ul v-if="selectedNode.info && !selectedNode.countShow"
                      class="item-content hover"
                      :class="
                      selectedNode.info.attributes.length > 2
                        ? 'item-min2'
                        : selectedNode.info.attributes.length > 0
                        ? 'item-min'
                        : ''
                    ">
                    <li v-for="item in selectedNode.info.attributes"
                        :key="item.name">
                      <div class="key">
                        {{ item.name }}
                      </div>
                      <div class="input cl-input-value">
                        <pre>{{ item.value }}</pre>
                      </div>
                    </li>
                  </ul>
                  <div class="items itemHeight">
                    <div class="item">
                      {{ $t('graph.inputs') }} (
                      {{
                        selectedNode.info.input.length +
                          selectedNode.info.inputControl.length
                      }})
                    </div>
                  </div>
                  <ul v-if="selectedNode.info"
                      class="item-content hover"
                      :class="
                      selectedNode.info.input.length > 1
                        ? 'item-min2'
                        : selectedNode.info.input.length > 0
                        ? 'item-min'
                        : ''
                    ">
                    <li v-for="item in selectedNode.info.input"
                        :key="item.$index"
                        @click="querySingleNode({ value: item.name })"
                        class="pointer">
                      <div class="input">{{ item.name }}</div>
                      <div class="size">{{ item.value }}</div>
                      <div class="clear"></div>
                    </li>
                    <li class="control-list"
                        v-if="
                        selectedNode.info &&
                          selectedNode.info.inputControl.length
                      ">
                      <div class="dependence-title"
                           @click="toggleControl('input')"
                           :class="selectedNode.showControl.input ? '' : 'hide'">
                        <img :src="require('@/assets/images/all-uptake.png')"
                             alt="" />
                        {{ $t('graph.controlDependencies') }}
                      </div>
                      <ul v-show="selectedNode.showControl.input">
                        <li v-for="item in selectedNode.info.inputControl"
                            :key="item.$index"
                            @click="querySingleNode({ value: item.name })"
                            class="pointer">
                          <div class="input">{{ item.name }}</div>
                          <div class="size">{{ item.value }}</div>
                          <div class="clear"></div>
                        </li>
                      </ul>
                    </li>
                  </ul>
                  <div class="items">
                    <div class="item">
                      {{ $t('graph.outputs') }} (
                      {{
                        selectedNode.info.output.length +
                          selectedNode.info.outputControl.length
                      }})
                    </div>
                  </div>
                  <ul v-if="selectedNode.info"
                      class="item-content hover"
                      :class="
                      selectedNode.info.output.length > 1
                        ? 'item-min2'
                        : selectedNode.info.output.length > 0
                        ? 'item-min'
                        : ''
                    ">
                    <li v-for="item in selectedNode.info.output"
                        :key="item.$index"
                        @click="querySingleNode({ value: item.name })"
                        class="pointer">
                      <div class="input">{{ item.name }}</div>
                      <div class="size">{{ item.value }}</div>
                      <div class="clear"></div>
                    </li>
                    <li class="control-list"
                        v-if="
                        selectedNode.info &&
                          selectedNode.info.outputControl.length
                      ">
                      <div class="dependence-title"
                           @click="toggleControl('output')"
                           :class="selectedNode.showControl.output ? '' : 'hide'">
                        <img :src="require('@/assets/images/all-uptake.png')"
                             alt="" />
                        {{ $t('graph.controlDependencies') }}
                      </div>
                      <ul v-show="selectedNode.showControl.output">
                        <li v-for="item in selectedNode.info.outputControl"
                            :key="item.$index"
                            @click="querySingleNode({ value: item.name })"
                            class="pointer">
                          <div class="input">{{ item.name }}</div>
                          <div class="size">{{ item.value }}</div>
                          <div class="clear"></div>
                        </li>
                      </ul>
                    </li>
                  </ul>
                  <div class="items"
                       v-if="selectedNode.info && selectedNode.info.output_i !== 0">
                    <div class="label item">{{ $t('graph.outputs_i') }}</div>
                    <span class="value">{{ selectedNode.info.output_i }}</span>
                  </div>
                </div>
              </div>
              <!-- Legend -->
              <div class="legend"
                   v-if="!fullScreen">
                <div class="title">
                  {{ $t('graph.legend') }}
                  <img :src="require('@/assets/images/all-drop-down.png')"
                       v-show="!showLegend"
                       @click="foldLegend"
                       alt="" />
                  <img :src="require('@/assets/images/all-uptake.png')"
                       v-show="showLegend"
                       @click="foldLegend"
                       alt="" />
                </div>
                <div v-show="showLegend"
                     class="legend-content">
                  <div class="legend-item">
                    <div class="pic">
                      <img :src="require('@/assets/images/name-scope.svg')"
                           alt="" />
                    </div>
                    <div class="legend-text"
                         :title="$t('graph.nameSpace')">
                      {{ $t('graph.nameSpace') }}
                    </div>
                  </div>
                  <div class="legend-item">
                    <div class="pic">
                      <img :src="require('@/assets/images/polymetric.svg')"
                           alt="" />
                    </div>
                    <div class="legend-text"
                         :title="$t('graph.polymetric')">
                      {{ $t('graph.polymetric') }}
                    </div>
                  </div>
                  <div class="legend-item">
                    <div class="pic">
                      <img :src="require('@/assets/images/virtual-node.svg')"
                           alt="" />
                    </div>
                    <div class="legend-text"
                         :title="$t('graph.virtualNode')">
                      {{ $t('graph.virtualNode') }}
                    </div>
                  </div>
                  <div class="legend-item">
                    <div class="pic">
                      <img :src="require('@/assets/images/operator-node.svg')"
                           alt="" />
                    </div>
                    <div class="legend-text"
                         :title="$t('graph.operatorNode')">
                      {{ $t('graph.operatorNode') }}
                    </div>
                  </div>
                  <div class="legend-item">
                    <div class="pic">
                      <img :src="require('@/assets/images/constant-node.svg')"
                           alt="" />
                    </div>
                    <div class="legend-text"
                         :title="$t('graph.constantNode')">
                      {{ $t('graph.constantNode') }}
                    </div>
                  </div>
                  <br>
                  <div class="legend-item">
                    <div class="pic">
                      <img :src="require('@/assets/images/data-flow.png')"
                           alt="" />
                    </div>
                    <div class="legend-text"
                         :title="$t('graph.dataFlowEdge')">
                      {{ $t('graph.dataFlowEdge') }}
                    </div>
                  </div>
                  <div class="legend-item">
                    <div class="pic">
                      <img :src="require('@/assets/images/control-dep.png')"
                           alt="" />
                    </div>
                    <div class="legend-text"
                         :title="$t('graph.controllDepEdge')">
                      {{ $t('graph.controllDepEdge') }}
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import CommonProperty from '@/common/common-property.js';
import RequestService from '@/services/request-service';
import {select, selectAll, zoom} from 'd3';
import 'd3-graphviz';
const d3 = {select, selectAll, zoom};
import commonGraph from '../../mixins/common-graph.vue';
import smallMap from '../../mixins/small-map.vue';

export default {
  mixins: [commonGraph, smallMap],
  data() {
    return {
      summaryPath: this.$route.query.summaryPath,
      graph: {}, // Basic information about graph0 in svg
      svg: {}, // Basic information about svg

      allGraphData: {}, // graph Original input data
      firstFloorNodes: [], // ID array of the first layer node.
      // Information about the selected node
      selectedNode: {
        info: {
          inputControl: [],
          input: [],
          outputControl: [],
          output: [],
          attributes: [],
        },
        showControl: {
          input: true,
          output: true,
        },
      },
      // Training job id
      trainJobID: '',
      nodesCountLimit: 1500, // Maximum number of sub-nodes in a namespace.
      maxChainNum: 70,

      // Node search box
      searchBox: {
        value: '',
        suggestions: [],
      },
      fileSearchBox: {
        value: '',
        suggestions: [],
      },
      showLegend: true, // Display Legend
      // Controls whether the loading is displayed.
      loading: {
        show: false,
        info: '',
      },
      scaleRange: [0.001, 1000], // graph zooms in and zooms out.
      rightShow: true, // Check whether the right side bar is displayed.
      fullScreen: false, // Display Full Screen
      graphviz: null,
      graphvizTemp: null,
      initOver: false,
      guide: {
        show: false,
        step: 1,
        top: '0%',
        left: '0%',
        content: [
          this.$t('graph.guideContent11'),
          this.$t('graph.guideContent12'),
          this.$t('graph.guideContent13'),
          this.$t('graph.guideContent14'),
        ],
        title: '',
      },
      language: '',
      defaultProps: {
        children: 'nodes',
        label: 'label',
        isLeaf: 'leaf',
      },
      treeFlag: true,
      props: {
        label: 'label',
        children: 'children',
        isLeaf: 'leaf',
      },
      node: null,
      resolve: null,
      treeWrapFlag: true,
      searchNode: null,
      searchResolve: null,
      isIntoView: true,
    };
  },
  computed: {},
  watch: {
    guide: {
      handler(newVal) {
        if (newVal.step === 1) {
          this.guide.top = '20%';
          this.guide.left = '48%';
        } else if (newVal.step === 2) {
          this.guide.top = '47%';
          this.guide.left = '62%';
        } else if (newVal.step === 3) {
          this.guide.top = '45%';
          this.guide.left = '52%';
        }
        this.guide.title = this.$t(`graph.guideTitle${newVal.step}`);
      },
      deep: true,
    },
  },
  mounted() {
    // Judging from the training job overview.
    if (!this.$route.query || !this.$route.query.train_id) {
      this.trainJobID = '';
      this.$message.error(this.$t('trainingDashboard.invalidId'));
      document.title = `${this.$t('graph.titleText')}-MindInsight`;
      return;
    }
    const showGuide = window.localStorage.getItem('graphShowGuide');
    if (!showGuide) {
      this.guide.show = true;
      window.localStorage.setItem('graphShowGuide', true);
    }

    this.trainJobID = this.$route.query.train_id;

    this.language = window.localStorage.getItem('milang');
    const languageList = ['zh-cn', 'en-us'];
    if (!this.language || !languageList.includes(this.language)) {
      this.language = languageList[0];
      window.localStorage.setItem('milang', this.language);
    }

    document.title = `${decodeURIComponent(this.trainJobID)}-${this.$t(
        'graph.titleText',
    )}-MindInsight`;
    this.getDatavisualPlugins();
    window.onresize = () => {
      if (this.graph.dom) {
        this.$nextTick(() => {
          setTimeout(() => {
            this.initSvg(false);
            this.initGraphRectData();
          }, 500);
        });
      }
    };
  },
  destroyed() {
    window.onresize = null;
  },
  methods: {
    /**
     * Tree linkage with graph  Expand of current node
     * @param {Obejct} nodes Data of children of current node
     * @param {Obejct} name  The name of the current node
     */
    nodeExpandLinkage(nodes, name) {
      const curNodeData = nodes.map((val) => {
        return {
          label: val.name.split('/').pop(),
          ...val,
        };
      });
      const node = this.$refs.tree.getNode(name);
      curNodeData.forEach((val) => {
        this.$refs.tree.append(val, name);
      });
      node.childNodes.forEach((val) => {
        if (
          val.data.type !== 'name_scope' &&
          val.data.type !== 'aggregation_scope'
        ) {
          val.isLeaf = true;
        }
      });
      node.expanded = true;
      node.loading = false;
      this.$refs.tree.setCurrentKey(name);
      this.$nextTick(() => {
        setTimeout(() => {
          const dom = document.querySelector('.el-tree-node.is-current.is-focusable');
          if (dom && this.rightShow) {
            dom.scrollIntoView();
          }
        }, 800);
      });
    },
    /**
     * Collapse node
     * @param {Object} _
     * @param {Object} node node data
     */
    nodeCollapse(_, node) {
      node.loaded = false;
      if (this.treeFlag) {
        this.dealDoubleClick(node.data.name);
      }
    },
    /**
     * Draw the tree
     * @param {Object} node tree root node
     * @param {Function} resolve callback function ,return next node data
     */
    loadNode(node, resolve) {
      if (node.level === 0) {
        node.childNodes = [];
        if (!this.node && !this.resolve) {
          this.node = node;
          this.resolve = resolve;
        }
      } else if (node.level >= 1) {
        this.isIntoView = false;
        this.queryGraphData(node.data.name, resolve);
      }
    },
    /**
     * Draw the tree
     * @param {Object} node tree root node
     * @param {Function} resolve callback function ,return next node data
     */
    loadSearchNode(node, resolve) {
      if (node.level === 0) {
        node.childNodes = [];
        if (!this.searchNode && !this.searchResolve) {
          this.searchNode = node;
          this.searchResolve = resolve;
        }
      } else if (node.level >= 1) {
        const params = {
          name: node.data.name,
          train_id: this.trainJobID,
          tag: this.fileSearchBox.value,
        };
        if (node.childNodes && node.childNodes.length) {
          node.expanded = true;
          node.loaded = true;
          node.loading = false;
          return;
        }
        RequestService.queryGraphData(params).then((res) => {
          if (res && res.data && res.data.nodes) {
            const data = res.data.nodes.map((val) => {
              return {
                label: val.name.split('/').pop(),
                leaf:
                  val.type === 'name_scope' || val.type === 'aggregation_scope'
                    ? false
                    : true,
                ...val,
              };
            });
            resolve(data);
          }
        });
      }
    },
    /**
     * Deal search data
     * @param {Array} arr search tree data
     */
    dealSearchResult(arr) {
      arr.forEach((val) => {
        if (val.nodes) {
          this.dealSearchResult(val.nodes);
        }
        val.label = val.name.split('/').pop();
      });
    },
    filterChange() {
      if (this.searchBox.value === '') {
        this.treeFlag = true;
        this.$nextTick(() => {
          setTimeout(() => {
            const dom = document.querySelector('.el-tree-node.is-current.is-focusable');
            if (dom && this.rightShow) {
              dom.scrollIntoView();
            }
          }, 800);
        });
      }
    },
    handleNodeClick(data) {
      this.isIntoView = false;
      this.selectedNode.name = data.name;
      if (this.treeFlag) {
        this.selectNode(true);
      } else {
        this.querySingleNode({value: data.name});
      }
    },
    /**
     * Add the location attribute to each node to facilitate the obtaining of node location parameters.
     */
    afterInitGraph() {
      setTimeout(() => {
        if (this.graphviz) {
          this.graphviz._data = null;
          this.graphviz._dictionary = null;
          this.graphviz = null;
        }

        if (this.graphvizTemp) {
          this.graphvizTemp._data = null;
          this.graphvizTemp._dictionary = null;
          this.graphvizTemp = null;
        }
      }, 100);
      this.fitGraph('graph');
      this.transplantChildrenDom();
      const svg = document.querySelector('#subgraphTemp svg');
      if (svg) {
        svg.remove();
      }
      this.$nextTick(() => {
        this.loading.show = false;
      });
      const elements = d3.select('#graph').selectAll('g.node, g.edge').nodes();
      elements.forEach((ele) => {
        if (!ele.hasAttribute('transform')) {
          ele.setAttribute('transform', 'translate(0,0)');
        }
        // The title value needs to be manually set for the virtual node.
        if (Array.prototype.includes.call(ele.classList, 'plain')) {
          const title = ele.querySelector('title');
          title.textContent = title.textContent.split('^')[0];
        }
      });
      d3.selectAll('g.edge>title').remove();
      // The graph generated by the plug-in has a useless title and needs to be deleted.
      document.querySelector('#graph g#graph0 title').remove();
      this.initGraphRectData();
      this.startApp();
    },
    /**
     * Initialization method executed after the graph rendering is complete
     */
    startApp() {
      const nodes = d3.selectAll('g.node, g.cluster');
      nodes.on(
          'click',
          (target, index, nodesList) => {
            this.clickEvent(target, index, nodesList, 'graph');
          },
          false,
      );
      // namespaces Expansion or Reduction
      nodes.on(
          'dblclick',
          (target, index, nodesList) => {
            this.dblclickEvent(target, index, nodesList, 'graph');
          },
          false,
      );
      this.initZooming('graph');
      if (this.selectedNode.name) {
        this.selectNode(true);
      }
    },
    /**
     * Double-click the processing to be performed on the node to expand or narrow the namespace or aggregation node.
     * @param {String} name Name of the current node (also the ID of the node)
     */
    dealDoubleClick(name) {
      this.loading.info = this.$t('graph.queryLoading');
      this.loading.show = true;
      this.$nextTick(() => {
        setTimeout(() => {
          name = name.replace('_unfold', '');
          if (this.allGraphData[name].isUnfold) {
            this.selectedNode.name = name;
            this.deleteNamespace(name);
          } else {
            this.queryGraphData(name);
          }
        }, 200);
      });
    },
    /**
     * To obtain graph data, initialize and expand the namespace or aggregate nodes.
     * @param {String} name Name of the current node.
     * @param {Function} resolve Callback function.
     */
    queryGraphData(name, resolve) {
      const namescopeChildLimit = 3500;
      const independentLayout = this.allGraphData[name]
        ? this.allGraphData[name].independent_layout
        : false;
      const params = {
        name: name,
        train_id: this.trainJobID,
        tag: this.fileSearchBox.value,
      };
      this.loading.info = this.$t('graph.queryLoading');
      this.loading.show = true;
      RequestService.queryGraphData(params)
          .then(
              (response) => {
                if (response && response.data && response.data.nodes) {
                  // If the namespace to be expanded is larger than the maximum number of subnodes,
                  // an error is reported and the namespace is highlighted.
                  const nodesCountLimit = name
                ? this.nodesCountLimit
                : namescopeChildLimit;
                  if (
                    !independentLayout &&
                response.data.nodes.length > nodesCountLimit
                  ) {
                    this.$message.error(this.$t('graph.tooManyNodes'));
                    this.packageDataToObject(name, false);
                    this.loading.show = false;
                  } else {
                    const nodes = JSON.parse(JSON.stringify(response.data.nodes));
                    if (nodes && nodes.length) {
                      this.packageDataToObject(name, true, nodes);
                      // If the name is empty, it indicates the outermost layer.
                      if (!name) {
                        const dot = this.packageGraphData();
                        this.initGraph(dot);
                      } else {
                        if (this.allGraphData[name].type === 'aggregation_scope') {
                          this.dealAggregationNodes(name);
                          if (
                            this.allGraphData[name].maxChainNum > this.maxChainNum
                          ) {
                            this.$message.error(this.$t('graph.tooManyChain'));
                            this.allGraphData[name].isUnfold = true;
                            this.selectedNode.name = name;
                            this.loading.show = false;
                            this.deleteNamespace(name);
                            return;
                          }
                        }
                        this.allGraphData[name].isUnfold = true;
                        this.selectedNode.name = `${name}_unfold`;
                        this.layoutNamescope(name, true);
                      }
                    } else {
                      this.initGraphRectData();
                      this.loading.show = false;
                    }
                    const data = response.data.nodes.map((val) => {
                      return {
                        label: val.name.split('/').pop(),
                        leaf:
                      val.type === 'name_scope' ||
                      val.type === 'aggregation_scope'
                        ? false
                        : true,
                        ...val,
                      };
                    });
                    if (name) {
                      if (resolve) {
                        resolve(JSON.parse(JSON.stringify(data)));
                      } else {
                        this.nodeExpandLinkage(response.data.nodes, name);
                      }
                    } else {
                      this.node.childNodes = [];
                      this.resolve(JSON.parse(JSON.stringify(data)));
                    }
                  }
                }
              },
              (error) => {
                this.loading.show = false;
              },
          )
          .catch((error) => {
          // A non-Google Chrome browser may not work properly.
            this.loading.show = false;
            if (error && error.includes('larger than maximum 65535 allowed')) {
              this.$message.error(this.$t('graph.dataTooLarge'));
            } else {
              this.$bus.$emit('showWarmText', true);
            }
            if (name && this.allGraphData[name]) {
              this.allGraphData[name].isUnfold = false;
              this.allGraphData[name].children = [];
              this.allGraphData[name].size = [];
              this.allGraphData[name].html = '';
            }
          });
    },
    /**
     * To obtain datavisual plugins
     */
    getDatavisualPlugins() {
      const params = {
        train_id: this.trainJobID,
      };
      RequestService.getDatavisualPlugins(params)
          .then((res) => {
            this.fileSearchBox.suggestions = [];
            if (
              !res ||
            !res.data ||
            !res.data.plugins ||
            !res.data.plugins.graph ||
            !res.data.plugins.graph.length
            ) {
              this.initOver = true;
              return;
            }
            const tags = res.data.plugins.graph;
            let hasFileSearchValue = false;
            tags.forEach((k) => {
              this.fileSearchBox.suggestions.push({
                value: k,
              });
              hasFileSearchValue =
              k === this.fileSearchBox.value || hasFileSearchValue;
            });
            if (!this.initOver) {
              this.initOver = true;
              this.fileSearchBox.value = tags.length ? tags[0] : '';
              this.queryGraphData();
            } else if (!hasFileSearchValue) {
              this.fileSearchBox.value = '';
            }
          })
          .catch(() => {
            this.fileSearchBox.suggestions = [];
            this.initOver = true;
            this.loading.show = false;
          });
    },
    /**
     * Close the expanded namespace.
     * @param {String} name The name of the namespace to be closed.
     */
    deleteNamespace(name) {
      this.loading.info = this.$t('graph.searchLoading');
      this.loading.show = true;
      if (!this.selectedNode.more) {
        this.packageDataToObject(name, false);
        this.layoutController(name);
      } else {
        this.allGraphData[name].isUnfold = true;
        this.selectedNode.name = `${name}_unfold`;
        this.layoutNamescope(name, true);
      }
    },
    /**
     * Controls the invoking method of the next step.
     * @param {String} name Name of the namespace to be expanded.
     */
    layoutController(name) {
      if (!this.loading.show) {
        this.loading.info = this.$t('graph.searchLoading');
        this.loading.show = true;
      }
      if (name.includes('/')) {
        const subPath = name.split('/').slice(0, -1).join('/');
        this.layoutNamescope(subPath, true);
      } else {
        const svg = document.querySelector('#graph svg');
        if (svg) {
          svg.remove();
        }
        const dot = this.packageGraphData();
        this.initGraph(dot);
      }
    },
    /**
     * Selecting a node
     * @param {Boolean} needFocus Whether to focus on the node
     */
    selectNode(needFocus = false) {
      window.getSelection().removeAllRanges();
      d3.selectAll(
          '.node polygon, .node ellipse, .node rect, .node path',
      ).classed('selected', false);
      const path = this.selectedNode.name.split('^');
      const node = {};
      let id = path[0].replace('_unfold', '');
      id = this.allGraphData[id].isUnfold ? `${id}_unfold` : id;
      node.eld3 = d3.select(`#graph g[id="${id}"]`);
      node.el = node.eld3.node();
      this.graph.dom.style.transition = '';

      const needDelay = path.length > 1;
      if ((needFocus || needDelay) && node.el) {
        this.selectNodePosition(id, needDelay);
      }
      node.eld3
          .select('polygon, rect, ellipse, path')
          .classed('selected', true);
      this.highlightProxyNodes(id.replace('_unfold', ''));
      this.$refs.tree.setCurrentKey(id.replace('_unfold', ''));
      if (this.isIntoView) {
        this.$nextTick(() => {
          setTimeout(() => {
            const dom = document.querySelector('.el-tree-node.is-current.is-focusable');
            if (dom && this.rightShow) {
              dom.scrollIntoView();
            }
          }, 800);
        });
      }
      this.isIntoView = true;
      this.setNodeData();
    },
    /**
     * The node information of the selected node is displayed and highlighted.
     */
    setNodeData() {
      this.selectedNode.info = {
        input: [],
        inputControl: [],
        output: [],
        outputControl: [],
        attributes: [],
      };
      this.selectedNode.showControl = {
        input: true,
        output: true,
      };

      const path = this.selectedNode.name.split('^');
      const selectedNode = this.allGraphData[path[0].replace('_unfold', '')];

      if (selectedNode && !selectedNode.name.includes('more...')) {
        this.selectedNode.show = true;
        this.selectedNode.name = selectedNode.name;
        this.selectedNode.title = selectedNode.name.replace('_unfold', '');
        this.selectedNode.type =
          selectedNode.type === 'name_scope' ||
          selectedNode.type === 'aggregation_scope'
            ? ''
            : selectedNode.type;
        this.selectedNode.countShow =
          selectedNode.type === 'name_scope' ||
          selectedNode.type === 'aggregation_scope';
        this.selectedNode.count = selectedNode.subnode_count;
        const attrTemp = JSON.parse(JSON.stringify(selectedNode.attr || {}));
        if (attrTemp.shape) {
          const shape = JSON.parse(attrTemp.shape);
          if (shape.length) {
            let str = '';
            for (let i = 0; i < shape.length; i++) {
              str += (str ? ',' : '') + JSON.stringify(shape[i]);
            }
            attrTemp.shape = str;
          }
        }

        this.selectedNode.info.attributes = Object.keys(attrTemp).map((key) => {
          return {
            name: key,
            value: attrTemp[key],
          };
        });

        Object.keys(selectedNode.input).forEach((key) => {
          const value = this.getEdgeLabel(selectedNode.input[key]);
          if (selectedNode.input[key].edge_type !== 'control') {
            this.selectedNode.info.input.push({
              name: key,
              value: value,
            });
          } else {
            this.selectedNode.info.inputControl.push({
              name: key,
              value: value,
            });
          }
        });

        Object.keys(selectedNode.output).forEach((key) => {
          const value = this.getEdgeLabel(selectedNode.output[key]);
          if (selectedNode.output[key].edge_type !== 'control') {
            this.selectedNode.info.output.push({
              name: key,
              scope: selectedNode.output[key].scope,
              value: value,
            });
          } else {
            this.selectedNode.info.outputControl.push({
              name: key,
              scope: selectedNode.output[key].scope,
              value: value,
            });
          }
        });
        this.selectedNode.info.output_i = selectedNode.output_i;
        this.highLightEdges(selectedNode);
      } else {
        this.selectedNode.show = false;
        this.selectedNode.name = '';
        this.selectedNode.title = '';
        this.selectedNode.type = '';
      }
    },
    /**
     * The position is offset to the current node in the center of the screen.
     * @param {String} nodeId Selected Node id
     * @param {Boolean} needDelay Delay required
     */
    selectNodePosition(nodeId, needDelay) {
      const nodeDom = document.querySelector(`#graph0 g[id="${nodeId}"]`);
      const nodeRect = nodeDom.getBoundingClientRect();

      const graph = {};
      graph.rect = this.graph.dom.getBoundingClientRect();
      graph.initWidth = graph.rect.width / this.graph.transform.k;
      graph.initHeight = graph.rect.height / this.graph.transform.k;

      const screenChange = {
        x:
          nodeRect.left +
          nodeRect.width / 2 -
          (this.svg.rect.left + this.svg.rect.width / 2),
        y:
          nodeRect.top +
          nodeRect.height / 2 -
          (this.svg.rect.top + this.svg.rect.height / 2),
      };

      this.graph.transform.x -=
        screenChange.x * (this.svg.originSize.width / graph.initWidth);
      this.graph.transform.y -=
        screenChange.y * (this.svg.originSize.height / graph.initHeight);

      this.graph.dom.setAttribute(
          'transform',
          `translate(${this.graph.transform.x},` +
          `${this.graph.transform.y}) scale(${this.graph.transform.k})`,
      );

      const transitionTime = Math.min(
          Math.abs(screenChange.x) * 2,
          Math.abs(screenChange.y) * 2,
        needDelay ? 800 : 0,
      );

      this.graph.dom.style.transition = `${transitionTime / 1000}s`;
      this.graph.dom.style['transition-timing-function'] = 'linear';

      setTimeout(() => {
        this.graph.dom.style.transition = '';
      }, transitionTime);
      let end = 0;
      this.setInsideBoxData();
      const timer = setInterval(() => {
        this.setInsideBoxData();
        end += 1;
        if (end > transitionTime) {
          clearInterval(timer);
        }
      }, 1);
    },
    /**
     * The drop-down list box of the search drop-down list box is controlled.
     * @param {Object} event Operation event of a component.
     */
    selectBoxVisibleTriggle(event) {
      setTimeout(() => {
        document.querySelector('.el-autocomplete-suggestion').style.display =
          event.type === 'blur' ? 'none' : 'block';
      }, 300);
    },
    /**
     * file select change
     */
    fileChange() {
      this.selectedNode = {
        info: {
          inputControl: [],
          input: [],
          outputControl: [],
          output: [],
          attributes: [],
        },
        showControl: {
          input: true,
          output: true,
        },
      };
      this.clickScope = {};
      this.searchBox.value = '';
      Object.keys(this.allGraphData).forEach((key) => {
        delete this.allGraphData[key];
      });
      d3.select('#graph svg').remove();
      this.firstFloorNodes = [];
      this.queryGraphData();
      this.treeFlag = true;
    },
    /**
     * refresh select list
     * @param {Boolean} expanded Should get data visual plugins or not.
     */
    getSelectList(expanded) {
      if (expanded) {
        this.getDatavisualPlugins();
      }
    },
    /**
     * The search drop-down list box displays the matched data by entering data.
     * @param {String} content Input parameters
     * @param {Object} callback Callback Function
     */
    searchNodesNames() {
      const params = {
        search: this.searchBox.value,
        train_id: this.trainJobID,
        tag: this.fileSearchBox.value,
        offset: 0,
        limit: 1000,
      };
      RequestService.searchNodesNames(params)
          .then(
              (response) => {
                if (response && response.data) {
                  this.treeFlag = false;
                  this.treeWrapFlag = true;
                  this.searchNode.childNodes = [];
                  const data = response.data.nodes.map((val) => {
                    return {
                      label: val.name.split('/').pop(),
                      ...val,
                    };
                  });
                  const currentData = JSON.parse(JSON.stringify(data));
                  currentData.forEach((val) => {
                    val.nodes = [];
                  });
                  this.searchResolve(currentData);
                  data.forEach((val, key) => {
                    if (val.nodes && val.nodes.length) {
                      val.nodes.forEach((value) => {
                        value.parentName = val.name;
                      });
                      this.dealSearchTreeData(val.nodes);
                    }
                  });
                }
              },
              (e) => {
                this.loading.show = false;
              },
          )
          .catch((e) => {
            this.$message.error(this.$t('public.dataError'));
          });
    },
    /**
     * Draw the tree
     * @param {Object} children child node
     */
    dealSearchTreeData(children) {
      children.forEach((val) => {
        const node = this.$refs.searchTree.getNode(val.parentName);
        val.label = val.name.split('/').pop();
        val.leaf =
          val.type === 'name_scope' || val.type === 'aggregation_scope'
            ? false
            : true;
        this.$refs.searchTree.append(val, node);
        node.expanded = true;
        if (val.nodes && val.nodes.length) {
          val.nodes.forEach((value) => {
            value.parentName = val.name;
          });
          this.dealSearchTreeData(val.nodes);
        }
      });
    },
    /**
     * Search for all data of a specific node and its namespace.
     * @param {Object} option Selected node data object
     */
    querySingleNode(option) {
      this.selectedNode.name = option.value;
      this.selectedNode.more = false;
      // If a node exists on the map, select the node.
      if (this.allGraphData[option.value]) {
        if (
          d3
              .select(`g[id="${option.value}"], g[id="${option.value}_unfold"]`)
              .size()
        ) {
          // If the namespace or aggregation node is expanded, you need to close it and select
          if (!this.allGraphData[option.value].isUnfold) {
            this.selectNode(true);
          } else {
            this.dealDoubleClick(option.value);
          }
        } else {
          const parentId = option.value.substring(
              0,
              option.value.lastIndexOf('/'),
          );
          if (
            this.allGraphData[parentId] &&
            this.allGraphData[parentId].isUnfold
          ) {
            const aggregationNode = this.allGraphData[parentId];
            if (aggregationNode && aggregationNode.childIdsList) {
              for (let i = 0; i < aggregationNode.childIdsList.length; i++) {
                if (aggregationNode.childIdsList[i].includes(option.value)) {
                  aggregationNode.index = i;
                  break;
                }
              }
            }
            this.loading.info = this.$t('graph.searchLoading');
            this.loading.show = true;
            this.selectedNode.name = option.value;

            this.$nextTick(() => {
              setTimeout(() => {
                this.layoutNamescope(parentId, true);
              }, 500);
            });
          }
        }
      } else {
        // If the node does not exist and is not a subnode of the aggregation node,
        // directly invoke the background for query.
        // If the node does not exist and is a subnode of the aggregation node, and the aggregation node is not
        // expanded, directly invoke the background to check the node.
        // If the node does not exist and is a child node in the aggregation node,
        // and the aggregation node is expanded but is not displayed on the diagram, you need to zoom out the
        // aggregated node, query the aggregation node again, and intercept the node array again.
        const params = {
          name: option.value,
          train_id: this.trainJobID,
          tag: this.fileSearchBox.value,
        };
        this.loading.info = this.$t('graph.searchLoading');
        this.loading.show = true;
        RequestService.querySingleNode(params)
            .then(
                (response) => {
                  if (response && response.data && response.data.children) {
                    const data = this.findStartUnfoldNode(response.data.children);
                    if (data) {
                      this.dealAutoUnfoldNamescopesData(data);
                    }
                    if (!this.treeFlag && response.data.children) {
                      this.dealTreeData(response.data.children, option.value);
                    }
                  }
                },
                (e) => {
                  this.loading.show = false;
                },
            )
            .catch((e) => {
              this.loading.show = false;
              this.$message.error(this.$t('public.dataError'));
            });
      }
    },
    /**
     * Draw the tree
     * @param {Object} children child node
     * @param {String} name The name of the node that needs to be highlighted
     */
    dealTreeData(children, name) {
      if (children.nodes) {
        const data = children.nodes.map((val) => {
          return {
            label: val.name.split('/').pop(),
            ...val,
          };
        });
        data.forEach((val) => {
          const node = this.$refs.tree.getNode(children.scope_name);
          if (node.childNodes) {
            if (
              node.childNodes
                  .map((value) => value.data.name)
                  .indexOf(val.name) === -1
            ) {
              this.$refs.tree.append(val, node);
            }
          } else {
            this.$refs.tree.append(val, node);
          }
        });
        const node = this.$refs.tree.getNode(children.scope_name);
        node.childNodes.forEach((val) => {
          if (
            val.data.type !== 'name_scope' &&
            val.data.type !== 'aggregation_scope'
          ) {
            val.isLeaf = true;
          }
        });
        node.expanded = true;
        node.loading = false;
      } else {
        this.$refs.tree.setCurrentKey(name);
        this.$nextTick(() => {
          setTimeout(() => {
            const dom = document.querySelector('.el-tree-node.is-current.is-focusable');
            if (dom && this.rightShow) {
              dom.scrollIntoView();
            }
          }, 800);
        });
      }
      if (children.children) {
        this.dealTreeData(children.children, name);
      }
    },
    /**
     * Processes all data of the queried node and the namespace to which the node belongs.
     * @param {Object} data All data of the node and the namespace to which the node belongs
     * @return {Object} The data object of the namespace to expand.
     */
    dealAutoUnfoldNamescopesData(data) {
      if (!data.scope_name) {
        return this.dealAutoUnfoldNamescopesData(data.children);
      } else {
        if (this.allGraphData[data.scope_name].isUnfold) {
          return this.dealAutoUnfoldNamescopesData(data.children);
        } else {
          // If the namespace is a namespace and the number of subnodes exceeds the upper limit,
          // an error is reported and the namespace is selected.
          if (
            this.allGraphData[data.scope_name].type === 'name_scope' &&
            data.nodes.length > this.nodesCountLimit
          ) {
            this.selectedNode.name = data.scope_name;
            this.querySingleNode({value: data.scope_name});
            this.$message.error(this.$t('graph.tooManyNodes'));
            this.$nextTick(() => {
              this.loading.show = false;
            });
          } else {
            // Normal expansion
            const nodes = JSON.parse(JSON.stringify(data.nodes));
            this.packageDataToObject(data.scope_name, true, nodes);
            if (
              this.allGraphData[data.scope_name].type === 'aggregation_scope'
            ) {
              this.dealAggregationNodes(data.scope_name);
              const aggregationNode = this.allGraphData[data.scope_name];
              if (aggregationNode) {
                for (let i = 0; i < aggregationNode.childIdsList.length; i++) {
                  if (
                    aggregationNode.childIdsList[i].includes(
                        this.selectedNode.name,
                    )
                  ) {
                    aggregationNode.index = i;
                    break;
                  }
                }
              }
              if (
                this.allGraphData[data.scope_name].maxChainNum >
                this.maxChainNum
              ) {
                this.selectedNode.name = data.scope_name;
                this.allGraphData[data.scope_name].isUnfold = false;
                this.deleteNamespace(data.scope_name);
                this.$message.error(this.$t('graph.tooManyChain'));
                this.$nextTick(() => {
                  this.loading.show = false;
                });
                return;
              }
            }

            if (data.children.scope_name) {
              this.dealAutoUnfoldNamescopesData(data.children);
            } else {
              this.loading.info = this.$t('graph.searchLoading');
              this.loading.show = true;
              this.$nextTick(() => {
                setTimeout(() => {
                  this.layoutNamescope(data.scope_name, true);
                }, 200);
              });
            }
          }
        }
      }
    },
    /**
     * Expansion and folding of control edges
     * @param {String} item Determines the control edge of the input or output.
     */
    toggleControl(item) {
      this.selectedNode.showControl[item] = !this.selectedNode.showControl[
          item
      ];
    },
    /**
     * Click the node information name.
     */
    nodeNameClick() {
      const nodeNameText = event.target;
      if (document.body.createTextRange) {
        const nodeNameTextRange = document.body.createTextRange();
        nodeNameTextRange.moveToElementText(nodeNameText);
        nodeNameTextRange.select();
      } else if (window.getSelection) {
        const nodeNameSelection = window.getSelection();
        const nodeNameTextRange = document.createRange();
        nodeNameTextRange.selectNodeContents(nodeNameText);
        nodeNameSelection.removeAllRanges();
        nodeNameSelection.addRange(nodeNameTextRange);
      }
    },
    /**
     * Collapse on the right
     */
    toggleRight() {
      this.rightShow = !this.rightShow;
      setTimeout(() => {
        this.initSvg(false);
        this.initGraphRectData();
      }, 500);
    },
    /**
     * Full-screen display
     */
    toggleScreen() {
      this.fullScreen = !this.fullScreen;
      setTimeout(() => {
        this.initSvg(false);
        this.initGraphRectData();
      }, 500);
    },
    /**
     * Download svg
     */
    downLoadSVG() {
      const svgXml = document.querySelector('#graph #graph0').innerHTML;
      const bbox = document.getElementById('graph0').getBBox();
      const viewBoxSize = `${bbox.x} ${bbox.y} ${bbox.width} ${bbox.height}`;
      const encodeStr =
        `<svg xmlns="http://www.w3.org/2000/svg" ` +
        `xmlns:xlink="http://www.w3.org/1999/xlink" ` +
        `width="${bbox.width}" height="${bbox.height}" ` +
        `viewBox="${viewBoxSize}">${CommonProperty.graphDownloadStyle}<g>${svgXml}</g></svg>`;

      const downloadLink = document.createElement('a');
      downloadLink.download = 'graph.svg';
      downloadLink.style.display = 'none';
      const blob = new Blob([encodeStr], {type: 'text/html'});
      downloadLink.href = URL.createObjectURL(blob);
      document.body.appendChild(downloadLink);
      downloadLink.click();
      document.body.removeChild(downloadLink);
    },
    /**
     * Fold the legend area.
     */
    foldLegend() {
      this.showLegend = !this.showLegend;
    },
    /**
     * Control Display User Guide
     */
    showUserGuide() {
      this.guide.content = [
        this.$t('graph.guideContent11'),
        this.$t('graph.guideContent12'),
        this.$t('graph.guideContent13'),
        this.$t('graph.guideContent14'),
      ];
      this.guide.show = true;
      this.guide.step = 1;
    },
    /**
     * Close user guide
     */
    closeUserGuide() {
      this.guide.show = false;
    },
    /**
     * Show the next step
     */
    guideNext() {
      if (this.guide.step < 3) {
        this.guide.step++;
        switch (this.guide.step) {
          case 2:
            this.guide.content = [this.$t('graph.guideContent2')];
            break;
          case 3:
            this.guide.content = [this.$t('graph.guideContent3')];
            break;
          default:
            break;
        }
      } else if (this.guide.step >= 3) {
        this.guide.show = false;
      }
    },
    /**
     * jump back to train dashboard
     */
    jumpToTrainDashboard() {
      this.$router.push({
        path: '/train-manage/training-dashboard',
        query: {
          id: this.trainJobID,
        },
      });
    },
  },
  // Components imported by the page
  components: {},
};
</script>
<style lang="scss">
.tooltip-container {
  .cl-graph-sidebar-tip {
    word-break: normal;
  }
}
.cl-graph-manage {
  height: 100%;
  .cl-graph-title {
    height: 50px;
    line-height: 50px;
    .path-message {
      display: inline-block;
      line-height: 20px;
      padding: 0px 4px 15px 4px;
      font-weight: bold;
      vertical-align: bottom;
    }
    .guide {
      cursor: pointer;
      margin-left: 10px;
      display: inline-block;
      line-height: 18px;
      font-size: 12px;
      .guide-icon {
        display: inline-block;
        width: 16px;
        height: 16px;
        vertical-align: -2.5px;
        margin-right: 4px;
        background: url('../../assets/images/guideIcon.svg');
      }
    }
    .guide:hover {
      color: #00a5a7;
      .guide-icon {
        background: url('../../assets/images/guideIconHover.svg');
      }
    }
  }
  .graph-p32 {
    height: 100%;
    position: relative;
    .guide-content {
      height: 100%;
      width: 100%;
      position: absolute;
      background-color: #c6c8cc;
      z-index: 9999;
      .step-pic {
        text-align: center;
        margin-top: 8px;
      }
      .step {
        height: 100%;
        background-repeat: round;
        user-select: none;
        img {
          width: 100%;
        }
      }
      .guide-span {
        font-size: 12px;
        color: #575d6c;
        line-height: 18px;
        text-align: left;
        display: inline-block;
      }
      .el-popover {
        .el-icon-close {
          cursor: pointer;
          position: absolute;
          right: 10px;
          top: 13px;
          font-size: 20px;
        }
        .el-icon-close:hover {
          color: #00a5a7;
        }
      }
      .el-popover__title {
        font-size: 16px;
        color: #252b3a;
        line-height: 24px;
        font-weight: bold;
      }
      .el-button {
        display: block;
        float: right;
        height: 28px;
        line-height: 27px;
        border-radius: 0;
        padding: 0 20px;
      }
    }
  }
  .cl-content {
    height: calc(100% - 50px);
    overflow: auto;
  }
  #graphs {
    width: 100%;
    height: 100%;
    font-size: 0;
    background: #f0f2f5;
    .search {
      margin-bottom: 15px;
      width: 100%;
    }
    .search-wrap {
      position: relative;
      .tree-wrap {
        position: absolute;
        left: 0;
        top: 32px;
        z-index: 101;
        width: 100%;
        max-height: 224px;
        overflow: auto;
        border: 1px solid #dcdfe6;
        border-top: none;
        .image-type {
          width: 20px;
          height: 10px;
          margin-right: 10px;
        }
        .el-tree {
          & > .el-tree-node {
            min-width: 100%;
            display: inline-block;
          }
          .custom-tree-node {
            padding-right: 8px;
          }
        }
      }
      .collapse_i {
        cursor: pointer;
      }
    }
    .cl-graph {
      position: relative;
      width: 100%;
      height: 100%;
      background-color: #fff;
      padding: 0 32px 10px;
      min-height: 700px;
      overflow: hidden;
    }
    .cl-graph.full-screen {
      position: absolute;
      top: 0;
      bottom: 0;
      left: -280px;
      right: 0;
      width: auto;
      height: auto;
      padding: 0;
      #sidebar {
        .node-info-con {
          height: calc(100% - 300px);
        }
      }
      .graph-container {
        width: 100%;
      }
    }
    #sidebar.right-hide {
      right: -442px;
    }
    #sidebar {
      position: absolute;
      right: 0;
      top: 0;
      width: 442px;
      height: 100%;
      border-radius: 6px;
      text-align: left;
      background-color: #ffffff;
      display: inline-block;
      box-shadow: 0 1px 3px 0px rgba(0, 0, 0, 0.1);
      color: #333333;
      font-size: 14px;
      line-height: 14px;
      padding: 18px 32px 10px;
      .sidebar-tooltip {
        position: absolute;
        height: 32px;
        top: 18px;
        left: 10px;
        display: flex;
        align-items: center;
        font-size: 16px;
        color: #6c7280;
      }
      div,
      span,
      pre {
        font-size: 14px;
      }
      #small-container {
        height: 209px;
        width: 100%;
        z-index: 100;
        border: 1px solid #e6ebf5;
        overflow: hidden;
        background-color: white;
        position: relative;
        #small-resize {
          width: 100%;
          height: 100%;
          position: absolute;
          left: 0;
          top: 0;
        }
        #small-map {
          height: 100%;
          width: 100%;
          position: relative;
          padding: 0;
        }
        #inside-box {
          background-color: #5b88f1;
          position: absolute;
          /* Transparency */
          opacity: 0.3;
          width: 100%;
          height: 100%;
          left: 0px;
          top: 0px;
          z-index: 200;
          cursor: move;
        }
      }
      .title {
        padding: 20px 0;
        font-size: 14px;
        color: #333333;
        img {
          float: right;
          margin-right: 10px;
          cursor: pointer;
        }
      }
      .graph-controls {
        padding: 10px 20px 0 20px;
        div {
          cursor: pointer;
          display: inline-block;
          margin-left: 20px;
        }
        img {
          cursor: pointer;
          vertical-align: middle;
        }
      }
      .node-info-con {
        ::-webkit-scrollbar-button {
          z-index: 200;
          width: 10px;
          height: 10px;
          background: #fff;
          cursor: pointer;
        }
        ::-webkit-scrollbar-button:horizontal:single-button:start {
          background-image: url('../../assets/images/scroll-btn-left.png');
          background-position: center;
        }
        ::-webkit-scrollbar-button:horizontal:single-button:end {
          background-image: url('../../assets/images/scroll-btn-right.png');
          background-position: center;
        }
        ::-webkit-scrollbar-button:vertical:single-button:start {
          background-image: url('../../assets/images/scroll-btn-up.png');
          background-position: center;
        }
        ::-webkit-scrollbar-button:vertical:single-button:end {
          background-image: url('../../assets/images/scroll-btn-down.png');
          background-position: center;
        }
        ::-webkit-scrollbar-thumb {
          background-color: #bac5cc;
        }
        ::-webkit-scrollbar {
          width: 6px;
          height: 6px;
        }
      }
      .node-info-container {
        height: calc(100% - 451px);
      }
      .node-info-container-long {
        height: calc(100% - 357px);
      }
      .node-info {
        font-size: 14px;
        padding: 0 20px;
        height: calc(100% - 54px);
        overflow: auto;
        color: #333;
        background-color: #f7faff;
        .clear {
          clear: both;
        }
        .hover {
          li:hover {
            background: #fce8b2;
          }
          .control-list {
            .dependence-title {
              line-height: 30px;
              cursor: pointer;
              font-weight: bold;
              img {
                vertical-align: middle;
                margin-right: 3px;
              }
            }
            .dependence-title.hide {
              img {
                margin-top: -3px;
                transform: rotate(-90deg);
              }
            }
            li:hover {
              background: #fce8b2;
            }
          }
          .control-list:hover {
            background: none;
          }
        }
        .pointer {
          cursor: pointer;
        }
        .item-content {
          max-height: calc(50% - 95px);
          overflow: auto;
          li {
            min-width: 100%;
            width: max-content;
          }
        }
        .item-min {
          min-height: 50px;
        }
        .item-min2 {
          min-height: 87px;
        }
        .items {
          line-height: 20px;
          padding: 9px 0;
          .items-over {
            max-height: 60px;
            overflow: auto;
          }
          .item {
            color: #999;
          }
        }
        .shape {
          vertical-align: top;
          width: 50px;
          word-break: break-all;
          display: inline-table;
          position: absolute;
          left: 0;
        }
        .key {
          vertical-align: top;
          width: 60px;
          word-break: break-all;
          display: inline-table;
        }
        .label {
          vertical-align: top;
          width: 70px;
          word-break: break-all;
          display: inline-block;
        }
        .value {
          vertical-align: top;
          display: inline-block;
          width: calc(100% - 70px);
          white-space: nowrap;
          overflow: auto;
        }
        .size {
          width: 310px;
          font-size: 12px;
          text-align: right;
        }
        .input {
          width: 100%;
          position: relative;
          display: inline-block;
          white-space: nowrap;
        }
        ul {
          li {
            line-height: 20px;
          }
        }
      }
      .legend {
        .legend-content {
          background-color: #f7faff;
          padding: 0 32px;
          height: 94px;
          overflow-y: auto;
        }
        .legend-item {
          padding: 5px 0;
          display: inline-block;
          width: 50%;
          font-size: 14px;
          line-height: 20px;
          .pic {
            width: 45px;
            text-align: center;
            display: inline-block;
            padding-left: 20px;
            vertical-align: middle;
            img {
              max-width: 45px;
              max-height: 15px;
              margin-left: -20px;
              vertical-align: middle;
            }
          }
          .legend-text {
            display: inline-block;
            padding-left: 20px;
            width: calc(100% - 45px);
            vertical-align: middle;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
          }
          .legend-text:hover {
            cursor: default;
          }
        }
      }
      .toggle-right {
        position: absolute;
        top: calc(50% - 43px);
        left: -16px;
        width: 18px;
        height: 86px;
        cursor: pointer;
        background-image: url('../../assets/images/toggle-right-bg.png');
      }
      .icon-toggle {
        width: 6px;
        height: 9px;
        background-image: url('../../assets/images/toggle-right-icon.png');
        position: absolute;
        top: calc(50% - 4.5px);
        left: calc(50% - 3px);
      }
      .icon-toggle.icon-left {
        transform: rotateY(180deg);
      }
    }
    .operate-button-list {
      position: absolute;
      right: 0;
      top: 0;
      z-index: 100;
      div {
        cursor: pointer;
        width: 12px;
        height: 12px;
        display: inline-block;
        margin: 5px;
      }
      .download-button {
        background-image: url('../../assets/images/download.png');
      }
      .full-screen-button {
        background-image: url('../../assets/images/full-screen.png');
      }
    }
    .graph-container.all {
      width: 100%;
    }
    .graph-container {
      .node:hover > path,
      .node:hover > ellipse,
      .node:hover > polygon,
      .node:hover > rect {
        stroke-width: 2px;
      }
      .node.cluster > rect:hover {
        stroke: #8df1f2;
      }
      .selected {
        stroke: red !important;
        stroke-width: 2px;
      }
    }
    .graph-container,
    #small-map {
      font-size: 16px;
      position: relative;
      display: inline-block;
      width: calc(100% - 442px);
      height: calc(100% - 5px);
      text-align: left;
      -webkit-touch-callout: none;
      -webkit-user-select: none;
      -khtml-user-select: none;
      -moz-user-select: none;
      -ms-user-select: none;
      user-select: none;
      .graph {
        height: 100%;
        background-color: #f7faff;
      }
      #graph0 > polygon {
        fill: transparent;
      }
      .node {
        cursor: pointer;
      }
      .edge {
        path {
          stroke: rgb(120, 120, 120);
        }
        polygon {
          fill: rgb(120, 120, 120);
        }
      }
      .edge.highlighted {
        path {
          stroke: red;
        }
        polygon {
          stroke: red;
          fill: red;
        }
        marker {
          path {
            fill: red;
          }
        }
      }
      .node.aggregation > polygon {
        stroke: #e3aa00;
        fill: #ffe794;
      }
      .node.cluster.aggregation > rect {
        stroke: #e3aa00;
        fill: #ffe794;
        stroke-dasharray: 3, 3;
      }
      .node > polygon {
        stroke: #00a5a7;
        fill: rgb(141, 241, 242);
      }
      .node > ellipse {
        stroke: #4ea6e6;
        fill: #b8e0ff;
      }
      .plain > path,
      .plain ellipse {
        stroke: #e37d29;
        fill: #ffd0a6;
        stroke-dasharray: 1.5, 1.5;
      }
      .edge-point ellipse {
        stroke: #a7a7a7;
        fill: #a7a7a7;
      }
      text {
        fill: black;
      }
    }
    // No data available.
    .image-noData {
      // Set the width and white on the right.
      width: 100%;
      height: 100%;
      background: #fff;
      position: absolute;
      top: 0;
      left: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      flex-direction: column;
      z-index: 200;
    }
    .noData-text {
      margin-top: 33px;
      font-size: 18px;
    }
  }
  .cl-display-block {
    display: block;
  }
  .cl-input-value {
    width: calc(100% - 70px) !important;
    margin-left: 10px !important;
  }
  .cl-close-btn {
    width: 20px;
    height: 20px;
    vertical-align: -3px;
    cursor: pointer;
    display: inline-block;
  }
  .cl-title-right {
    padding-right: 32px;
  }
}
#graphTemp,
#subgraphTemp {
  position: absolute;
  bottom: 0;
  visibility: hidden;
}
</style>
