wujingjing
2025-01-03 9b9e63bb526ec60dda3f33855759874a39a45cb4
src/views/project/yw/systemManage/agentGraph/AgentGraph.vue
@@ -1,170 +1,35 @@
<template>
   <div v-loading="firstLoading" class="h-full w-full bg-white relative">
      <!-- <div class="absolute right-4 top-4 z-10">
         <span class="ywifont ywicon-zishiying cursor-pointer" title="自适应" @click="selfAdaptClick"></span>
      </div> -->
      <div class="h-full w-full" ref="graphRef"></div>
      <div
         class="absolute left-4 top-4 z-10 w-16 divide-y-[1.5px] divide-solid divide-gray-100 rounded-lg"
         style="box-shadow: 0 0 15px #dbdee6"
      >
         <div
            class="flex-column content-center items-center border-x-0 py-2 hover:bg-gray-200 active:bg-gray-300 cursor-pointer"
            @click="toolBarItemClick(item)"
            v-for="item in toolBarFunList"
         >
            <span class="ywifont !text-[20px] mb-1 p-1.5" :class="[`ywicon-${item.icon}`]"></span>
            <span>{{ item.label }}</span>
         </div>
      </div>
      <div class="absolute right-2 top-4 py-7 px-5 rounded-lg w-96" style="box-shadow: 0 0 15px #dbdee6" v-if="searchIsShow">
         <span
            class="absolute ywifont ywicon-guanbi right-4 top-4 cursor-pointer hover:bg-gray-200 active:bg-gray-300"
            @click="closeSearch"
         ></span>
         <div class="flex-column">
            <span class="mb-5">查找</span>
            <el-input v-model="searchValue" @input="debounceSearch" placeholder="输入查找内容" v-elInputFocus></el-input>
         </div>
      </div>
   <div class="h-full" v-loading="firstLoading">
      <TreeGraph v-if="graphData" :data="graphData" class="h-full" :maxCount="maxCount" />
   </div>
