⭐ 作者简介:码上言
⭐ 代表教程:Spring Boot + vue-element 开发个人博客项目实战教程
⭐专栏内容:个人博客系统
⭐我的文档网站:http://xyhwh-nav.cn/
后端代码gitee地址:https://gitee.com/whxyh/personal_blog
前端代码gitee地址:https://gitee.com/whxyh/personal_vue
文章目录
- 前言
- 1、问题修改
- 2、首页统计
- 2.1、首页顶部统计
- 2.2、发文数量图表
- 2.3、文章分类占比
- 2.4、在线用户
- 2.5、获取公告
- 2.6、词云
- 完结
前言
人生若只如初见,何事秋风悲画扇。写到这里,真的要说再见了,这个是本博客教程的最后一篇,真的是最后一篇了!感谢各位小伙伴们的陪伴和支持,教程时间拉的很长,中间也想过放弃,但最终我还是坚持写完了本篇教程,也算是有始有终。本教程是全部免费分享给各位小伙伴和需要学习的同学们。
由于个人技术和时间有限,教程写的不是很好,但我初心是想写一个很基础的项目教程,稍微有点基础的都可以学会。在这期间也认识了很多的小伙伴,有些小伙伴用来当做自己的毕设和课设等,我免费提供了技术支持,不收费用的(在有时间的情况下),在这里非常感谢大家对我的认可和支持。
希望大家再未来的日子里,继续追逐自己的梦想,勇敢地面对生活中的挑战。
1、问题修改
在我测试的时候,发现添加文章时,选择添加分类报错,大家可以先把自己的项目跑起来测试一下是否报错。
场景:在添加文章的时候,不要选择已有的分类,要新添加一个分类,然后点击发布文章就会报错了。
定位:定位到保存文章的方法saveCategory
。
当我们没有从分类表中查询到分类,返回的是一个null,然后接下来又对这个null进行赋值所以报错。我们还需要再新new一个对象来存放新添加的分类即可。具体代码如下:
private Category saveCategory(ArticleInsertBO bo) {
if (StrUtil.isEmpty(bo.getCategoryName())) {
return null;
}
Category cat = new Category();
Category category = categoryService.getCategoryByName(bo.getCategoryName());
if (category == null && !ArticleArtStatusEnum.DRAFT.getStatus().equals(bo.getArtStatus())) {
cat.setCategoryName(bo.getCategoryName());
categoryService.saveCategory(cat);
}
return cat;
}
然后重启项目,再测试一下没有报错了。
暂时就发现了这一个错误,后边如果有再进行补充。
2、首页统计
现在其他的功能都已经完成,还差首页的数据是在页面写死的,如何能让图表和我们系统联动呢,接下来我们一起来学习一下。
在我们对接图表的时候,我们首先要了解到图表的数据情况,横坐标代表什么,纵坐标代表什么,只有了解图表要展示什么数据我们才能编写接口,要返回什么数据给前端。接下来我会带大家分析一下几种图表的接口开发,这个在以后的工作中都会用到。
首先将项目前后端运行起来,进入页面首页,在最顶部有四个展示数据:文章数量、分类数量、标签数量、用户数量等,现在看到的数据都是假的,我们要将这四个换成我们系统的数据,当发布一篇文章之后,这里的文章数量要**+1**才是我们想要的。
接下来的开发流程是,先来写一个模块的接口,然对接前端,然后再写下一个模块的接口进行对接。
2.1、首页顶部统计
顶部的统计需要4个数据即可,也就是说我们只要返给前端四个字段就可以了。先来创建一个返回类,定义4个字段。
在vo的包中新建一个StatisticsTopCountVO.java
类
package com.blog.personalblog.vo;
import lombok.Data;
/**
* @author: SuperMan
* @create: 2023-05-20
**/
@Data
public class StatisticsTopCountVO {
/**
* 文章总数
*/
private Integer articleCount;
/**
* 分类总数
*/
private Integer categoryCount;
/**
* 用户总数
*/
private Integer userCount;
/**
* 标签总数
*/
private Integer tagCount;
}
接着在service包中新建一个统计的接口StatisticsService.java
。
package com.blog.personalblog.service;
import com.blog.personalblog.vo.StatisticsTopCountVO;
/**
* @author: SuperMan
* @create: 2023-05-20
**/
public interface StatisticsService {
/**
* 首页顶部数据统计
* @return
*/
StatisticsTopCountVO getTopCount();
}
然后再来创建一个统计的实现类,这个大家都应该轻车熟路了,我下面就简单的介绍即可。
/**
* @author: SuperMan
* @create: 2023-05-20
**/
@Service
public class StatisticsServiceImpl implements StatisticsService {
@Resource
private StatisticsMapper statisticsMapper;
@Override
public StatisticsTopCountVO getTopCount() {
StatisticsTopCountVO topCount = statisticsMapper.getTopCount();
return topCount;
}
}
因为要查询数据库,所以还要创建一个StatisticsMapper.java
和StatisticsMapper.xml
@Repository
public interface StatisticsMapper {
StatisticsTopCountVO getTopCount();
}
xml:
注意:在xml中编写的sql,大家尽量现在自己的sql工具中运行一下看是否有错误,没错误的话在往xml中写,要不然xml中有错误不太好查找问题。
这里用到了多张表查询,一定要确保数据的准确。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.blog.personalblog.mapper.StatisticsMapper">
<resultMap id="BaseResultMap" type="com.blog.personalblog.vo.StatisticsTopCountVO">
<result column="article_count" property="articleCount" jdbcType="INTEGER" />
<result column="category_count" property="categoryCount" jdbcType="INTEGER" />
<result column="user_count" property="userCount" jdbcType="INTEGER" />
<result column="tag_count" property="tagCount" jdbcType="INTEGER" />
</resultMap>
<select id="getTopCount" resultMap="BaseResultMap">
SELECT
(SELECT COUNT(*) FROM person_article) AS article_count,
(SELECT COUNT(*) FROM person_category) AS category_count,
(SELECT COUNT(*) FROM person_user) AS user_count,
(SELECT COUNT(*) FROM person_tag) AS tag_count
</select>
</mapper>
然后编写一个IndexController.java
api接口。
@Api(tags = "首页统计")
@RestController
@RequestMapping("/index")
public class IndexController {
@Resource
private StatisticsService statisticsService;
/**
* 顶部统计查询
* @return
*/
@ApiOperation(value = "首页顶部统计查询")
@PostMapping("/getTopCount")
@OperationLogSys(desc = "首页顶部统计查询", operationType = OperationType.SELECT)
public JsonResult<Object> getTopCount() {
StatisticsTopCountVO topCount = statisticsService.getTopCount();
return JsonResult.success(topCount);
}
}
后端的接口已经完成,再重新运行一下项目。之后打开前端项目,对接接口。
先在/src/api
目录下新建一个接口管理的文件:index.js
然后将后端的接口对接,注意没有请求的参数。直接地址和请求方式即可。
import request from '@/utils/request'
export function indexTopCount() {
return request({
url: '/index/getTopCount',
method: 'post'
})
}
然后在/views/dashboard/components
目录下打开PanelGroup.vue
。先将接口的方法引进来。
import { indexTopCount } from '@/api/index'
然后添加data()
方法和created()
方法。
data() {
return {
list: [],
listLoading: true,
}
},
created() {
this.getTopCount()
},
在methods方法中,添加一个getTopCount()
方法。这里就拿到了后端接口返回的数据,list现在就有数据了,然后去将假数据换成list中的数据即可。
getTopCount() {
this.listLoading = true
indexTopCount().then(response => {
this.list = response.data
this.listLoading = false
})
}
将原来的数据删除,然后在el-row标签中添加:data="list"
,然后用list.的方式就可以获取到数据了。
<template>
<el-row :gutter="40" class="panel-group" :data="list">
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel">
<div class="card-panel-icon-wrapper icon-people">
<svg-icon icon-class="documentation" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">
文章数量
</div>
<div style="font-size: 20px">
{{list.articleCount}}
</div>
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel">
<div class="card-panel-icon-wrapper icon-message">
<svg-icon icon-class="component" class-name="card-panel-icon"/>
</div>
<div class="card-panel-description">
<div class="card-panel-text">
分类数量
</div>
<div style="font-size: 20px">
{{list.categoryCount}}
</div>
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel">
<div class="card-panel-icon-wrapper icon-money">
<svg-icon icon-class="icon" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">
标签数量
</div>
<div style="font-size: 20px">
{{list.tagCount}}
</div>
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel">
<div class="card-panel-icon-wrapper icon-shopping">
<svg-icon icon-class="people" class-name="card-panel-icon"/>
</div>
<div class="card-panel-description">
<div class="card-panel-text">
用户数量
</div>
<div style="font-size: 20px">
{{list.userCount}}
</div>
</div>
</div>
</el-col>
</el-row>
</template>
然后我们来看一下页面。此时就有了数据,也确实是我们数据库的真实数据。
2.2、发文数量图表
接下来要完成图表数据的接口,其实不难的,我先来教大家方法,然后再编写。先来分析一下这个图表,横坐标是日期,纵坐标是数量,然后去前端看一下图表的假数据是写在哪的,会发现一共写了两个地方,xAxis
是横坐标的数据,series
是数量,而且都是数组。这时就要想,我们返回的数据是怎么样的格式呢,首先返回的肯定是一个List类型的集合,List中添加的是多个对象格式的数据,对象中有两个字段,一个是日期,另一个是这一天的文章数量。
知道了这些,我们先来写接口,还是在vo中创建一个类:StatisticsBaseCountVO.java
package com.blog.personalblog.vo;
import lombok.Data;
/**
* @author: SuperMan
* @create: 2023-05-20
**/
@Data
public class StatisticsBaseCountVO {
/**
* 时间,例如:02-01
*/
private String date;
/**
* 条数
*/
private Long count;
public StatisticsBaseCountVO() {
}
public StatisticsBaseCountVO(String date, Long count) {
this.date = date;
this.count = count;
}
}
然后写接口,还是在StatisticsService中,包括接下来的所有统计的接口都会写在这个接口里,我下面就不再写了。
/**
* 文章近一周统计数据
* @return
*/
List<StatisticsBaseCountVO> getArticleCount();
接下来写实现类,这个可能稍微有点复杂,我还是结合代码进行分析。
首先先获取到文章数据,我这里全部查出来了,因为量不大,还是可以的。然后再根据创建时间进行过滤,采用的是Java8的新特性,大家可以去学习学习:https://blog.csdn.net/m0_51014049/article/details/129600237
LocalDate today = LocalDate.now();
//过滤近7天数据
List<Article> articles = articleService.getAll();
List<Article> articlesInLastWeek = articles.stream()
.filter(article -> article.getCreateTime().toLocalDate().isAfter(today.minusDays(7)))
.collect(Collectors.toList());
此时拿到了近七天的文章数据,然后根据时间进行分组统计。返回的是一个Map格式的,其中key是时间,value是数量。
Map<LocalDate, Long> map = articlesInLastWeek.stream()
.collect(Collectors.groupingBy(article -> article.getCreateTime().toLocalDate(), Collectors.counting()));
然后根据map,进行前端返回格式的组装,有的日期一天都没有发文,则默认给它赋值为0,要确保7天的数据都有。用到了putIfAbsent() 方法会先判断指定的键(key)是否存在,不存在则将键/值对插入到 HashMap 中。
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("MM-dd");
for (int i = 0; i < 7; i++) {
LocalDate date = today.minusDays(i);
map.putIfAbsent(date, 0L);
StatisticsBaseCountVO articleCount = new StatisticsBaseCountVO(date.format(dateFormat), map.get(date));
list.add(articleCount);
}
然后在排序一下
list = list.stream().sorted(Comparator.comparing(StatisticsBaseCountVO::getDate)).collect(Collectors.toList());
完整代码:
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("MM-dd");
@Override
public List<StatisticsBaseCountVO> getArticleCount() {
List<StatisticsBaseCountVO> list = new ArrayList<>();
LocalDate today = LocalDate.now();
//过滤近7天数据
List<Article> articles = articleService.getAll();
List<Article> articlesInLastWeek = articles.stream()
.filter(article -> article.getCreateTime().toLocalDate().isAfter(today.minusDays(7)))
.collect(Collectors.toList());
Map<LocalDate, Long> map = articlesInLastWeek.stream()
.collect(Collectors.groupingBy(article -> article.getCreateTime().toLocalDate(), Collectors.counting()));
for (int i = 0; i < 7; i++) {
LocalDate date = today.minusDays(i);
map.putIfAbsent(date, 0L);
StatisticsBaseCountVO articleCount = new StatisticsBaseCountVO(date.format(dateFormat), map.get(date));
list.add(articleCount);
}
//排序
list = list.stream().sorted(Comparator.comparing(StatisticsBaseCountVO::getDate)).collect(Collectors.toList());
return list;
}
这个获取全部的的文章的接口要写一下,之前的是分页的,这个不分页,我将代码展示一下就不多介绍了,具体可以看我源码。
List<Article> getAll();
@Override
public List<Article> getAll() {
List<Article> all = articleMapper.findAll();
return all;
}
接着去controller中添加一个接口。
/**
* 近一周发文的数量
* @return
*/
@ApiOperation(value = "近一周发文的数量")
@PostMapping("/getWeekNum")
@OperationLogSys(desc = "近一周发文的数量", operationType = OperationType.SELECT)
public JsonResult<Object> getWeekNum() {
List<StatisticsBaseCountVO> list = statisticsService.getArticleCount();
return JsonResult.success(list);
}
打开前端代码,还是在index.js中先添加接口。
export function getWeekArticleCount() {
return request({
url: '/index/getWeekNum',
method: 'post'
})
}
然后打开BarChart.vue
文件,引入接口
import { getWeekArticleCount } from '@/api/index'
在data方法中定义一个list数组
data() {
return {
chart: null,
list: [],
}
},
接着改造一下mounted()这个声明周期的构造函数。获取后端返回的数据,赋值给list。
mounted() {
getWeekArticleCount().then(res => {
if(res.code === 200) {
this.list = res.data;
this.initChart();
} else {
this.$message({
type: 'error',
message: res.msg
});
}
})
},
然后找到xAxis
数组,将里面的data日期数据换成我们后端返回的日志。
data: this.list.map(item => item.date),
如果你不知道map是什么,为什么这样写就可以,你可以去搜一下list的map()用法,我这里不再说明。
同样下面的series
数组中的data数据取list中的count数据。
series: [
{
name: '文章数',
type: 'bar',
barWidth: '60%',
data: this.list.map(item => item.count)
}
]
修改完成之后,打开页面查看是否有数据,如果没有的话可以先发布一篇文章试一下。
2.3、文章分类占比
从页面上可以看出,这个是一个饼状图,代表着每个数据的占比,我这里写的是文章标签的占比,这一个标签有多少篇文章,后续可根据自己的需求进行修改,这里教给大家的是开发的流程。从饼状图上可以看出,一共需要两个数据,一个是标签的名称,另一个是对应标签的文章数。那么我们的接口只要将这两个数据返回即可,注意:是多条数据,应该返回一个List集合。
还是和之前一样,后端写接口、实现,我这里不再多写,我只将代码展示一下,具体的可以去gitee上下载代码看。
新建StatisticsTagCountVO.java
package com.blog.personalblog.vo;
import lombok.Data;
/**
* @author: SuperMan
* @create: 2023-05-20
**/
@Data
public class StatisticsTagCountVO {
/**
* 标签名称
*/
private String tagName;
/**
* 标签总数
*/
private Integer tagCount;
}
接口:
/**
* 获取标签数据
* @return
*/
List<StatisticsTagCountVO> getTagCount();
实现类:
@Override
public List<StatisticsTagCountVO> getTagCount() {
List<StatisticsTagCountVO> tagCount = statisticsMapper.getTagCount();
return tagCount;
}
Mapper:
List<StatisticsTagCountVO> getTagCount();
xml:
<select id="getTagCount" resultMap="BaseResultTagMap">
SELECT s.tag_name, COUNT(*) AS tag_count FROM person_article_tag a
left join person_tag s on a.tag_id = s.id
GROUP BY tag_id
</select>
controller:
/**
* 获取标签数据
* @return
*/
@ApiOperation(value = "获取标签数据")
@PostMapping("/getTagCount")
@OperationLogSys(desc = "获取标签数据", operationType = OperationType.SELECT)
public JsonResult<Object> getTagCount() {
List<StatisticsTagCountVO> tagCount = statisticsService.getTagCount();
return JsonResult.success(tagCount);
}
前端页面,在index.js
中添加接口
export function getTagCount() {
return request({
url: '/index/getTagCount',
method: 'post'
})
}
在PieChart.vue
中引入
import { getTagCount } from '@/api/index'
然后再data中添加一个list数组
data() {
return {
chart: null,
list: []
}
},
获取后端数据,赋值给list。
这里要注意一下,原来的假数据是这种格式:{ value: 1048, name: 'Search Engine' }
,key和value对应,我们要将拿到的数据先进行改造一下。for循环了一下数据,然后定义了一个数组,然后在for循环中定义了一个Object对象,将对应的数据给Object对象,然后再添加到数组中。
mounted() {
getTagCount().then(res => {
if(res.code === 200) {
var getData = [];
for(let i = 0; i < res.data.length; i++) {
var obj = new Object();
obj.name = res.data[i].tagName;
obj.value = res.data[i].tagCount;
getData[i] = obj;
}
this.list = getData;
this.initChart();
} else {
this.$message({
type: 'error',
message: res.msg
});
}
})
},
将假数据替换成list
series: [
{
type: 'pie',
radius: '50%',
data: this.list,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
再去看页面就会有我们接口查出的数据绘制的图表了,如果没有图表生成,先看一下接口有没有数据。或者控制台看一下有没有报错信息等。
2.4、在线用户
这个图表是一个折线图,横坐标现在修改成每分钟数,纵坐标为在线人数。
开发流程:在线统计我们只做一个简单的统计流程,在我们登录的接口中,当登录完成之后,将数据放到统计的缓存中,然后定时去或者这个缓存的数据。再将分钟划分,拼装成返回给前端的数据。
先在统计中定义两个接口。一个登出,一个登录。
void login(String username, Long date);
void logout(String username, Long date);
然后在实现类中先来定一个全局的缓存,再实现这两个方法。
private Map<String, Long> users = new HashMap<>();
@Override
public void login(String username, Long date) {
users.put(username, date);
}
@Override
public void logout(String username, Long date) {
users.remove(username, date);
}
打开登录的接口,在登录完成之后,调用登录的接口。
@ApiOperation(value = "登录")
@PostMapping("/login")
@OperationLogSys(desc = "登录", operationType = OperationType.LOGIN)
public JsonResult<Object> login(@RequestBody LoginModel loginModel){
logger.info("{} 在请求登录! ", loginModel.getUsername());
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(loginModel.getUsername(), loginModel.getPassword(), false);
try {
subject.login(token);
Map<String, Object> ret = new HashedMap();
ret.put("token", subject.getSession().getId());
logger.info("{} login success", loginModel.getUsername());
getLoginInfoLog(loginModel, 0);
//修改上个登录的时间
User user = userService.getUserByUserName(loginModel.getUsername());
userService.updateLoginTime(user.getId());
//在线人数
statisticsService.login(user.getUserName(), System.currentTimeMillis());
。。。。。。
退出的接口:
@RequestMapping("/logout")
public JsonResult logout(){
User user=(User) SecurityUtils.getSubject().getPrincipal();
Subject subject = SecurityUtils.getSubject();
if(subject.isAuthenticated()) {
subject.logout();
}
//在线人数
statisticsService.logout(user.getUserName(), System.currentTimeMillis());
return JsonResult.success("退出登录");
}
这样统计的map就维护好了,接下来要统计每分钟的用户数,使用了@Scheduled
定时执行该方法。
/**
* key:时间,HH:mm
* value: 人数
*/
private Map<String, Long> countUser = new HashMap<>();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
@Scheduled(cron = "0 */1 * * * ?")
public void getOnlineUsers() {
long currentTime = System.currentTimeMillis();
Long count = 0L;
for (long loginTime : users.values()) {
if (currentTime - loginTime <= 60000) {
count++;
}
}
Date date= new Date(currentTime);
countUser.putIfAbsent(sdf.format(date), count);
}
此时的定时还没有效果,要在启动类的方法上加上@EnableScheduling
才可以。
在service中写一个在线用户统计的接口。
/**
* 获取在线人数
* @return
*/
List<StatisticsBaseCountVO> getOnline();
实现类:
实现类和文章统计的的日期划分差不多,只是这里划分为分钟。大家先自己研究一下代码吧,应该可以看懂的。
@Override
public List<StatisticsBaseCountVO> getOnline() {
Map<String, StatisticsBaseCountVO> map = new HashMap<>();
//分钟划分
Date date = new Date();
List<String> res = new ArrayList<>();
if (date != null) {
Calendar ca = Calendar.getInstance();
ca.setTime(date);
for (int i = 0; i < 60; i++) {
ca.add(Calendar.MINUTE, -1);
res.add(sdf.format(ca.getTime()));
}
}
countUser.forEach((key, v) -> {
StatisticsBaseCountVO baseCount = new StatisticsBaseCountVO();
baseCount.setDate(key);
baseCount.setCount(v);
map.put(key, baseCount);
});
res.forEach(m -> {
map.computeIfAbsent(m, k -> {
StatisticsBaseCountVO count = new StatisticsBaseCountVO(k, 0L);
return count;
});
});
List<StatisticsBaseCountVO> sort = CollUtil.sort(map.values(), Comparator.comparing(StatisticsBaseCountVO::getDate));
return sort;
}
然后写一个controller接口。
/**
* 在线人数
* @return
*/
@ApiOperation(value = "在线人数")
@PostMapping("/getOnline")
@OperationLogSys(desc = "在线人数", operationType = OperationType.SELECT)
public JsonResult<Object> getOnline() {
List<StatisticsBaseCountVO> online = statisticsService.getOnline();
return JsonResult.success(online);
}
前端的话和之前的文章的图表差不一样的,不会的可以去看我的项目代码,我这里只贴代码了。
export function getOnlineCount() {
return request({
url: '/index/getOnline',
method: 'post'
})
}
data() {
return {
chart: null,
list: [],
}
},
mounted() {
getOnlineCount().then(res => {
if(res.code === 200) {
this.list = res.data;
this.initChart();
} else {
this.$message({
type: 'error',
message: res.msg
});
}
})
},
绘制:
xAxis: {
type: 'category',
data: this.list.map(item => item.date)
},
series: [
{
data: this.list.map(item => item.count),
type: 'line',
smooth: true
}
]
2.5、获取公告
这个比较简单了,我们先来写一个controller接口,再来写实现的方法等。
/**
* 获取最新前5条公告
* @return
*/
@ApiOperation(value = "获取最新前5条公告")
@PostMapping("/getNoticeList")
@OperationLogSys(desc = "获取最新前5条公告", operationType = OperationType.SELECT)
public JsonResult<Object> getNoticeList() {
List<Notice> list = noticeService.getNoticeTopFive();
return JsonResult.success(list);
}
在公告的service中新增一个查询的方法。
/**
* 获取前5条公告
* @return
*/
List<Notice> getNoticeTopFive();
实现类:
@Override
public List<Notice> getNoticeTopFive() {
List<Notice> noticeList = noticeMapper.getNoticeTopFive();
return noticeList;
}
mapper:
List<Notice> getNoticeTopFive();
xml:
<select id="getNoticeTopFive" resultMap="BaseResultMap">
SELECT * FROM person_notice ORDER BY create_time DESC LIMIT 5;
</select>
前端对接也比较简单的。
export function getNoticeList() {
return request({
url: '/index/getNoticeList',
method: 'post'
})
}
在created的方法中添加查询的方法
import { getNoticeList } from '@/api/index'
created() {
getNoticeList().then(res => {
if(res.code === 200) {
this.list = res.data;
} else {
this.$message({
type: 'error',
message: res.msg
});
}
})
.......
将之前的假数据删除掉,然后改成以下的格式。
<el-collapse v-model="activeName" accordion>
<el-collapse-item v-for="(item,index) in list" :key="index">
<template slot="title">
{{item.noticeTitle}}
<i class="ssf ssf-colse" @click.stop="close(item,index)"></i>
</template>
<div>{{item.noticeContent}}</div>
</el-collapse-item>
</el-collapse>
再来看一下页面就会显示了。
2.6、词云
就剩下最后一个了,这个词云我是用的是标签的数据,在前端的页面中看到,每个属性都会有不同的背景颜色、字体颜色等,所以说我们后还要生成颜色返回给前端。
先来定一个对象:StatisticsWordCloudVO.java
package com.blog.personalblog.vo;
import lombok.Data;
/**
* @author: SuperMan
* @create: 2023-05-20
**/
@Data
public class StatisticsWordCloudVO {
/**
* 标签名称
*/
private String tagName;
/**
* 背景颜色
*/
private String bgColor;
/**
* 颜色
*/
private String color;
/**
* 数值
*/
private String value;
}
接口:
/**
* 获取词云数据
* @return
*/
@ApiOperation(value = "获取词云数据")
@PostMapping("/getWordCloud")
@OperationLogSys(desc = "获取词云数据", operationType = OperationType.SELECT)
public JsonResult<Object> getWordCloud() {
List<StatisticsWordCloudVO> wordCloud = statisticsService.getWordCloud();
return JsonResult.success(wordCloud);
}
定义service接口:
List<StatisticsWordCloudVO> getWordCloud();
实现类:
@Override
public List<StatisticsWordCloudVO> getWordCloud() {
List<StatisticsWordCloudVO> list = new ArrayList<>();
//获取全部标签
List<Tag> tags = tagService.getTagsByTagName(new TagBO());
tags.forEach(t -> {
int n = ((int) (Math.random() * (100 - 0))) + 0;
StatisticsWordCloudVO cloud = new StatisticsWordCloudVO();
Random rng = new Random();
int red = rng.nextInt(256);
int green = rng.nextInt(256);
int blue = rng.nextInt(256);
String colorString = String.format("rgb(%d, %d, %d, %.2f)", red, green, blue, 0.12f);
cloud.setBgColor(colorString);
cloud.setTagName(t.getTagName());
String hexColor = String.format("#%02X%02X%02X", red, green, blue);
cloud.setColor(hexColor);
cloud.setValue(String.valueOf(n));
list.add(cloud);
});
return list;
}
这里使用了String.format
进行拼装成rgb,这个词云以后用的比较少,大家先了解一下即可。
前端还是和之前一样,引入接口地址。
export function getWordCloud() {
return request({
url: '/index/getWordCloud',
method: 'post'
})
}
获取接口数据:
import { getWordCloud } from '@/api/index'
mounted () {
getWordCloud().then(res => {
if(res.code === 200) {
this.dataList = res.data;
} else {
this.$message({
type: 'error',
message: res.msg
});
}
})
页面展示也要改一下
<div class="cloud-box">
<span
v-for="(item, index) in dataList"
:key="index"
@click="getDataInfo(item)"
:style="{color:item.color,background:item.bgColor}"
>
{{ item.tagName }}
</span>
</div>
然后看一下页面是不是我们后端的数据展示。
完结
到这里真的要再见了,博客项目的全部教程都已经完结了,一共24篇文章,代码我已经全部上传到仓库中,在文章的最下面会有地址。一路走来坚持到现在感觉也挺有成就感的,能为大家带来一些技术上的入门和学习,已经感到很好了。感谢大家的一路陪伴,再见!
预告:新的项目教程已经在规划,会增加很多的知识点和难度,自动化部署、短信发送、权限管理等操作。欢迎大家来订阅。
代码地址:
Gitee:
后端地址:https://gitee.com/whxyh/personal_blog
前端地址:https://gitee.com/whxyh/personal_vue
GitHub:
后端地址:https://github.com/dawandou/personal_blog_pro
前端地址:https://github.com/dawandou/personal_blog_vue