后端(springboot3+mybatis+redis+tokenJWT),前端(vue3+elementplus+axios+pinia+tokestores)前后端分离最完整最简易的项目

一、结果截图(结果截图只是展现功能的一小部分,只要您把前后端项目运行起来探讨,方能体会该项目中的精髓,该项目是本人JavaEE期末实验作业,结合Javaweb课程所开发)

1.登录页面:

2.注册页面:

3.首页:

4.审核页面:

5.添加申报页面:

6.删除和批量删除

7.管理员基本信息:

8.管理员更换用户头像页面:

9.管理员更改密码页面:

10.账号注销页面:

二、代码实验(后端(springboot3+mybatisplus+redis+toke,注意:必须开启redis服务,必须登录用户才能查看首页,账号123456,密码123456)

1.创建数据库以及添加数据。

/*
 Navicat MySQL Data Transfer

 Source Server         : 我的期望的
 Source Server Type    : MySQL
 Source Server Version : 80031
 Source Host           : localhost:3306
 Source Schema         : projectdb

 Target Server Type    : MySQL
 Target Server Version : 80031
 File Encoding         : 65001

 Date: 20/06/2024 15:23:02
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for projectinf
-- ----------------------------
DROP TABLE IF EXISTS `projectinf`;
CREATE TABLE `projectinf`  (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '项目编号',
  `projectName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '项目名称',
  `startDate` date NOT NULL COMMENT '开始时间',
  `endDate` date NOT NULL,
  `status` int NOT NULL COMMENT '0-已申报\r\n1-审核中\r\n2-已审核',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 18 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of projectinf
-- ----------------------------
INSERT INTO `projectinf` VALUES (1, '北京社会科学基金2011年度申报', '2024-06-15', '2024-06-15', 0);
INSERT INTO `projectinf` VALUES (2, '国家自然科学基金2011年度申报', '2024-06-14', '2024-06-14', 2);
INSERT INTO `projectinf` VALUES (3, '国家社会科学基金2011年度申报', '2024-06-11', '2024-06-14', 1);
INSERT INTO `projectinf` VALUES (20, '北京社会科学基金2018年度申报', '2024-06-03', '2024-06-20', 2);
INSERT INTO `projectinf` VALUES (19, '北京社会科学基金2015年度申报', '2024-06-03', '2024-06-10', 1);
INSERT INTO `projectinf` VALUES (18, '北京社会科学基金2012年度申报', '2024-06-02', '2024-06-21', 0);
INSERT INTO `projectinf` VALUES (21, '北京社会科学基金2021年度申报', '2024-06-10', '2024-06-26', 2);
INSERT INTO `projectinf` VALUES (22, '南京社会科学基金2011年度申报', '2024-06-10', '2024-06-20', 0);
INSERT INTO `projectinf` VALUES (23, '南京社会科学基金2021年度申报', '2024-06-04', '2024-06-20', 1);
INSERT INTO `projectinf` VALUES (24, '江苏社会科学基金2011年度申报', '2024-06-04', '2024-06-19', 2);
INSERT INTO `projectinf` VALUES (25, '南京自然科学基金2011年度申报', '2024-06-02', '2024-06-20', 1);
INSERT INTO `projectinf` VALUES (26, '南京自然科学基金2015年度申报', '2024-06-10', '2024-06-12', 1);
INSERT INTO `projectinf` VALUES (27, '广东自然科学基金2011年度申报', '2024-06-11', '2024-06-19', 0);
INSERT INTO `projectinf` VALUES (28, '海南自然科学基金2011年度申报', '2024-06-17', '2024-06-26', 1);
INSERT INTO `projectinf` VALUES (29, '海南自然科学基金2021年度申报', '2024-06-11', '2024-06-12', 1);
INSERT INTO `projectinf` VALUES (30, '海南自然科学基金2012年度申报', '2024-06-11', '2024-06-12', 1);
INSERT INTO `projectinf` VALUES (31, '海南自然科学基金2024年度申报', '2024-06-11', '2024-06-19', 2);
INSERT INTO `projectinf` VALUES (32, '广东自然科学基金2024年度申报', '2024-06-11', '2024-06-19', 1);
INSERT INTO `projectinf` VALUES (33, '新疆自然科学基金2024年度申报', '2024-06-10', '2024-06-19', 1);
INSERT INTO `projectinf` VALUES (34, '黑河自然科学基金2024年度申报', '2024-06-10', '2024-06-19', 2);
INSERT INTO `projectinf` VALUES (35, '海南自然科学基金2026年度申报', '2024-06-11', '2024-06-27', 0);
INSERT INTO `projectinf` VALUES (36, '上海社会科学基金2024年度申报', '2024-06-10', '2024-06-19', 1);
INSERT INTO `projectinf` VALUES (37, '上海社会科学基金2021年度申报', '2024-06-11', '2024-06-11', 0);

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
  `password` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
  `userpic` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `email` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `phone` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `address` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `isadmin` bit(1) NOT NULL DEFAULT b'0',
  `isvalidate` bit(1) NOT NULL DEFAULT b'0',
  PRIMARY KEY (`id`, `username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 57 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (35, '123', '202cb962ac59075b964b07152d234b70', 'https://big-eventa.oss-cn-beijing.aliyuncs.com/0696295f-a304-4a04-a3dd-de76da78dfd6.webp', '张三', '234234@232111', '12432467899', '广东省佛山市', b'0', b'0');
INSERT INTO `user` VALUES (52, '2342334', '0fd6f5a9f28ce32f8228594e7a2fc917', 'https://big-eventa.oss-cn-beijing.aliyuncs.com/31013a65-6e20-4f84-97f8-f02b0c7a0fcd.jpg', '', '', '', '', b'0', b'0');
INSERT INTO `user` VALUES (56, '123456', 'e10adc3949ba59abbe56e057f20f883e', 'https://big-eventa.oss-cn-beijing.aliyuncs.com/0696295f-a304-4a04-a3dd-de76da78dfd6.webp', '张三', '234234@232111', '12432467899', '广东省佛山市', b'0', b'0');
SET FOREIGN_KEY_CHECKS = 1;

 2.创建springboot,项目名为springboot3_mybatisplus_redis_JWT。

3.导入pom.xml文件的依赖。 

 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.4.0</version>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.5</version>
</dependency>

4.在项目中创建所需的包 

5.在resoures包下创建application.properties和application.yml。 

 (1)application.propertie配置:

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/projectdb
spring.datasource.username=root
spring.datasource.password=123456
server.port=9095
#mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#mybatis.configuration.map-underscore-to-camel-case=true
spring.web.resources.static-locations=classpath:/

(2)application.yml: 

spring:
  mvc:
    servlet:
      load-on-startup: 1
  spring:
    redis:
      host: localhost   #服务器地址 你服务器或本机地址
      port: 6379           #连接端口
      database: projectdb         #数据库索引,默认0
      password: 123456     #密码
      jedis:
        pool:
          max-active: 8    #连接池最大连接数(使用负值表示没有限制)
          max-wait: -1     #最大阻塞等待时间(使用负值表示没有限制)
          max-idle: 8      #最大空闲连接数
          min-idle: 0      #最小空闲连接数
      timeout: 5000        #连接超时时间(毫秒)
# 加入日志功能
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 设置MyBatis-Plus的全局配置
  global-config:
    db-config:
#      # 设置实体类所对应的表的统一前缀
#      table-prefix: t_
      # 设置统一的主键生成策略
      id-type: auto

   6.在intercepters包下创建Logininterceptor类。 

package org.example.springboot3_mybatisplus_redis_jwt.intercepters;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.example.springboot3_mybatisplus_redis_jwt.utils.JwtUtil;
import org.example.springboot3_mybatisplus_redis_jwt.utils.ThreadLocalUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.util.Map;

@Component
public class Logininterceptor implements HandlerInterceptor {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println(request.getRequestURI());

        //令牌验证
        String token = request.getHeader("cookieshopUser");
        try {
            ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
            String redisToken = operations.get(token);
            if (redisToken==null){
                throw new RuntimeException();
            }
            Map<String, Object> claims = JwtUtil.parseToken(token);
            ThreadLocalUtil.set(claims);//把业务数据储存为ThreaddLocal中
            return true;
        } catch (Exception e) {
            response.setStatus(401);
            return false;
        }

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        ThreadLocalUtil.remove();
    }
}

7.在pojo包下创建projectinf、Result、User类。 

(1)projectinf类:

package org.example.springboot3_mybatisplus_redis_jwt.pojo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class projectinf {
    private int id;
    @TableField(value = "projectName")
    private String projectName;
    @TableField(value = "startDate")
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    private Date startDate;
    @TableField(value = "endDate")
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    private Date endDate;
    private int status;
}

(2)Result实体类:

package org.example.springboot3_mybatisplus_redis_jwt.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

//统一响应结果
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Result<T> {
    private Integer code;//业务状态码  0-成功  1-失败
    private String message;//提示信息
    private T data;//响应数据
    //快速返回操作成功响应结果(带响应数据)
    public static <E> Result<E> success(E data) {
        return new Result<>(0, "操作成功", data);
    }

    //快速返回操作成功响应结果
    public static Result success() {
        return new Result(0, "操作成功", null);
    }
    public static Result error(String message) {
        return new Result(1, message, null);
    }
}

(3)User实体类: 

package org.example.springboot3_mybatisplus_redis_jwt.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
    private Integer id;
    private String username;
    private String email;
    private String password;
    private String userpic;
    private String name;
    private String phone;
    private String address;
    private boolean isadmin;
    private boolean isvalidate;
}

8.在utils包下创建JwtUtil、Md5Util和ThreadLocalUtil实体类。 

 (1)JwtUtil实体类:

package org.example.springboot3_mybatisplus_redis_jwt.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtil {
    private static final String KEY = "quanfaqiang";
    //接收业务数据,生成token并返回
    public static String genToken(Map<String, Object> claims) {
        return JWT.create()
                .withClaim("claims", claims)
                .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12))
                .sign(Algorithm.HMAC256(KEY));
    }
    //接收token,验证token,并返回业务数据
    public static Map<String, Object> parseToken(String token) {
        return JWT.require(Algorithm.HMAC256(KEY))
                .build()
                .verify(token)
                .getClaim("claims")
                .asMap();
    }
}

(2) Md5Util实体类:

package org.example.springboot3_mybatisplus_redis_jwt.utils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Md5Util {
    /**
     * 默认的密码字符串组合,用来将字节转换成 16 进制表示的字符,apache校验下载的文件的正确性用的就是默认的这个组合
     */
    protected static char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    protected static MessageDigest messagedigest = null;

    static {
        try {
            messagedigest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException nsaex) {
            System.err.println(Md5Util.class.getName() + "初始化失败,MessageDigest不支持MD5Util。");
            nsaex.printStackTrace();
        }
    }

    /**
     * 生成字符串的md5校验值
     *
     * @param s
     * @return
     */
    public static String getMD5String(String s) {
        return getMD5String(s.getBytes());
    }

    /**
     * 判断字符串的md5校验码是否与一个已知的md5码相匹配
     *
     * @param password  要校验的字符串
     * @param md5PwdStr 已知的md5校验码
     * @return
     */
    public static boolean checkPassword(String password, String md5PwdStr) {
        String s = getMD5String(password);
        return s.equals(md5PwdStr);
    }


    public static String getMD5String(byte[] bytes) {
        messagedigest.update(bytes);
        return bufferToHex(messagedigest.digest());
    }

    private static String bufferToHex(byte bytes[]) {
        return bufferToHex(bytes, 0, bytes.length);
    }

    private static String bufferToHex(byte bytes[], int m, int n) {
        StringBuffer stringbuffer = new StringBuffer(2 * n);
        int k = m + n;
        for (int l = m; l < k; l++) {
            appendHexPair(bytes[l], stringbuffer);
        }
        return stringbuffer.toString();
    }

    private static void appendHexPair(byte bt, StringBuffer stringbuffer) {
        char c0 = hexDigits[(bt & 0xf0) >> 4];// 取字节中高 4 位的数字转换, >>>
        // 为逻辑右移,将符号位一起右移,此处未发现两种符号有何不同
        char c1 = hexDigits[bt & 0xf];// 取字节中低 4 位的数字转换
        stringbuffer.append(c0);
        stringbuffer.append(c1);
    }

}

  (3)ThreadLocalUtil 工具类:

