wujingjing
2024-11-14 ce1658eb1c17ba7e8fe3f0a21771b4af5ccee8cd
三种布局方式
已修改5个文件
已添加1个文件
222 ■■■■ 文件已修改
customer_list/common/static/fonts/ywiconfont/iconfont.css 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
customer_list/common/static/fonts/ywiconfont/iconfont.ttf 补丁 | 查看 | 原始文档 | blame | 历史
customer_list/common/static/fonts/ywiconfont/iconfont.woff 补丁 | 查看 | 原始文档 | blame | 历史
customer_list/common/static/fonts/ywiconfont/iconfont.woff2 补丁 | 查看 | 原始文档 | blame | 历史
src/views/project/yw/systemManage/agentGraph/AgentGraph.vue 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/project/yw/systemManage/agentGraph/types.ts 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
customer_list/common/static/fonts/ywiconfont/iconfont.css
@@ -1,8 +1,8 @@
@font-face {
  font-family: "ywifont"; /* Project id 4655417 */
  src: url('iconfont.woff2?t=1730272121109') format('woff2'),
       url('iconfont.woff?t=1730272121109') format('woff'),
       url('iconfont.ttf?t=1730272121109') format('truetype');
  src: url('iconfont.woff2?t=1731549790634') format('woff2'),
       url('iconfont.woff?t=1731549790634') format('woff'),
       url('iconfont.ttf?t=1731549790634') format('truetype');
}
.ywifont {
@@ -13,6 +13,34 @@
  -moz-osx-font-smoothing: grayscale;
}
.ywicon-buju:before {
  content: "\e655";
}
.ywicon-cebianlan:before {
  content: "\e60e";
}
.ywicon-lianjie:before {
  content: "\e60d";
}
.ywicon-fenxiang1:before {
  content: "\e8b0";
}
.ywicon-cubelifangti:before {
  content: "\e6fc";
}
.ywicon-loading1:before {
  content: "\e617";
}
.ywicon-loading:before {
  content: "\e615";
}
.ywicon-fangda:before {
  content: "\e81d";
}
customer_list/common/static/fonts/ywiconfont/iconfont.ttf
Binary files differ
customer_list/common/static/fonts/ywiconfont/iconfont.woff
Binary files differ
customer_list/common/static/fonts/ywiconfont/iconfont.woff2
Binary files differ
src/views/project/yw/systemManage/agentGraph/AgentGraph.vue
@@ -1,28 +1,54 @@
<template>
    <div v-loading="firstLoading" class="h-full w-full bg-white relative">
        <div class="h-full w-full" ref="graphRef"></div>
    <div v-loading="firstLoading" class="h-full w-full bg-white relative flex flex-col">
        <!-- <div class="toolbar flex-0 border-x-0 border-b border-t-0 border-gray-200 border-solid flex-items-center">
            <span>
                æ ¼å¼
            </span>
        </div> -->
        <div class="graph flex-auto">
            <div class="h-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"
                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"
            >
                <span class="ywifont !text-[20px] mb-1 p-1.5" :class="[`ywicon-${item.icon}`]"></span>
                <span>{{ item.label }}</span>
                <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>
        <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 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="absolute right-2 top-4 py-7 px-5 rounded-lg w-96" style="box-shadow: 0 0 15px #dbdee6" v-if="layoutIsShow">
                <span
                    class="absolute ywifont ywicon-guanbi right-4 top-4 cursor-pointer hover:bg-gray-200 active:bg-gray-300"
                    @click="closeLayout"
                ></span>
                <div class="flex-column">
                    <span class="mb-5">布局</span>
                    <el-select v-model="activeLayout" @change="layoutChange">
                        <el-option
                            v-for="item in Object.keys(customLayoutMap)"
                            :key="item"
                            :value="parseInt(item)"
                            :label="customLayoutMap[item]"
                        ></el-option>
                    </el-select>
                </div>
            </div>
        </div>
    </div>
