SpringBoot+Vue3实现echarts数据统计

统计那些数据

1.统计不同分类下用户发布旅游攻略帖子的数量(反映出:大家比较喜欢去哪类的旅游景点)饼图

统计出的数据要有价值

2.统计不同用户发布帖子数量Top5 (反映出: 平台活跃用户Top5) 柱状图

3.统计最近一周每天平台用户发布的贴子数量(反映出:每天的活跃情况) 折线图

统计是解决一些实际数据的整合或者反馈出一些现象.所以它要具有一些实用的意义。

如何去熟悉一个统计图

1.去官网体验一下

2.改一改它的数据,找到该统计图所需要的数据在哪个地方

3.分析该数据的结构(数据结构)重要!!

饼图数据部分的数据结构:

1
2
3
4
5
data: [
{ value: 1048, name: '风景名胜' },
{ value: 735, name: '历史古迹' },
{ value: 580, name: '人文景观' },
],

前端: [] 表示一个数组(对应多条数据) , {} 表示一个对象(对应一个数据)

后端:List 某个对象或者是Map<key,value>

该数据结构从后端返回的数据应该是一个什么样子的呢?List` 或 List<Map<key,value>>

4.后台接口封装该数据所需要的数据

1.echarts官网

官网: 快速上手 - 使用手册 - Apache ECharts

2.echarts 安装与实用

cd vue

npm install echarts --save

import * as echarts from “echarts”;

3.数据结构

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
// 饼图 在配置项哪里可以配
let pieOptions = {
title: {
text: '不同分类下用户发布旅游攻略帖子的数量',
subtext: '数据统计维度:攻略分类',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a}<br/>{b}:{c} ({d}%)' //配置项
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: '数据占比', //鼠标移上去显示内容
type: 'pie',
radius: '50%',
center: ['50%','60%'],
data: [
{ value: 1048, name: '风景名胜' },
{ value: 735, name: '历史古迹' },
{ value: 580, name: '人文景观' },
],
// emphasis: {
// itemStyle: {
// shadowBlur: 10,
// shadowOffsetX: 0,
// shadowColor: 'rgba(0, 0, 0, 0.5)'
// }
// }
}
]
};


放在Home.vue下

问题处理:说明dom没法初始化,onMount是等页面dom元素加载完毕之后开始执行

image-20250307204918091.png

1
2
3
4
5
6
7
8
9
const loadPie = () => {
let chartDom = document.getElementById('pie');
let myChart = echarts.init(chartDom);
myChart.setOption(pieOptions);
}
onMounted(()=>{
loadPie()
})

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
@RestController
@RequestMapping("/echarts")
public class EchartsController {

@Resource
public CategoryService categoryService;

@Resource
public IntroductionService introductionService;

@RequestMapping("/pie")
public Result pie() {
List<Map<String,Object>>list = new ArrayList<>();
//思路: 查询出不同的分类信息
List<Category> categories = categoryService.selectAll(new Category());
//根据不同分类查询有多少篇文章
// 查询出所有的帖子信息
List<Introduction> introductions = introductionService.selectAll(new Introduction());
for (Category category : categories) {
long count = introductions.stream().filter(x -> category.getId().equals(x.getCategoryId())).count();//category.getId()是为了防止后面哪个为空
Map<String,Object> map = new HashMap<>();
map.put("name",category.getTitle());
map.put("value",count);
list.add(map);
}
return Result.success(list);
}

}

柱状图

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
// 柱状图
let barOptions = {
title: {
text: '不同用户发布帖子数量Top5',
subtext: '统计维度,用户昵称',
left: 'center'
},
// 1. 扩大底部边距(关键步骤)
// 将 grid.bottom 从 30% 增加到 40%,为旋转后的标签预留更多空间:
grid: { //--------------增加这个属性 bottom就是离底部的距离
top: '20%',
bottom: '30%'
},
legend: {
orient: 'vertical',
left: 'left'
},
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
name: '用户昵称',
axisLabel: {
// show: true, // 是否显示刻度标签,默认显示
interval: 0, // 坐标轴刻度标签的显示间隔,在类目轴中有效;默认会采用标签不重叠的策略间隔显示标签;可以设置成0强制显示所有标签;如果设置为1,表示『隔一个标签显示一个标签』,如果值为2,表示隔两个标签显示一个标签
rotate: -60, // 刻度标签旋转的角度,在类目轴的类目标签显示不下的时候可以通过旋转防止标签之间重叠;旋转的角度从-90度到90度
inside: false, // 刻度标签是否朝内,默认朝外
margin: 30, // 刻度标签与轴线之间的距离
align: 'right',
verticalAlign: 'middle',
fontSize: 12
},
nameLocation: "center",
nameGap: 35
},
yAxis: {
type: 'value',
name: '攻略数量', // 注意这里的注释是中文
},
tooltip: {
trigger: 'item',
},
series: [
{
data: [120, 280, 150, 80, 70, 110, 130], // 示例数据:横坐标维度对应的值(纵坐标)
type: 'bar',
itemStyle: {
// 其他配置项
normal: {
color: function () {
return "#" + Math.floor(Math.random()*(256*256*256-1)).toString(16);
}
}
}
}
],
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const loadBar = () => {
request.get("/echarts/bar").then(res=>{
if(res.code === '200') {
let chartDom = document.getElementById('bar');
let myChart = echarts.init(chartDom);
barOptions.xAxis.data = res.data.xAxis
barOptions.series[0].data = res.data.yAxis
myChart.setOption(barOptions);
}
})

// let chartDom = document.getElementById('bar');
// let myChart = echarts.init(chartDom);
// myChart.setOption(barOptions);
}
onMounted(()=>{
loadPie()
loadBar()
})

后端:

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
@GetMapping("/bar")
public Result bar() {
Map<String,Object>resultMap = new HashMap<>();
List<String>xList = new ArrayList<>();
List<Long>yList = new ArrayList<>();
Map<String,Long> map = new HashMap<>();

// 查询所有的用户
List<User>users = userService.selectAll(new User());
// 查询出所有的帖子信息
List<Introduction>introductions = introductionService.selectAll(new Introduction());
for (User user : users) {
long count = introductions.stream().filter(x -> user.getId().equals(x.getUserId())).count();
// xList.add(user.getName());
// yList.add(count);
map.put(user.getName(),count);

}
//对map进行排序,按照value
LinkedHashMap<String, Long> sortedMap = map.entrySet().stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

for (String key : sortedMap.keySet()) {
xList.add(key);
yList.add(sortedMap.get(key));
}

//top 几的问题 截断一下
if(xList.size()>5 && yList.size()>5){
xList = xList.subList(0,5);
yList = yList.subList(0,5);
}



resultMap.put("xAxis",xList);
resultMap.put("yAxis",yList);
return Result.success(resultMap);
}

折线图和柱状图几乎是一样的 改一下type就行 bar->line

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
// 折现图
let lineOptions = {
title: {
text: '最近一周每天平台用户发布帖子的数量',
subtext: '统计维度: 最近一周',
left: 'center'
},
// 1. 扩大底部边距(关键步骤)
// 将 grid.bottom 从 30% 增加到 40%,为旋转后的标签预留更多空间:
grid: { //--------------增加这个属性 bottom就是离底部的距离
// top: '20%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
name: '日期',
},
yAxis: {
type: 'value',
name: '攻略数量', // 注意这里的注释是中文
},
series: [
{
data: [120, 280, 150, 80, 70, 110, 130], // 示例数据:横坐标维度对应的值(纵坐标)
type: 'line',
smooth: true,
markLine: {
data: [{type: 'average',name: '最近一周攻略发布数量平均值'}]
},
markPoint: {
data: [
{type: 'max',name: '最大值'},
{type: 'min',name: '最小值'},
]
}
}
],
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@GetMapping("/line")
public Result line() {
Map<String,Object>resultMap = new HashMap<>();
List<Long>yList = new ArrayList<>();
// 获取最近多少天的年月日list 这个可以复用
Date today = new Date();
DateTime start = DateUtil.offsetDay(today, -7);
List<String> xList = DateUtil.rangeToList(start, today, DateField.DAY_OF_YEAR).stream().map(DateUtil::formatDate).toList();

//把所有的帖子查出来
List<Introduction> introductions = introductionService.selectAll(new Introduction());
for (String day : xList) {
long count = introductions.stream().filter(x -> ObjectUtil.isNotEmpty(x.getTime()) && x.getTime().contains(day)).count();
yList.add(count);
}
// x.getTime().contains(day) 的作用是 筛选出时间字段(time)中包含指定日期(day)的帖子记录 ,从而统计某一天的帖子数量。具体解释如下:
//潜在问题
//格式一致性 :如果 getTime() 返回的时间格式与 day 的格式不一致(例如 getTime() 返回 "05/10/2023",而 day 是 "2023-10-05"),contains 会失效。
//误判风险 :如果 day 是 "10",而 getTime() 返回 "2023-10-05",contains 会匹配到月份部分,导致逻辑错误。
resultMap.put("xAxis",xList);
resultMap.put("yAxis",yList);
return Result.success(resultMap);
}