package org.example.springboot3_mybatisplus_redis_jwt.utils;

/**
 * ThreadLocal 工具类
 */
@SuppressWarnings("all")
public class ThreadLocalUtil {
    //提供ThreadLocal对象,
    private static final ThreadLocal THREAD_LOCAL = new ThreadLocal();
    //根据键获取值
    public static <T> T get(){
        return (T) THREAD_LOCAL.get();
    }

    //存储键值对
    public static void set(Object value){
        THREAD_LOCAL.set(value);
    }
    //清除ThreadLocal 防止内存泄漏
    public static void remove(){
        THREAD_LOCAL.remove();
    }
}

9.在config包下创建MybatisPlusConfig和WebConfig配置类。 

(1)MybatisPlusConfig配置类:

package org.example.springboot3_mybatisplus_redis_jwt.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

 (2)WebConfig配置类:

package org.example.springboot3_mybatisplus_redis_jwt.config;
import org.example.springboot3_mybatisplus_redis_jwt.intercepters.Logininterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private Logininterceptor logininterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(logininterceptor).excludePathPatterns("/login","/register","/index");
    }
}

10.在mapper包下创建UserMapper和projectinfMapper接口。 

(1)projectinfMapper接口:

package org.example.springboot3_mybatisplus_redis_jwt.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.springboot3_mybatisplus_redis_jwt.pojo.projectinf;

@Mapper
public interface projectinfMapper extends BaseMapper<projectinf> {
}

(2)UserMapper接口:

package org.example.springboot3_mybatisplus_redis_jwt.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.springboot3_mybatisplus_redis_jwt.pojo.User;

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

  11.在controller包下创建FileUploadController、projectinfController和UserController控制类。其中FileUploadController类为文件上传、projectinfController类有申报查询全部列表、根据申报项目名查找、删除、批量删除、搜索、修改、添加功能。UserController类有登录、注册、注销、根据id删除、添加、修改密码、搜索等功能。 

 (1) FileUploadController控制类:

package org.example.springboot3_mybatisplus_redis_jwt.controlller;
import org.example.springboot3_mybatisplus_redis_jwt.pojo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;

//@CrossOrigin(origins = {"*","null"})
@RestController
public class FileUploadController {
    @Autowired
    private Environment env;
    @PostMapping("/upload")
    public Result saveFile(MultipartFile file) {
        System.out.println("上传文件运行了");

        if (file.isEmpty()) {
            return Result.success("文件为空!");
        }
        // 给文件重命名
        String fileName = UUID.randomUUID() + "." + file.getContentType()
                .substring(file.getContentType().lastIndexOf("/") + 1);
        System.out.println(fileName+"===============");
        try {
            // 获取保存路径
            String path = getSavePath();
            File files = new File(path, fileName);
            System.out.println(files);
            File parentFile = files.getParentFile();
            System.out.println(parentFile);
            if (!parentFile.exists()) {
                parentFile.mkdir();
            }
            file.transferTo(files);
        } catch (IOException e) {
            e.printStackTrace();
        }
//        return Result.success(fileName); // 返回重命名后的文件名
//        return Result.success("http://localhost:9095/upload/"+fileName); // 返回重命名后的文件名
        System.out.println(getPort());
        return Result.success("http://localhost:"+getPort()+"/upload/"+fileName); // 返回重命名后的文件名
    }
    public String getSavePath() {
        // 这里需要注意的是ApplicationHome是属于SpringBoot的类
        // 获取项目下resources/static/img路径
        ApplicationHome applicationHome = new ApplicationHome(this.getClass());

        // 保存目录位置根据项目需求可随意更改
        return applicationHome.getDir().getParentFile()
                .getParentFile().getAbsolutePath() + "\\src\\main\\resources\\upload";
    }
    public int getPort() {
        // 获取当前应用绑定的端口号
        return env.getProperty("local.server.port", Integer.class);
    }

}

 (2)projectinfController控制类:

package org.example.springboot3_mybatisplus_redis_jwt.controlller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.example.springboot3_mybatisplus_redis_jwt.pojo.Result;
import org.example.springboot3_mybatisplus_redis_jwt.pojo.projectinf;
import org.example.springboot3_mybatisplus_redis_jwt.service.projectinfService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;

@RestController
@RequestMapping("/project")
public class projectinfController {
    @Autowired
    private projectinfService service;

    @GetMapping("all")
    public Result getAll(Integer currentPage, Integer pageSize){
        System.out.println(currentPage+":"+pageSize);
        Page<projectinf> all = service.getAll(currentPage, pageSize);
        System.out.println(all.getRecords());
        return Result.success(all);
    }

    @DeleteMapping("/delete/{id}")
    public Result delete(@PathVariable Integer id) {
        service.delete(id);
        return Result.success("删除成功");
    }

    @DeleteMapping("/deleteMore/{ids}")
    public Result deleteIds(@PathVariable List<Integer> ids) {
        System.out.println(ids+"sssssssssssssssssss");
        service.deleteIds(ids);
        return Result.success("删除成功");
    }


    @GetMapping("/search")
    public Result searchGoodsTypeName( String name, String status, Integer currentPage, Integer pageSize,String start,String end) {
        System.out.println(start + "sssssssssssssssssssssssssssssss");
        Page<projectinf> typeList = service.search(start,end,name,status, currentPage, pageSize);
        return Result.success(typeList);
    }

    @PostMapping("/update")
    public Result update(@RequestBody projectinf projectinf) {
        System.out.println(projectinf);
        int update = service.update(projectinf);
        if(update>0){
            System.out.println("审核成功");
        }
        return Result.success(update);
    }

    @GetMapping("/selectProjectName")
    public Result selectTypeName(String projectName) {
        Boolean flag = service.selectName(projectName);
        System.out.println(flag);
        return Result.success(flag);
    }

    @PostMapping("/add")
    public Result add(@RequestBody projectinf projectinf) {
        System.out.println(projectinf);
        int add = service.add(projectinf);
        if (add>0){
            System.out.println("添加申报成功");
        }
        return Result.success(add);
    }
}

 (3)UserController控制类:

package org.example.springboot3_mybatisplus_redis_jwt.controlller;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.example.springboot3_mybatisplus_redis_jwt.pojo.Result;
import org.example.springboot3_mybatisplus_redis_jwt.pojo.User;
import org.example.springboot3_mybatisplus_redis_jwt.service.UserService;
import org.example.springboot3_mybatisplus_redis_jwt.utils.JwtUtil;
import org.example.springboot3_mybatisplus_redis_jwt.utils.Md5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;

