动态添加删除echarts的series

# 直接帖答案

主要是 updateSeries 方法,通过 setOption 的第二个参数来选择合并,具体说明看这里替换合并案例,注意 id 为必填项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import * as echarts from "echarts";

var chartDom = document.getElementById("main");
var myChart = echarts.init(chartDom);
var option;

option = {
title: {
text: "Stacked Line",
},
tooltip: {
trigger: "axis",
},
legend: {
data: ["Email", "Union Ads", "Video Ads", "Direct", "Search Engine"],
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
},
toolbox: {
feature: {
saveAsImage: {},
},
},
xAxis: {
type: "category",
boundaryGap: false,
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
},
yAxis: {
type: "value",
},
series: [
{
name: "Email",
id: "email",
type: "line",
stack: "Total",
data: [120, 132, 101, 134, 90, 230, 210],
},
{
name: "Union Ads",
id: "unionAds",
type: "line",
stack: "Total",
data: [220, 182, 191, 234, 290, 330, 310],
},
],
};

option && myChart.setOption(option);

function updateSeries() {
const series = [
{
name: "Email",
id: "email",
type: "line",
stack: "Total",
data: [120, 132, 101, 134, 90, 230, 210],
},
{
name: "Direct",
id: "Direct",
type: "line",
stack: "Total",
data: [320, 332, 301, 334, 390, 330, 320],
},
];

myChart.setOption(
{
series,
},
{ replaceMerge: ["series"] }
);
}

# 前言

今天接到一个需求,通过给表格的列名添加多选框,选择一列数据就给 echarts 添加一个 serie,来实现动态加载

# 实际操作

当我实际做的时候发现下面这样并不管用

1
2
3
myChart.setOption({
series:newSeries,
});

发现它可以新增但并不会删除,于是乎就想到了图例,通过图例的选中状态可以控制折线的显示跟隐藏,然后再通过修改 data 来删除图例。于是就有了下面的代码,这样做是可行的,但是在删除的时候折线虽然没有了,图例也没有了但是 series 的数据还是存在的,总觉得不合适

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
<template>
<div class="data-display">
<div v-if="showEchart" ref="echartRef" class="echart"></div>
<div v-if="showTable" class="table" :style="{
borderRadius: 'var(--el-border-radius-base)'
}">
<el-auto-resizer>
<template #default="{ height, width }">
<el-table-v2 :columns="columns" :data="tableData" :width="width" :height="height" fixed />
</template>
</el-auto-resizer>
</div>
</div>
</template>

<script setup lang="ts">
import { h, onMounted, ref } from 'vue';
import { init, EChartsType, EChartsOption, SeriesOption, DataZoomComponentOption } from 'echarts';
import { Column, ElCheckbox } from 'element-plus';
import { Alignment } from 'element-plus/es/components/table-v2/src/constants';

interface DataDisplay {
times?: Array<string>;
datas: Array<DisplayValue>;
}

interface DisplayValue {
name: string;
unit: string;
values: number[];
value: number;
}

interface ColumnsInfo {
isChecked: boolean;
max?: number;
min?: number;
difference?: number;
unit?: string;
name: string;
}

defineOptions({
name: 'data-display'
});

const data: DataDisplay = {
times: Array.from({ length: 21 }, (v, k) => k + 1),
datas: [
{
name: 'test1',
unit: 'u1',
values: Array.from({ length: 21 }, () => Math.floor(Math.random() * 10000))
},
{
name: 'test2',
unit: 'u2',
values: Array.from({ length: 21 }, () => Math.floor(Math.random() * 10))
},
{
name: 'test3',
unit: 'u3',
values: Array.from({ length: 21 }, () => Math.floor(Math.random() * 1000))
},
{
name: 'test4',
unit: 'u4',
values: Array.from({ length: 21 }, () => Math.floor(Math.random() * 10000))
},
{
name: 'test5',
unit: 'u5',
values: Array.from({ length: 21 }, () => Math.floor(Math.random() * 100))
},
{
name: 'test6',
unit: 'u6',
values: Array.from({ length: 21 }, () => Math.floor(Math.random() * 100))
},
{
name: 'test7',
unit: 'u6',
values: Array.from({ length: 21 }, () => Math.floor(Math.random() * 100))
},
{
name: 'test8',
unit: 'u6',
values: Array.from({ length: 21 }, () => Math.floor(Math.random() * 100))
},
{
name: 'test9',
unit: 'u6',
values: Array.from({ length: 21 }, () => Math.floor(Math.random() * 100))
},
{
name: 'test10',
unit: 'u6',
values: Array.from({ length: 21 }, () => Math.floor(Math.random() * 100))
},
{
name: 'test11',
unit: 'u6',
values: Array.from({ length: 21 }, () => Math.floor(Math.random() * 100))
},
{
name: 'test12',
unit: 'u6',
values: Array.from({ length: 21 }, () => Math.floor(Math.random() * 100))
}
]
}

