⭐ 作者简介:码上言
⭐ 代表教程:Spring Boot + vue-element 开发个人博客项目实战教程
⭐专栏内容:个人博客系统
⭐我的文档网站:http://xyhwh-nav.cn/
后端代码gitee地址:https://gitee.com/whxyh/personal_blog
前端代码gitee地址:https://gitee.com/whxyh/personal_vue
项目部署视频
https://www.bilibili.com/video/BV1sg4y1A7Kv/?vd_source=dc7bf298d3c608d281c16239b3f5167b
文章目录
前言
人生若只如初见,何事秋风悲画扇。写到这里,真的要说再见了,这个是本博客教程的最后一篇,真的是最后一篇了!感谢各位小伙伴们的陪伴和支持,教程时间拉的很长,中间也想过放弃,但最终我还是坚持写完了本篇教程,也算是有始有终。本教程是全部免费分享给各位小伙伴和需要学习的同学们。由于个人技术和时间有限,教程写的不是很好,但我初心是想写一个很基础的项目教程,稍微有点基础的都可以学会。在这期间也认识了很多的小伙伴,有些小伙伴用来当做自己的毕设和课设等,我免费提供了技术支持,不收费用的(在有时间的情况下),在这里非常感谢大家对我的认可和支持。
希望大家再未来的日子里,继续追逐自己的梦想,勇敢地面对生活中的挑战。
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