@RestController
@RequestMapping
public class UserController {

    @Autowired
    private UserService userService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @GetMapping("/SelectAll")
    public Result SelectAll(int currentPage, int pageSize) {
        Page<User> user = userService.SelectAll(currentPage, pageSize);
        System.out.println(user);
        return Result.success(user);

    }
    @PostMapping("/login")
    public Result login(String username, String password) {
        System.out.println(username + ":" + password);
        User loginuser = userService.findByUserName(username);
        if (loginuser == null) {
            return Result.error("用户名不存在");
        }
        if (Md5Util.getMD5String(password).equals(loginuser.getPassword())) {
            HashMap<String, Object> stringObjectHashMap = new HashMap<>();
            stringObjectHashMap.put("id", loginuser.getId());
            stringObjectHashMap.put("username", loginuser.getUsername());
            String token = JwtUtil.genToken(stringObjectHashMap);

            //把token存储到redisz中
            ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
            operations.set(token,token/*, TimeUnit.HOURS*/);
            return Result.success(token);
        }
        return Result.error("密码错误");
    }

    @PostMapping("/register")
    public Result register(String username, String password) {
        User u = userService.findByUserName(username);
        System.out.println(u);
        if (u == null) {
            userService.register(username, password);
            return Result.success("注册成功");
        } else {
            return Result.error("用户名已被占用");
        }
    }

    @PostMapping("/logoff")
    public Result logoff(String username, String password) {
        boolean logoff = userService.logoff(username, password);
        System.out.println(logoff);
        if (logoff) {
            System.out.println("注销成功");
        }else {
            System.out.println("用户名或密码不正确");
        }
        return Result.success(logoff);
    }




    @DeleteMapping("/deleteId/{id}")
    public Result<String> DeleteId(@PathVariable Integer id) {
        userService.DeleteId(id);
        return Result.success("删除成功");
    }

    @DeleteMapping("/deleteMore/{ids}")
    public Result deleteIds(@PathVariable List<Integer> ids) {
        System.out.println(ids+"sssssssssssssssssss");
        userService.deleteIds(ids);
        return Result.success("商品批量删除成功");
    }


    @GetMapping("/selectId")
    public Result SelectId(@RequestParam Integer id) {
        User user = userService.SelectId(id);
        System.out.println(id);
        System.out.println(user);
        return Result.success(user);
    }

    @GetMapping("/selectName")
    public Result selectName(String username) {
        User loginuser = userService.findByUserName(username);
        System.out.println(loginuser);
        return Result.success(loginuser);
    }

    @PostMapping("/add")
    public Result add(@RequestBody User user) {
        System.out.println(user);
        userService.Add(user);
        return Result.success("添加成功");
    }

    @GetMapping("/selectPassword")
    public Result selectpassword(String password, Integer id) {
        System.out.println(password+":"+id);
        Boolean flag = userService.selectPassword(password, id);
        System.out.println(flag);
        if (flag == true) {
            System.out.println("校验成功");
        }else {
            System.out.println("校验失败");
        }
        return  Result.success(flag);
    }

    @PutMapping("/update")
    public Result update(@RequestBody User user) {
        System.out.println(user);
        userService.update(user);
        return Result.success("修改成功");
    }

    @PatchMapping("/updatePwd")
    public Result updatePwd(String password, Integer id) {
        System.out.println(password+":"+id);
        userService.updatePwd(password, id);
        return Result.success("密码修改成功");
    }

    @PatchMapping("/updateUserAddress")
    public Result updateUserAddress(String UserAddress, int id) {
        userService.updateUserAddress(UserAddress, id);
        return Result.success(id + "修改地址成功");
    }

    @GetMapping("/userInfo")
    public Result<User> userInfo(/*@RequestHeader(name="Authorization") String token*/) {
//        Map<String, Object> map = JwtUtil.parseToken(token);
//        String username=(String)map.get("username");
//
//        Map<String, Object> map = ThreadLocalUtil.get();
//        System.out.println(map);
//        String username = (String) map.get("username");
//        User user = userService.findByUserName(username);
//        return Result.success(user);

        ValueOperations<String, String> stringStringValueOperations = stringRedisTemplate.opsForValue();
        String username1 = stringStringValueOperations.get("username");
        User user1 = userService.findByUserName(username1);
        return Result.success(user1);

    }

    @PatchMapping("/updateAvatar")
    public Result updateAvatar(@RequestParam  String avatarUrl){
        System.out.println(avatarUrl);
        userService.updateAvatar(avatarUrl);
        return Result.success("更新用户头像完成");
    }

    @GetMapping("/serchUserName")
    public Result searchGoods(String userName,Integer currentPage, Integer pageSize){
        Page<User> userList=userService.searchUser(userName,currentPage,pageSize);
        return Result.success(userList);
    }
    @GetMapping("/selectUsername")
    public Result selectUsername(String username){
        System.out.println(username);
        Boolean flag=userService.selectUsername(username);
        System.out.println(flag);
        return Result.success(flag);
    }

}

   12.在service包下创建projectinfService和UserService服务接口类,还有impl包:

(1)projectinfService服务接口类:

package org.example.springboot3_mybatisplus_redis_jwt.service;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.example.springboot3_mybatisplus_redis_jwt.pojo.projectinf;
import java.util.List;

public interface projectinfService {
    Page<projectinf> getAll(Integer currentPage, Integer pageSize);

    void delete(Integer id);

    void deleteIds(List<Integer> ids);

    Page<projectinf> search(String date, String end,String name, String status, Integer currentPage, Integer pageSize);

    int update(projectinf projectinf);

    int add(projectinf projectinf);

    Boolean selectName(String projectName);
}

(2)UserService服务接口类:

package org.example.springboot3_mybatisplus_redis_jwt.service;


import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.example.springboot3_mybatisplus_redis_jwt.pojo.User;

import java.util.List;

public interface UserService {
    Page<User> SelectAll(int currentPage, int pageSize);

    User findByUserName(String username);

    void register(String username, String password);

    void updateAvatar(String avatarUrl);

    Page<User> searchUser(String userName, Integer currentPage, Integer pageSize);

    Boolean selectUsername(String username);

    void updateUserAddress(String userAddress, int id);

    void updatePwd(String password, Integer id);

    void update(User user);

    Boolean selectPassword(String password, Integer id);

    void Add(User user);

    void DeleteId(Integer id);

    void deleteIds(List<Integer> ids);

    boolean logoff(String username, String password);

    User SelectId(Integer id);
}

13.在impl包下创建projectinfServiceImpl、UserServiceImpl服务实现类。 

 (1)projectinfServiceImpl服务实现类:

package org.example.springboot3_mybatisplus_redis_jwt.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.example.springboot3_mybatisplus_redis_jwt.mapper.projectinfMapper;
import org.example.springboot3_mybatisplus_redis_jwt.pojo.projectinf;
import org.example.springboot3_mybatisplus_redis_jwt.service.projectinfService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

@Service
public class projectinfServiceImpl implements projectinfService {

    @Autowired
    private projectinfMapper mapper;
    @Override
    public Page<projectinf> getAll(@RequestParam(name = "currentPage", defaultValue = "1") Integer currentPage, @RequestParam(name = "pageSize", defaultValue = "5")Integer pageSize) {
        Page<projectinf> projectinfPage = new Page<>(currentPage, pageSize);
        QueryWrapper<projectinf> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByAsc("id");
        Page<projectinf> projectinfPage1 = mapper.selectPage(projectinfPage, queryWrapper);
        projectinfPage1.getRecords().forEach(System.out::println);
        projectinfPage1.getCurrent();
        projectinfPage1.getSize();
        projectinfPage1.getPages();
        return projectinfPage;

    }

    @Override
    public void delete(Integer id) {
        mapper.deleteById(id);
    }

    @Override
    public void deleteIds(List<Integer> ids) {
        mapper.deleteBatchIds(ids);
    }

    @Override
    public Page<projectinf> search( String start,String end, String name, String status, Integer currentPage, Integer pageSize) {
        Page<projectinf> projectinfPage = new Page<>(currentPage, pageSize);
        QueryWrapper<projectinf> queryWrapper = new QueryWrapper<>();
        if (start != null&&!start.equals("")) {
            queryWrapper.ge("startDate", start);
        }
        if (end!=null&&!end.equals("")) {
            queryWrapper.le("endDate", end);
        }
        if (name!=null&&!name.equals("")) {
            queryWrapper.like("projectName", name);
        }
        if (status!=null&&!status.equals("")) {
            queryWrapper.eq("status", status);

        }

        Page<projectinf> projectinfPage1 = mapper.selectPage(projectinfPage, queryWrapper);
        return projectinfPage1;
    }

    @Override
    public int update(projectinf projectinf) {
        int i = mapper.updateById(projectinf);
        return i;
    }

    @Override
    public int add(projectinf inf) {
        int i = mapper.insert(inf);
        return i;

    }

    @Override
    public Boolean selectName(String projectName) {
        List<projectinf> typeList = mapper.selectList(new QueryWrapper<projectinf>().eq("projectName", projectName));
        if (typeList != null && typeList.size() > 0) {
            return true;
        }
        return false;
    }
}

 (2) UserServiceImpl 服务实现类: 

