实现效果 :
1. 在uniapp 中写echarts 树结构图需要使用
<script module="echarts" lang="renderjs">
否则会无法显示echarts 图形
rebderjs 代码 引入了 /static/echarts.min.js
是在 ECharts 在线构建 定制你的echarts
<template><view><view class="echarts" :prop="option" :change:prop="echarts.update"></view></view>
</template><script>export default {name: 'Echarts',props: {option: {type: Object,required: true}},methods: {// echarts 点击跳转的onViewClick(options) {console.log("已进入");console.log(options);if (null !== options.test?.lawId && undefined !== options.test?.lawId) {uni.navigateTo({url: ' ', // 替换为你的详情页面路径和查询参数fail: function(res) {console.error('页面跳转失败:', res);}});} else {console.log(options);}}}}
</script><script module="echarts" lang="renderjs">import _ from 'lodash';export default {data() {return {chart: null,options: "",fontSize: 12,symbolSize: 7,paddingH: 8,paddingW: 4,height: 20,width: 300,width2: 80,startTime: null, // 用于记录开始时间debouncedResize: _.debounce(this.resize, 500), // 防抖动处理elesCounts: 0,loading: true}},mounted() {if (typeof window.echarts === 'object') {this.init();} else {// 动态引入类库const script = document.createElement('script')// script.src = './static/echarts.min.js'script.src = './static/echarts/echarts1.min.js'script.onload = this.initdocument.head.appendChild(script);//这两行代码解决在小程序中echarts点击失效问题}},methods: {/*** 初始化echarts*/init() {echarts.env.touchEventsSupported = true;echarts.env.wxa = false;echarts.env.canvasSupported = false;echarts.env.svgSupported = true;echarts.env.domSupported = true;//渲染前this.loading = true;this.chart = echarts.init(this.$el, {renderer: 'canvas'});if (this.loading) {this.chart.showLoading('default', {text: '数据加载中...',color: '#333',textColor: '#666',maskColor: 'rgba(255, 255, 255, 0)',zlevel: 0});}// 延迟执行以确保图表渲染完成setTimeout(() => {this.chart.hideLoading();}, 1000); this.update(this.option);// 监听图表渲染完成事件},getLeftNodeChildren(zoom, children) {this.symbolSize = Math.min(7, this.symbolSize * zoom);return children.map(child => ({...child,label: {...child.label,fontSize: Math.max(2, Math.min(12, this.fontSize * zoom)),padding: [Math.min(4, this.paddingW * zoom), Math.min(8, this.paddingH * zoom)],height: this.height,width: this.width},symbolSize: this.symbolSize,children: child.children ? this.getLeftNodeChildren(zoom, child.children) : [],}));},/*** 监测数据更新* @param {Object} option*/update(option) {if (this.chart) {// 因App端,回调函数无法从renderjs外传递,故在此自定义设置相关回调函数if (option) {this.options = option// tooltipif (option.tooltip) {console.log(option.tooltip.position);// 判断是否设置tooltip的位置if (option.tooltip.positionStatus) {option.tooltip.position = this.tooltipPosition()}// 判断是否格式化tooltipif (option.tooltip.formatterStatus) {option.tooltip.formatter = this.tooltipFormatter(option.tooltip.formatterUnit, option.tooltip.formatFloat2, option.tooltip.formatThousands)}}// 设置新的optionthis.chart.setOption(option, option.notMerge);// 确保在初始化后禁用触摸事件支持if (typeof this.chart === 'object') {this.chart.getZr().handler.touchEventsSupported = false;this.chart.getZr().handler.wxa = false;}// 添加条件语句来确保属性已经存在if (this.chart._chartsViews && this.chart._chartsViews[0] && this.chart._chartsViews[0]._data) {let elesArr = Array.from(new Set(this.chart._chartsViews[0]._data._graphicEls));this.elesCounts = elesArr.length;console.log(elesArr.length);if (elesArr.length >= 130) {option.series[0].top = '-600px';option.series[0].left = '-600px';console.log(88888);// console.log(111111);this.chart.resize({width: 1200,height: 5000});} else {this.resize(); // 使用防抖动的 resize'}}// this.chart.resize({// width: 800,// height: 400// });this.chart.off("click")//跳转前先解绑,防止重复跳转this.chart.off('click');// 使用 ECharts 内部点击事件this.chart.on("click", params => {console.log(params.data);// 判断点击的是否是树图的节点if (params.componentType === 'series' && params.seriesType === 'tree') {// 阻止事件冒泡params.event.event.stopPropagation();// 处理点击事件console.log('Tree节点被点击,数据为:', params.data);}this.$ownerInstance.callMethod('onViewClick', {test: params.data})});this.chart.on('treeroam', this.handleTreeRoam);}}},// 使用 _.throttle 包装 handleTreeRoam 函数,确保其每300毫秒最多执行一次handleTreeRoam: _.throttle(function(res) {const opt = this.options;const zoom = res.zoom || 1;console.log(zoom);if (zoom == 1) {this.chart.setOption(this.options);return;}console.log(zoom);// 更新字体大小const fontSize = Math.max(2, Math.min(12, this.fontSize * zoom));this.fontSize = fontSize;const padding = [Math.min(4, this.paddingW * zoom), Math.min(8, this.paddingH * zoom)];this.paddingW = padding[0];this.paddingH = padding[1];const heights = Math.min(30, this.height * zoom);this.height = heights;const widths = Math.min(300, this.width * zoom);this.width = widths;const widths2 = Math.min(80, this.width2 * zoom);this.width2 = widths2;const newOption = {...opt,series: [{...opt.series[0],label: {...opt.series[0].label,fontSize: fontSize,padding: padding,},leaves: {...opt.series[0].leaves,label: {...opt.series[0].leaves.label,fontSize: fontSize,padding: padding,},},data: opt.series[0].data.map(node1 => ({...node1,label: {...node1.label,fontSize: fontSize,padding: padding,height: heights,},symbolSize: this.symbolSize,children: node1.children.map(node2 => ({...node2,label: {...node2.label,fontSize: fontSize,padding: padding,height: heights,width: widths2},symbolSize: this.symbolSize,children: this.getLeftNodeChildren(zoom, node2.children),})),})),}],};this.options = newOption;// this.chart.setOption(this.options, true);if (zoom != 1) {if (this.elesCounts > 130) {const opt = this.options.series[0].data[0];const height = opt.label.height;console.log(height);if (opt.label.height < 20 && opt.label.height >= 10) {this.chart.resize({width: 900,height: 3800});} else {this.chart.resize({width: 450,height: 2500});}} else {this.resize();}this.chart.setOption(this.options, true);}}, 1000), // 300 毫秒节流// 使用 _.debounce 包装 setOption 函数,确保其在 300 毫秒后执行resizeDebounce: _.debounce(function() {if (this.chart) {this.resize();}}, 300), // 300 毫秒防抖// 使用 _.debounce 包装 setOption 函数,确保其在 300 毫秒后执行debouncedSetOption: _.debounce(function() {if (this.chart) {console.log(99999);}}, 300), // 300 毫秒防抖resize() {const opt = this.options.series[0].data[0];let elesArr = Array.from(new Set(this.chart._chartsViews[0]._data._graphicEls));let dep = this.chart._chartsViews[0]._data.tree.root.height; // 获取树高let layer_height = 10; // 层级之间的高度let layer_width = 25; // 兄弟节点之间的距离const height = opt.label.height;const elesCount = elesArr.length;// dep 是节点深度 elesCount 是节点的数量 这里根据节点深度和数量进行设置// 根据自己需求完善 if (dep == 4) {if (elesCount < 10) {// 调整高度和宽度逻辑if (opt.label.height == 30) {layer_height = 130;layer_width = 120;} else if (opt.label.height < 25 && opt.label.height >= 20) {layer_height = 130;layer_width = 110;} else if (opt.label.height < 20 && opt.label.height >= 15) {layer_height = 130;layer_width = 100;} else if (opt.label.height < 15 && opt.label.height >= 10) {layer_height = 130;layer_width = 80;} else if (opt.label.height < 10 && opt.label.height > 5) {layer_height = 130;layer_width = 70;} else {layer_height = 130;layer_width = 70;}console.log('opt is 4 and elesCount is less than 10');} let newHeight = layer_height * elesArr.length;let currentWidth = layer_width * (elesArr.length - 1) || layer_width;let newWidth = Math.max(currentWidth, layer_width);console.log(newWidth);console.log(newHeight);// 保持图表中心在可视区域this.chart.resize({width: newWidth,height: newHeight});},/*** 设置tooltip的位置,防止超出画布*/tooltipPosition() {return (point, params, dom, rect, size) => {//其中point为当前鼠标的位置,size中有两个属性:viewSize和contentSize,分别为外层div和tooltip提示框的大小let x = point[0]let y = point[1]let viewWidth = size.viewSize[0]let viewHeight = size.viewSize[1]let boxWidth = size.contentSize[0]let boxHeight = size.contentSize[1]let posX = 0 //x坐标位置let posY = 0 //y坐标位置if (x < boxWidth) { //左边放不开posX = 5} else { //左边放的下posX = x - boxWidth}if (y < boxHeight) { //上边放不开posY = 5} else { //上边放得下posY = y - boxHeight}return [posX, posY]}},}}
</script><style lang="scss" scoped>.echarts {width: 100vh;height: 100vh;}.p-line-charts-out {height: 100%;width: 100%;.p-line-charts {width: 100% !important;height: calc(100% - 25px) !important;}}
</style>
echarts 展示页面 : 基本代码调试一下就可以了
<view ref="echartsContainer"><echarts ref="echarts" :option="option" style="height: 100vh; width: 100vw;"></echarts>
<script >import echarts from '@/pages/userCenter/business.vue';convertToTreeData(data) {return {tooltip: {trigger: 'item',triggerOn: 'mousemove',},series: [{type: 'tree',scaleLimit: {min: 0.5,max: 2},data: [data],top: '5%',left: '3%',bottom: '5%',right: '10%',initialTreeDepth: -1,roam: true,zoom: 1,symbolSize: 1,label: {fontSize: 9,position: 'right',show: true,borderRadius: 5,borderWidth: 1,verticalAlign: 'middle',align: 'center',color: '#fff'},leaves: {label: {position: 'right',verticalAlign: 'middle',align: 'left',fontSize: 12, // 初始字体大小padding: [4, 8], // 初始内边距},},lineStyle: {color: '#ccc',width: 1.5,type: 'solid'},emphasis: {disabled: true,focus: 'ancestor'},// 当节点大于100 时 关闭动画containLabel: true,}]};},</script>