mybatis-plus+Vue代码实现登录注册+个人信息展示(第二篇)

先展示最后的效果图

/*
 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

 完成了!撒花!

posted @ 2022-05-24 11:18  回头是岸不可能回头  阅读(733)  评论(0)    收藏  举报