package org.example.springboot3_mybatisplus_redis_jwt.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.example.springboot3_mybatisplus_redis_jwt.mapper.UserMapper;
import org.example.springboot3_mybatisplus_redis_jwt.pojo.User;
import org.example.springboot3_mybatisplus_redis_jwt.service.UserService;
import org.example.springboot3_mybatisplus_redis_jwt.utils.Md5Util;
import org.example.springboot3_mybatisplus_redis_jwt.utils.ThreadLocalUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;


    @Override
    public Page<User> SelectAll(int currentPage, int pageSize) {
        Page<User> page = new Page<>(currentPage, pageSize);
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.orderByAsc("id");
        Page<User> userPage = userMapper.selectPage(page, userQueryWrapper);
        System.out.println("当前页:" + userPage.getCurrent());
        System.out.println("每页记录数:" + userPage.getSize());
        System.out.println("总记录数:" + userPage.getTotal());
        System.out.println("总页数:" + userPage.getPages());
        List<User> userPageList = userPage.getRecords();
        userPageList.forEach(System.out::println);
        return userPage;

    }

    @Override
    public User findByUserName(String username) {
        return userMapper.selectOne(new QueryWrapper<User>().eq("username", username));
    }

    @Override
    public void register(String username, String password) {
        String md5String = Md5Util.getMD5String(password);
        User user = new User();
        user.setUsername(username);
        user.setPassword(md5String);
        userMapper.insert(user);
    }
    @Override
    public boolean logoff(String username, String password) {
        int delete = userMapper.delete(new QueryWrapper<User>().eq("username", username).eq("password", Md5Util.getMD5String(password)));
        if (delete > 0) {
            return true;
        }
        return false;
    }

    @Override
    public void DeleteId(Integer id) {
        userMapper.deleteById(id);
    }
    @Override
    public void deleteIds(List<Integer> ids) {
        userMapper.deleteBatchIds(ids);
    }

    @Override
    public User SelectId(Integer id) {
        return userMapper.selectOne(new QueryWrapper<User>().eq("id", id));
    }

    @Override
    public void Add(User user) {
        user.setPassword(Md5Util.getMD5String(user.getPassword()));
        userMapper.insert(user);
    }
    @Override
    public Boolean selectPassword(String password, Integer id) {
        User user = userMapper.selectById(id);
        if (user != null) {
            if (Md5Util.getMD5String(password).equals(user.getPassword())) {
                return true;
            }
            return false;
        }
        return false;
    }
    @Override
    public void update(User user) {
        user.setPassword(Md5Util.getMD5String(user.getPassword()));
        userMapper.updateById(user);
    }
    @Override
    public void updatePwd(String password, Integer id) {
        System.out.println(password+":"+id);
        String md5String = Md5Util.getMD5String(password);
        LambdaUpdateWrapper<User> userLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        userLambdaUpdateWrapper.eq(User::getId, id);
        userLambdaUpdateWrapper.set(User::getPassword, md5String);
        userMapper.update(userLambdaUpdateWrapper);
    }
    @Override
    public void updateUserAddress(String userAddress, int id) {
        LambdaUpdateWrapper<User> eq = new LambdaUpdateWrapper<User>().eq(User::getAddress, userAddress).eq(User::getId, id);
        System.out.println(eq);
        userMapper.update(eq);

    }

    @Override
    public void updateAvatar(String avatarUrl) {
        Map<String, Object> map = ThreadLocalUtil.get();
        Integer id = (Integer) map.get("id");
        System.out.println(id+":"+avatarUrl);
        LambdaUpdateWrapper<User> userLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        userLambdaUpdateWrapper.eq(User::getId, id);
        userLambdaUpdateWrapper.set(User::getUserpic, avatarUrl);
        userMapper.update(userLambdaUpdateWrapper);
        System.out.println("===============");
    }
    @Override
    public Page<User> searchUser(String userName, Integer currentPage, Integer pageSize) {
        Page<User> page = new Page<>(currentPage, pageSize);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        Page<User> userPage = userMapper.selectPage(page, queryWrapper.like("username",userName));
        return userPage;
    }

    @Override
    public Boolean selectUsername(String username) {
        List<User> username1 = userMapper.selectList(new QueryWrapper<User>().eq("username", username));
        System.out.println(username1);
        if (username1.size() > 0) {
            return true;
        }else {
            return false;
        }
    }
}

14.后端最终目录

 

三、前端(vue3+elementplus+pinia+html5) 

1.创建vue3项目。

任意创建一个目录,在该目录下的路径上cmd回车进入命令行界面,然后运行npm init vue@latest命令回车,输入y再回车,然后除了pinia选是外其它全部选否回车。

2.删除非必要组件,比如HelloWord.vue相关组件等。

3.安装mybatisplus以及插件。

npm install element-plus --save

npm install axios

npm install sass -D

npm install pinia-persistedstate-plugin

 4.修改main.js

import './assets/main.scss'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { createPersistedState } from 'pinia-plugin-persistedstate';
import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import locale from "element-plus/dist/locale/zh-cn.js";

const app = createApp(App)
const pinia = createPinia();
const persist = createPersistedState();
pinia.use(persist);
app.use(pinia)
app.use(router)
app.use(ElementPlus, { locale });//这是国际化配置,比如分页时候
// app.use(ElementPlus)
app.mount('#app')

5.修改App.vue

<script setup>
</script>

<template>
  <RouterView />
</template>

<style scoped>
</style>

6.修改vite.config.js(添加前后端跨越配置)

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  },
  server: {
    proxy: {
      "/api": {
        target: "http://localhost:9095/", // 后端服务器地址
        changeOrigin: true, // 是否改变请求域名
        rewrite: (path) => path.replace(/^\/api/, ""), //将原有请求路径中的api替换为''
      },
    },
  },
})

 7.在src包下创建utils包,而且创建request.js文件

/定制请求的实例
//导入axios  npm install axios
import axios from 'axios';

import { ElMessage } from 'element-plus'
//定义一个变量,记录公共的前缀  ,  baseURL
// const baseURL = 'http://localhost:9095';
const baseURL = '/api';
const instance = axios.create({ baseURL })

import { useTokenStore } from '@/stores/token.js'
//添加请求拦截器
instance.interceptors.request.use(
    (config) => {
        //请求前的回调
        //添加token
        const tokenStore = useTokenStore();
        //判断有没有token
        if (tokenStore.token) {
            config.headers.cookieshopUser = tokenStore.token
        }
        return config;
    },
    (err) => {
        //请求错误的回调
        Promise.reject(err)
    }
)

/* import {useRouter} from 'vue-router'
const router = useRouter(); */

import router from '@/router'
//添加响应拦截器
instance.interceptors.response.use(
    result => {
        return result.data;
        //判断业务状态码
        // if (result.data.code === 0) {
        //     return result.data;
        // }

        // //操作失败
        // //alert(result.data.msg?result.data.msg:'服务异常')
        // ElMessage.error(result.data.msg ? result.data.msg : '服务异常')
        // //异步操作的状态转换为失败
        // return Promise.reject(result.data)

    },
    err => {
        //判断响应状态码,如果为401,则证明未登录,提示请登录,并跳转到登录页面
        if (err.response.status === 401) {
            ElMessage.error('请先登录')
            router.push('/login')
        } else {
            ElMessage.error('服务异常')
        }

        return Promise.reject(err);//异步的状态转化成失败的状态
    }
)

export default instance;

8.在src包下创建stores包,而且创建token.js和useInfo.js文件。 

(1)token.js:文件

import { defineStore } from "pinia";
import { ref } from "vue";

export const useTokenStore = defineStore(
  "token",
  () => {
    const token = ref("");
    const setToken = (newToken) => {
      token.value = newToken;
    };
    const removeToken = () => {
      token.value = "";
    };
    return { token, setToken, removeToken };
  },
  {
    persist: true,
  }
);

(2)useInfo.js文件:

import { defineStore } from "pinia";
import { ref } from "vue";

export const useUserInfoStore = defineStore(
  "userInfo",
  () => {
    //1.定义用户信息
    const info = ref({});
    //2.定义修改用户信息的方法
    const setInfo = (newInfo) => {
      info.value = newInfo;
    };
    //3.定义清空用户信息的方法
    const removeInfo = () => {
      info.value = {};
    };

    return { info, setInfo, removeInfo };
  },
  {
    persist: true,
  }
);

   9.在assets包下创建mian.scss全局样式(在此包下还有添加图片,该步骤省略)

body {
  margin: 0;
  background-color: #f5f5f5;
}

/* fade-slide */
.fade-slide-leave-active,
.fade-slide-enter-active {
  transition: all 0.3s;
}

.fade-slide-enter-from {
  transform: translateX(-30px);
  opacity: 0;
}

.fade-slide-leave-to {
  transform: translateX(30px);
  opacity: 0;
}

10.在src包下创建api包,而且创建classes.js和user.js文件。 

  (1)classes.js文件:

import request from "@/utils/request.js";

//获取列表
export const classesInfoGetService = (params) => {
    return request.get("/project/all", { params: params });
};

//判断项目名称是否存在
export const classesNameGetService = (params) => {
    return request.get("/project/selectProjectName?projectName=" + params);
};

//获取搜索列表
export const classesSerchNameGetService = (params) => {
    return request.get("/project/search", { params: params });
};

