uniapp 端开发 echarts 树结构图

实现效果 :

  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.init
				document.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
						// tooltip
						if (option.tooltip) {
							console.log(option.tooltip.position);
							// 判断是否设置tooltip的位置
							if (option.tooltip.positionStatus) {
								option.tooltip.position = this.tooltipPosition()

							}
							// 判断是否格式化tooltip
							if (option.tooltip.formatterStatus) {
								option.tooltip.formatter = this.tooltipFormatter(option.tooltip.formatterUnit, option
									.tooltip.formatFloat2, option.tooltip.formatThousands)
							}
						}


						// 设置新的option
						this.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>
相关推荐
LJ小番茄14 分钟前
Vue 常见的几种通信方式(总结)
前端·javascript·vue.js·html
黑狼传说18 分钟前
前端项目优化:极致最优 vs 相对最优 —— 深入探索与实践
前端·性能优化
장숙혜24 分钟前
前端-CDN的理解及CDN一些使用平台
前端
FakeOccupational2 小时前
nodejs 007:错误npm error Error: EPERM: operation not permitted, symlink
前端·npm·node.js
奶糖 肥晨2 小时前
react是什么?
前端·react.js·前端框架
亦舒.2 小时前
JSDelivr & NPM CDN 国内加速节点
前端·npm·node.js
代码搬运媛2 小时前
code eintegrity npm err sha512
前端·npm·node.js
阳光开朗_大男孩儿2 小时前
DBUS属性原理
linux·服务器·前端·数据库·qt
pan_junbiao3 小时前
Vue组件:模板引用ref属性的使用
前端·javascript·vue.js
LvManBa3 小时前
Vue学习记录之四(computed的用法)
前端·vue.js·学习