使用Vue3和ECharts实现拓扑图

ECharts是一款由百度开源的数据可视化图表库,它提供了丰富的图表类型以及高度可定制化的选项。结合Vue3的强大响应式能力和生态,我们可以轻松地创建出一个具有动态效果和良好用户体验的拓扑图。本文将通过一个具体的代码示例,详细介绍如何实现这一目标。

实现效果如图404aee5bed404af08cc43950ef93dd7d.png

环境准备

首先,确保你的开发环境中已经安装了Node.js。接下来,我们需要安装Vue CLI以及Vue3和ECharts。

安装Vue CLI

npm install -g @vue/cli

创建Vue3项目

vue create my-vue3-project
cd my-vue3-project
npm install

安装Vue3和ECharts

npm install vue@next echarts

创建Vue3组件

我们将创建一个新的Vue3单文件组件(SFC),用于展示拓扑图。

文件结构

my-vue3-project/
├── src/
│   ├── components/
│   │   └── TopologyChart.vue
│   └── App.vue
└── public/
    └── index.html

TopologyChart.vue 组件

<template>
    <div>
        <div id="topology_chart" style="width: 100%; height: 400px;" />
    </div>
</template>

<script setup>
import { ref, onMounted, nextTick, onUnmounted } from 'vue'
import * as echarts from 'echarts'

// 示例数据
const topologyDatas = ref([
    {
        name: 'Root Node 1',
        level: 1,
        symbol: 'image:///img/icon1.png',  // 图标路径
        symbolSize: 50,
        datas: {
            department: 'Department A',
            description: 'Description of Root Node 1'
        },
        children: [
            {
                name: 'Child Node 1-1',
                level: 2,
                symbol: 'image:///img/icon2.png',
                symbolSize: 40,
                datas: {
                    department: 'Subdepartment A',
                    description: 'Description of Child Node 1-1'
                },
                children: [
                    {
                        name: 'Grandchild Node 1-1-1',
                        level: 3,
                        symbol: 'image:///img/icon3.png',
                        symbolSize: 30,
                        datas: {
                            department: 'Sub-subdepartment A',
                            description: 'Description of Grandchild Node 1-1-1'
                        }
                    },
                    {
                        name: 'Grandchild Node 1-1-2',
                        level: 3,
                        symbol: 'image:///img/icon4.png',
                        symbolSize: 30,
                        datas: {
                            department: 'Sub-subdepartment B',
                            description: 'Description of Grandchild Node 1-1-2'
                        }
                    }
                ]
            },
            {
                name: 'Child Node 1-2',
                level: 2,
                symbol: 'image:///img/icon5.png',
                symbolSize: 40,
                datas: {
                    department: 'Subdepartment B',
                    description: 'Description of Child Node 1-2'
                },
                children: [
                    {
                        name: 'Grandchild Node 1-2-1',
                        level: 3,
                        symbol: 'image:///img/icon6.png',
                        symbolSize: 30,
                        datas: {
                            department: 'Sub-subdepartment C',
                            description: 'Description of Grandchild Node 1-2-1'
                        }
                    }
                ]
            }
        ]
    },
    {
        name: 'Root Node 2',
        level: 1,
        symbol: 'image:///img/icon7.png',
        symbolSize: 50,
        datas: {
            department: 'Department B',
            description: 'Description of Root Node 2'
        },
        children: [
            {
                name: 'Child Node 2-1',
                level: 2,
                symbol: 'image:///img/icon8.png',
                symbolSize: 40,
                datas: {
                    department: 'Subdepartment C',
                    description: 'Description of Child Node 2-1'
                }
            }
        ]
    }
])