//添加
export const classesAddService = (classesModel) => {
    return request.post("/project/add", classesModel);
};

//修改
export const classesUpdateService = (classesModel) => {
    return request.post("/project/update", classesModel);
};

//删除
export const classesDeleteService = (id) => {
    return request.delete("/project/delete/" + id);
};
//批量删除
export const classesDeletemoreService = (ids) => {
    return request.delete("/project/deleteMore/" + ids);
};

 (2)user.js文件:

import request from "@/utils/request.js";
//注册
export const userRegisterService = (registerData) => {
  const params = new URLSearchParams();
  for (let key in registerData) {
    params.append(key, registerData[key]);
  }
  return request.post("/register", params);
};
//登录
export const userLoginService = (loginData) => {
  const params = new URLSearchParams();
  for (let key in loginData) {
    params.append(key, loginData[key]);
  }
  return request.post("/login", params);
};

//注销
export const userLogoffService = (logoff) => {
  const params = new URLSearchParams();
  for (let key in logoff) {
    params.append(key, logoff[key]);
  }
  return request.post("/logoff", params);
};

//获取个人信息
export const userInfoGetService = () => {
  return request.get("/userInfo");
};
//检查密码和原密码是否相同
export const CheckUserPwdGetService = (params) => {
  return request.get("/selectPassword", { params: params });
};

//修改个人信息
export const userInfoUpdateService = (userInfo) => {
  return request.put("/update", userInfo);
};

//修改头像
export const userAvatarUpdateService = (avatarUrl) => {
  let params = new URLSearchParams();
  params.append("avatarUrl", avatarUrl);
  return request.patch("/updateAvatar", params);
};
//修改密码
export const userPwdUpdateService = (param) => {
  let params = new URLSearchParams();
  for (let key in param) {
    params.append(key, param[key])
  }
  return request.patch("/updatePwd", params);
};

//获取客户列表
export const userCoustomGetService = (params) => {
  return request.get("/SelectAll", { params: params });
};

//获取搜索列表
export const coustomSerchNameGetService = (params) => {
  return request.get("/serchUserName", { params: params });
};

//添加客户
export const coustomAddService = (coustomModel) => {
  return request.post("/add", coustomModel);
};

//修改分类
export const coustomUpdateService = (coustomModel) => {
  return request.put("/update", coustomModel);
};

//删除客户
export const coustomDeleteService = (id) => {
  return request.delete("/deleteId/" + id);
};
//批量删除分类
export const coustomDeleteMoreService = (ids) => {
  return request.delete("/deleteMore/" + ids);
};

//判断客户名是否存在
export const coustomNameGetService = (params) => {
  return request.get("/selectUsername?username=" + params);
};

 11.在views包下创建project包和user包。而且在project包创建All.vue文件;在user包下创建LogOff.vue、UserAvatar.vue、UserInfo.vue|、UserResetPassword.vue;在views包下Layout.vue和Login.vue 

(1)Layout.vue:

<script setup>
import {
  Management,
  Promotion,
  UserFilled,
  User,
  Crop,
  EditPen,
  SwitchButton,
  CaretBottom,
} from "@element-plus/icons-vue";
//默认未加载图
import avatar from "@/assets/default.png";
//导入接口函数
import { userInfoGetService } from "@/api/user.js";
//导入pinia
import { useUserInfoStore } from "@/stores/userInfo.js";

const userInfoStore = useUserInfoStore();

//获取个人信息
const getUserInfo = async () => {
  let result = await userInfoGetService();
  //存储pinia
  // userInfoStore.info = result.data;
  userInfoStore.setInfo(result.data);
};
getUserInfo();

//dropDown条目被点击后,回调的函数
import { useRouter } from "vue-router";

const router = useRouter();
import { ElMessage, ElMessageBox } from "element-plus";
import { useTokenStore } from "@/stores/token.js";

