本文不适合新手观看(很费时间)。本文初心也只是 给自己的气泡图研发做个总结并且分享代码。 文章不清晰之处还请谅解,不懂的可提问。
本文也对鼠标置顶的提示框 tooltip 进行了自定义。使用 rich 富文本去实现。
![](https://img.haomeiwen.com/i15697111/536f2ae504178422.jpg)
本文重点是 根据时间戳来生成对应时间上的 气泡数据。区分 series 项。重点也是 series 这个配置
首先是基本配置。
computed: {
// 基本配置
baseConfig: function () {
return {
// title: {
// text: "当前剩余在翼循环:1234 剩余飞行小时:3456",
// textStyle: {
// fontSize: 10,
// },
// },
legend: {
right: "2%",
itemWidth: 8,
textStyle: { fontSize: 10 },
},
grid: {
left: 30,
bottom: 30,
top: 25,
right: 30,
containLabel: true,
},
yAxis: {
type: "value",
interval: 0.5,
data: Array.from({ length: this.yAxisCount }, (_, i) => i),
max: this.yAxisCount,
min: 0,
splitLine: {
show: true,
lineStyle: {
color: this.yAxisLineStyle,
width: 1,
},
},
axisLabel: { show: false },
axisTick: { show: false },
axisLine: { show: false },
},
};
},
}
视觉上 y轴的 顶部和 底部线条不需要展示。这里采用 白色颜色 和 背景颜色来达到隐藏效果
//设置 y轴线的颜色来达到视觉效果
yAxisLineStyle: function() {
if (this.yAxisCount === 2) {
return ["#fff", "#fff", "#F1F1F1", "#fff", "#fff"];
}
if (this.yAxisCount === 3) {
return ["#fff", "#fff", "#F1F1F1", "#fff", "#fff", "#F1F1F1", "#fff"];
}
},
此时气泡图的数据 和 y轴刻度无关。 只是为了区分 不同项的数据。用 不同y值来分割开。故而 使用 yaxis.interval = 0.5 。并且设置 min = 0 ,max 为 数据项的数量。
data 的数据声明如下。不需要太复杂:
data() {
return {
//总共需要几条y轴
yAxisCount: 0,
//x轴跨度最大最小 时间戳
minTime: 0,
maxTime: 0,
};
},
另外的 prop , 这里是重点。 其中的 seriesItemList 就是你要传入的 气泡数据集 。 参数配置看如下。
markLineList 就是标记线 , 你可以自定义标记线。 本图的 标记线是 需求所需。位置也是 基于 时间戳即可。
props: {
/**
* @params seriesItemList
* name 数据集名称
* color 数据气泡颜色以及 toopTip颜色
* yAxisValue y轴的值 (传 1 或 2)
* data 数据集 Array类型
* [ x轴时间戳, 气泡值大小 , toolTips 提示框]
* toolTips : {
* label : 数据项名称
* value : 数据值
* }
*
* Example :
* {
* name: "计划维修费",
* yAxisValue: 1,
color: "red",
data: [
[
1597470795000,
2396973,
{
title: "第三次送修",
tipItem: [
{ label: "TSN", value: 123456 },
{ label: "csn", value: 12345 },
{ label: "总成本", value: 1054654 },
]
}
]
]
}
*/
seriesItemList: {
type: Array,
default: () => [],
},
/**
* @params markLineList
* name 标记线名称
* value x轴的值 时间戳
* Example :
* {
* name: "预计处置日期",
value: 1728986303000
}
*/
markLineList: {
type: Array,
default: () => [],
},
//必须设定气泡图的时间跨度,限制图表显示范围。
/**
* @timeSpan
* [ 起始时间戳 , 结束时间戳]
*/
timeSpan: {
type: Array,
default: () => [],
},
},
标记线的 配置也是 额外的方法去构建。 如下::
generatorMarkLineItem(lineList) {
const mlList = lineList.map(item => ({
xAxis: item.value,
label: {
show: true,
formatter: params =>
this.defineMarkLineContent(item.name, params.value),
rich: {
a: { color: "grey", padding: [24, 0, 2, 0] },
b: { color: "grey" },
},
position: "start",
},
symbol: "none",
lineStyle: { type: "dotted", color: "#D9D9D9" },
}));
mlList.push({
xAxis: this.getTodayTime(),
name: "",
label: {
show: true,
formatter: params => this.formatDay(params.value),
fontSize: 15,
padding: [0, 0, 5, 0],
},
symbol: "none",
lineStyle: { type: "dotted", color: "#178ED3" },
});
return mlList;
},
有些标记线可能需要标记当天。故而最后用了 push 去 添加一个当天的。可根据需求删除。
标记线鼠标置顶会展示label 数据。本文用的 富文本 rich 去自定义。不做介绍。
标记线构建完毕需要设置到一个 series 项里边。不然不会生效。本文采用添加一个空的 series 防止被干扰。
在本需求中, x 轴需要设置为 value。并且时间间隔是 2年, 故而 interval 是 两年。
echarts 会去自动计算你的 x轴时间的起始和结束,故而一定要设置 max 和min
setEchartOption(seriesConfig, markLine) {
// 这个push一定要的,防止标记线被关闭
//把标记线放在最后一个
seriesConfig.push({
name: "",
type: "scatter",
data: [],
markLine: {
symbol: "diamond",
symbolSize: "8",
silent: true,
data: markLine,
},
});
return {
...this.baseConfig,
xAxis: {
type: "value",
// 预期的刻度值是每两年的 1 月
// 如果设置为365天,则会出现前一年12月的刻度值容易误解
// 但是设置为366天,也会出现 2 月的刻度值,综合考虑,设置为366天
interval: 24 * 60 * 60 * 366 * 2 * 1000,
min: this.minTime,
max: this.maxTime,
axisLabel: {
formatter: val => {
return this.formatYear(val);
},
margin: 15,
color: "black",
},
axisLine: { show: false },
axisTick: { show: false },
},
tooltip: {
trigger: "item",
axisPointer: {
type: "line",
},
formatter: params => this.defineTooltipContent(params),
borderWidth: 1,
borderColor: "white",
padding: 0,
},
series: seriesConfig,
};
},
整个组件源代码如下。 需要哪些配置项可自取。也可立即使用。
<template>
<div class="">
<div ref="main" style="width: 100%; height: 210px"></div>
</div>
</template>
<script>
export default {
name: "engineRepairPlanCharts",
props: {
/**
* @params seriesItemList
* name 数据集名称
* color 数据气泡颜色以及 toopTip颜色
* yAxisValue y轴的值 (传 1 或 2)
* data 数据集 Array类型
* [ x轴时间戳, 气泡值大小 , toolTips 提示框]
* toolTips : {
* label : 数据项名称
* value : 数据值
* }
*
* Example :
* {
* name: "计划维修费",
* yAxisValue: 1,
color: "red",
data: [
[
1597470795000,
2396973,
{
title: "第三次送修",
tipItem: [
{ label: "TSN", value: 123456 },
{ label: "csn", value: 12345 },
{ label: "总成本", value: 1054654 },
]
}
]
]
}
*/
seriesItemList: {
type: Array,
default: () => [],
},
/**
* @params markLineList
* name 标记线名称
* value x轴的值 时间戳
* Example :
* {
* name: "预计处置日期",
value: 1728986303000
}
*/
markLineList: {
type: Array,
default: () => [],
},
//必须设定气泡图的时间跨度,限制图表显示范围。
/**
* @timeSpan
* [ 起始时间戳 , 结束时间戳]
*/
timeSpan: {
type: Array,
default: () => [],
},
},
data() {
return {
//总共需要几条y轴
yAxisCount: 0,
//x轴跨度最大最小 时间戳
minTime: 0,
maxTime: 0,
};
},
watch: {
seriesItemList: {
handler(newVal) {
if (newVal.length <= 0) return;
this.generatorTimeBetweenList();
this.$nextTick(() => {
this.initChart();
});
},
deep: true,
immediate: true,
},
},
mounted() {
this.$nextTick(() => {
this.initChart();
});
},
computed: {
// 基本配置
baseConfig: function() {
return {
legend: {
right: "2%",
itemWidth: 30,
textStyle: { fontSize: 16 },
},
grid: {
left: 30,
bottom: 30,
top: 45,
right: 30,
containLabel: true,
},
yAxis: {
type: "value",
interval: 0.5,
max: this.yAxisCount,
min: 0,
splitLine: {
show: true,
lineStyle: {
// 通过 yAxisLineStyle 来设置 y轴线的颜色,与背景色一致时则展示出隐藏效果
color: this.yAxisLineStyle,
width: 1,
},
},
axisLabel: { show: false },
axisTick: { show: false },
axisLine: { show: false },
},
};
},
//设置 y轴线的颜色来达到视觉效果
yAxisLineStyle: function() {
if (this.yAxisCount === 2) {
return ["#fff", "#fff", "#F1F1F1", "#fff", "#fff"];
}
if (this.yAxisCount === 3) {
return ["#fff", "#fff", "#F1F1F1", "#fff", "#fff", "#F1F1F1", "#fff"];
}
},
},
methods: {
initChart() {
// 清空图表
if (this.seriesItemList.length <= 0) return;
//此处的 prop是 this.markLineList
const marklinelist = this.markLineList;
// 此处的 test 的 prop 是 this.seriesItemList
const series = this.seriesItemList.map(item =>
this.generatorSeriesItem(item)
);
const markLine = this.generatorMarkLineItem(marklinelist);
// 初始化图表
const myChart = this.$echarts.init(this.$refs.main);
const option = this.setEchartOption( series, markLine);
console.log('options', option)
// TODO 会出现历史数据,需要清空图表,原因待查
myChart.clear();
myChart.setOption(option);
window.addEventListener("resize", () => {
myChart.resize();
});
},
// 设置图表配置
setEchartOption(seriesConfig, markLine) {
// 这个push一定要的,防止标记线被关闭
//把标记线放在最后一个
seriesConfig.push({
name: "",
type: "scatter",
data: [],
markLine: {
symbol: "diamond",
symbolSize: "8",
silent: true,
data: markLine,
},
});
return {
...this.baseConfig,
xAxis: {
type: "value",
// 预期的刻度值是每两年的 1 月
// 如果设置为365天,则会出现前一年12月的刻度值容易误解
// 但是设置为366天,也会出现 2 月的刻度值,综合考虑,设置为366天
interval: 24 * 60 * 60 * 366 * 2 * 1000,
min: this.minTime,
max: this.maxTime,
axisLabel: {
formatter: val => {
return this.formatYear(val);
},
margin: 15,
color: "black",
},
axisLine: { show: false },
axisTick: { show: false },
},
tooltip: {
trigger: "item",
axisPointer: {
type: "line",
},
formatter: params => this.defineTooltipContent(params),
borderWidth: 1,
borderColor: "white",
padding: 0,
},
series: seriesConfig,
};
},
//生成 markLineItem
generatorMarkLineItem(lineList) {
const mlList = lineList.map(item => ({
xAxis: item.value,
label: {
show: true,
formatter: params =>
this.defineMarkLineContent(item.name, params.value),
rich: {
a: { color: "grey", padding: [24, 0, 2, 0] },
b: { color: "grey" },
},
position: "start",
},
symbol: "none",
lineStyle: { type: "dotted", color: "#D9D9D9" },
}));
mlList.push({
xAxis: this.getTodayTime(),
name: "",
label: {
show: true,
formatter: params => this.formatDay(params.value),
fontSize: 15,
padding: [0, 0, 5, 0],
},
symbol: "none",
lineStyle: { type: "dotted", color: "#178ED3" },
});
return mlList;
},
//生成 series 项
generatorSeriesItem(item) {
const maxYaxis = item.yAxisValue + 1;
const data = item.data.map(row => [
row[0],
item.yAxisValue >= 2 ? item.yAxisValue + 0.5 : item.yAxisValue,
row[1],
row[2],
]);
if (item.data[0]) {
const firstCopyData = item.data[0];
//添加一个不存在的值 去支撑图表的y轴高度
data.push([firstCopyData[0], maxYaxis, ""]);
}
if (this.yAxisCount < maxYaxis) {
this.yAxisCount = maxYaxis;
}
return {
name: item.name,
type: "scatter",
symbolSize: val => this.symbolSizeComputer(val[2]),
data,
label: {
formatter: params => params.value[2],
position: "bottom",
show: true,
},
itemStyle: {
color: item.color,
},
};
},
//获取数据对应的配置
getToolTipConfig(name, color) {
let bottomFontColor = "#fff";
let titleShow = true;
let dateDayShow = true;
if (name === "基准维修费") {
bottomFontColor = "#000";
} else if (name === "使用费") {
bottomFontColor = "#000";
titleShow = false;
dateDayShow = false;
}
return {
color,
bottomFontColor,
titleShow,
dateDayShow,
};
},
//气泡的大小
symbolSizeComputer(data) {
return Math.sqrt(data || 0) / 1e2;
},
//获取当天的时间戳
getTodayTime() {
const now = new Date();
return new Date(
`${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`
).getTime();
},
//生成 x轴的 区间值
generatorTimeBetweenList() {
//使用传入参数的时间
let startTime = this.getNewYearTimestamp(this.timeSpan[0]);
const endTime = this.getNewYearTimestamp(this.timeSpan[1]);
const realEndTime = new Date(endTime);
realEndTime.setFullYear(endTime.getFullYear() + 1);
this.minTime = startTime.getTime();
this.maxTime = realEndTime.getTime();
},
// 格式化日期 只显示年
formatYear(time) {
var date = new Date(time);
var y = 1900 + date.getYear();
return y;
},
//格式化日期 年月日
formatDay(time) {
var date = new Date(time);
var y = 1900 + date.getYear();
var m = "0" + (date.getMonth() + 1);
var d = "0" + date.getDate();
return `${y}-${m.substring(m.length - 2, m.length)}-${d.substring(
d.length - 2,
d.length
)}`;
},
getNewYearTimestamp(time) {
const year = new Date(time);
year.setMonth(0); // 设置月份为1月(0表示1月)
year.setDate(1); // 设置日期为1日
year.setHours(0, 0, 0, 0); // 设置时间为0时0分0秒0毫秒
return year;
},
//定义 提示组件内容
defineTooltipContent(params) {
const config = this.getToolTipConfig(params.seriesName, params.color);
const date = config.dateDayShow
? this.formatDay(params.data[0])
: this.formatYear(params.data[0]);
const tipData = params.data[3];
if (!tipData) return `<div class="tip-item">${date}</div>`;
const tipItem = tipData.tipItem;
let child = "";
if (tipItem && tipItem.length > 0) {
child = tipItem.reduce((pre, cur, idx) => {
let bg = "";
if (idx === tipItem.length - 1) {
bg = `<div class="tip-bottom tip-item" style="background-color:${config.color};color:${config.bottomFontColor}"><div>`;
} else {
bg = `<div class="tip-item"> <div ><span style='color: ${config.color}'>♦ </span>`;
}
return (pre += `${bg}${cur.label}</div><div>${cur.value}</div></div>`);
}, "");
}
return (
`<div class="tip-item"><div>${date}</div><div>${
config.titleShow ? tipData.title : ""
}</div></div>` + child
);
},
//定义标线的显示内容
defineMarkLineContent(label, date) {
return [`{a|${label}}`, `{b|${this.formatDay(date)}}`].join("\n");
},
},
};
</script>
<style lang="scss" scoped>
/deep/ .tip-item {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
padding: 2px 7px;
> div:last-of-type {
text-align: right;
padding-left: 20px;
}
}
/deep/ .tip-bottom {
color: white;
border-radius: 0 0 5px 5px;
padding: 4px 7px;
}
.engine-repair-plan-charts {
margin-left: -5px;
margin-right: -2px;
}
</style>
网友评论