const echartRef = ref<HTMLDivElement | null>(null);
let echart: EChartsType | null = null;
let echartOptions: EChartsOption = {
tooltip: {
trigger: 'axis',
formatter: params => {
let str = '';
params.forEach(item => {
const rowData = tableData.find(s => s.time == item.name);
const unit = columnsInfo.value.find(s => s.name === item.seriesName).unit;
str += `${item.marker}${item.seriesName}: ${rowData[item.seriesName]} ${unit}<br/>`;
});
return str;
}
},
legend: {
itemGap: 10
},
grid: {
top: '12%',
left: '1%',
right: '10%',
containLabel: true
},
xAxis: [
{
type: 'category',
data: data.times
}
],
yAxis: [
{
type: 'value',
max: 100,
min: 0,
axisLabel: {
formatter: value => {
// 保留小数点后两位
return value.toFixed(2);
}
}
}
],
dataZoom: [
{
type: 'slider',
show: true,
start: 94,
end: 100,
handleSize: 8
},
{
type: 'inside',
start: 94,
end: 100
}
]
};

const showEchart = ref<boolean>(true);
const showTable = ref<boolean>(false);

const columns = ref<Array<Column>>([]);
const tableData = [];

const columnsInfo = ref<Array<ColumnsInfo>>([]);

let currentDisplayColumn = [];

function initEchart() {
echart = init(echartRef.value);
if (data.times.length <= 10) {
echartOptions.dataZoom = null;
} else {
(echartOptions.dataZoom as DataZoomComponentOption[]).forEach(item => {
item.start = 100 - (10 / data.times.length) * 100;
item.end = 100;
});
}
echart.setOption(echartOptions);
}

function updateEchartSerise() {
const option = echart.getOption();
const checkList = columnsInfo.value.filter(s => s.isChecked).map(s => s.name);

const deleteList = currentDisplayColumn.filter(s => !checkList.includes(s));

const selected = option.legend[0].selected;
checkList.forEach(s => {
selected[s] = true;
});
deleteList.forEach(s => {
selected[s] = false;
});

currentDisplayColumn = checkList;

const series: Array<SeriesOption> = data.datas
.filter(s => checkList.includes(s.name))
.map(data => {
const option: SeriesOption = {
name: data.name,
type: 'line',
data: data.values.map(val => {
return ((val / columnsInfo.value.find(s => s.name === data.name).difference) * 100).toFixed(2);
})
};
return option;
});

echart.setOption({
series,
legend: { data: checkList, selected }
});
}

function initTable() {
const propNames = [];
const newData = data.datas.map(item => {
columnsInfo.value.push({
isChecked: false,
name: item.name
});

propNames.push(item.name);
return {
title: `${item.name}(${item.unit})`,
dataKey: item.name,
width: 150,
minWidth: 150,
align: Alignment.CENTER
};
});
columns.value = [
{
title: '时间',
dataKey: 'time',
width: 150,
align: Alignment.CENTER
},
...newData
];
columns.value.forEach(item => {
item.headerCellRenderer = ({ column }) => {
if (column.dataKey === 'time') {
return h('div', column.title);
}
return h(ElCheckbox, {
modelValue: columnsInfo.value.find(s => s.name === column.dataKey).isChecked,
label: column.title,
onChange: val => {
columnsInfo.value.find(s => s.name === column.dataKey).isChecked = val as boolean;
updateEchartSerise();
}
});
};
});

propNames.forEach((name, i) => {
const columnInfo = columnsInfo.value.find(s => s.name === name);
columnInfo.max = Math.max(...data.datas[i].values);
columnInfo.min = Math.min(...data.datas[i].values);
columnInfo.difference = Math.abs(columnInfo.max) + Math.abs(columnInfo.min);
columnInfo.unit = data.datas[i].unit;
data.times.forEach((time, index) => {
let rowData;
if (tableData.find(s => s.time === time)) {
rowData = tableData.find(s => s.time === time);
} else {
rowData = {
time
};
tableData.push(rowData);
}
rowData[name] = data.datas[i].values[index];
});
});

showTable.value = true;
}

onMounted(() => {
initTable();
if (showEchart.value) {
initEchart();
}
});
</script>

<style lang="scss" scoped>
.data-display {
height: 80vh;
padding: 0.25rem 0.5rem;

.echart {
width: 100%;
min-height: 18rem;
height: 50%;
}

.table {
width: 100%;
height: 40%;
border: 1px solid var(--el-border-color);
border-radius: 0;
}
}
</style>

于是就网上查怎么动态修改始终没有看到结果,还看到有说 series 数据没法删除,于是乎就去翻源码看看是不是真的,看源码才发现 setOption 方法有方法重载,第二个参数可以 SetOptionOpts 类型就去研究了一下,于是乎就有了下面的版本的 updateEchartSeries 方法,简简修改了两行配置代码就能删除一大堆逻辑代码,顿时间就舒服了😋

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function updateEchartSeries() {
const checkList = columnsInfo.value.filter(s => s.isChecked).map(s => s.name);
const series: Array<SeriesOption> = data.datas
.filter(s => checkList.includes(s.name))
.map(data => {
const option: SeriesOption = {
name: data.name,
type: 'line',
id: data.name,
data: data.values.map(val => {
return ((val / columnsInfo.value.find(s => s.name === data.name).difference) * 100).toFixed(2);
})
};
return option;
});

echart.setOption({
series,
}, { replaceMerge: ['series'] });

}


动态添加删除echarts的series
https://nierifeng.github.io/2024/04/13/如何通过表格来动态加载echart的数据/
作者
sugar
发布于
2024年4月13日
许可协议