实现效果 :
-
在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>