一、项目介绍
1.1 项目介绍
本项目为一个报名系统,实现了基本的报名流程,功能完善,前后端皆有个人独立开发,功能相对不是特别难,但该有的功能还是都已经实现。
1.2 技术架构
主要技术栈: SpringBoot2 + Vue3 + MySQL8.0
1.3 主要功能
角色设计: 管理员/普通用户(报名者)
首页
数据信息
报名详情 / 报名表 / 黑名单 / 信息列表
权限设置
角色管理 / 用户管理
系统设置
修改密码
管理员默认拥有所有权限,而普通用户则只拥有 首页/ 数据信息(报名详情、报名表)/ 系统设置(修改密码)权限信息可以修改。
1.4 运行项目
前端:
# 安装依赖npm install# 启动开发服务器npm run dev
高德地图密钥配置:在MapContrainer.vue中配置高德地图的密钥 ,如果没有请先去高德地图开发者平台注册
window._AMapSecurityConfig = {securityJsCode: "你的安全密钥",};AMapLoader.load({key: "Web端开发者Key", // 申请好的Web端开发者Key,首次调用 load 时必填version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15plugins: ["AMap.Geolocation"], // 只使用 Geolocation 插件})
启动,访问 http://localhost:5173/
后端:
要启动你的项目,其他开发者需要按照以下步骤操作:
-
配置环境: 确保安装了以下环境和工具:
-
JDK 8 或更高版本
-
Maven
-
MySQL 数据库
-
-
配置数据库:
-
创建一个新的 MySQL 数据库。
-
在 `src/main/resources/application.yaml 文件中配置数据库连接信息:
-
-
安装依赖: 使用 Maven 安装项目依赖:
mvn clean install
-
运行项目,打开浏览器,访问 http://localhost:8085。
通过以上步骤,其他开发者应该能够成功启动并运行你的项目。
二、数据库设计
此项目共包含6张表; apply 报名表、 blacklist 黑名单、 information 报名信息表、menu 权限菜单表、 role角色表、 user 用户表。
各表设计如下:
-- 报名列表 -- auto-generated definitioncreate table apply(id int auto_incrementprimary key,type_id int not null,typename varchar(32) not null,is_delete int default 0 not null,description varchar(128) null,is_show int default 0 not null,begin_time datetime not null,end_time datetime not null)comment '报名列表';-- 黑名单 -- auto-generated definitioncreate table blacklist(id int auto_incrementprimary key,user_id int not null,is_delete int default 0 not null,name varchar(24) not null comment '用户名',grade int not null,professional varchar(32) not null comment '专业',description varchar(128) null comment '描述',add_time datetime not null,school varchar(64) not null comment '学校')comment '黑名单';-- 报名信息表 -- auto-generated definitioncreate table information(id int auto_incrementprimary key,user_id int not null comment '用户id',name varchar(32) not null,apply_id int not null,is_delete int default 0 not null,user_add_time datetime not null,school varchar(32) not null,professional varchar(32) default '其他' not null comment '专业',grades int not null comment '年级',phone varchar(32) not null,email varchar(128) not null,sex varchar(24) not null)comment '报名信息表';-- 权限列表 -- auto-generated definitioncreate table menu(id int auto_increment comment '主键id'primary key,name varchar(32) not null comment '菜单名称',path varchar(64) not null comment '路径',icon varchar(32) not null comment '菜单的图标',pid int default 0 not null)comment '权限列表';-- 用户角色列表 -- auto-generated definitioncreate table role(id int auto_increment comment '主键id'primary key,role_name varchar(32) not null comment '角色名称',is_delete int default 0 not null comment '0 启用, 1 禁用',description varchar(128) default '暂无具体描述' not null comment '描述',rights json null comment '角色所拥有的权限')comment '用户角色列表';-- 用户列表 -- auto-generated definitioncreate table user(id int auto_increment comment '主键id'primary key,user_name varchar(32) not null comment '用户名',password varchar(32) default '123' not null comment '密码',role_id int not null comment '角色ID',description varchar(64) default '暂无具体描述信息' not null comment '描述',is_delete int default 0 not null comment '是否已经被删除,0 : 启用, 1:已经被删除')comment '用户列表';
三、后端
3.1 项目资源一览:
3.2 Jwt的使用
配置Jwt的主要过程如下:
3.2.1 引入依赖/ 配置参数
<!-- JWT --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.19.2</version></dependency><!--jdk大于8时需要添加--><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId></dependency>
在application.yaml中配置:
config:jwt:secret: 123456 # 密钥expire: 36000 # 过期时间header: token # 请求头名称
3.2.2 创建JwtConfig配置类
package com.registration.registration_system.config;import io.jsonwebtoken.Claims;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;import java.util.Date;@ConfigurationProperties(prefix = "config.jwt", ignoreUnknownFields = true)@Component@Datapublic class JwtConfig {private String secret;private long expire;private String header;// 生成tokenpublic String createToken(String subject) {// 当前时间与过期时间Date now = new Date();Date expireDate = new Date(now.getTime() + expire*1000);return Jwts.builder().setHeaderParam("typ", "JWT").setSubject(subject) // 设置主题.setIssuedAt(now) // 设置签发时间.setExpiration(expireDate) // 设置过期时间.signWith(SignatureAlgorithm.HS512, secret) // 设置签名.compact();}// 解析tokenpublic Claims getTokenClaims(String token) {try {return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();} catch (Exception e) {throw new RuntimeException(e);}}// 获取token中的用户名public String getUsernameFromToken(String token) {Claims claims = getTokenClaims(token);if (claims != null) {return claims.getSubject();}return null;}}
3.2.3 创建一个拦截器 TokenInterceptor,添加 JwtConfig配置类
package com.registration.registration_system.config;import lombok.AllArgsConstructor;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils;import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@Component@AllArgsConstructorpublic class TokenInterceptor extends HandlerInterceptorAdapter {private final JwtConfig jwtConfig;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 获取请求头中的tokenString token = request.getHeader(jwtConfig.getHeader());// 如果token为空if(StringUtils.isEmpty(token)) {sendError(response, "token为空");return false;}// 解析tokentry {jwtConfig.getTokenClaims(token);} catch (Exception e) {sendError(response, "token过期或不正确,请重新登录");return false;}return true;}// 返回错误信息private void sendError(HttpServletResponse response, String message) {response.setStatus(401);response.setContentType("application/json;charset=UTF-8");response.setCharacterEncoding("UTF-8");try {response.getWriter().write(message);} catch (Exception e) {e.printStackTrace();}}}
3.2.4 添加自定义的拦截器到webConfig配置类中
package com.registration.registration_system.config;import lombok.AllArgsConstructor;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.CorsRegistry;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration@AllArgsConstructorpublic class WebConfig implements WebMvcConfigurer {private final TokenInterceptor tokenInterceptor;/*** 拦截器配置* @param registry*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(tokenInterceptor).addPathPatterns("/**").excludePathPatterns("/adminapi/user/login/**");}/*** 跨域配置* @param registry*/@Overridepublic void addCorsMappings(CorsRegistry registry) {WebMvcConfigurer.super.addCorsMappings(registry);}}
到此,就已经配置好了jwt,使用示例如下,我们希望在调用登录接口的时候生成token并返回,主要代码可以在serviceImpl中实现:
/*** 登录* @param user* @return*/@Transactional@Overridepublic UserVo login(User user) {User new_user = userMapper.selectUser(user);if (new_user == null) {return null;} else {// 创建voUserVo vo = new UserVo();BeanUtils.copyProperties(new_user, vo);// 生成tokenString token = jwtConfig.createToken(new_user.getUserName());vo.setToken(token);// 查询roleRole role = new Role();role.setId(new_user.getRoleID());List<Role> roleList = roleMapper.selectRoleList(role);// 设置role为roleList的第一个vo.setRole(roleList.get(0));return vo;}}
3.3 PageHelper 分页插件
3.3.1 引入依赖
<!--分页插件--><dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.6</version></dependency><dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifactId><version>4.2</version></dependency>
3.3.2 使用
/*** 获取报名列表* @return*/@Overridepublic PageInfo<Apply> getApplyList(ApplyDto applyDto) {PageHelper.startPage(applyDto.getPageNum(), applyDto.getPageSize());Apply apply = new Apply();BeanUtils.copyProperties(applyDto, apply);List<Apply> applyList = applyMapper.selectApplyList(apply);return new PageInfo<>(applyList);}
分页插件的使用实际上非常简单,
首先,从前端接收到pageNum和PageSize之后,我们在serviceImpl层调用mapper接口之前,调用PageHelper.startPage来设置查询的数量和当前页。
然后我们正常进去查询,代码中所示是因为要有符合mapper接口的参数传入所以重新new了一个apply。
最后,返回一个 PageInfo<>类型的对象。
3.4 实现数据表导出成excel格式
/*** 导出信息* @param informationDto* @param response* @throws IOException*/@Overridepublic void exportInformation(InformationDto informationDto, HttpServletResponse response) throws IOException {// 创建一个 InformationVo 对象,并将 informationDto 的属性复制到该对象中InformationVo vo = new InformationVo();BeanUtils.copyProperties(informationDto, vo);// 创建一个空的列表,用于存储导出的数据List<Map<String, Object>> list = CollUtil.newArrayList();// 从数据库中查询信息列表List<InformationVo> infoList = informationMapper.selectInfoList(vo);// 遍历查询到的信息列表,将每条信息转换为 Map 并添加到 list 中for (InformationVo info : infoList) {Map<String, Object> row1 = new LinkedHashMap<>();row1.put("用户编号", info.getUserId());row1.put("姓名", info.getName());row1.put("性别", info.getSex());row1.put("参报项目", info.getTypeName());row1.put("报名时间", info.getUserAddTime());row1.put("学校", info.getSchool());row1.put("年级", info.getGrades());row1.put("专业", info.getProfessional());row1.put("手机号码", info.getPhone());row1.put("邮箱", info.getEmail());// 将每条信息的 Map 添加到 list 中list.add(row1);}// 创建一个 ExcelWriter 对象,用于写入 Excel 文件ExcelWriter writer = ExcelUtil.getWriter(true);// 一次性写出内容,强制输出标题writer.write(list, true);// 设置响应头,指定内容类型和文件名response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");String fileName = URLEncoder.encode("下载信息", "UTF-8");response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");// 获取响应的输出流ServletOutputStream out = response.getOutputStream();// 将 ExcelWriter 中的数据刷新到输出流中writer.flush(out, true);// 关闭 ExcelWriter,释放内存writer.close();// 关闭输出流IoUtil.close(System.out);}
Controller层接收一个额外参数response,然后我们在Service实现类中,先查询需要导入的数据,然后生成表头,最后写入Excel文件。
四、前端
本节主要记录在进行前端开发时所用到的一些插件
4.1 js-file-download
js-file-download
是一个用于在浏览器中触发文件下载的 JavaScript 库。它可以方便地将数据(如文本、JSON、二进制数据等)下载为文件,而无需服务器端的支持。这个库非常适合用于前端应用程序中需要生成和下载文件的场景。
4.1.1安装
npm install js-file-download# 或者yarn add js-file-download
4.2.2 使用
<template><div><el-button type="success" @click="handleExport">导出<el-icon><Download /></el-icon></el-button></div></template><script setup>import fileDownload from 'js-file-download';const handleExport = async () => {try {const res = await axios.get(`/adminapi/user/export?userName=${searchValue.value.userName}&roleName=${searchValue.value.roleName}`,{responseType: "blob",});// console.log(res);fileDownload(res.data,"注册用户列表" + new Date().toLocaleString() + ".xlsx");ElMessage.success("导出成功");} catch (error) {ElMessage.error("导出失败");}};</script><style scoped>/* 添加你的样式 */</style>
这段代码使用 axios
发送 GET 请求以获取用户列表的 Excel 文件,并使用 js-file-download
库在浏览器中触发文件下载,同时显示导出成功或失败的消息。
4.2 tsparticles
tsparticles
是一个用于创建和管理粒子动画效果的 JavaScript 库。它可以在网页上生成各种粒子效果,如雪花、星空、气泡等,适用于背景动画、交互效果等场景。这个库非常灵活,支持多种配置和自定义效果。
4.2.1 安装
npm install tsparticles# 或者yarn add tsparticles
4.2.2 使用
<template><vue-particlesid="tsparticles"@particles-loaded="particlesLoaded":options="tsOption"/></template><script setup>import tsOption from "@/utils/tspartice.js";// 粒子效果const particlesLoaded = async (container) => {console.log("Particles container loaded", container);};</script><style scoped></style>
其中,tspartice.js文件 (由于内容过多显示部分):
const option = {autoPlay: true,background: {color: {value: "#043564",},image: "url('./bg.png')",position: "0 50%",repeat: "no-repeat",size: "100%",opacity: 1,},backgroundMask: {composite: "destination-out",cover: {color: {value: "#fff",},opacity: 1,},enable: false,},clear: true,defaultThemes: {},delay: 0,fullScreen: {enable: true,zIndex: 0,},...};export default option;
4.3 dayjs
dayjs
是一个轻量级的 JavaScript 日期库,具有与 Moment.js
类似的 API,但体积更小,性能更高。它用于解析、验证、操作和显示日期和时间。
-
解析日期:支持多种日期格式的解析。
-
格式化日期:可以将日期格式化为各种字符串格式。
-
操作日期:支持日期的加减操作。
-
本地化:支持多种语言的本地化。
-
插件系统:通过插件扩展功能,如相对时间、UTC、时区等。
4.3.1 安装
npm install dayjs# 或者yarn add dayjs
4.3.2 使用
import dayjs from 'dayjs';// 解析日期const now = dayjs();console.log(now.format()); // 当前日期和时间// 格式化日期const formattedDate = now.format('YYYY-MM-DD');console.log(formattedDate); // 例如:2023-10-01// 操作日期const nextWeek = now.add(7, 'day');console.log(nextWeek.format('YYYY-MM-DD')); // 例如:2023-10-08// 本地化import 'dayjs/locale/zh-cn';dayjs.locale('zh-cn');console.log(now.format('MMMM D, YYYY')); // 例如:十月 1, 2023
4.4 其他插件
4.4.1 ECharts
主要功能: ECharts 是一个基于 JavaScript 的图表库,支持多种图表类型(如折线图、柱状图、饼图等),并提供丰富的交互功能。适合用于可视化数据展示。
安装方法:
npm install echarts --save
4.4.2 Element Plus 和 @element-plus/icons-vue
主要功能:
-
Element Plus: 一个基于 Vue 3 的 UI 组件库,提供了一系列高质量的组件,帮助开发者快速构建用户界面。
-
@element-plus/icons-vue: 提供 Element Plus 组件库的图标集,方便在项目中使用。
安装方法:
npm install element-plus --savenpm install @element-plus/icons-vue --save
4.4.3 Vue Router
主要功能: Vue Router 是 Vue.js 官方的路由管理器,允许开发者创建单页应用程序(SPA)时实现页面导航。支持动态路由匹配、嵌套路由等功能。
安装方法:
npm install vue-router@4 --save
4.4.4 Pinia 和 pinia-plugin-persistedstate
主要功能:
-
Pinia: Vue 3 的状态管理库,提供了简单易用的 API 和类型支持,成为 Vuex 的替代品。
-
pinia-plugin-persistedstate: Pinia 的一个插件,用于将状态持久化存储到本地存储或其他存储方案,以便在页面刷新后保持状态。
安装方法:
npm install pinia --savenpm install pinia-plugin-persistedstate --save
4.4.5 @amap/amap-jsapi-loader
主要功能: AMap JavaScript API Loader 是一个加载高德地图 JavaScript API 的工具,简化了 API 的引入和使用,使得开发者能够方便地在 Vue 项目中集成高德地图服务。
安装方法:
npm install @amap/amap-jsapi-loader --save