const tokenStore = useTokenStore();
const handleCommand = (command) => {
  if (command === "logout") {
    //退出登录
    ElMessageBox.confirm("你确认退出登录码?", "温馨提示", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(async () => {
        //用户点击了确认
        //清空pinia中的token和个人信息
        // userInfoStore.info = {};
        // tokenStore.token = "";
        tokenStore.removeToken();
        userInfoStore.removeInfo();
        //跳转到登录页
        router.push("/login");
      })
      .catch(() => {
        //用户点击了取消
        ElMessage({
          type: "info",
          message: "取消退出",
        });
      });
  } else {
    //路由
    router.push(command);
  }
};
</script>

<template>
  <el-container class="layout-container">
    <!-- 右侧主区域 -->
    <el-container>
      <!-- 头部区域 -->
      <el-header>
        <div>
          管理员<strong>{{
            userInfoStore.info.username
              ? userInfoStore.info.username
              : userInfoStore.info.name
          }}</strong>
        </div>
        <el-dropdown placement="bottom-end" @command="handleCommand">
          <span class="el-dropdown__box">
            <el-avatar
              :src="
                userInfoStore.info.userpic ? userInfoStore.info.userpic : avatar
              "
            />
            <el-icon>
              <CaretBottom />
            </el-icon>
          </span>
          <template #dropdown>
            <el-dropdown-menu>
              <el-dropdown-item command="/user/info" :icon="User"
                >基本资料
              </el-dropdown-item>
              <el-dropdown-item command="/user/avatar" :icon="Crop"
                >更换头像
              </el-dropdown-item>
              <el-dropdown-item command="/user/repwd" :icon="EditPen"
                >重置密码
              </el-dropdown-item>
              <el-dropdown-item command="/user/logoff" :icon="EditPen"
                >账号注销
              </el-dropdown-item>
              <el-dropdown-item command="/all" :icon="Crop"
                >后台管理
              </el-dropdown-item>
              <el-dropdown-item command="logout" :icon="SwitchButton"
                >退出登录
              </el-dropdown-item>
            </el-dropdown-menu>
          </template>
        </el-dropdown>
      </el-header>
      <!-- 中间区域 -->
      <el-main>
        <!-- <div style="width: 1290px; height: 570px; border: 1px solid red">
          内容展示区
        </div> -->
        <router-view />
      </el-main>
      <!-- 底部区域 -->
      <el-footer>申报项目信息管理系统 ©2024</el-footer>
    </el-container>
  </el-container>
</template>

<style lang="scss" scoped>
.layout-container {
  height: 100vh;

  .el-aside {
    background-color: #232323;

    &__logo {
      height: 120px;
      background: url("@/assets/logo.png") no-repeat center / 120px auto;
    }

    .el-menu {
      border-right: none;
    }
  }

  .el-header {
    background-color: #fff;
    display: flex;
    align-items: center;
    justify-content: space-between;

    .el-dropdown__box {
      display: flex;
      align-items: center;

      .el-icon {
        color: #999;
        margin-left: 10px;
      }

      &:active,
      &:focus {
        outline: none;
      }
    }
  }

  .el-footer {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    color: #666;
  }
}
</style>

(2)Login.vue:

<script setup>
import { User, Lock } from "@element-plus/icons-vue";
import { ref } from "vue";
import { ElMessage } from "element-plus";

//控制注册与登录表单的显示, 默认显示注册
const isRegister = ref(false);
const registerData = ref({
  username: "",
  password: "",
  rePassword: "",
});
const checkRePassword = (rule, value, callback) => {
  if (value === "") {
    callback(new Error("请确认密码"));
  } else if (value !== registerData.value.password) {
    callback(new Error("请确保两次输入密码一样"));
  } else {
    callback();
  }
};
const rules = {
  username: [
    { required: true, message: "请输入用户名", trigger: "blur" },
    { min: 2, max: 16, message: "长度为5~16为非空字符", trigger: "blur" },
  ],
  password: [
    { required: true, message: "请输入密码", trigger: "blur" },
    { min: 5, max: 16, message: "长度为5~16为非空字符", trigger: "blur" },
  ],
  rePassword: [{ validator: checkRePassword, trigger: "blur" }],
};

import { userRegisterService, userLoginService } from "@/api/user.js";
const register = async () => {
  let result = await userRegisterService(registerData.value);
  if (result.code === 0) {
    return ElMessage.success("注册成功");
  }
  if (result.message === "用户名已被占用") {
    return ElMessage.warning("用户名已被占用");
  }
  ElMessage.error("注册失败");
};

import { useTokenStore } from "@/stores/token.js";
import { useRouter } from "vue-router";
const tokenStore = useTokenStore();
const router = useRouter();
const login = async () => {
  let result = await userLoginService(registerData.value);
  console.log(result);
  if (result.code === 0) {
    router.push("/");
    console.log(result.data);
    tokenStore.setToken(result.data);
    return ElMessage.success("登录成功");
  }
  if (result.message === "用户名不存在") {
    return ElMessage.warning("用户名不存在");
  }
  if (result.message === "密码错误") {
    return ElMessage.error("密码错误");
  }
  ElMessage.error("登录失败");
};
const clearRegisterData = () => {
  registerData.value = {
    username: "",
    password: "",
    rePassword: "",
  };
};
</script>

<template>
  <el-row class="login-page">
    <el-col :span="12" class="bg"></el-col>
    <el-col :span="6" :offset="3" class="form">
      <!-- 注册表单 -->
      <el-form
        ref="form"
        size="large"
        autocomplete="off"
        v-if="isRegister"
        :model="registerData"
        :rules="rules"
      >
        <el-form-item>
          <h1>注册</h1>
        </el-form-item>
        <el-form-item prop="username">
          <el-input
            :prefix-icon="User"
            placeholder="请输入用户名"
            v-model="registerData.username"
          ></el-input>
        </el-form-item>
        <el-form-item prop="password">
          <el-input
            :prefix-icon="Lock"
            type="password"
            placeholder="请输入密码"
            v-model="registerData.password"
          ></el-input>
        </el-form-item>
        <el-form-item prop="rePassword">
          <el-input
            :prefix-icon="Lock"
            type="password"
            placeholder="请输入再次密码"
            v-model="registerData.rePassword"
          ></el-input>
        </el-form-item>
        <!-- 注册按钮 -->
        <el-form-item>
          <el-button
            class="button"
            type="primary"
            auto-insert-space
            @click="register()"
          >
            注册
          </el-button>
        </el-form-item>
        <el-form-item class="flex">
          <el-link
            type="info"
            :underline="false"
            @click="
              isRegister = false;
              clearRegisterData();
            "
          >
            ← 返回
          </el-link>
        </el-form-item>
      </el-form>
      <!-- 登录表单 -->
      <el-form
        ref="form"
        size="large"
        autocomplete="off"
        v-else
        :model="registerData"
        :rules="rules"
      >
        <el-form-item>
          <h1>登录</h1>
        </el-form-item>
        <el-form-item prop="username">
          <el-input
            :prefix-icon="User"
            placeholder="请输入用户名"
            v-model="registerData.username"
          ></el-input>
        </el-form-item>
        <el-form-item prop="password">
          <el-input
            name="password"
            :prefix-icon="Lock"
            type="password"
            placeholder="请输入密码"
            v-model="registerData.password"
          ></el-input>
        </el-form-item>
        <el-form-item class="flex">
          <div class="flex">
            <el-checkbox>记住我</el-checkbox>
            <el-link type="primary" :underline="false">忘记密码?</el-link>
          </div>
        </el-form-item>
        <!-- 登录按钮 -->
        <el-form-item>
          <el-button
            class="button"
            type="primary"
            auto-insert-space
            @click="login"
            >登录</el-button
          >
        </el-form-item>
        <el-form-item class="flex">
          <el-link
            type="info"
            :underline="false"
            @click="
              isRegister = true;
              clearRegisterData();
            "
          >
            注册 →
          </el-link>
        </el-form-item>
      </el-form>
    </el-col>
  </el-row>
</template>

<style lang="scss" scoped>
/* 样式 */
.login-page {
  height: 100vh;
  background-color: #fff;

  .bg {
    background: url("@/assets/login_bg.jpg") no-repeat center / cover;
    border-radius: 0 20px 20px 0;
  }

  .form {
    display: flex;
    flex-direction: column;
    justify-content: center;
    user-select: none;

    .title {
      margin: 0 auto;
    }

    .button {
      width: 100%;
    }

    .flex {
      width: 100%;
      display: flex;
      justify-content: space-between;
    }
  }
}
</style>

 (3)All.vue:

<script lang="ts" setup>
import { Edit, Delete } from "@element-plus/icons-vue";

import { ref } from "vue";
const classesTable = ref([]);
//分页条数据模型
const pageNum = ref(1); //当前页
const total = ref(20); //总条数
const pageSize = ref(10); //每页条数
const pageTotle = ref(12); //总页数

//当每页条数发生了变化,调用此函数
const onSizeChange = (size) => {
  pageSize.value = size;
  getClassesList();
};
//当前页码发生变化,调用此函数
const onCurrentChange = (num) => {
  pageNum.value = num;
  getClassesList();
};
//获取数据
import {
  classesInfoGetService,
  classesNameGetService,
  classesAddService,
  classesUpdateService,
  classesDeleteService,
  classesDeletemoreService,
  classesSerchNameGetService,
} from "@/api/classes.js";
const getClassesList = async () => {
  let params = {
    currentPage: pageNum.value,
    pageSize: pageSize.value,
  };
  let result = await classesInfoGetService(params);
  console.log(result.data);
  classesTable.value = result.data.records;
  pageNum.value = result.data.current;
  pageSize.value = result.data.size;
  total.value = result.data.total;
  pageTotle.value = result.data.pages;
};
getClassesList();

// 隔行变色的函数
const tableRowClassName = ({ row, rowIndex }) => {
  return rowIndex % 2 === 0 ? "even-row" : "odd-row";
};

//控制添加弹窗
const dialogVisible = ref(false);

//添加数据模型
const classesModel = ref({
  id: 1,
  projectName: "",
  startDate: Date,
  endDate: Date,
  status: "",
});

//访问后台,添加文章分类
import { ElMessage } from "element-plus";
const addclasses = () => {
  if (formRef.value) {
    formRef.value.validate(async (valid, fields) => {
      if (valid) {
        console.log("submit!");
        let returnflag = await classesNameGetService(
          classesModel.value.projectName
        );
        console.log(
          JSON.stringify(returnflag) + "ddddddddddddddddd" + returnflag.data
        );
        if (returnflag.data) {
          return ElMessage.error("该申报已添加");
        }
        let result = await classesAddService(classesModel.value);
        // console.log(result);
        console.log(result.data);
        if (result.data > 0) {
          ElMessage.success("申报添加成功");
        } else {
          ElMessage.error("申报添加失败");
        }

        //隐藏弹窗
        dialogVisible.value = false;
        //再次访问后台接口,查询所有分类
        getClassesList();
      } else {
        console.error("error submit!", fields);
        ElMessage.error("不能为空!");
        // 可以在这里处理表单错误,比如显示错误提示
      }
    });
  }
};

const title = ref("");

// 修改回显
const showDialog = (row) => {
  title.value = "申报项目信息";
  dialogVisible.value = true;
  //将row中的数据赋值给classesModel
  classesModel.value.projectName = row.projectName;
  //修改的时候必须传递分类的id,所以扩展一个id属性
  classesModel.value.id = row.id;
  classesModel.value.startDate = row.startDate;
  classesModel.value.endDate = row.endDate;
  if (row.status === 0) {
    classesModel.value.status = "已申报";
  } else if (row.status === 1) {
    classesModel.value.status = "审核中";
  } else {
    classesModel.value.status = "已审核";
  }
};

const formRef = ref(null);
//修改
const updateClasses = () => {
  if (formRef.value) {
    formRef.value.validate(async (valid, fields) => {
      if (valid) {
        console.log("submit!");
        if (classesModel.value.status === "已申报") {
          classesModel.value.status = "0";
        } else if (classesModel.value.status === "审核中") {
          classesModel.value.status = "1";
        } else if (classesModel.value.status === "已审核") {
          classesModel.value.status = "2";
        }
        console.log(classesModel.value.startDate);
        let result = await classesUpdateService(classesModel.value);
        if (result.data > 0) {
          ElMessage.success("审核成功");
        } else {
          ElMessage.error("审核失败");
        }

        //隐藏弹窗
        dialogVisible.value = false;
        //再次访问后台接口,查询所有分类
        getClassesList();
      } else {
        console.error("error submit!", fields);
        ElMessage.error("不能为空!");
        // 可以在这里处理表单错误,比如显示错误提示
      }
    });
  }
};

//清空模型数据
const clearClassesModel = () => {
  (classesModel.value.id = null),
    (classesModel.value.projectName = ""),
    (classesModel.value.startDate = null),
    (classesModel.value.endDate = null),
    (classesModel.value.status = "");
};

import { ElMessageBox } from "element-plus";
//删除分类
const deleteClasses = (row) => {
  ElMessageBox.confirm("你确认删除该信息吗?", "温馨提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(async () => {
      //用户点击了确认
      let result = await classesDeleteService(row.id);
      ElMessage.success("删除成功");
      getClassesList();
    })
    .catch(() => {
      //用户点击了取消
      ElMessage({
        type: "info",
        message: "取消删除",
      });
    });
};