@@ -32,25 +58,17 @@
import { Graph, GraphOptions, treeToGraphData } from '@antv/g6';
import { defaultsDeep } from 'lodash';
import { onMounted, ref } from 'vue';
import { CustomLayout, OrgTreeItem, customLayoutMap, getLayoutType } from './types';
import { getMetricAgentListByPost, getMetricNameListByPost } from '/@/api/metrics';
import router from '/@/router';
import { DeepPartial } from '/@/utils/types';
import { deepClone } from '/@/utils/other';
const graphRef = ref<HTMLDivElement>(null);
let graph: Graph;
const firstLoading = ref(false);
const agentId = router.currentRoute.value.query.id as string;
type OrgTreeItem = {
    treeId: string;
    logicId: string;
    model: any;
    type: 'agent' | 'metrics' | 'dimension';
    label: string;
    level: number;
    children?: OrgTreeItem[];
};
const convertOrgTreeToTreeNode = (orgTreeData: OrgTreeItem) => {
    const treeData = {
        id: orgTreeData.treeId,
@@ -60,30 +78,12 @@
    };
    return treeData;
};
const enum CustomLayout {
    /** @description ç´§å‡‘树布局 n <= 30*/
    NormalTree,
    /** @description åž‚直紧凑树,根节点在中间 30 < n <= 50*/
    Vertical,
    /** @description è¾å°„紧凑树,> 50 */
    Radial,
}
const getLayoutType = (maxLeafLen: number) => {
    if (maxLeafLen > 50) {
        return CustomLayout.Radial;
    } else if (30 < maxLeafLen) {
        return CustomLayout.Vertical;
    } else {
        return CustomLayout.NormalTree;
    }
};
const getLayoutOption = (layoutType: CustomLayout) => {
    let option: DeepPartial<GraphOptions> = {};
    switch (layoutType) {
        case CustomLayout.Radial:
            option = {
                autoFit: 'view',
                // autoFit: 'view',
                edge: {
                    type: 'cubic-radial',
                },
@@ -144,10 +144,24 @@
    }
    return option;
};
const graphRender = async() => {
    await graph.render();
    if (activeLayout.value === CustomLayout.Radial) {
        selfAdapt();
    }
};
const rebuildGraph = (extraOption: DeepPartial<GraphOptions>) => {
    const finalOptions = defaultsDeep(extraOption, commonOption);
    graph.setOptions(finalOptions);
    graphRender();
};
let commonOption: GraphOptions;
const initGraph = async (orgTreeData: OrgTreeItem, layoutType: CustomLayout) => {
    const treeNodeData = convertOrgTreeToTreeNode(orgTreeData);
    const commonOption: GraphOptions = {
    commonOption = {
        container: graphRef.value,
        data: treeToGraphData(treeNodeData),
        node: {
@@ -213,8 +227,7 @@
    const finalOption: GraphOptions = defaultsDeep(extraOption, commonOption);
    graph = new Graph(finalOption);
    window.graph = graph;
    graph.render();
    graphRender();
};
const getLeaf = (item) => {
@@ -305,9 +318,15 @@
//#region ====================== å·¥å…·æ  ======================
const enum ToolBarType {
    /** @description å¸ƒå±€ */
    Layout,
    /** @description æœç´¢ */
    Search,
    /** @description æ”¾å¤§ */
    ZoomIn,
    /** @description ç¼©å° */
    ZoomOut,
    /** @description è‡ªé€‚应 */
    Reset,
}
type ToolBarItem = {
@@ -316,10 +335,15 @@
    type: ToolBarType;
};
const toolBarFunList: ToolBarItem[] = [
    // {
    //     icon: 'sousuo',
    //     label: '搜索',
    //     type: ToolBarType.Search,
    // },
    {
        icon: 'sousuo',
        label: '搜索',
        type: ToolBarType.Search,
        icon: 'buju',
        label: '布局',
        type: ToolBarType.Layout,
    },
    {
        icon: 'fangda',
@@ -343,6 +367,9 @@
const ZOOM_OFFSET = 0.15;
const toolBarItemClick = (item: ToolBarItem) => {
    switch (item.type) {
        case ToolBarType.Layout:
            layoutIsShow.value = true;
            break;
        case ToolBarType.Search:
            searchIsShow.value = true;
            break;
@@ -367,6 +394,18 @@
    searchIsShow.value = false;
};
//#endregion
//#region ====================== å¸ƒå±€ ======================
const layoutIsShow = ref(false);
const closeLayout = () => {
    layoutIsShow.value = false;
};
const activeLayout = ref<CustomLayout>();
const layoutChange = (layoutType: CustomLayout) => {
    const extraOption = getLayoutOption(layoutType);
    rebuildGraph(extraOption);
};
//#endregion
onMounted(async () => {
    if (!agentId) return;
    firstLoading.value = true;
@@ -376,6 +415,7 @@
    });
    setTimeout(() => {
        const layoutType = getLayoutType(maxLevelNodeCount);
        activeLayout.value = layoutType;
        initGraph(orgTreeData as OrgTreeItem, layoutType);
        // initEvent();
src/views/project/yw/systemManage/agentGraph/types.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
export const enum CustomLayout {
    /** @description ç´§å‡‘树布局 n <= 30*/
    NormalTree,
    /** @description åž‚直紧凑树,根节点在中间 30 < n <= 50*/
    Vertical,
    /** @description è¾å°„紧凑树,> 50 */
    Radial,
}
export const customLayoutMap ={
    [CustomLayout.NormalTree]:'紧凑树布局',
    [CustomLayout.Vertical]:'垂直对称布局',
    [CustomLayout.Radial]:'辐射状布局'
}
export const getLayoutType = (maxLeafLen: number) => {
    if (maxLeafLen > 50) {
        return CustomLayout.Radial;
    } else if (30 < maxLeafLen) {
        return CustomLayout.Vertical;
    } else {
        return CustomLayout.NormalTree;
    }
};
export type OrgTreeItem = {
    treeId: string;
    logicId: string;
    model: any;
    type: 'agent' | 'metrics' | 'dimension';
    label: string;
    level: number;
    children?: OrgTreeItem[];
};