</template>
<script setup lang="ts">
import { DagreLayout } from '@antv/layout';
import { Graph } from '@antv/x6';
import { onMounted, ref } from 'vue';
import { routeMap } from './routeMap';
import { circleShape, edgeShape, ellipseShape, rectShape } from './shape';
import { getMetricAgentListByPost, getMetricNameListByPost } from '/@/api/metrics';
import TreeGraph from '/@/components/graph/treeGraph/TreeGraph.vue';
import router from '/@/router';
import { travelTree } from '/@/utils/util';
const graphRef = ref<HTMLDivElement>(null);
let graph: Graph;
const firstLoading = ref(false);
import { getMetricAgentListByPost, getMetricNameListByPost } from '/@/api/metrics';
import { OrgTreeItem } from './types';
const agentId = router.currentRoute.value.query.id as string;
const initGraph = () => {
   // 初始化画布
   graph = new Graph({
      container: graphRef.value,
      async: true,
      interacting: false,
      connecting: {
         anchor: 'orth',
         connector: 'rounded',
         connectionPoint: 'boundary',
         router: {
            name: 'er',
            args: {
               offset: 24,
               direction: 'H',
            },
         },
      },
      mousewheel: {
         enabled: true,
      },
      panning: {
         enabled: true,
      },
   });
};
const getLeaf = (item) => {
   if (item.Children && item.Children.length > 0) {
      const first = item.Children[0];
      return getLeaf(first);
   } else {
      return item;
   }
};
const initEvent = () => {
   graph.on('node:click', ({ cell, e, node, view }) => {
      return;
      const target = e.target;
      const targetParent = target.parentElement;
      if (!targetParent.className?.baseVal?.includes('name')) return;
      const data = node.getData();
      const leafData = getLeaf(data);
      const routeName = routeMap.get(leafData.Code);
      router.push({
         name: routeName,
      });
   });
};
const loadData = (treeData: any[]) => {
   const data: any = {
      nodes: [],
      edges: [],
const maxCount = ref(null);
const graphData = ref(null);
const firstLoading = ref(false);
const convertOrgTreeToTreeNode = (orgTreeData: OrgTreeItem) => {
   const treeData = {
      id: orgTreeData.treeId,
      label: orgTreeData.label,
      data: orgTreeData,
      children: orgTreeData.children?.length > 0 ? orgTreeData.children.map((item) => convertOrgTreeToTreeNode(item)) : [],
   };
   travelTree(
      treeData,
      (item) => {
         let shapeAttrs = null;
         switch (item.type) {
            case 'agent':
               shapeAttrs = rectShape(item.treeId, item.label);
               break;
            case 'metrics':
               shapeAttrs = ellipseShape(item.treeId, item.label);
               break;
            case 'dimension':
               shapeAttrs = circleShape(item.treeId, item.label);
               break;
            default:
               break;
         }
         data.nodes.push({
            id: item.treeId,
            ...shapeAttrs,
            children: item.children?.map((item) => item.treeId),
            data: item,
         });
         if (item.children) {
            for (const child of item?.children) {
               const sourceId = `${item.treeId}-b`;
               const targetId = `${child.treeId}-t`;
               data.edges.push({
                  source: { cell: item.treeId, port: sourceId },
                  target: { cell: child.treeId, port: targetId },
                  ...edgeShape,
               });
            }
         }
      },
      undefined,
      undefined,
      'children'
   );
   const dagreLayout = new DagreLayout({
      type: 'dagre',
      rankdir: 'TB',
      // align: 'UL',
      // ranksep: 30,
      nodesep: 30,
      controlPoints: false,
   });
   let newData = dagreLayout.layout(data);
   graph.fromJSON(newData);
   selfAdapt();
   return treeData;
};
const getFirstOrgTreeList = async () => {
   // const res = await GetSMCenterFirstOrgTreeList();
   /** @description 维度数量 */
   let dimensionCount = 0;
   /** @description 指标数量 */
   let metricsCount = 0;
   const allAgentRes = getMetricAgentListByPost();
   const metricsRes = getMetricNameListByPost({
      agent_id: agentId,
@@ -174,139 +39,65 @@
   const allAgentList = allAgentResult?.values ?? [];
   const metricsList = metricsResult?.values ?? [];
   // const foundAgent = allAgentList.find(item=>item.)
   metricsCount = metricsList.length;
   const foundAgent = allAgentList.find((item) => item.id === agentId);
   if (!foundAgent) return [];
   const agentTreeId = `agent-${foundAgent.id}`;
   let logicTree = [
      {
         treeId: agentTreeId,
         logicId: foundAgent.id,
         model: foundAgent,
         type: 'agent',
         get label() {
            return this.model.title;
         },
         children: metricsList.map((curVal) => {
            const metricsTreeId = `${agentTreeId}-metrics-${curVal.id}`;
            const metrics = {
               treeId: metricsTreeId,
               logicId: curVal.id,
               type: 'metrics',
               model: curVal,
               get label() {
                  return this.model.title;
               },
               children: (curVal.dimensions ?? []).map((item) => {
                  const dimensionTreeId = `${metricsTreeId}-dimension-${item.id}`;
                  return {
                     treeId: dimensionTreeId,
                     logicId: item.id,
                     type: 'dimension',
                     model: item,
                     get label() {
                        return this.model.title;
                     },
                  };
               }),
            };
            return metrics;
         }, []),
   let logicTree: OrgTreeItem = {
      treeId: agentTreeId,
      logicId: foundAgent.id,
      model: foundAgent,
      type: 'agent',
      get label() {
         return this.model.title;
      },
   ];
      level: 0,
      children: metricsList.map((curVal) => {
         const metricsTreeId = `${agentTreeId}-metrics-${curVal.id}`;
         const dimensionList = curVal.dimensions ?? [];
         const metrics: OrgTreeItem = {
            treeId: metricsTreeId,
            logicId: curVal.id,
            type: 'metrics',
            model: curVal,
            get label() {
               return this.model.title;
            },
            level: 1,
            children: dimensionList.map((item) => {
               const dimensionTreeId = `${metricsTreeId}-dimension-${item.id}`;
               return {
                  treeId: dimensionTreeId,
                  logicId: item.id,
                  type: 'dimension',
                  model: item,
                  get label() {
                     return this.model.title;
                  },
                  level: 2,
               };
            }),
         };
         dimensionCount += metrics.children.length;
         return metrics;
      }, []),
   };
   const resData = logicTree;
   return resData;
   const maxCount = Math.max(dimensionCount, metricsCount);
   return [resData, maxCount];
};
const selfAdapt = () => {
   setTimeout(() => {
      graph.zoomToFit();
   }, 100);
};
//#region ====================== 工具栏 ======================
const enum ToolBarType {
   Search,
   ZoomIn,
   ZoomOut,
   Reset,
}
type ToolBarItem = {
   icon: string;
   label: string;
   type: ToolBarType;
};
const toolBarFunList: ToolBarItem[] = [
   {
      icon: 'sousuo',
      label: '搜索',
      type: ToolBarType.Search,
   },
   {
      icon: 'fangda',
      label: '放大',
      type: ToolBarType.ZoomIn,
   },
   {
      icon: 'suoxiao',
      label: '缩小',
      type: ToolBarType.ZoomOut,
   },
   {
      icon: 'zishiying',
      label: '重置',
      type: ToolBarType.Reset,
   },
];
const searchIsShow = ref(false);
const searchValue = ref('');
const ZOOM_OFFSET = 0.05;
const toolBarItemClick = (item: ToolBarItem) => {
   switch (item.type) {
      case ToolBarType.Search:
         searchIsShow.value = true;
         break;
      case ToolBarType.ZoomIn:
         graph.zoom(ZOOM_OFFSET);
         break;
      case ToolBarType.ZoomOut:
         graph.zoom(-ZOOM_OFFSET);
         break;
      case ToolBarType.Reset:
         selfAdapt();
      default:
         break;
   }
};
const debounceSearch = () => {};
const closeSearch = () => {
   searchIsShow.value = false;
};
//#endregion
onMounted(async () => {
   if (!agentId) return;
   firstLoading.value = true;
   const orgTreeList = await getFirstOrgTreeList().catch(() => {
   const [orgTreeData, maxLevelNodeCount] = await (getFirstOrgTreeList() as any).finally(() => {
      firstLoading.value = false;
   });
   setTimeout(() => {
      initGraph();
      initEvent();
      loadData(orgTreeList as any);
      firstLoading.value = false;
   }, 100);
   maxCount.value = maxLevelNodeCount;
   graphData.value = convertOrgTreeToTreeNode(orgTreeData);
});
</script>
<style scoped lang="scss">
:deep(g[data-shape='rect'].x6-node > text:hover) {
   // text-decoration: underline;
   cursor: pointer;
}
</style>
<style scoped lang="scss"></style>