先展示最后的效果图


/*
Navicat Premium Data Transfer
Source Server : localhost
Source Server Type : MySQL
Source Server Version : 80020
Source Host : localhost:3306
Source Schema : the_sound
Target Server Type : MySQL
Target Server Version : 80020
File Encoding : 65001
Date: 25/04/2022 20:02:49
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` int(0) NOT NULL AUTO_INCREMENT COMMENT 'id',
`username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户名',
`password` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '密码',
`nickname` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '昵称',
`email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '邮箱',
`phone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '电话',
`address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '地址',
`create_time` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间',
`avatar_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '头像',
`role` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '角色',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 45 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, 'admin', 'admin', '程序员青戈', 'admin@qq.com', '13988997788', '安徽合肥', '2022-01-22 21:10:27', 'http://localhost:9090/file/8d966b0e6cf84fe191a72a58b8293b23.png', 'ROLE_ADMIN');
INSERT INTO `sys_user` VALUES (16, 'xiaoqiao', 'xiaoqiao', '小乔', 'xiaoqiao@qq.com', '18009800098', '河北', '2022-02-26 22:10:14', NULL, 'ROLE_TEACHER');
INSERT INTO `sys_user` VALUES (18, 'hanxin', 'hanxin', '韩信', 'hanxin@qq.com', '17809827734', '山东', '2022-03-29 16:59:44', '', 'ROLE_STUDENT');
INSERT INTO `sys_user` VALUES (19, 'yase', 'yase', '亚瑟', 'yase@qq.com', '19708374665', '山西', '2022-04-29 16:59:44', '', 'ROLE_STUDENT');
INSERT INTO `sys_user` VALUES (20, 'lixin', 'lixin', '李信', 'lixin@qq.com', '18273645535', '福建', '2022-05-29 17:12:04', '2', 'ROLE_STUDENT');
INSERT INTO `sys_user` VALUES (25, 'anqila', 'anqila', '安琪拉', 'anqila@qq.com', '18277364554', '山东太原', '2022-06-08 17:00:47', NULL, 'ROLE_STUDENT');
INSERT INTO `sys_user` VALUES (26, 'daji', 'daji', '妲己', 'daji@qq.com', '18763466653', '山东日照', '2022-07-08 17:20:01', NULL, 'ROLE_STUDENT');
INSERT INTO `sys_user` VALUES (28, 'yvji', 'yvji', '虞姬', 'yvji@qq.com', '19876544474', '武汉', '2022-11-09 10:41:07', 'http://localhost:9090/file/7de0e50f915547539db12023cf997276.jpg', 'ROLE_STUDENT');
INSERT INTO `sys_user` VALUES (29, 'sunbin', 'sunbin', '孙膑', 'sunbin@qq.com', '19800987655', '山东', '2022-12-10 11:53:31', NULL, 'ROLE_STUDENT');
INSERT INTO `sys_user` VALUES (30, 'nezha', 'nezha', '哪吒', 'nezha@qq.com', '18099876778', '河北', '2022-04-19 20:53:57', NULL, NULL);
INSERT INTO `sys_user` VALUES (31, 'zhenji', 'zhenji', '甄姬', 'zhenji@qq.com', '19888765534', '山东', '2022-04-19 20:54:16', NULL, NULL);
INSERT INTO `sys_user` VALUES (32, 'tangseng', 'tangseng', '唐僧', 'tangseng@qq.com', '12999897767', '东土大唐', '2022-04-20 15:53:46', NULL, NULL);
INSERT INTO `sys_user` VALUES (33, 'zhubajie', 'zhubajie', '猪八戒', 'zhubajie@qq.com', '13387876655', '高老庄', '2022-04-20 15:56:02', NULL, NULL);
INSERT INTO `sys_user` VALUES (34, 'shaseng', 'shaseng', '沙僧', 'shaseng@qq.com', '18744332273', '流沙河', '2022-04-20 16:00:26', NULL, NULL);
INSERT INTO `sys_user` VALUES (35, 'bailongma', 'bailongma', '白龙马', 'bailongma@qq.com', '17787675844', '东海', '2022-04-20 16:01:55', NULL, NULL);
SET FOREIGN_KEY_CHECKS = 1;
/*
Navicat Premium Data Transfer
Source Server : localhost
Source Server Type : MySQL
Source Server Version : 80020
Source Host : localhost:3306
Source Schema : the_sound
Target Server Type : MySQL
Target Server Version : 80020
File Encoding : 65001
Date: 24/05/2022 09:47:56
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for sys_file
-- ----------------------------
DROP TABLE IF EXISTS `sys_file`;
CREATE TABLE `sys_file` (
`id` int(0) NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '文件名称',
`type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '文件类型',
`size` bigint(0) NULL DEFAULT NULL COMMENT '文件大小(kb)',
`url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '下载链接',
`md5` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '文件md5',
`is_delete` tinyint(1) NULL DEFAULT 0 COMMENT '是否删除',
`enable` tinyint(1) NULL DEFAULT 1 COMMENT '是否禁用链接',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 59 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_file
-- ----------------------------
INSERT INTO `sys_file` VALUES (53, 'cat01.jpg', 'jpg', 49, 'http://localhost:8081/file/d42af3dcbdf448619bca2a600ba06977.jpg', '69a342bd86a6391ab2f11d625d1be66c', 0, 1);
INSERT INTO `sys_file` VALUES (56, 'cat02.jpg', 'jpg', 48, 'http://localhost:8081/file/1a233affbf6f4c4c91e0150077084d76.jpg', '125f8475aafbea2e6aefa4afe1a0b8b0', 0, 1);
INSERT INTO `sys_file` VALUES (57, 'cat01.jpg', 'jpg', 49, 'http://localhost:8081/file/d42af3dcbdf448619bca2a600ba06977.jpg', '69a342bd86a6391ab2f11d625d1be66c', 0, 1);
INSERT INTO `sys_file` VALUES (58, '1909.xlsx', 'xlsx', 7, 'http://localhost:8081/file/0ebc37a103924abb990999e5aac0e4fb.xlsx', 'b0dff391d5f1b3a0749b848e1cd47b97', 0, 1);
INSERT INTO `sys_file` VALUES (59, '1909A.xlsx', 'xlsx', 10, 'http://localhost:8081/file/23bfadb445f04dfe8407814414aea8cc.xlsx', '68c66db9bc4d4ca76a5ac860fabfe498', 0, 1);
INSERT INTO `sys_file` VALUES (60, 'a50d03a567456f9281ee4e8aa1c26f41.jpeg', 'jpeg', 673, 'http://localhost:8081/file/26567c539a884d74b578604330720de3.jpeg', '704bdfa63a60bc18d94dec17a4d5c97e', 0, 1);
INSERT INTO `sys_file` VALUES (61, 'cat04.jpg', 'jpg', 47, 'http://localhost:8081/file/56536d0b717b4b3a9790d566d72b1aec.jpg', '5f72d4abcfd428edee500ca0b25c0cf4', 0, 1);
INSERT INTO `sys_file` VALUES (62, 'cat02.jpg', 'jpg', 48, 'http://localhost:8081/file/1a233affbf6f4c4c91e0150077084d76.jpg', '125f8475aafbea2e6aefa4afe1a0b8b0', 0, 1);
INSERT INTO `sys_file` VALUES (63, 'cat02.jpg', 'jpg', 48, 'http://localhost:8081/file/1a233affbf6f4c4c91e0150077084d76.jpg', '125f8475aafbea2e6aefa4afe1a0b8b0', 0, 1);
INSERT INTO `sys_file` VALUES (64, 'cat01.jpg', 'jpg', 49, 'http://localhost:8081/file/d42af3dcbdf448619bca2a600ba06977.jpg', '69a342bd86a6391ab2f11d625d1be66c', 0, 1);
INSERT INTO `sys_file` VALUES (65, 'cat04.jpg', 'jpg', 47, 'http://localhost:8081/file/56536d0b717b4b3a9790d566d72b1aec.jpg', '5f72d4abcfd428edee500ca0b25c0cf4', 0, 1);
INSERT INTO `sys_file` VALUES (66, 'a50d03a567456f9281ee4e8aa1c26f41.jpeg', 'jpeg', 673, 'http://localhost:8081/file/26567c539a884d74b578604330720de3.jpeg', '704bdfa63a60bc18d94dec17a4d5c97e', 0, 1);
INSERT INTO `sys_file` VALUES (67, 'a50d03a567456f9281ee4e8aa1c26f41.jpeg', 'jpeg', 673, 'http://localhost:8081/file/26567c539a884d74b578604330720de3.jpeg', '704bdfa63a60bc18d94dec17a4d5c97e', 0, 1);
INSERT INTO `sys_file` VALUES (68, 'cat06.jpg', 'jpg', 51, 'http://localhost:8081/file/1c958e9a73634e5f857866148834e246.jpg', '4a2377f5fc57bbf4bcc2e2ae1076b6d5', 0, 1);
SET FOREIGN_KEY_CHECKS = 1;
后端springboot项目中(以下是在原有项目中添加,如有不全的地方请找第一篇)
创建和controller同级包common在里面创建interface接口Constants
/**
* @description: 定义状态码
* @param: null
* @return:
* @author the_sound
* @date: 2022/5/19
*/
public interface Constants {
// 成功标识位
String CODE_200 = "200";
// 权限不足
String CODE_401 = "401";
// 参数不足
String CODE_400 = "400";
// 系统错误
String CODE_500 = "500";
// 其它业务异常
String CODE_600 = "600";
}
在common里面创建Result.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author the_sound
* @version 1.0
* @description: 接口同意返回包装类
* @date 2022/5/19 17:04
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
// 定义一个code,根据code判断是否成功,前端和后端约定code是多少来判断是否成功
private String code;
// 定义一个返回提醒,如果请求失败,在msg里面告诉前端它的原因是什么
private String msg;
// 定义一个后台所需要携带的数据
private Object data;
// 返回成功信息
public static Result success(){
return new Result(Constants.CODE_200, "", null);
}
// 返回成功信息
public static Result success(Object data){
return new Result(Constants.CODE_200, "", data);
}
// 返回请求失败信息
public static Result error(String code, String msg){
return new Result(code, msg, null);
}
// 返回请求失败信息
public static Result error(){
return new Result(Constants.CODE_500, "系统错误", null);
}
}
在config中创建InterceptorConfig.java
import com.boot.springboot.config.interceptor.JwtInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author the_sound
* @version 1.0
* @description: 注册拦截器
* 所有的配置类都要加一个 @Configuration 注解
* @date 2022/5/22 19:19
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor())
// 拦截所有请求,通过判断 token 是否合法来决定是否需要登录
.addPathPatterns("/**")
// 排除规则
.excludePathPatterns("/**/login", "/**/register", "/**/export", "/**/import");
}
/**
* @description: 将 JwtInterceptor 注册成一个bean 注入到容器中
* @param:
* @return: com.boot.springboot.config.interceptor.JwtInterceptor
* @author ls
* @date: 2022/5/22
*/
@Bean
public JwtInterceptor jwtInterceptor(){
return new JwtInterceptor();
}
}
在config中创建子包interceptor,在子包中创建JwtInterceptor.java
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.boot.springboot.common.Constants;
import com.boot.springboot.entity.User;
import com.boot.springboot.exception.ServiceException;
import com.boot.springboot.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author the_sound
* @version 1.0
* @description: 拦截器拦截token
* @date 2022/5/20 11:37
*/
public class JwtInterceptor implements HandlerInterceptor {
@Autowired
IUserService userService;
/**
* @description: 验证token
* @param: request
response
handler
* @return: boolean
* @author ls
* @date: 2022/5/22
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 从 http 请求头中取出 token
String token = request.getHeader("token");
// 如果不是映射到方法直接通过 (如果可以确定所有请求都可以映射到方法上去,这一步就可以去掉)
if(!(handler instanceof HandlerMethod)){
return true;
}
// 执行认证 判断token是否为空字符串
if (StrUtil.isBlank(token)) {
throw new ServiceException(Constants.CODE_401,"无token,请重新登录");
}
// 获取 token 中的 user id
String userId;
try {
// 从 token中获取 userID
userId = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
response.setHeader("location","/#/login");
throw new ServiceException(Constants.CODE_401,"token验证失败,请重新登录");
}
// 根据token中的userID查询数据库,如果查询不出来就代表不存在
User user = userService.getById(userId);
if (user == null) {
throw new ServiceException(Constants.CODE_401,"用户不存在,请重新登录");
}
// 用户密码加签 生成 jwtVerifier 然后 验证 token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try {
// 验证token
jwtVerifier.verify(token);
} catch (JWTVerificationException e) {
response.setHeader("location","/#/login");
throw new ServiceException(Constants.CODE_401,"token验证失败,请重新登录");
}
// 如果前面都通过了 就给他一个true
return true;
}
}
在utils中创建TokenUtils.java
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.boot.springboot.entity.User;
import com.boot.springboot.service.IUserService;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
/**
* @author the_sound
* @version 1.0
* @description: token工具类
* 将 TokenUtils 使用 @Component 注册为 springboot 的一个 bean
* @date 2022/5/20 10:33
*/
@Component
public class TokenUtils {
private static IUserService staticUserService;
/**
* @description: 将userService对象引入
* @param: null
* @return:
* @author ls
* @date: 2022/5/22
*/
@Resource
private IUserService userService;
/**
* @description: 通过 @PostConstruct(在我们后台项目启动的时候,将对象赋值给静态对象,因为我们下面是一个静态方法,引用数据也必须是静态的) 获取
* @param:
* @return: void
* @author ls
* @date: 2022/5/22
*/
@PostConstruct
public void setUserService() {
staticUserService = userService;
}
/**
* 过期时间5分钟
*/
private static final long EXPIRE_TIME = 5 * 60 * 1000;
/**
* @description: 生成token
* @param: userId
sign
* @return: java.lang.String
* @author ls
* @date: 2022/5/20
*/
public static String getToken(String userId,String sign){
// 将 user id 保存到 token 里面
return JWT.create().withAudience(userId)
//2小时后token过期
.withExpiresAt(DateUtil.offsetHour(new Date(),2))
// 以 password 作为 token 的密钥
.sign(Algorithm.HMAC256(sign));
}
/**
* @description: 获取当前登录的用户信息
* @param:
* @return: com.boot.springboot.entity.User
* @author ls
* @date: 2022/5/22
*/
public static User getCurrentUser(){
try {
// 获取当前请求的request
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// 然后用request去拿token
String token = request.getHeader("token");
if (StrUtil.isBlank(token)) {
// 获取token解析
String userId = JWT.decode(token).getAudience().get(0);
// 如果没有发生异常
return staticUserService.getById(Integer.valueOf(userId));
}
} catch (Exception e){
return null;
}
return null;
}
}
在entity中创建子包dto,在子包中创建UserDTO.java
import cn.hutool.core.annotation.Alias;
import lombok.Data;
/**
* @author the_sound
* @version 1.0
* @description: 用来传输用户数据的一个类,接收前端登录请求的参数的类, 将返回结果统一包装返回
* @date 2022/4/26 20:51
*/
@Data
public class UserDTO {
@Alias("用户名")
private String username;
@Alias("密码")
private String password;
@Alias("昵称")
private String nickname;
@Alias("头像")
private String avatarUrl;
private String token;
}
在controller中的UserController.java中添加
/**
* @description: 登录 将参数返回到数据库里面,然后再返回一个用户的实体,再返回前端
* @param: user
* @return: boolean
* @author ls
* @date: 2022/5/19
*/
@PostMapping("/login")
public Result login(@RequestBody UserDTO userDTO) {
String username = userDTO.getUsername();
String password = userDTO.getPassword();
// 使用工具类中的方法进行校验
if (StrUtil.isBlank(username) || StrUtil.isBlank(password)){
return Result.error(Constants.CODE_400,"参数错误");
}
return Result.success(userService.login(userDTO));
}
/**
* @description: 注册
* @param: userDTO
* @return: com.boot.springboot.common.Result
* @author ls
* @date: 2022/5/20
*/
@PostMapping("/register")
public Result register(@RequestBody UserDTO userDTO){
String username = userDTO.getUsername();
String password = userDTO.getPassword();
// 使用工具类中的方法进行校验
if (StrUtil.isBlank(username) || StrUtil.isBlank(password)){
return Result.error(Constants.CODE_400,"参数错误");
}
return Result.success(userService.register(userDTO));
}
/**
* @description: 通过username将我们的用户信息从数据库查出来
* @param: username
* @return: com.boot.springboot.common.Result
* @author ls
* @date: 2022/5/20
*/
@GetMapping("/username/{username}")
public Result username(@PathVariable String username) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",username);
return Result.success(userService.getOne(queryWrapper));
}
在service中生成的IUserService.java中添加
import com.boot.springboot.controller.dto.UserDTO;
import com.boot.springboot.entity.User;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 服务类
* </p>
*
* @author theSound
* @since 2022-04-22
*/
public interface IUserService extends IService<User> {
UserDTO login(UserDTO userDTO);
User register(UserDTO userDTO);
}
在service的impl中生成的UserServiceImpl.java中添加
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.boot.springboot.common.Constants;
import com.boot.springboot.controller.dto.UserDTO;
import com.boot.springboot.entity.User;
import com.boot.springboot.exception.ServiceException;
import com.boot.springboot.mapper.UserMapper;
import com.boot.springboot.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.boot.springboot.utils.TokenUtils;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author theSound
* @since 2022-04-22
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
// 打印异常日志
private static final Log LOG = Log.get();
/**
* @description: 登录
* @param: user
* @return: boolean
* @author ls
* @date: 2022/5/19
*/
@Override
public UserDTO login(UserDTO userDTO) {
User user = getUserInfo(userDTO);
if (user != null){
// 复制对象间的属性 第一个是原对象 第二个是需要复制的对象 第三个是是否忽略大小写
BeanUtil.copyProperties(user,userDTO,true);
// 设置token
String token = TokenUtils.getToken(user.getId().toString(), user.getPassword());
userDTO.setToken(token);
return userDTO;
}else {
// 自定义异常 业务异常
throw new ServiceException(Constants.CODE_600,"用户名或密码错误");
}
}
/**
* @description: 注册
* @param: userDTO
* @return: com.boot.springboot.common.Result
* @author ls
* @date: 2022/5/20
*/
@Override
public User register(UserDTO userDTO) {
User user = getUserInfo(userDTO);
if (user == null){
user = new User();
BeanUtil.copyProperties(userDTO,user,true);
save(user); // 把copy完之后的对象存到数据库中
}else {
// 自定义异常 业务异常
throw new ServiceException(Constants.CODE_600,"用户已存在");
}
return user;
}
/**
* @description: 封装重复代码 用的时候调用
* @param: userDTO
* @return: com.boot.springboot.entity.User
* @author ls
* @date: 2022/5/20
*/
private User getUserInfo(UserDTO userDTO){
// 如果去数据库查询就要用实体类
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",userDTO.getUsername());
queryWrapper.eq("password",userDTO.getPassword());
User user;
try {
// 从数据库查询用户信息
user = getOne(queryWrapper);
} catch (Exception e) {
// 打印异常日志
LOG.error(e);
// 自定义异常
throw new ServiceException(Constants.CODE_500,"系统错误");
}
return user;
}
}
这样我们的后端就完成了
前端vue
在views中创建Login.vue (登录)
<template>
<div class="wrapper">
<!-- 登录框-->
<div style="margin: 200px auto; background-color: #fff; width: 350px; height: 300px; padding: 20px; border-radius: 10px">
<div style="margin: 20px 0; text-align: center; font-size: 24px"><b>登 录</b></div>
<el-form :model="user" :rules="rules" ref="userForm">
<el-form-item prop="username">
<el-input size="medium" style="margin: 10px 0" prefix-icon="el-icon-user" v-model="user.username"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input size="medium" style="margin: 10px 0" prefix-icon="el-icon-lock" show-password v-model="user.password"></el-input>
</el-form-item>
<el-form-item style="margin: 10px 0; text-align: right">
<el-button type="primary" size="small" autocomplete="off" @click="login()">登录</el-button>
<el-button type="warning" size="small" autocomplete="off" @click="$router.push('/register')">注册</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
user: {}, // 创建一个user对象
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 10, message: '长度在 3 到 5 个字符', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }
],
}
}
},
methods: {
login() {
this.$refs['userForm'].validate((valid) => { // 通过valid这个参数的值(true/false)来判断表单是否合法
if (valid) { // 表单校验合法
this.request.post("http://localhost:8081/user/login", this.user).then(res => {
if(res.code === '200') {
localStorage.setItem("user",JSON.stringify(res.data)) // 存储用户信息到浏览器
this.$router.push("/")
this.$message.success("登录成功")
} else {
this.$message.error(res.msg)
}
})
} else {
return false;
}
})
}
}
}
</script>
<style>
/*登录背景*/
.wrapper {
/*vh表示100%窗口高度*/
height: 100vh;
background-image: linear-gradient(to bottom right, #F598A8FF, #6B79D7FF);
overflow: hidden;
}
</style>
在views中创建Register.vue (注册)
<template>
<div class="wrapper">
<!-- 注册 -->
<div style="margin: 200px auto; background-color: #fff; width: 350px; height: 350px; padding: 20px; border-radius: 10px">
<div style="margin: 20px 0; text-align: center; font-size: 24px"><b>注 册</b></div>
<el-form :model="user" :rules="rules" ref="userForm">
<el-form-item prop="username">
<el-input placeholder="请输入账号" size="medium" style="margin: 5px 0" prefix-icon="el-icon-user" v-model="user.username"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input placeholder="请输入密码" type="password" size="medium" style="margin: 5px 0" prefix-icon="el-icon-lock" show-password v-model="user.password"></el-input>
</el-form-item>
<el-form-item prop="confirmPassword">
<el-input placeholder="请确认密码" type="password" size="medium" style="margin: 5px 0" prefix-icon="el-icon-lock" show-password v-model="user.confirmPassword"></el-input>
</el-form-item>
<el-form-item style="margin: 5px 0; text-align: right">
<el-button type="primary" size="small" autocomplete="off" @click="register()">注册</el-button>
<el-button type="warning" size="small" autocomplete="off" @click="$router.push('/login')">返回登录</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
user: {}, // 创建一个user对象
/*var validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入密码'));
} else {
if (this.user.password !== '') {
this.$refs.user.validateField('password');
}
callback();
}
},
var validatePass2 = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== this.user.confirmPassword) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
},*/
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 10, message: '长度在 3 到 5 个字符', trigger: 'blur' }
],
password: [
// { validator: validatePass, trigger: 'blur' }
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }
],
confirmPassword: [
// { validator: validatePass2, trigger: 'blur' }
{ required: true, message: '请确定密码', trigger: 'blur' },
{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }
],
}
}
},
methods: {
register() {
this.$refs['userForm'].validate((valid) => { // 通过valid这个参数的值(true/false)来判断表单是否合法
if (valid) { // 表单校验合法
if (this.user.password !== this.user.confirmPassword){
this.$message.error("两次输入的密码不一致")
return false // 如果不一样就不往下继续请求了
}
this.request.post("http://localhost:8081/user/register", this.user).then(res => {
if(res.code === '200') {
this.$router.push("/login")
this.$message.success("注册成功")
} else {
this.$message.error(res.msg)
}
})
} else {
return false;
}
})
}
}
}
</script>
<style>
/*登录背景*/
.wrapper {
/*vh表示100%窗口高度*/
height: 100vh;
background-image: linear-gradient(to bottom right, #F598A8FF, #6B79D7FF);
overflow: hidden;
}
</style>
在components中修改Header.vue (个人信息)
<template>
<div style=" line-height: 60px;display: flex">
<div style="flex: 1;font-size: 20px">
<span :class="collapseBtnClass" style="cursor: pointer" @click="collapse()"></span>
<el-breadcrumb separator="/" style="display: inline-block; margin-left: 10px" >
<el-breadcrumb-item :to="'/'">首页</el-breadcrumb-item>
<el-breadcrumb-item>{{ currentPathName }}</el-breadcrumb-item>
</el-breadcrumb>
</div>
<el-dropdown style="width: 150px;cursor: pointer;text-align: right">
<div style="display: inline-block">
<img :src="user.avatarUrl" alt=""
style="width: 50px;border-radius: 50%;position: relative;top: 10px;right: 5px">
<span>{{ user.nickname }} <i class="el-icon-arrow-down" style="margin-left: 5px"></i></span>
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item style="font-size: 14px;">
<router-link to="/person">个人信息</router-link>
</el-dropdown-item>
<el-dropdown-item style="font-size: 14px;">
<span style="text-decoration: none" @click="logOut()">退出</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</template>
<script>
export default {
name: "Header",
props: {
collapseBtnClass: String,
user: Object
},
computed: {
currentPathName(){
// 在这里取出我们在route里面设置的当前路由信息currentPathName
return this.$store.state.currentPathName // 需要监听的数据
}
},
data() {
return {
}
},
methods: {
collapse() { // 点击收缩按钮时会触发按钮
this.isCollapse = !this.isCollapse
if (this.isCollapse) { // 收缩
this.sideWidth = 65
this.collapseBtnClass = 'el-icon-s-unfold' // 缩起图标控制
this.logoTextShow = false // 收缩是不显示文字
} else { // 展开
this.sideWidth = 200
this.collapseBtnClass = 'el-icon-s-fold' // 展开图标控制
this.logoTextShow = true // 显示文字
}
},
logOut() {
// 跳到登录页面
this.$router.push("/login")
// 清除后台记录
localStorage.removeItem("user")
// 提示
this.$message.success("退出成功")
}
}
}
</script>
<style scoped>
</style>
在views中修改Manage.vue (个人信息)
<template>
<div class="home">
<el-container style="height: 100%;">
<!--左侧边栏-->
<el-aside :width="sideWidth + 'px'" style="background-color: rgb(238, 241, 246);height: 100%; box-shadow:2px 0 6px rgb(0, 21, 41, 0.35)">
<Aside :isCollapse="isCollapse" :logoTextShow="logoTextShow"/>
</el-aside>
<!--右边-->
<el-container>
<!--头部-->
<el-header style="border-bottom: 1px solid #ccc;">
<Header :collapse="isCollapse" :collapseBtnClass="collapseBtnClass" :user="user"/>
</el-header>
<!--主体-->
<el-main>
<!-- 表示当前页面的子路由会在 router-view 里面展示 -->
<router-view @refreshUser="getUser()"/>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
import Header from "@/components/Header"
import Aside from "@/components/Aside";
// import request from "@/utils/request";
export default {
name: 'Manage',
components: {Aside,Header},
data() {
return {
collapseBtnClass: 'el-icon-s-fold', // 收缩图标控制 默认展开
isCollapse: false, // 点击收缩图标控制 默认是展开 false
sideWidth: 200, // 导航栏宽度 动态 初始值是200
logoTextShow: true, // 默认是显示的标题 当收缩是为false
user: {}
}
},
created() {
// 从后台获取最新的User数据
this.getUser()
},
methods: {
collapse() { // 点击收缩按钮时会触发按钮
this.isCollapse = !this.isCollapse
if (this.isCollapse) { // 收缩
this.sideWidth = 65
this.collapseBtnClass = 'el-icon-s-unfold' // 缩起图标控制
this.logoTextShow = false // 收缩是不显示文字
} else { // 展开
this.sideWidth = 200
this.collapseBtnClass = 'el-icon-s-fold' // 展开图标控制
this.logoTextShow = true // 显示文字
}
},
getUser() { // 异步
let username = localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")).username : ""
// 从后台获取User数据
this.request.get("http://localhost:8081/user/username/" + username).then(res => {
// 重新赋值后台的最新User数据
this.user = res.data
})
},
}
}
</script>
<style>
.home {
height: 100%;
}
</style>
在views中创建Person.vue (个人信息)
<template>
<el-card style="width: 500px;">
<el-form label-width="80px" size="small">
<el-upload
class="avatar-uploader"
action="http://localhost:8081/file/upload"
:show-file-list="false"
:on-success="handleAvatarSuccess"
>
<img v-if="form.avatarUrl" :src="form.avatarUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
<el-form-item label="用户名">
<el-input v-model="form.username" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="昵称">
<el-input v-model="form.nickname" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="form.email" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="电话">
<el-input v-model="form.phone" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="地址">
<el-input v-model="form.address" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="save">确 定</el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<script>
export default {
name: "Person",
data() {
return {
form: {},// 绑定一个对象,form表单中有用到
// 通过三目表达式来判断是否转json,如果没有就返回一个空的字符串就行
user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
}
},
created() {
this.getUser().then(res => { // 后台提供一个接口并通过接口返回出来
console.log(res)
// 赋值
this.form = res
})
},
methods: {
async getUser() { // 同步
return (await this.request.get("http://localhost:8081/user/username/" + this.user.username)).data
},
save() { // 添加/修改点击确定按钮
this.request.post("http://localhost:8081/user", this.form).then(res => {
if (res.code === '200') {
this.$message.success("保存成功")
// 出发父级User的方法
this.$emit("refreshUser")
// 更新浏览器存储的用户信息
// this.getUser().then(res => {
// res.token = JSON.parse(localStorage.getItem("user")).token
// localStorage.setItem("user", JSON.stringify(res))
// })
} else {
this.$message.error("保存失败")
}
})
},
handleAvatarSuccess(res) {
this.form.avatarUrl = res
}
}
}
</script>
<style>
.avatar-uploader {
text-align: center;
padding-bottom: 10px;
}
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 138px;
height: 138px;
line-height: 138px;
text-align: center;
}
.avatar {
width: 138px;
height: 138px;
display: block;
}
</style>
在router 中的 index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import store from "@/store";
Vue.use(VueRouter)
const routes = [
// {
// path: '/',
// name: 'home',
// component: HomeView
// },
{
path: '/',
name: 'Manage',
component: () => import('../views/Manage.vue'),
redirect: "/home",
children: [
{ path: '/home', name: '首页', component: () => import('../views/Home.vue') },
{ path: '/user', name: '用户管理', component: () => import('../views/User.vue') },
{ path: '/person', name: '个人信息', component: () => import('../views/Person.vue') },
]
},
{ // 登录
path: '/login',
name: 'Login',
component: () => import('../views/Login.vue')
},
{ // 注册
path: '/register',
name: 'Register',
component: () => import('../views/Register.vue')
},
]
const router = new VueRouter({
// mode: "history",
// base: process.env.BASE_URL,
routes
})
router.beforeEach((to, from, next) => { // 路由守卫
localStorage.setItem("currentPathName", to.name) // 设置当前路由名称
store.commit("setPath") // 触发store的数据更新
next()
})
export default router
完成了!撒花!