//表格上选择多选框
const selectedRows = ref([]);
const handleSelectionChange = (val) => {
  selectedRows.value = val;
};
const ids = ref([]);
const deleteClassesMore = () => {
  if (selectedRows.value.length > 0) {
    ElMessageBox.confirm("你确认批量删除该信息?", "温馨提示", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(async () => {
        selectedRows.value.forEach((row) => {
          console.log(row.id);
          ids.value.push(row.id);
        });
        console.log(ids.value);
        console.log(ids.value[1]);

        let result = await classesDeletemoreService(ids.value);
        ElMessage.success("批量删除成功");
        ids.value = [];
        getClassesList();
      })
      .catch(() => {
        //用户点击了取消
        ElMessage({
          type: "info",
          message: "取消删除",
        });
      });
  } else {
    ElMessage.error("您还未选择删除分类");
  }
};

//搜索
const searchClassesName = ref("");
const searchClassesstatus = ref("");
const searchClassesstartDate = ref("");
const searchClassesendDate = ref("");
const searchClassesbuttom = async () => {
  let params = {
    status: searchClassesstatus.value,
    start: searchClassesstartDate.value,
    end: searchClassesendDate.value,
    name: searchClassesName.value,
    currentPage: pageNum.value,
    pageSize: pageSize.value,
  };
  let result = await classesSerchNameGetService(params);
  console.log(result.data);
  classesTable.value = result.data.records;
  pageNum.value = result.data.current;
  pageSize.value = result.data.size;
  total.value = result.data.total;
  pageTotle.value = result.data.pages;
};
</script>
<template>
  <el-card class="page-container">
    <template #header>
      <div class="header">
        <span>搜索栏</span>
        <div>
          <el-input
            v-model="searchClassesName"
            placeholder="项目名称"
            clearable
            style="width: 330px"
          ></el-input>
          <el-select
            v-model="searchClassesstatus"
            placeholder="申报状态"
            clearable
            style="width: 130px; padding: 0px 20px"
          >
            <el-option label="全部" value="" />
            <el-option label="已申报" value="0" />
            <el-option label="审核中" value="1" />
            <el-option label="已审核" value="2" />
          </el-select>

          <el-date-picker
            v-model="searchClassesstartDate"
            type="date"
            placeholder="日期范围开始"
            format="YYYY-MM-DD"
            value-format="YYYY-MM-DD"
          />
          <el-date-picker
            v-model="searchClassesendDate"
            type="date"
            placeholder="日期范围结束"
            format="YYYY-MM-DD"
            value-format="YYYY-MM-DD"
          />
          <el-button type="primary" @click="searchClassesbuttom"
            >搜索</el-button
          >
          <el-button
            type="primary"
            @click="
              (dialogVisible = true), (title = '添加申报'); //逗号,分号都可以
              clearClassesModel();
            "
            >添加申报</el-button
          ><el-button type="primary" @click="deleteClassesMore"
            >批量删除</el-button
          >
        </div>
      </div>
    </template>
    <h1 style="font-size: 35px; text-align: center">申报项目列表</h1>
    <el-table
      :data="classesTable"
      v-model:selection="selectedRows"
      @selection-change="handleSelectionChange"
      style="width: 100%"
      border
      highlight
      :row-class-name="tableRowClassName"
      :header-cell-style="{ textAlign: 'center' }"
      :cell-style="{ textAlign: 'center' }"
    >
      <el-table-column width="100" type="selection"> </el-table-column>
      <el-table-column label="序号" width="80" type="index"> </el-table-column>
      <el-table-column label="项目编号" width="90" prop="id"> </el-table-column>
      <el-table-column label="项目名称" prop="projectName"></el-table-column>
      <el-table-column label="申报开始日期" prop="startDate"> </el-table-column>
      <el-table-column label="申报结束日期" prop="endDate"></el-table-column>
      <el-table-column label="申报状态" prop="status">
        <template #default="scope">
          <p v-if="scope.row.status == 0">已申报</p>
          <p v-else-if="scope.row.status == 1">审核中</p>
          <p v-else>已审核</p>
        </template>
      </el-table-column>
      <el-table-column label="操作" width="120">
        <template #default="{ row }">
          <el-button
            v-show="row.status === 1 || row.status == 0"
            circle
            plain
            @click="showDialog(row)"
            text
            primary
            color="#626aef"
            >审核</el-button
          >
          <el-button
            :icon="Delete"
            circle
            plain
            type="danger"
            @click="deleteClasses(row)"
          ></el-button>
        </template>
      </el-table-column>
      <template #empty>
        <el-empty description="没有数据" />
      </template>
    </el-table>
    <!-- 分页条 -->
    <el-pagination
      layout="->,  prev, pager, next,jumper,total, sizes"
      v-model:current-page="pageNum"
      v-model:page-size="pageSize"
      :page-sizes="[5, 10, 15, 20, 30, 50, 100]"
      background
      page-count:pageTotle
      :total="total"
      @size-change="onSizeChange"
      @current-change="onCurrentChange"
      style="margin-top: 20px; justify-content: center"
    />
    <!-- <div style="margin-top: 20px">选中的行:{{ selectedRows }}</div> -->
    <!-- 添加弹窗 -->
    <el-dialog v-model="dialogVisible" :title="title" width="30%">
      <el-form
        :model="classesModel"
        label-width="auto"
        label-position="left"
        style="padding-right: 30px"
        ref="formRef"
      >
        <el-form-item
          label="项目编号"
          prop="id"
          v-show="title == '申报项目信息'"
        >
          <el-input v-model="classesModel.id" disabled></el-input>
        </el-form-item>
        <el-form-item
          label="项目名称"
          prop="projectName"
          :rules="{
            required: true,
            message: '不能为空',
            trigger: 'blur',
          }"
        >
          <el-input
            v-model="classesModel.projectName"
            minlength="1"
            maxlength="100"
            clearable
          ></el-input>
        </el-form-item>
        <el-form-item
          label="申报开始时间"
          prop="startDate"
          v-show="title == '申报项目信息'"
          :rules="{
            required: true,
            message: '不能为空',
            trigger: 'blur',
          }"
        >
          <el-input v-model="classesModel.startDate" clearable></el-input>
        </el-form-item>
        <el-form-item
          label="申报结束时间"
          prop="endDate"
          v-show="title == '申报项目信息'"
          :rules="{
            required: true,
            message: '不能为空',
            trigger: 'blur',
          }"
        >
          <el-input v-model="classesModel.endDate" clearable></el-input>
        </el-form-item>
        <el-form-item
          label="申报开始时间"
          prop="startDate"
          v-show="title == '添加申报'"
          :rules="{
            required: true,
            message: '不能为空',
            trigger: 'blur',
          }"
        >
          <el-date-picker
            v-model="classesModel.startDate"
            type="date"
            placeholder="申报开始时间"
          />
        </el-form-item>
        <el-form-item
          label="申报结束时间"
          prop="endDate"
          v-show="title == '添加申报'"
          :rules="{
            required: true,
            message: '不能为空',
            trigger: 'blur',
          }"
        >
          <el-date-picker
            v-model="classesModel.endDate"
            type="date"
            placeholder="申报结束时间"
          />
        </el-form-item>
        <el-form-item
          label="申报状态"
          prop="status"
          :rules="{
            required: true,
            message: '不能为空',
            trigger: 'blur',
          }"
        >
          <el-select
            v-model="classesModel.status"
            placeholder="申报状态"
            clearable
            style="width: 130px; padding: 0px 20px"
          >
            <el-option
              label="已申报"
              value="0"
              v-show="classesModel.status == '已申报'"
            />
            <el-option v-show="title == '添加申报'" label="已申报" value="0" />
            <el-option label="审核中" value="1" />
            <el-option label="已审核" value="2" />
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="dialogVisible = false">返回</el-button>
          <el-button
            type="primary"
            @click="title == '添加申报' ? addclasses() : updateClasses()"
            >{{ title == "添加申报" ? "确定" : "审核" }}
          </el-button>
        </span>
      </template>
    </el-dialog>
  </el-card>
</template>