// 初始化图表
const initChart = () => {
    const chartDom = document.getElementById('topology_chart');
    const myChart = echarts.init(chartDom);

    const option = {
        dataZoom: [
            {
                type: 'slider',
                show: true
            }
        ],
        series: [
            {
                type: 'tree',
                initialTreeDepth: 2,
                data: topologyDatas.value,
                lineStyle: {
                    color: '#35EB68' // 设置线条颜色
                },
                top: 60,
                left: 20,
                right: 20,
                bottom: 60,
                orient: 'vertical',
                edgeShape: 'polyline',
                expandAndCollapse: true,
                roam: true, // 启用平移和缩放
                zoom: true, // 允许缩放
                pan: true, // 允许平移
                label: {
                    fontSize: 12,
                    position: 'right',
                    formatter: (params) => formatLabel(params),
                    rich: {
                        a: { lineHeight: 22, color: '#fff', fontSize: 12 },
                        b: { lineHeight: 22, color: '#fff', fontSize: 12 },
                        c: { lineHeight: 22, color: '#fff', fontSize: 12 }
                    }
                },
                leaves: {
                    label: {
                        position: 'right'
                    }
                }
            }
        ]
    };

    myChart.setOption(option);
    myChart.resize();

    window.addEventListener('resize', () => {
        myChart.resize();
    });

    return myChart;
};

// 格式化标签
const formatLabel = (params) => {
    let res = '';
    if (params.data.level === 1) {
        res += `{a|部门:${params.data.datas.department}}\n{b|描述:${params.data.datas.description}}`;
    } else if (params.data.level === 2) {
        res += `{c|部门:${params.data.datas.department}}\n{c|描述:${params.data.datas.description}}`;
    } else {
        res += `{c|部门:${params.data.datas.department}}\n{c|职责:${params.data.datas.description}}`;
    }

    return res;
};

// 组件挂载后初始化图表
let chartInstance;

onMounted(() => {
    nextTick(() => {
        chartInstance = initChart();
    });
});

// 清除事件监听器,防止内存泄漏
onUnmounted(() => {
    window.removeEventListener('resize', () => chartInstance?.resize());
    chartInstance?.dispose();
});
</script>

<style scoped>
/* 可以在这里添加CSS样式 */
</style>

数据源结构

在上述代码中,topologyDatas 定义了一个包含多个根节点(root node)的数据结构。每个根节点可以有多个子节点(child node),子节点又可以有多个子节点,以此类推。每个节点包含以下属性:

  • name:节点的名称。
  • level:节点所在的层级。
  • symbol:节点的图标路径。
  • symbolSize:节点图标的大小。
  • datas:附加的信息,如部门名称和描述。
  • children:子节点列表。

例如:

{
    name: 'Root Node 1',
    level: 1,
    symbol: 'image:///img/icon1.png',
    symbolSize: 50,
    datas: {
        department: 'Department A',
        description: 'Description of Root Node 1'
    },
    children: [
        // 子节点
    ]
}

符号(Symbol)

在ECharts中,symbol 属性用于定义图表中数据点的符号形状。您可以使用各种预定义的形状,也可以使用图像作为符号。在本例中,我们使用图像作为符号:

symbol: 'image:///img/icon1.png',

这里的 'image:///img/icon1.png' 表示使用位于 /img/icon1.png 的图像作为节点的图标。

初始化图表

initChart 函数负责初始化ECharts图表,并设置基本的配置选项。其中包括:

  • dataZoom:数据缩放组件,用于实现数据区域缩放功能。
  • series.tree:树形图的配置项,包括初始展开的深度、方向、边的形状等。
  • label.formatter:标签格式化函数,用于自定义节点上的标签内容。

生命周期管理

使用Vue3的组合API (onMounted, onUnmounted) 来控制图表的初始化和销毁过程,确保图表能够在组件挂载时初始化,并在组件卸载时清除事件监听器。

使用组件

App.vue中引入并使用TopologyChart.vue组件。

<template>
  <div id="app">
    <TopologyChart />
  </div>
</template>

<script>
import TopologyChart from './components/TopologyChart.vue';

export default {
  name: 'App',
  components: {
    TopologyChart
  }
}
</script>

<style>
/* 全局样式 */
</style>

总结

通过上述步骤,我们成功地使用Vue3和ECharts实现了一个带有图标节点的拓扑图。这种图不仅能够直观地展示数据之间的关系,还通过添加图标增强了视觉效果。你可以进一步扩展此示例,比如增加更多的交互功能或改变图表样式,以满足不同的需求。

希望这篇文章能帮助你更好地理解和使用Vue3和ECharts来创建拓扑图。如果有什么问题或建议,请随时留言交流!

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端学步

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值