|
@@ -13,7 +13,8 @@ import _ from 'lodash'
|
|
|
import * as echarts from 'echarts';
|
|
import * as echarts from 'echarts';
|
|
|
|
|
|
|
|
export default {
|
|
export default {
|
|
|
- name: "LineChart",//折线图组件
|
|
|
|
|
|
|
+ name: "LineChart",
|
|
|
|
|
+ emits: ['dataPointClick'],
|
|
|
data() {
|
|
data() {
|
|
|
return {
|
|
return {
|
|
|
echart: null,//图标实例
|
|
echart: null,//图标实例
|
|
@@ -23,6 +24,7 @@ export default {
|
|
|
legendSelected: {},//图例选中状态
|
|
legendSelected: {},//图例选中状态
|
|
|
legenSelectList: [],//选择的图例
|
|
legenSelectList: [],//选择的图例
|
|
|
legenAllList: this.title,//所有的图例数据
|
|
legenAllList: this.title,//所有的图例数据
|
|
|
|
|
+ _tooltipDetailHandler: null,//tooltip详情按钮点击处理器
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
@@ -85,10 +87,6 @@ export default {
|
|
|
type: Array,
|
|
type: Array,
|
|
|
default: () => []
|
|
default: () => []
|
|
|
},//图例标题
|
|
},//图例标题
|
|
|
- // isSetMarkNumber:{
|
|
|
|
|
- // type: Boolean,
|
|
|
|
|
- // default: false
|
|
|
|
|
- // },//是否设置辅助线label值
|
|
|
|
|
markNumber: {
|
|
markNumber: {
|
|
|
type: Array,
|
|
type: Array,
|
|
|
default: () => []
|
|
default: () => []
|
|
@@ -99,11 +97,6 @@ export default {
|
|
|
default: false
|
|
default: false
|
|
|
},//是否是曲线图
|
|
},//是否是曲线图
|
|
|
|
|
|
|
|
- // showMarkLine: {
|
|
|
|
|
- // type: Boolean,
|
|
|
|
|
- // default: false
|
|
|
|
|
- // },//是否显示辅助线平均值
|
|
|
|
|
-
|
|
|
|
|
showBackground: {
|
|
showBackground: {
|
|
|
type: Boolean,
|
|
type: Boolean,
|
|
|
default: true,
|
|
default: true,
|
|
@@ -155,6 +148,22 @@ export default {
|
|
|
disableLabelRotate: {
|
|
disableLabelRotate: {
|
|
|
type: Boolean,
|
|
type: Boolean,
|
|
|
default: false
|
|
default: false
|
|
|
|
|
+ },
|
|
|
|
|
+ tooltipDetailData: {
|
|
|
|
|
+ type: Array,
|
|
|
|
|
+ default: () => []
|
|
|
|
|
+ },
|
|
|
|
|
+ clickDataPoint: {
|
|
|
|
|
+ type: Boolean,
|
|
|
|
|
+ default: false
|
|
|
|
|
+ },
|
|
|
|
|
+ showTooltipDetail: {
|
|
|
|
|
+ type: Boolean,
|
|
|
|
|
+ default: true
|
|
|
|
|
+ },
|
|
|
|
|
+ showYAxisInteger: {
|
|
|
|
|
+ type: Boolean,
|
|
|
|
|
+ default: false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
},
|
|
},
|
|
@@ -169,7 +178,12 @@ export default {
|
|
|
},//重新加载图表
|
|
},//重新加载图表
|
|
|
|
|
|
|
|
datay: {
|
|
datay: {
|
|
|
- handler: 'LoadEchart',
|
|
|
|
|
|
|
+ handler() {
|
|
|
|
|
+ this.LoadEchart();
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ this.bindChartEvents();
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
deep: true,
|
|
deep: true,
|
|
|
},//数值变化时重新加载图表
|
|
},//数值变化时重新加载图表
|
|
|
},
|
|
},
|
|
@@ -179,12 +193,17 @@ export default {
|
|
|
beforeDestroy() {
|
|
beforeDestroy() {
|
|
|
// 组件销毁移除监听 防止内存泄漏
|
|
// 组件销毁移除监听 防止内存泄漏
|
|
|
window.removeEventListener('resize', this.HandleResize);
|
|
window.removeEventListener('resize', this.HandleResize);
|
|
|
|
|
+ if (this._tooltipDetailHandler && this.echart) {
|
|
|
|
|
+ this.echart.getDom().removeEventListener('click', this._tooltipDetailHandler);
|
|
|
|
|
+ }
|
|
|
this.echart.dispose(); // 销毁之前的实例
|
|
this.echart.dispose(); // 销毁之前的实例
|
|
|
},
|
|
},
|
|
|
mounted() {
|
|
mounted() {
|
|
|
-
|
|
|
|
|
- this.LoadEchart();//初始化图表
|
|
|
|
|
- this.HandleResize()
|
|
|
|
|
|
|
+ this.LoadEchart();
|
|
|
|
|
+ this.HandleResize();
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ this.bindChartEvents();
|
|
|
|
|
+ });
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
|
markLine(index) {
|
|
markLine(index) {
|
|
@@ -214,6 +233,20 @@ export default {
|
|
|
//加载echarts
|
|
//加载echarts
|
|
|
LoadEchart() {
|
|
LoadEchart() {
|
|
|
|
|
|
|
|
|
|
+ const container = this.$refs.line_chart;
|
|
|
|
|
+
|
|
|
|
|
+ // 空数据处理
|
|
|
|
|
+ if (!this.datax || this.datax.length === 0 || !this.datay || this.datay.length === 0) {
|
|
|
|
|
+ if (this.echart) {
|
|
|
|
|
+ this.echart.dispose();
|
|
|
|
|
+ this.echart = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (container) {
|
|
|
|
|
+ container.innerHTML = '<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#909399;font-size:14px;">暂无数据</div>';
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 简化图例选中列表的初始化
|
|
// 简化图例选中列表的初始化
|
|
|
this.legenSelectList = this.legendList.length > 0
|
|
this.legenSelectList = this.legendList.length > 0
|
|
|
? [...this.legendList]
|
|
? [...this.legendList]
|
|
@@ -221,11 +254,14 @@ export default {
|
|
|
|
|
|
|
|
this.legenAllList = [...this.title];
|
|
this.legenAllList = [...this.title];
|
|
|
|
|
|
|
|
|
|
+ // 清空可能存在的"暂无数据"文本
|
|
|
|
|
+ container.innerHTML = '';
|
|
|
|
|
+
|
|
|
if (this.echart) {
|
|
if (this.echart) {
|
|
|
this.echart.dispose(); // 销毁之前的实例
|
|
this.echart.dispose(); // 销毁之前的实例
|
|
|
}
|
|
}
|
|
|
//devicePixelRatio: 2 2表示每个逻辑像素对应 2 个物理像素。这会进一步提高图表的清晰度,适用于更高分辨率的屏幕 解决高分辨率显示器文字模糊的问题
|
|
//devicePixelRatio: 2 2表示每个逻辑像素对应 2 个物理像素。这会进一步提高图表的清晰度,适用于更高分辨率的屏幕 解决高分辨率显示器文字模糊的问题
|
|
|
- this.echart = echarts.init(this.$refs.line_chart, { devicePixelRatio: 2 });//获取echarts容器 如果组件同一个页面多处使用 这里不能用id获取实例
|
|
|
|
|
|
|
+ this.echart = echarts.init(container, { devicePixelRatio: 2 });//获取echarts容器 如果组件同一个页面多处使用 这里不能用id获取实例
|
|
|
|
|
|
|
|
// 图例选中状态初始化
|
|
// 图例选中状态初始化
|
|
|
this.legendSelected = {};
|
|
this.legendSelected = {};
|
|
@@ -248,81 +284,87 @@ export default {
|
|
|
.filter(index => index !== -1)
|
|
.filter(index => index !== -1)
|
|
|
.map(index => this.datay[index]);
|
|
.map(index => this.datay[index]);
|
|
|
|
|
|
|
|
- // const maxValue = visibleData.reduce((max, series) => {
|
|
|
|
|
- // const seriesMax = Math.max(...series);
|
|
|
|
|
- // return seriesMax > max ? seriesMax : max;
|
|
|
|
|
- // }, -Infinity);
|
|
|
|
|
-
|
|
|
|
|
- // const yAxisMax = Math.ceil((maxValue+2) / 5) * 5;//向上取整到5的倍数
|
|
|
|
|
- // console.log("打印最大值",maxValue);
|
|
|
|
|
const extraText = this.extraText ? "占比" : ""
|
|
const extraText = this.extraText ? "占比" : ""
|
|
|
const dataCount = this.datax.length;
|
|
const dataCount = this.datax.length;
|
|
|
- // if(maxValue<10)
|
|
|
|
|
- // {
|
|
|
|
|
- // maxValue=9
|
|
|
|
|
- // }
|
|
|
|
|
- //计算每个x轴的宽度
|
|
|
|
|
let totalWidth = this.$refs.line_chart.clientWidth;
|
|
let totalWidth = this.$refs.line_chart.clientWidth;
|
|
|
- let singleSeriesWidth = (totalWidth - 100) / dataCount;
|
|
|
|
|
- // console.log("打印x轴宽度",singleSeriesWidth);
|
|
|
|
|
|
|
+ const gridWidth = totalWidth - 80;
|
|
|
|
|
+ let singleSeriesWidth = dataCount > 0 ? gridWidth / dataCount : gridWidth;
|
|
|
|
|
+ // Estimate label width: Chinese chars ~12px, "M月D日 HH时" ~11 chars
|
|
|
|
|
+ const avgLabelWidth = Math.max(...this.datax.map(d => d.length * 12));
|
|
|
|
|
+ const minGap = avgLabelWidth + 8;
|
|
|
|
|
+ let labelInterval = 0;
|
|
|
|
|
+ if (singleSeriesWidth < minGap) {
|
|
|
|
|
+ labelInterval = Math.ceil(minGap / singleSeriesWidth) - 1;
|
|
|
|
|
+ }
|
|
|
let gridTop = this.showMarkPoint ? 45 : 20;
|
|
let gridTop = this.showMarkPoint ? 45 : 20;
|
|
|
// 无图例 默认20 有图例 70
|
|
// 无图例 默认20 有图例 70
|
|
|
if (this.title.length > 0) {
|
|
if (this.title.length > 0) {
|
|
|
gridTop = 70;
|
|
gridTop = 70;
|
|
|
}
|
|
}
|
|
|
|
|
+ const hasDetailData = this.tooltipDetailData.length > 0
|
|
|
let option = {
|
|
let option = {
|
|
|
|
|
|
|
|
tooltip: {
|
|
tooltip: {
|
|
|
trigger: 'axis',
|
|
trigger: 'axis',
|
|
|
|
|
|
|
|
axisPointer: {
|
|
axisPointer: {
|
|
|
- type: 'line', // 指示器类型,cross 表示交叉指示线
|
|
|
|
|
- // lineStyle: {
|
|
|
|
|
- // color: 'red',
|
|
|
|
|
- // width: 2,
|
|
|
|
|
- // type: 'dashed'
|
|
|
|
|
- // },
|
|
|
|
|
- // crossStyle: {
|
|
|
|
|
- // color: '#999', // 交叉线颜色
|
|
|
|
|
- // width: 1, // 交叉线宽度
|
|
|
|
|
- // type: 'dashed' // 交叉线样式
|
|
|
|
|
- // }
|
|
|
|
|
|
|
+ type: 'line',
|
|
|
|
|
+ lineStyle: {
|
|
|
|
|
+ color: '#999',
|
|
|
|
|
+ width: 1,
|
|
|
|
|
+ type: 'dashed',
|
|
|
|
|
+ },
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
renderModel: 'html',//使用html渲染模式
|
|
renderModel: 'html',//使用html渲染模式
|
|
|
confine: true,//是否将 tooltip 框限制在图表的区域内。
|
|
confine: true,//是否将 tooltip 框限制在图表的区域内。
|
|
|
extraCssText: 'border-radius: 4px;padding:5px 0px 5px 5px;white-space:normal;word-warp:break-word;max-width: 400px;', // 设置最大宽度和高度
|
|
extraCssText: 'border-radius: 4px;padding:5px 0px 5px 5px;white-space:normal;word-warp:break-word;max-width: 400px;', // 设置最大宽度和高度
|
|
|
enterable: true,
|
|
enterable: true,
|
|
|
|
|
+ hideDelay: 800,
|
|
|
formatter: (params) => {
|
|
formatter: (params) => {
|
|
|
- // console.log(params);
|
|
|
|
|
-
|
|
|
|
|
- // console.log("打印this.tooltipData",this.tooltipData);
|
|
|
|
|
-
|
|
|
|
|
let tooltip = `<div class='tooltip_content'>`;
|
|
let tooltip = `<div class='tooltip_content'>`;
|
|
|
let title = params[0].name;
|
|
let title = params[0].name;
|
|
|
- tooltip += `<div class='tooltip_title'>${title}</div>`;
|
|
|
|
|
- params.forEach((item, index) => {
|
|
|
|
|
- // console.log("打印item",item);
|
|
|
|
|
- let name = item.seriesName;
|
|
|
|
|
- let rate = item.value + this.unit;//占比
|
|
|
|
|
- if (this.tooltipData.length > 0) {
|
|
|
|
|
- let tooltipItem = this.tooltipData?.[item.seriesIndex]?.[item.dataIndex] || '';
|
|
|
|
|
- tooltip += `<div class='tooltip_student'>
|
|
|
|
|
- <div class='tooltip_line_icon' style='background:${item.color}'></div>
|
|
|
|
|
- <div class='tooltip_student_name'>
|
|
|
|
|
- ${tooltipItem.name}:${tooltipItem.value}
|
|
|
|
|
- </div></div>`;
|
|
|
|
|
|
|
+ const dataIndex = params[0].dataIndex;
|
|
|
|
|
+ // showTooltipDetail 控制是否显示详情按钮和学校列表
|
|
|
|
|
+ if (this.tooltipDetailData.length > 0 && this.showTooltipDetail) {
|
|
|
|
|
+ tooltip += `<div class='tooltip_header'>`;
|
|
|
|
|
+ tooltip += `<div class='tooltip_title'>${title}</div>`;
|
|
|
|
|
+ tooltip += `<div class='tooltip_detail_btn' data-index="${dataIndex}">详情</div>`;
|
|
|
|
|
+ tooltip += `</div>`;
|
|
|
|
|
+ const detail = this.tooltipDetailData[dataIndex] || {};
|
|
|
|
|
+ const schoolList = detail.schoolList || [];
|
|
|
|
|
+ const totalCount = schoolList.reduce((sum, s) => sum + (s.count || 0), 0);
|
|
|
|
|
+ tooltip += `<div class='tooltip_total'>人数总计:${totalCount}</div>`;
|
|
|
|
|
+ if (schoolList.length > 0) {
|
|
|
|
|
+ tooltip += `<div class='tooltip_school_list'>`;
|
|
|
|
|
+ schoolList.forEach((school) => {
|
|
|
|
|
+ tooltip += `<div class='tooltip_school_item'>
|
|
|
|
|
+ <span class='tooltip_school_name'>${school.xname || ''}:${school.count || 0}</span>
|
|
|
|
|
+ </div>`;
|
|
|
|
|
+ });
|
|
|
|
|
+ tooltip += `</div>`;
|
|
|
}
|
|
}
|
|
|
- else {
|
|
|
|
|
- tooltip += `<div class='tooltip_student'><div class='tooltip_line_icon' style='background:${item.color}'></div><div class='tooltip_student_name'>${name}${extraText}:${rate || '-'}</div></div>`;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ tooltip += `<div class='tooltip_title'>${title}</div>`;
|
|
|
|
|
+ params.forEach((item) => {
|
|
|
|
|
+ let name = item.seriesName;
|
|
|
|
|
+ let value = this.showYAxisInteger ? Math.round(item.value) : item.value;
|
|
|
|
|
+ let rate = value + this.unit;
|
|
|
|
|
+ if (this.tooltipData.length > 0) {
|
|
|
|
|
+ let tooltipItem = this.tooltipData?.[item.seriesIndex]?.[item.dataIndex] || '';
|
|
|
|
|
+ tooltip += `<div class='tooltip_student'>
|
|
|
|
|
+ <div class='tooltip_student_name'>
|
|
|
|
|
+ ${tooltipItem.name}:${tooltipItem.value}
|
|
|
|
|
+ </div></div>`;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const extraText = '';
|
|
|
|
|
+ tooltip += `<div class='tooltip_student'>
|
|
|
|
|
+ <div class='tooltip_student_name'>${rate }</div></div>`;
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
tooltip += `</div>`;
|
|
tooltip += `</div>`;
|
|
|
return tooltip;
|
|
return tooltip;
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
legend: {
|
|
legend: {
|
|
@@ -349,7 +391,6 @@ export default {
|
|
|
pageButtonPosition: 'end', // 分页按钮显示在右侧
|
|
pageButtonPosition: 'end', // 分页按钮显示在右侧
|
|
|
orient: 'horizontal', // 图例横向排列(也可以设为 'vertical')
|
|
orient: 'horizontal', // 图例横向排列(也可以设为 'vertical')
|
|
|
alignTo: 'none',//alignTo 可以用于图例 (legend) 的配置项,决定图例如何对齐到其他元素。常见的值包括 'left', 'right', 'center' 等。
|
|
alignTo: 'none',//alignTo 可以用于图例 (legend) 的配置项,决定图例如何对齐到其他元素。常见的值包括 'left', 'right', 'center' 等。
|
|
|
- // icon:'emptyCircle',//标记类型 'circle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow', 'none'
|
|
|
|
|
},
|
|
},
|
|
|
grid: {
|
|
grid: {
|
|
|
left: this.gridLeft,
|
|
left: this.gridLeft,
|
|
@@ -366,70 +407,38 @@ export default {
|
|
|
|
|
|
|
|
axisTick: {
|
|
axisTick: {
|
|
|
alignWithLabel: true,//不然刻度线跟随标签对齐
|
|
alignWithLabel: true,//不然刻度线跟随标签对齐
|
|
|
- // show: !this.yInverse, // 显示刻度线
|
|
|
|
|
show: !hideTick,
|
|
show: !hideTick,
|
|
|
inside: false, // 刻度线朝外(默认是朝内)
|
|
inside: false, // 刻度线朝外(默认是朝内)
|
|
|
length: 8, // 刻度线长度
|
|
length: 8, // 刻度线长度
|
|
|
- // lineStyle: {
|
|
|
|
|
- // color: "#E4E7ED", // 刻度线颜色
|
|
|
|
|
- // width: 1 // 线宽
|
|
|
|
|
- // },
|
|
|
|
|
- // color:'#E4E7ED'
|
|
|
|
|
},
|
|
},
|
|
|
axisLine: {
|
|
axisLine: {
|
|
|
show: !this.yInverse,
|
|
show: !this.yInverse,
|
|
|
},
|
|
},
|
|
|
axisLabel: {
|
|
axisLabel: {
|
|
|
- interval: 0,//0 显示所有刻度
|
|
|
|
|
- rotate: this.disableLabelRotate ? 0 : (singleSeriesWidth < 60 ? 45 : 0),
|
|
|
|
|
- fontSize: this.fontSize ? this.fontSize : 14,
|
|
|
|
|
|
|
+ interval: labelInterval,
|
|
|
|
|
+ rotate: 0,
|
|
|
|
|
+ fontSize: this.fontSize ? this.fontSize : 12,
|
|
|
color: this.fontColor || "#666",
|
|
color: this.fontColor || "#666",
|
|
|
fontWeight: 400,
|
|
fontWeight: 400,
|
|
|
- formatter: function (value) {
|
|
|
|
|
-
|
|
|
|
|
- //一个文字的长度大概约16.6 按16算
|
|
|
|
|
-
|
|
|
|
|
- const valueWidth = value.length * 14;
|
|
|
|
|
- // console.log("lineChart打印字的大概宽度",valueWidth);
|
|
|
|
|
- // console.log("打印singleSeriesWidth",singleSeriesWidth);
|
|
|
|
|
- if (valueWidth > singleSeriesWidth) {
|
|
|
|
|
-
|
|
|
|
|
- //单系列宽度小于50 倾斜显示
|
|
|
|
|
- if (singleSeriesWidth < 80) {
|
|
|
|
|
- return value;
|
|
|
|
|
- }
|
|
|
|
|
- else {
|
|
|
|
|
- let maxLength = Math.floor(singleSeriesWidth / 14);
|
|
|
|
|
-
|
|
|
|
|
- return value.slice(0, maxLength) + '...';
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- }
|
|
|
|
|
- else {
|
|
|
|
|
- return value;
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
yAxis: {
|
|
yAxis: {
|
|
|
type: 'value',
|
|
type: 'value',
|
|
|
|
|
+ triggerEvent: true,
|
|
|
axisLabel: {
|
|
axisLabel: {
|
|
|
margin: 20,
|
|
margin: 20,
|
|
|
- formatter: '{value}' + this.unit,
|
|
|
|
|
|
|
+ formatter: this.showYAxisInteger ? (v) => Math.round(v) + this.unit : '{value}' + this.unit,
|
|
|
interval: 0, // 显示所有 label
|
|
interval: 0, // 显示所有 label
|
|
|
fontSize: this.fontSize ? this.fontSize : 14,
|
|
fontSize: this.fontSize ? this.fontSize : 14,
|
|
|
color: this.fontColor || "#666",
|
|
color: this.fontColor || "#666",
|
|
|
fontWeight: 400
|
|
fontWeight: 400
|
|
|
},
|
|
},
|
|
|
|
|
+ minInterval: this.showYAxisInteger ? 1 : 0,
|
|
|
splitNumber: 5,//固定显示5个刻度 非强制 不一定生效
|
|
splitNumber: 5,//固定显示5个刻度 非强制 不一定生效
|
|
|
|
|
|
|
|
axisTick: {
|
|
axisTick: {
|
|
|
interval: 0 // 显示所有刻度线
|
|
interval: 0 // 显示所有刻度线
|
|
|
},
|
|
},
|
|
|
- // splitNumber:4,//固定将y轴划分为5段
|
|
|
|
|
-
|
|
|
|
|
splitLine: {
|
|
splitLine: {
|
|
|
show: true, //显示横向网格线
|
|
show: true, //显示横向网格线
|
|
|
lineStyle:
|
|
lineStyle:
|
|
@@ -446,30 +455,6 @@ export default {
|
|
|
},// 网格线设置隔行背景色
|
|
},// 网格线设置隔行背景色
|
|
|
},
|
|
},
|
|
|
inverse: this.yInverse,
|
|
inverse: this.yInverse,
|
|
|
- // min:this.showBackground?0: (val) => {
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- // if(val.min > 0 || val.min < -10) {
|
|
|
|
|
- // // return Math.floor(val.min / 10) * 10
|
|
|
|
|
- // return Math.floor(val.min)
|
|
|
|
|
- // }else {
|
|
|
|
|
- // return val.min + (val.min % 10)
|
|
|
|
|
- // }
|
|
|
|
|
- // },
|
|
|
|
|
- // max:yAxisMax,//固定x轴最高值位100
|
|
|
|
|
- // max: this.unit != '%' ? (val) => {
|
|
|
|
|
- // // console.log(val.max)
|
|
|
|
|
- // // let num = Math.ceil(val.max)
|
|
|
|
|
- // // let num1 = Math.abs(Math.ceil(val.max))
|
|
|
|
|
- // // let count = 0
|
|
|
|
|
- // // do {
|
|
|
|
|
- // // num1 = parseInt(num1 / 10)
|
|
|
|
|
- // // count++
|
|
|
|
|
- // // } while (num1 > 0);
|
|
|
|
|
- // // let num2 = (parseInt(num / (10 ** (count - 1))) + 1 ) * (10 ** (count - 1))
|
|
|
|
|
- // // return num2
|
|
|
|
|
- // return Math.ceil(val.max / 10) * 10
|
|
|
|
|
- // } : null,
|
|
|
|
|
},
|
|
},
|
|
|
graphic: {
|
|
graphic: {
|
|
|
elements: [{
|
|
elements: [{
|
|
@@ -481,92 +466,109 @@ export default {
|
|
|
series: this.datay.map((data, index) => ({
|
|
series: this.datay.map((data, index) => ({
|
|
|
type: 'line',
|
|
type: 'line',
|
|
|
name: this.title[index],
|
|
name: this.title[index],
|
|
|
- // symbol: 'none',//设置无折线点
|
|
|
|
|
- symbol: "emptyCircle",//设置折线点为圆形
|
|
|
|
|
- showSymbol: true,//显示小圆点
|
|
|
|
|
- symbolSize: 8,//折线点大小
|
|
|
|
|
|
|
+ symbol: "emptyCircle",
|
|
|
|
|
+ showSymbol: !hasDetailData,
|
|
|
|
|
+ symbolSize: 8,
|
|
|
label: {
|
|
label: {
|
|
|
- show: this.isShowLabel, //是否显示数值
|
|
|
|
|
|
|
+ show: this.isShowLabel,
|
|
|
color: this.labelColor || this.colors[index],
|
|
color: this.labelColor || this.colors[index],
|
|
|
formatter: "{c}" + this.unit
|
|
formatter: "{c}" + this.unit
|
|
|
},
|
|
},
|
|
|
- smooth: this.isSmooth,//设置折线平滑
|
|
|
|
|
- markLine: this.markLine(index), //平均线
|
|
|
|
|
|
|
+ smooth: this.isSmooth,
|
|
|
|
|
+ markLine: this.markLine(index),
|
|
|
|
|
|
|
|
markPoint: this.showMarkPoint ? {
|
|
markPoint: this.showMarkPoint ? {
|
|
|
- // symbolSize:25,//气泡大小
|
|
|
|
|
- // label: {
|
|
|
|
|
- // show: true,
|
|
|
|
|
- // position: 'inside',
|
|
|
|
|
- // color: '#fff',
|
|
|
|
|
- // z: 0, // 降低层级
|
|
|
|
|
- // emphasis: { z: 0 }
|
|
|
|
|
- // },
|
|
|
|
|
data: [
|
|
data: [
|
|
|
{
|
|
{
|
|
|
type: "max",
|
|
type: "max",
|
|
|
name: "最大值",
|
|
name: "最大值",
|
|
|
label: { color: "#fff", },
|
|
label: { color: "#fff", },
|
|
|
- // z: index*5+1,
|
|
|
|
|
- // symbolOffset: [0, index * 8 - 10], // 每一个点依次向下偏移
|
|
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
type: "min",
|
|
type: "min",
|
|
|
name: "最小值",
|
|
name: "最小值",
|
|
|
label: { color: "#fff", },
|
|
label: { color: "#fff", },
|
|
|
- // z:index+1,
|
|
|
|
|
- // symbolOffset: [0, index * 8 - 10], // 每一个点依次向下偏移
|
|
|
|
|
},
|
|
},
|
|
|
],
|
|
],
|
|
|
- } : undefined,//是否显示气泡
|
|
|
|
|
|
|
+ } : undefined,
|
|
|
itemStyle: {
|
|
itemStyle: {
|
|
|
color: this.colors[index],
|
|
color: this.colors[index],
|
|
|
borderColor: this.colors[index],
|
|
borderColor: this.colors[index],
|
|
|
- borderWidth: 1,//空心圆的边框粗细
|
|
|
|
|
|
|
+ borderWidth: 2,
|
|
|
shadowColor: 'rgba(0, 0, 0, .1)',
|
|
shadowColor: 'rgba(0, 0, 0, .1)',
|
|
|
shadowBlur: 0,
|
|
shadowBlur: 0,
|
|
|
},
|
|
},
|
|
|
|
|
+ symbol: hasDetailData ? 'emptyCircle' : 'circle',
|
|
|
|
|
+ symbolSize: hasDetailData ? 0 : 6,
|
|
|
lineStyle: {
|
|
lineStyle: {
|
|
|
- width: 3,//设置折线的粗细
|
|
|
|
|
|
|
+ width: 2,
|
|
|
},
|
|
},
|
|
|
areaStyle: this.showBackground ? {
|
|
areaStyle: this.showBackground ? {
|
|
|
color: {
|
|
color: {
|
|
|
type: 'linear',
|
|
type: 'linear',
|
|
|
- x: 0,//渐变起点x
|
|
|
|
|
- y: 0,//渐变起点y
|
|
|
|
|
- x2: 0,//渐变终点x
|
|
|
|
|
- y2: 1,//渐变终点y
|
|
|
|
|
|
|
+ x: 0,
|
|
|
|
|
+ y: 0,
|
|
|
|
|
+ x2: 0,
|
|
|
|
|
+ y2: 1,
|
|
|
colorStops: [
|
|
colorStops: [
|
|
|
{
|
|
{
|
|
|
- offset: 0, color: this.colors[index] + '30' // 30% 透明度
|
|
|
|
|
|
|
+ offset: 0, color: hasDetailData ? 'rgba(46, 100, 250, 0.40)' : (this.colors[index] + '30')
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
- offset: 1, color: this.colors[index] + '00' // 0% 透明度
|
|
|
|
|
|
|
+ offset: 1, color: hasDetailData ? 'rgba(46, 100, 250, 0.04)' : (this.colors[index] + '00')
|
|
|
}
|
|
}
|
|
|
],
|
|
],
|
|
|
},
|
|
},
|
|
|
- } : undefined,//折线背景阴影设置
|
|
|
|
|
|
|
+ } : undefined,
|
|
|
emphasis: {
|
|
emphasis: {
|
|
|
focus: 'series',
|
|
focus: 'series',
|
|
|
- blurScope: 'coordinateSystem'
|
|
|
|
|
|
|
+ blurScope: 'coordinateSystem',
|
|
|
|
|
+ scale: true,
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: '#fff',
|
|
|
|
|
+ borderColor: '#2E64FA',
|
|
|
|
|
+ borderWidth: 8,
|
|
|
|
|
+ shadowColor: 'rgba(46, 100, 250, 0.3)',
|
|
|
|
|
+ shadowBlur: 8,
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
data: data,
|
|
data: data,
|
|
|
})),
|
|
})),
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- this.LabelMouseOver();//X轴文字过程显示省略号,鼠标hover上去时,弹出一个tooltip显示全称
|
|
|
|
|
|
|
+ this.LabelMouseOver();
|
|
|
this.echart.setOption(option);
|
|
this.echart.setOption(option);
|
|
|
this.echart.on("legendselectchanged", this.HandleLegendSelectChanged);
|
|
this.echart.on("legendselectchanged", this.HandleLegendSelectChanged);
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
|
|
+ bindChartEvents() {
|
|
|
|
|
+ if (!this.echart) return;
|
|
|
|
|
+ if (!this.datax || this.datax.length === 0) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 通过 tooltip 的 enterable: true,鼠标可进入 tooltip 区域
|
|
|
|
|
+ // 使用事件委托监听详情按钮点击
|
|
|
|
|
+ const dom = this.echart.getDom();
|
|
|
|
|
+ dom.removeEventListener('click', this._tooltipDetailHandler);
|
|
|
|
|
+ this._tooltipDetailHandler = (e) => {
|
|
|
|
|
+ if (e.target && e.target.classList && e.target.classList.contains('tooltip_detail_btn')) {
|
|
|
|
|
+ const dataIndex = parseInt(e.target.getAttribute('data-index'), 10);
|
|
|
|
|
+ if (!isNaN(dataIndex) && dataIndex >= 0 && dataIndex < this.datax.length) {
|
|
|
|
|
+ const detail = this.tooltipDetailData[dataIndex] || {};
|
|
|
|
|
+ const schoolList = detail.schoolList || [];
|
|
|
|
|
+ this.$emit('dataPointClick', {
|
|
|
|
|
+ dataIndex,
|
|
|
|
|
+ schoolList,
|
|
|
|
|
+ rawData: detail
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+ dom.addEventListener('click', this._tooltipDetailHandler);
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
// X轴文字过程显示省略号,鼠标hover上去时,弹出一个tooltip显示全称
|
|
// X轴文字过程显示省略号,鼠标hover上去时,弹出一个tooltip显示全称
|
|
|
LabelMouseOver() {
|
|
LabelMouseOver() {
|
|
|
- // 注意这里,是以X轴显示内容过长为例,如果是y轴的话,需要把params.componentType == 'xAxis'改为yAxis
|
|
|
|
|
- // 判断是否创建过div框,如果创建过就不再创建了
|
|
|
|
|
- // 该div用来盛放文本显示内容的,方便对其悬浮位置进行处理
|
|
|
|
|
let elementDiv = document.getElementById("tooltipContent");
|
|
let elementDiv = document.getElementById("tooltipContent");
|
|
|
- // console.log("elementDiv",elementDiv)
|
|
|
|
|
|
|
|
|
|
if (!elementDiv) {
|
|
if (!elementDiv) {
|
|
|
let div = document.createElement("div");
|
|
let div = document.createElement("div");
|
|
@@ -593,7 +595,6 @@ export default {
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
this.echart.on("mouseout", function (params) {
|
|
this.echart.on("mouseout", function (params) {
|
|
|
- //注意这里,我是以X轴显示内容过长为例,如果是y轴的话,需要改为yAxis
|
|
|
|
|
if (params.componentType == "xAxis") {
|
|
if (params.componentType == "xAxis") {
|
|
|
let elementDiv = document.querySelector("#tooltipContent");
|
|
let elementDiv = document.querySelector("#tooltipContent");
|
|
|
elementDiv.style.cssText = "display:none";
|
|
elementDiv.style.cssText = "display:none";
|
|
@@ -614,7 +615,6 @@ export default {
|
|
|
},
|
|
},
|
|
|
//全选
|
|
//全选
|
|
|
ChangeCheckAll(value) {
|
|
ChangeCheckAll(value) {
|
|
|
- // console.log("打印value",this.checkAll);
|
|
|
|
|
if (value) {
|
|
if (value) {
|
|
|
//选中全选
|
|
//选中全选
|
|
|
this.legenSelectList = this.legenAllList;
|
|
this.legenSelectList = this.legenAllList;
|
|
@@ -633,7 +633,6 @@ export default {
|
|
|
HandleLegendSelectChanged(params) {
|
|
HandleLegendSelectChanged(params) {
|
|
|
const legendData = this.echart.getOption().legend[0].data;
|
|
const legendData = this.echart.getOption().legend[0].data;
|
|
|
const selected = params.selected;
|
|
const selected = params.selected;
|
|
|
- // 检查是否有任何图例项被隐藏
|
|
|
|
|
this.legendSelected = selected;
|
|
this.legendSelected = selected;
|
|
|
const allSelected = legendData.every(
|
|
const allSelected = legendData.every(
|
|
|
(seriesName) => selected[seriesName]
|
|
(seriesName) => selected[seriesName]
|
|
@@ -642,7 +641,6 @@ export default {
|
|
|
(seriesName) => !selected[seriesName]
|
|
(seriesName) => !selected[seriesName]
|
|
|
);
|
|
);
|
|
|
const someSelected = !allSelected && !noneSelected;
|
|
const someSelected = !allSelected && !noneSelected;
|
|
|
- // 更新“显示全部”按钮的状态
|
|
|
|
|
if (noneSelected) {
|
|
if (noneSelected) {
|
|
|
this.checkAll = false;
|
|
this.checkAll = false;
|
|
|
}
|
|
}
|
|
@@ -735,9 +733,6 @@ export default {
|
|
|
|
|
|
|
|
// X轴文字过程显示省略号,鼠标hover上去时,弹出一个tooltip显示全称
|
|
// X轴文字过程显示省略号,鼠标hover上去时,弹出一个tooltip显示全称
|
|
|
extension() {
|
|
extension() {
|
|
|
- // 注意这里,是以X轴显示内容过长为例,如果是y轴的话,需要把params.componentType == 'xAxis'改为yAxis
|
|
|
|
|
- // 判断是否创建过div框,如果创建过就不再创建了
|
|
|
|
|
- // 该div用来盛放文本显示内容的,方便对其悬浮位置进行处理
|
|
|
|
|
let elementDiv = document.getElementById("extension");
|
|
let elementDiv = document.getElementById("extension");
|
|
|
if (!elementDiv) {
|
|
if (!elementDiv) {
|
|
|
let div = document.createElement("div");
|
|
let div = document.createElement("div");
|
|
@@ -748,7 +743,6 @@ export default {
|
|
|
this.myChart.on("mouseover", (params) => {
|
|
this.myChart.on("mouseover", (params) => {
|
|
|
if (params.componentType == "xAxis") {
|
|
if (params.componentType == "xAxis") {
|
|
|
let elementDiv = document.querySelector("#extension");
|
|
let elementDiv = document.querySelector("#extension");
|
|
|
- //设置悬浮文本的位置以及样式
|
|
|
|
|
let elementStyle =
|
|
let elementStyle =
|
|
|
"position: absolute;z-index: 99999;color: #fff;font-size: 12px;padding: 5px;display: inline;border-radius: 4px;background-color: #303133;box-shadow: rgba(0, 0, 0, 0.3) 2px 2px 8px";
|
|
"position: absolute;z-index: 99999;color: #fff;font-size: 12px;padding: 5px;display: inline;border-radius: 4px;background-color: #303133;box-shadow: rgba(0, 0, 0, 0.3) 2px 2px 8px";
|
|
|
elementDiv.style.cssText = elementStyle;
|
|
elementDiv.style.cssText = elementStyle;
|
|
@@ -763,7 +757,6 @@ export default {
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
this.myChart.on("mouseout", function (params) {
|
|
this.myChart.on("mouseout", function (params) {
|
|
|
- //注意这里,我是以X轴显示内容过长为例,如果是y轴的话,需要改为yAxis
|
|
|
|
|
if (params.componentType == "xAxis") {
|
|
if (params.componentType == "xAxis") {
|
|
|
let elementDiv = document.querySelector("#extension");
|
|
let elementDiv = document.querySelector("#extension");
|
|
|
elementDiv.style.cssText = "display:none";
|
|
elementDiv.style.cssText = "display:none";
|
|
@@ -785,4 +778,63 @@ export default {
|
|
|
.is_show_all {
|
|
.is_show_all {
|
|
|
top: 10px;
|
|
top: 10px;
|
|
|
}
|
|
}
|
|
|
-</style>
|
|
|
|
|
|
|
+
|
|
|
|
|
+:deep(.tooltip_content) {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ min-width: 180px;
|
|
|
|
|
+}
|
|
|
|
|
+:deep(.tooltip_header) {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin-bottom: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+:deep(.tooltip_title) {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ color: #333333;
|
|
|
|
|
+}
|
|
|
|
|
+:deep(.tooltip_detail_btn) {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #2E64FA;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ padding: 2px 8px;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+}
|
|
|
|
|
+:deep(.tooltip_total) {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ font-weight: 400;
|
|
|
|
|
+ color: #666666;
|
|
|
|
|
+ margin-bottom: 6px;
|
|
|
|
|
+}
|
|
|
|
|
+:deep(.tooltip_school_list) {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 4px;
|
|
|
|
|
+ margin-bottom: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+:deep(.tooltip_school_item) {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 6px;
|
|
|
|
|
+ color: #606266;
|
|
|
|
|
+}
|
|
|
|
|
+:deep(.tooltip_line_icon) {
|
|
|
|
|
+ display: inline-block;
|
|
|
|
|
+ width: 8px;
|
|
|
|
|
+ height: 8px;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+:deep(.tooltip_school_name) {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+}
|
|
|
|
|
+:deep(.tooltip_student) {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 6px;
|
|
|
|
|
+}
|
|
|
|
|
+:deep(.tooltip_student_name) {
|
|
|
|
|
+ color: #606266;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|