<style lang="scss" >
.page-container {
  min-height: 100%;
  box-sizing: border-box;

  .header {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
}

.el-table .even-row {
  background-color: #f09191;
}
.el-table .odd-row {
  background-color: #ffffff;
}

.demo-date-picker {
  display: flex;
  width: 100%;
  padding: 0;
  flex-wrap: wrap;
}
.demo-date-picker .block {
  padding: 30px 0;
  text-align: center;
  border-right: solid 1px var(--el-border-color);
  flex: 1;
}
.demo-date-picker .block:last-child {
  border-right: none;
}
.demo-date-picker .demonstration {
  display: block;
  color: var(--el-text-color-secondary);
  font-size: 14px;
  margin-bottom: 20px;
}
</style>

(4)LogOff.vue:

<script setup>
import { ref } from "vue";
import { userLogoffService } from "@/api/user.js";
import { ElMessage, ElMessageBox } from "element-plus";
import { useRouter } from "vue-router";
const router = useRouter();
import { useUserInfoStore } from "@/stores/userInfo.js";
const userInfoStore = useUserInfoStore();
import { useTokenStore } from "@/stores/token.js";
const tokenStore = useTokenStore();

const data = ref({
  username: "",
  password: "",
});
const logoff = () => {
  ElMessageBox.confirm("确定注销用户吗?", "警告", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(async () => {
      if (data.value.username != userInfoStore.info.username) {
        return ElMessage.error("用户名或密码不正确");
      } else {
        let result = await userLogoffService(data.value);
        console.log(result);
        if (result.data) {
          router.push("/login");
          tokenStore.removeToken();
          userInfoStore.removeInfo();
          return ElMessage.success("注销成功");
        }
        ElMessage.error("用户名或密码不正确");
      }
    })
    .catch(() => {
      ElMessage({
        type: "info",
        message: "已取消",
      });
    });
};
</script>
<template>
  <el-card class="page-container">
    <template #header>
      <div class="header">
        <span>账户注销</span>
      </div>
    </template>
    <el-row>
      <el-col :span="12">
        <el-form :model="data" label-width="100px" size="large">
          <el-form-item label="登录名称">
            <el-input v-model="data.username"></el-input>
          </el-form-item>
          <el-form-item label="用户密码" prop="password">
            <el-input v-model="data.password"></el-input>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="logoff()">提交</el-button>
          </el-form-item>
        </el-form>
      </el-col>
    </el-row>
  </el-card>
</template>

  (5)UserAvatar.vue:

<script setup>
import { ref } from "vue";
import avatar from "@/assets/default.png";
import { Plus, Upload } from "@element-plus/icons-vue";
const uploadRef = ref();

//读取用户信息
import { useUserInfoStore } from "@/stores/userInfo.js";
const userInfoStore = useUserInfoStore();
const imgUrl = ref(userInfoStore.info.userpic);

//读取token信息
import { useTokenStore } from "@/stores/token.js";
const tokenStore = useTokenStore();

//图片上传成功的回调
const uploadSuccess = (result) => {
  //回显图片
  imgUrl.value = result.data;
};

//调用接口,更新头像url
import { userAvatarUpdateService } from "@/api/user.js";
import { ElMessage } from "element-plus";
const updateAvatar = async () => {
  let result = await userAvatarUpdateService(imgUrl.value);
  ElMessage.success(result.message ? result.message : "修改成功");
  console.log(userInfoStore.info);
  //更新pinia中的数据
  userInfoStore.info.userpic = imgUrl.value;
};
</script>

<template>
  <el-card>
    <template #header>
      <div class="header">
        <span>更换头像</span>
      </div>
    </template>
    <el-row>
      <el-col :span="12">
        <el-upload
          ref="uploadRef"
          class="avatar-uploader"
          :show-file-list="false"
          :auto-upload="true"
          action="/api/upload"
          name="file"
          :headers="{ cookieshopUser: tokenStore.token }"
          :on-success="uploadSuccess"
        >
          <img v-if="imgUrl" :src="imgUrl" class="avatar" />
          <img v-else :src="avatar" width="278" />
        </el-upload>
        <br />
        <el-button
          type="primary"
          :icon="Plus"
          size="large"
          @click="uploadRef.$el.querySelector('input').click()"
        >
          选择图片
        </el-button>
        <el-button
          type="success"
          :icon="Upload"
          size="large"
          @click="updateAvatar"
        >
          上传头像
        </el-button>
      </el-col>
    </el-row>
  </el-card>
</template>

<style lang="scss" scoped>
.avatar-uploader {
  :deep() {
    .avatar {
      width: 278px;
      height: 278px;
      display: block;
    }

    .el-upload {
      border: 1px dashed var(--el-border-color);
      border-radius: 6px;
      cursor: pointer;
      position: relative;
      overflow: hidden;
      transition: var(--el-transition-duration-fast);
    }

    .el-upload:hover {
      border-color: var(--el-color-primary);
    }

    .el-icon.avatar-uploader-icon {
      font-size: 28px;
      color: #8c939d;
      width: 278px;
      height: 278px;
      text-align: center;
    }
  }
}
</style>

(6)UserInfo.vue:

<script setup>
import { ref } from "vue";
import { useUserInfoStore } from "@/stores/userInfo.js";
const userInfoStore = useUserInfoStore();
const userInfo = ref({
  ...userInfoStore.info,
});
// import { useClassesInfoStore } from "@/stores/classes.js";
// const classesInfoStore = useClassesInfoStore();
// const classesInfo = ref({
//   ...classesInfoStore.info,
// });
const rules = {
  nickname: [
    { required: true, message: "请输入用户昵称", trigger: "blur" },
    {
      pattern: /^\S{2,10}$/,
      message: "昵称必须是2-10位的非空字符串",
      trigger: "blur",
    },
  ],
  email: [
    { required: true, message: "请输入用户邮箱", trigger: "blur" },
    { type: "email", message: "邮箱格式不正确", trigger: "blur" },
  ],
};

//修改用户信息
import { userInfoUpdateService } from "@/api/user.js";
import { ElMessage } from "element-plus";
const updateUserInfo = async () => {
  let result = await userInfoUpdateService(userInfo.value);
  ElMessage.success(result.message ? result.message : "修改成功");
  //更新pinia中的数据
  userInfoStore.info.name = userInfo.value.name;
  userInfoStore.info.email = userInfo.value.email;
  userInfoStore.info.phone = userInfo.value.phone;
  userInfoStore.info.address = userInfo.value.address;
};
</script>
<template>
  <el-card class="page-container">
    <template #header>
      <div class="header">
        <span>基本资料</span>
      </div>
    </template>
    <el-row>
      <el-col :span="12">
        <el-form
          :model="userInfo"
          :rules="rules"
          label-width="100px"
          size="large"
        >
          <el-form-item label="登录名称">
            <el-input v-model="userInfo.username" disabled></el-input>
          </el-form-item>
          <el-form-item label="用户昵称" prop="name">
            <el-input v-model="userInfo.name"></el-input>
          </el-form-item>
          <el-form-item label="用户邮箱" prop="email">
            <el-input v-model="userInfo.email"></el-input>
          </el-form-item>
          <el-form-item label="用户电话" prop="phone">
            <el-input v-model="userInfo.phone"></el-input>
          </el-form-item>
          <el-form-item label="用户地址" prop="address">
            <el-input v-model="userInfo.address"></el-input>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="updateUserInfo"
              >提交修改</el-button
            >
          </el-form-item>
        </el-form>
      </el-col>
    </el-row>
  </el-card>
</template>

(7)UserResetPassword.vue:

<script setup>
import { reactive, ref } from "vue";
import { ElMessage } from "element-plus";
import { CheckUserPwdGetService, userPwdUpdateService } from "@/api/user.js";
import { useUserInfoStore } from "@/stores/userInfo.js";

const userInfoStore = useUserInfoStore();
const userInfo = ref({ ...userInfoStore.info });

const ruleForm = reactive({
  originPwd: "",
  newPwd: "",
  oncePwd: "",
});

const checkOncePwd = (rule, value, callback) => {
  if (value === "") {
    callback(new Error("再次输入密码不能为空"));
  } else if (value !== ruleForm.newPwd) {
    callback(new Error("两次输入密码不一致"));
  } else {
    callback();
  }
};

const ruleFormRef = ref();

const rules = reactive({
  originPwd: [{ required: true, message: "原密码不能为空", trigger: "blur" }],
  newPwd: [{ required: true, message: "新密码不能为空", trigger: "blur" }],
  oncePwd: [
    { required: true, message: "再次输入密码不能为空", trigger: "blur" },
    { validator: checkOncePwd, trigger: "blur" },
  ],
});

const submitForm = () => {
  const formEl = ruleFormRef.value;
  if (!formEl) return;
  formEl.validate(async (valid) => {
    if (valid) {
      let data = {
        id: userInfo.value.id,
        password: ruleForm.originPwd,
      };
      try {
        let result = await CheckUserPwdGetService(data);
        if (result.data) {
          let data1 = {
            id: userInfo.value.id,
            password: ruleForm.newPwd,
          };
          let result1 = await userPwdUpdateService(data1);
          formEl.resetFields();
          ElMessage.success(result1.data);
        } else {
          ElMessage.error("原密码错误");
        }
      } catch (error) {
        console.error("密码验证出错:", error);
        ElMessage.error("密码验证出错,请稍后再试");
      }
      console.log("submit!");
    } else {
      console.log("error submit!");
    }
  });
};

const resetForm = () => {
  const formEl = ruleFormRef.value;
  if (!formEl) return;
  formEl.resetFields();
};
</script>
<template>
  <el-card>
    <template #header>
      <span>重置密码</span>
    </template>
    <div style="text-algin: center; padding: 10px 10px">
      <el-form
        ref="ruleFormRef"
        style="max-width: 600px"
        :model="ruleForm"
        status-icon
        :rules="rules"
        label-width="auto"
        class="demo-ruleForm"
      >
        <el-form-item label="原密码" prop="originPwd">
          <el-input
            v-model="ruleForm.originPwd"
            type="password"
            autocomplete="off"
          />
        </el-form-item>
        <el-form-item label="新密码" prop="newPwd">
          <el-input
            v-model="ruleForm.newPwd"
            type="password"
            autocomplete="off"
          />
        </el-form-item>
        <el-form-item label="再次输入密码" prop="oncePwd">
          <el-input
            v-model="ruleForm.oncePwd"
            type="password"
            autocomplete="off"
          />
        </el-form-item>
        <el-form-item>
          <div>
            <el-button type="primary" @click="submitForm"> 提交 </el-button>
            <el-button @click="resetForm">重置</el-button>
          </div>
        </el-form-item>
      </el-form>
    </div>
  </el-card>
</template>

12.在router包下修改index.js

import { createRouter, createWebHistory } from 'vue-router'
import LoginVue from "@/views/Login.vue";
import LayoutVue from "@/views/Layout.vue";
import UserInfoVue from "@/views/user/UserInfo.vue";
import UserAvatarVue from "@/views/user/UserAvatar.vue";
import UserResetPasswordVue from "@/views/user/UserResetPassword.vue";
import LogOff from "@/views/user/LogOff.vue";
import AllVue from "@/views/project/All.vue";

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'LayoutVue',
      component: LayoutVue,
      redirect: "/all",
      children: [
        {
          path: "/all",
          component: AllVue,
        },
        {
          path: "/user/info",
          component: UserInfoVue,
        },
        {
          path: "/user/repwd",
          component: UserResetPasswordVue,
        },
        {
          path: "/user/avatar",
          component: UserAvatarVue,
        },
        {
          path: "/user/logoff",

          component: LogOff,
        },

      ],
    },
    {
      path: "/login",
      name: "login",
      component: LoginVue,
    },
  ]
})

export default router

13.前端最终目录。

四、代码地址:

1.前端:projectinf_qian: vue3+elementplus+pinia+token统一拦截

2.后端:projectinf_hou: springboot3+mybatisplus+redis+JWt+统一拦截器token

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值