兔子系统2.0
这里是接着上次注册登录之后继续写的一些主页面,用户信息修改(基本信息、密码、安全问题和头像上传)、用户管理的及退出登录,并且对于用户表格做了搜索、分页和导出Excel的操作。
也是一边写一边进行调整,可能跟上次相比,代码有些改动,不过这边尽量把代码全部提供出来,并且加了大量注释方便小白阅读理解(目录比较长,就不加在这里了,需要的话可以看左边的)。
展示:
兔子系统 2.0 基本操作演示
一、数据库表设计
用户表
我这里把上次说的头像的更换给补上了,因为我这里是本地写的,没有服务器什么的,所以就直接把头像图片文件存到数据库了,一般是存到系统部署的服务器上。
小知识:
在 Oracle 数据库中,BLOB(Binary Large Object)是一种用于存储大量二进制数据的数据类型。它的主要用途包括:
存储多媒体数据:BLOB 可以存储图像、音频、视频等多媒体文件,适用于需要保存大量二进制数据的应用场景。
存储文件:可以用来保存文档、PDF、Office 文件等非结构化数据,方便在数据库中管理和检索。
处理大数据量:BLOB 可以存储最大达 4 GB 的数据,适合需要处理大量数据的应用程序。
与其他数据类型的结合:可以与其他数据类型(如 VARCHAR2)一起使用,实现更复杂的数据存储和检索需求。
-- 创建用户表
CREATE TABLE CUSTOMER (
ID VARCHAR2(32) NOT NULL,
ACCOUNT VARCHAR2(9) NOT NULL,
ROLE VARCHAR2(1) DEFAULT '2' NOT NULL,
HEAD_PORTRAIT_NAME VARCHAR2(200),
HEAD_PORTRAIT_FILE BLOB,
NICK_NAME VARCHAR2(32) NOT NULL,
PASSWORD VARCHAR2(64) NOT NULL,
SALT VARCHAR2(32) NOT NULL,
BIRTHDAY Date,
AGE VARCHAR2(3),
SEX VARCHAR(1),
SAFETY_PROBLEM1_ID VARCHAR2(64),
SAFETY_PROBLEM2_ID VARCHAR2(64),
SAFETY_PROBLEM3_ID VARCHAR2(64),
SAFETY_PROBLEM1_ANSWER VARCHAR2(32),
SAFETY_PROBLEM2_ANSWER VARCHAR2(32),
SAFETY_PROBLEM3_ANSWER VARCHAR2(32),
CREATE_TIME Date DEFAULT SYSDATE NOT NULL,
UPDATE_TIME Date DEFAULT SYSDATE NOT NULL,
IS_DELETE varchar(1) DEFAULT 'N' NOT NULL
);
-- 各个字段的注释
COMMENT ON TABLE CUSTOMER IS '用户表';
COMMENT ON COLUMN CUSTOMER.ID IS '用户表ID';
COMMENT ON COLUMN CUSTOMER.ACCOUNT IS '账号';
COMMENT ON COLUMN CUSTOMER.ROLE IS '角色 0 超级管理员;1 管理员;2 普通用户';
COMMENT ON COLUMN CUSTOMER.HEAD_PORTRAIT_NAME IS '头像图片名字';
COMMENT ON COLUMN CUSTOMER.HEAD_PORTRAIT_FILE IS '头像二进制文件';
COMMENT ON COLUMN CUSTOMER.NICK_NAME IS '昵称';
COMMENT ON COLUMN CUSTOMER.PASSWORD IS '密码';
COMMENT ON COLUMN CUSTOMER.SALT IS '盐加密';
COMMENT ON COLUMN CUSTOMER.BIRTHDAY IS '出生日期';
COMMENT ON COLUMN CUSTOMER.AGE IS '年龄';
COMMENT ON COLUMN CUSTOMER.SEX IS '性别 0 女;1 男;2 保密';
COMMENT ON COLUMN CUSTOMER.SAFETY_PROBLEM1_ID IS '安全问题1id';
COMMENT ON COLUMN CUSTOMER.SAFETY_PROBLEM2_ID IS '安全问题2id';
COMMENT ON COLUMN CUSTOMER.SAFETY_PROBLEM3_ID IS '安全问题3id';
COMMENT ON COLUMN CUSTOMER.SAFETY_PROBLEM1_ANSWER IS '安全问题1答案';
COMMENT ON COLUMN CUSTOMER.SAFETY_PROBLEM2_ANSWER IS '安全问题2答案';
COMMENT ON COLUMN CUSTOMER.SAFETY_PROBLEM3_ANSWER IS '安全问题3答案';
COMMENT ON COLUMN CUSTOMER.CREATE_TIME IS '创建时间';
COMMENT ON COLUMN CUSTOMER.UPDATE_TIME IS '更新时间';
COMMENT ON COLUMN CUSTOMER.IS_DELETE IS '是否删除 Y 已删除;N 未删除';
安全问题表
-- 创建安全问题表
CREATE TABLE SAFETYPROBLEM (
ID VARCHAR2(32) NOT NULL,
PROBLEM VARCHAR2(64) NOT NULL,
CREATE_TIME Date DEFAULT SYSDATE NOT NULL,
UPDATE_TIME Date DEFAULT SYSDATE NOT NULL,
IS_DELETE varchar(1) DEFAULT 'N' NOT NULL
);
-- 各个字段的注释
COMMENT ON TABLE SAFETYPROBLEM IS '安全问题表';
COMMENT ON COLUMN SAFETYPROBLEM.ID IS '安全问题表ID';
COMMENT ON COLUMN SAFETYPROBLEM.PROBLEM IS '问题';
COMMENT ON COLUMN SAFETYPROBLEM.CREATE_TIME IS '创建时间';
COMMENT ON COLUMN SAFETYPROBLEM.UPDATE_TIME IS '更新时间';
COMMENT ON COLUMN SAFETYPROBLEM.IS_DELETE IS '是否删除 Y 已删除;N 未删除';
角色表
-- 创建角色表
CREATE TABLE ROLE (
ID VARCHAR2(32) NOT NULL,
ROLE_NAME VARCHAR2(64) NOT NULL,
CREATE_TIME Date DEFAULT SYSDATE NOT NULL,
UPDATE_TIME Date DEFAULT SYSDATE NOT NULL,
IS_DELETE varchar(1) DEFAULT 'N' NOT NULL
);
-- 各个字段的注释
COMMENT ON TABLE ROLE IS '角色表';
COMMENT ON COLUMN ROLE.ID IS '角色表ID';
COMMENT ON COLUMN ROLE.ROLE_NAME IS '角色名';
COMMENT ON COLUMN ROLE.CREATE_TIME IS '创建时间';
COMMENT ON COLUMN ROLE.UPDATE_TIME IS '更新时间';
COMMENT ON COLUMN ROLE.IS_DELETE IS '是否删除 Y 已删除;N 未删除';
二、后端配置文件
application.yml
application.yml
是 Spring Boot 项目的配置文件之一,主要用于定义应用程序的各种配置参数。使用 YAML 格式可以使配置更易读、层次分明。
## 指定web容器访问端口号
rabbit:
name: 兔子系统
version: v-1.0
## web容器端口号
server:
port: 8081
## 配置数据库连接
spring:
servlet:
multipart:
max-file-size: 40MB
max-request-size: 40MB
## 设置单个文件上传的最大内存(头像上传用的)
datasource:
driver-class-name: oracle.jdbc.OracleDriver
url: jdbc:oracle:thin:@//localhost:1521/ORCL
username: 数据库用户名
password: 密码
## 配置mybatis中mapper.xml文件扫描
mybatis:
type-aliases-package: com.rabbitSystem.carrot.pojo.sel.*
mapper-locations: classpath:mapper/*.xml # mapper.xml文件映射
pom.xml
pom.xml
是 Maven 项目的核心配置文件,Maven 是一个用于项目管理和构建的工具,文件定义了项目的基本信息、依赖项、插件、构建设置等。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.12</version> <!-- 降级到适用于 JDK 1.8 的版本 -->
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.rabbitsystem</groupId>
<artifactId>carrot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rabbit-system</name>
<description>rabbit-system</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>1.8</java.version> <!-- 保持 JDK 1.8 -->
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version> <!-- 使用与 Spring Boot 2.7.x 兼容的 MyBatis 版本 -->
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId> <!-- 使用适合 JDK 1.8 的 ojdbc8 -->
<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.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>2.2.0</version> <!-- 使用与 Spring Boot 2.7.x 兼容的 MyBatis 版本 -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.12</version> <!-- 降级到适用于 JDK 1.8 的版本 -->
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
三、后端代码
目录结构(common里面的都是一些工具类,后面代码的提供顺序是根据写代码的步骤来的,不是根据这个图片顺序来的,望理解)
1、工具类
1、AjaxResult
用于返回给前端 AjaxResult 类型数据的工具类。
package com.rabbitSystem.carrot.common.core;
import com.rabbitSystem.carrot.common.utils.StringUtils;
import java.util.HashMap;
/**
* 操作消息提醒
*/
public class AjaxResult extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
/**
* 状态码
*/
public static final String CODE_TAG = "code";
/**
* 返回内容
*/
public static final String MSG_TAG = "msg";
/**
* 数据对象
*/
public static final String DATA_TAG = "data";
/**
* 数据总数量
*/
public static final String DATA_COUNT = "count";
/**
* 状态类型
*/
public enum Type {
/**
* 成功
*/
SUCCESS(200),
/**
* 警告
*/
WARN(301),
/**
* 校验失败
*/
FAILED(-1),
/**
* 错误
*/
ERROR(500);
private final int value;
Type(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
/**
* 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
*/
public AjaxResult() {
}
/**
* 初始化一个新创建的 AjaxResult 对象
*
* @param type 状态类型
* @param msg 返回内容
*/
public AjaxResult(Type type, String msg) {
super.put(CODE_TAG, type.value);
super.put(MSG_TAG, msg);
}
/**
* 初始化一个新创建的 AjaxResult 对象
*
* @param type 状态类型
* @param msg 返回内容
* @param data 数据对象
*/
public AjaxResult(Type type, String msg, Object data) {
super.put(CODE_TAG, type.value);
super.put(MSG_TAG, msg);
if (StringUtils.isNotNull(data)) {
super.put(DATA_TAG, data);
}
}
/**
* 初始化一个新创建的 AjaxResult 对象
*
* @param data 数据对象
*/
public AjaxResult(Object data) {
if (StringUtils.isNotNull(data)) {
super.put(DATA_TAG, data);
}
}
/**
* 初始化一个新创建的 AjaxResult 对象
* @param type 状态类型
* @param msg 返回内容
* @param data 数据对象
* @param count 数据总数量
*/
public AjaxResult(Type type, String msg, Object data,Integer count) {
super.put(CODE_TAG, type.value);
super.put(MSG_TAG, msg);
if (StringUtils.isNotNull(data)) {
super.put(DATA_TAG, data);
}
if(count!=null){
super.put(DATA_COUNT,count);
}
}
/**
* 方便链式调用
*
* @param key 键
* @param value 值
* @return 数据对象
*/
@Override
public AjaxResult put(String key, Object value) {
super.put(key, value);
return this;
}
/**
* 返回成功消息
*
* @return 成功消息
*/
public static AjaxResult success() {
return AjaxResult.success("操作成功");
}
/**
* 返回成功数据
*
* @return 成功消息
*/
public static AjaxResult success(Object data) {
return AjaxResult.success("操作成功", data);
}
/**
* 返回成功消息
*
* @param msg 返回内容
* @return 成功消息
*/
public static AjaxResult success(String msg) {
return AjaxResult.success(msg, null);
}
/**
* 返回成功消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 成功消息
*/
public static AjaxResult success(String msg, Object data) {
return new AjaxResult(Type.SUCCESS, msg, data);
}
/**
* 返回警告消息
*
* @param msg 返回内容
* @return 警告消息
*/
public static AjaxResult warn(String msg) {
return AjaxResult.warn(msg, null);
}
/**
* 返回警告消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 警告消息
*/
public static AjaxResult warn(String msg, Object data) {
return new AjaxResult(Type.WARN, msg, data);
}
/**
* 返回错误消息
*
* @return
*/
public static AjaxResult error() {
return AjaxResult.error("操作失败");
}
/**
* 返回错误消息
*
* @param msg 返回内容
* @return 警告消息
*/
public static AjaxResult error(String msg) {
return AjaxResult.error(msg, null);
}
/**
* 返回错误消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 警告消息
*/
public static AjaxResult error(String msg, Object data) {
return new AjaxResult(Type.ERROR, msg, data);
}
}
2、CorsConfig
解决前后端跨域的问题。
package com.rabbitSystem.carrot.common.core;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
/**
* 解决跨域问题
*/
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*") // 允许任意域名访问,或者替换为特定的域名
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的请求方法
.allowedHeaders("*") // 允许的请求头
.maxAge(3600); // 可选的,设置预检请求的缓存时间
}
}
3、PasswordEncoder
密码加密的工具类。
package com.rabbitSystem.carrot.common.utils;
import java.security.MessageDigest;
/**
* @Description: 密码加密
*/
public class PasswordEncoder {
//返回十六进制数字字符串
private final static String[] HEX_DIGITS = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d",
"e", "f" };
private final static String MD5 = "MD5";
private Object salt;
private String algorithm;
public PasswordEncoder(Object salt) {
this(salt, MD5);
}
public PasswordEncoder(Object salt, String algorithm) {
this.salt = salt;
this.algorithm = algorithm;
}
/**
* 密码加密
*
* @param rawPass
* @return
*/
public String encode(String rawPass) {
String result = null;
try {
MessageDigest md = MessageDigest.getInstance(algorithm);
// 加密后的字符串
result = byteArrayToHexString(md.digest(mergePasswordAndSalt(rawPass).getBytes("utf-8")));
} catch (Exception ex) {
}
return result;
}
/**
* 密码匹配验证
*
* @param encPass
* 密文
* @param rawPass
* 明文
* @return
*/
public boolean matches(String encPass, String rawPass) {
String pass1 = "" + encPass;
String pass2 = encode(rawPass);
return pass1.equals(pass2);
}
/**
* 密码匹配验证
*
* @param encPass
* 密文
* @param rawPass
* 密文
* @return
*/
public boolean matchesNotSalt(String encPass, String rawPass) {
String pass1 = "" + encPass;
String pass2 = rawPass;
return pass1.equals(pass2);
}
private String mergePasswordAndSalt(String password) {
if (password == null) {
password = "";
}
if ((salt == null) || "".equals(salt)) {
return password;
} else {
return password + "{" + salt.toString() + "}";
}
}
/**
* 转换字节数组为16进制字串
*
* @param b
* 字节数组
* @return 16进制字串
*/
private String byteArrayToHexString(byte[] b) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++) {
resultSb.append(byteToHexString(b[i]));
}
return resultSb.toString();
}
/**
* 将字节转换为16进制
*
* @param b
* @return
*/
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n = 256 + n;
}
int d1 = n / 16;
int d2 = n % 16;
return HEX_DIGITS[d1] + HEX_DIGITS[d2];
}
}
4、PasswordUtils
密码解密的工具类。
package com.rabbitSystem.carrot.common.utils;
import java.util.UUID;
/**
* @Description: 密码工具类
*/
public class PasswordUtils {
/**
* 匹配密码
* @param salt 盐
* @param rawPass 明文
* @param encPass 密文
* @return
*/
public static boolean matches(String salt, String rawPass, String encPass) {
return new PasswordEncoder(salt).matches(encPass, rawPass);
}
/**
* 明文密码加密
* @param rawPass 明文
* @param salt
* @return
*/
public static String encode(String rawPass, String salt) {
return new PasswordEncoder(salt).encode(rawPass);
}
/**
* 获取加密盐
* @return
*/
public static String getSalt() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 20);
}
}
5、SnowflakeIdWorker
生成雪花id的工具类。
package com.rabbitSystem.carrot.common.utils;
import org.apache.log4j.Logger;
import java.util.Random;
/**
* SnowFlake算法的优点:
* ① 高性能高可用:生成时不依赖于数据库,完全在内存中生成。
* ② 容量大:每秒中能生成数百万的自增ID。
* ③ ID自增:存入数据库中,索引效率高。
* SnowFlake算法的缺点:
* 依赖与系统时间的一致性,如果系统时间被回调,或者改变,可能会造成id冲突或者重复。
*/
public class SnowflakeIdWorker {
private Logger logger = Logger.getLogger(SnowflakeIdWorker.class);
//保存数字0-9 和 大小写字母
private final String STR = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
//机器ID 2进制5位 32位减掉1位 31个
private final long WORKER_ID;
//设置一个时间初始值 2^41 - 1 差不多可以用69年
private final static long TWE_POCH = 1288834974657L;
//代表一毫秒内生成的多个id的最新序号 12位 4096 -1 = 4095 个
private long SEQUENCE = 0L;
//5位的机器id
private final static long WORKER_ID_BITS = 5L;
//这个是二进制运算,就是5 bit最多只能有31个数字,也就是说机器id最多只能是32以内
public final static long MAX_WORKER_ID = -1L ^ -1L << WORKER_ID_BITS;
//每毫秒内产生的id数
private final static long SEQUENCE_BITS = 10L;
private final static long WORKER_ID_SHIFT = SEQUENCE_BITS;
private final static long TIME_STAMPLEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
public final static long SEQUENCE_MASK = -1L ^ -1L << SEQUENCE_BITS;
//记录产生时间毫秒数,判断是否是同1毫秒
private long LAST_TIMESTAMP = -1L;
public SnowflakeIdWorker(final long workerId) {
// 检查机器id是否超过31 不能小于0
if (workerId > MAX_WORKER_ID || workerId < 0) {
throw new IllegalArgumentException(String.format(
"worker Id can't be greater than %d or less than 0",
MAX_WORKER_ID));
}
this.WORKER_ID = workerId;
}
// 这个是核心方法,通过调用nextId()方法,让当前这台机器上的snowflake算法程序生成一个全局唯一的id
public synchronized String nextId() {
// 获取当前时间戳,单位是毫秒
long timestamp = this.timeGen();
if (this.LAST_TIMESTAMP == timestamp) {
/** 一个毫秒内最多只能有4096个数字,无论你传递多少进来,
这个位运算保证始终就是在4096这个范围内,避免你自己传递个sequence超过了4096这个范围
*/
this.SEQUENCE = (this.SEQUENCE + 1) & SEQUENCE_MASK;
//当某一毫秒的时间,产生的id数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生ID
if (this.SEQUENCE == 0) {
// logger.info("###########" + SEQUENCE_MASK);
timestamp = this.tilNextMillis(this.LAST_TIMESTAMP);
}
} else {
this.SEQUENCE = 0;
}
if (timestamp < this.LAST_TIMESTAMP) {
try {
throw new Exception(
String.format(
"Clock moved backwards. Refusing to generate id for %d milliseconds",
this.LAST_TIMESTAMP - timestamp));
} catch (Exception e) {
e.printStackTrace();
}
}
// 这儿记录一下最近一次生成id的时间戳,单位是毫秒
this.LAST_TIMESTAMP = timestamp;
// 这儿就是最核心的二进制位运算操作,生成一个64bit的id
// 先将当前时间戳左移,放到41 bit那儿;将机房id左移放到5 bit那儿;将机器id左移放到5 bit那儿;将序号放最后12 bit
// 最后拼接起来成一个64 bit的二进制数字,转换成10进制就是个long型
long nextId = ((timestamp - TWE_POCH << TIME_STAMPLEFT_SHIFT))
| (this.WORKER_ID << WORKER_ID_SHIFT) | (this.SEQUENCE);
/* logger.info("timestamp:" + timestamp + ",timestampLeftShift:"
+ TIME_STAMPLEFT_SHIFT + ",nextId:" + nextId + ",workerId:"
+ WORKER_ID + ",sequence:" + SEQUENCE);*/
return randomNextId(nextId);
}
/**
* 当某一毫秒的时间,产生的id数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生ID
* @param lastTimestamp
* @return
*/
private long tilNextMillis(final long lastTimestamp) {
long timestamp = this.timeGen();
while (timestamp <= lastTimestamp) {
timestamp = this.timeGen();
}
return timestamp;
}
//获取当前时间戳
private long timeGen() {
return System.currentTimeMillis();
}
private String randomNextId(Long nextId){
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 16; i++) {
//创建一个新的随机数生成器
Random random = new Random();
//返回[0,string.length)范围的int值 作用:保存下标
int index = random.nextInt(STR.length());
//charAt() : 返回指定索引处的 char 值 ==》赋值给char字符对象ch
char ch = STR.charAt(index);
char id = String.valueOf(nextId).charAt(i);
sb.append(id);
// append(char c) :将 char 参数的字符串表示形式追加到此序列 ==》即将每次获取的ch值作拼接
sb.append(ch);
}
return sb.toString();
}
/**
* @Description: 获取唯一id
* @param workerId 工作id
*/
public static String getId(String workerId){
if(workerId == null && "".equals(workerId)){
workerId = "2";
}
SnowflakeIdWorker worker = new SnowflakeIdWorker(Long.valueOf(workerId));
return worker.nextId();
}
/**
* %0是在num转化为字符后,长度达不到length的时候,前面以0补足。
* d是个占位符,会被参数num所替换。
*
* @param num 传递的数字
* @param length 参数length代表的是格式化后字符串的总长度
* @return
*/
public static String leftFormatInteger(int num, int length) {
//String.format用来格式化字符串(按指定的规则连接字符串或输出其它变量并返回新的字符串)
return String.format("%0"+length+"d", num);
}
public static void main(String[] args){
System.out.println(leftFormatInteger(3,9) + SnowflakeIdWorker.getId("0008"));
String id = SnowflakeIdWorker.getId("000001");
System.out.println(id +"\t" + id.length());
System.out.println("000051l2f9R8n3X8j6j9k13808L7Z0y312a3d".length());
}
}
6、StringUtils
字符串工具类,针对字符串的一些操作。
package com.rabbitSystem.carrot.common.utils;
import java.util.Collection;
import java.util.Map;
/**
* 字符串工具类
*/
public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
* 空字符串
*/
private static final String NULLSTR = "";
/**
* 下划线
*/
private static final char SEPARATOR = '_';
/**
* 获取参数不为空值
*
* @param value defaultValue 要判断的value
* @return value 返回值
*/
public static <T> T nvl(T value, T defaultValue) {
return value != null ? value : defaultValue;
}
/**
* * 判断一个Collection是否为空, 包含List,Set,Queue
*
* @param coll 要判断的Collection
* @return true:为空 false:非空
*/
public static boolean isEmpty(Collection<?> coll) {
return isNull(coll) || coll.isEmpty();
}
/**
* * 判断一个Collection是否非空,包含List,Set,Queue
*
* @param coll 要判断的Collection
* @return true:非空 false:空
*/
public static boolean isNotEmpty(Collection<?> coll) {
return !isEmpty(coll);
}
/**
* * 判断一个对象数组是否为空
*
* @param objects 要判断的对象数组
* * @return true:为空 false:非空
*/
public static boolean isEmpty(Object[] objects) {
return isNull(objects) || (objects.length == 0);
}
/**
* * 判断一个对象数组是否非空
*
* @param objects 要判断的对象数组
* @return true:非空 false:空
*/
public static boolean isNotEmpty(Object[] objects) {
return !isEmpty(objects);
}
/**
* * 判断一个Map是否为空
*
* @param map 要判断的Map
* @return true:为空 false:非空
*/
public static boolean isEmpty(Map<?, ?> map) {
return isNull(map) || map.isEmpty();
}
/**
* * 判断一个Map是否为空
*
* @param map 要判断的Map
* @return true:非空 false:空
*/
public static boolean isNotEmpty(Map<?, ?> map) {
return !isEmpty(map);
}
/**
* * 判断一个字符串是否为空串
*
* @param str String
* @return true:为空 false:非空
*/
public static boolean isEmpty(String str) {
return isNull(str) || NULLSTR.equals(str.trim());
}
/**
* * 判断一个字符串是否为非空串
*
* @param str String
* @return true:非空串 false:空串
*/
public static boolean isNotEmpty(String str) {
return !isEmpty(str);
}
/**
* * 判断一个对象是否为空
*
* @param object Object
* @return true:为空 false:非空
*/
public static boolean isNull(Object object) {
return object == null;
}
/**
* * 判断一个对象是否非空
*
* @param object Object
* @return true:非空 false:空
*/
public static boolean isNotNull(Object object) {
return !isNull(object);
}
/**
* * 判断一个对象是否是数组类型(Java基本型别的数组)
*
* @param object 对象
* @return true:是数组 false:不是数组
*/
public static boolean isArray(Object object) {
return isNotNull(object) && object.getClass().isArray();
}
/**
* 去空格
*/
public static String trim(String str) {
return (str == null ? "" : str.trim());
}
/**
* 截取字符串
*
* @param str 字符串
* @param start 开始
* @return 结果
*/
public static String substring(final String str, int start) {
if (str == null) {
return NULLSTR;
}
if (start < 0) {
start = str.length() + start;
}
if (start < 0) {
start = 0;
}
if (start > str.length()) {
return NULLSTR;
}
return str.substring(start);
}
/**
* 截取字符串
*
* @param str 字符串
* @param start 开始
* @param end 结束
* @return 结果
*/
public static String substring(final String str, int start, int end) {
if (str == null) {
return NULLSTR;
}
if (end < 0) {
end = str.length() + end;
}
if (start < 0) {
start = str.length() + start;
}
if (end > str.length()) {
end = str.length();
}
if (start > end) {
return NULLSTR;
}
if (start < 0) {
start = 0;
}
if (end < 0) {
end = 0;
}
return str.substring(start, end);
}
/**
* 格式化文本, {} 表示占位符<br>
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
* 例:<br>
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
*
* @param template 文本模板,被替换的部分用 {} 表示
* @param params 参数值
* @return 格式化后的文本
*/
/* public static String format(String template, Object... params) {
if (isEmpty(params) || isEmpty(template)) {
return template;
}
return StrFormatter.format(template, params);
}*/
/**
* 下划线转驼峰命名
*/
public static String toUnderScoreCase(String str) {
if (str == null) {
return null;
}
StringBuilder sb = new StringBuilder();
// 前置字符是否大写
boolean preCharIsUpperCase = true;
// 当前字符是否大写
boolean curreCharIsUpperCase = true;
// 下一字符是否大写
boolean nexteCharIsUpperCase = true;
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (i > 0) {
preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
} else {
preCharIsUpperCase = false;
}
curreCharIsUpperCase = Character.isUpperCase(c);
if (i < (str.length() - 1)) {
nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
}
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) {
sb.append(SEPARATOR);
} else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) {
sb.append(SEPARATOR);
}
sb.append(Character.toLowerCase(c));
}
return sb.toString();
}
/**
* 是否包含字符串
*
* @param str 验证字符串
* @param strs 字符串组
* @return 包含返回true
*/
public static boolean inStringIgnoreCase(String str, String... strs) {
if (str != null && strs != null) {
for (String s : strs) {
if (str.equalsIgnoreCase(trim(s))) {
return true;
}
}
}
return false;
}
/**
* 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
*
* @param name 转换前的下划线大写方式命名的字符串
* @return 转换后的驼峰式命名的字符串
*/
public static String convertToCamelCase(String name) {
StringBuilder result = new StringBuilder();
// 快速检查
if (name == null || name.isEmpty()) {
// 没必要转换
return "";
} else if (!name.contains("_")) {
// 不含下划线,仅将首字母大写
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
// 用下划线将原始字符串分割
String[] camels = name.split("_");
for (String camel : camels) {
// 跳过原始字符串中开头、结尾的下换线或双重下划线
if (camel.isEmpty()) {
continue;
}
// 首字母大写
result.append(camel.substring(0, 1).toUpperCase());
result.append(camel.substring(1).toLowerCase());
}
return result.toString();
}
/**
* 驼峰式命名法
* 例如:user_name->userName
*/
public static String toCamelCase(String s) {
if (s == null) {
return null;
}
if (s.indexOf(SEPARATOR) == -1) {
return s;
}
s = s.toLowerCase();
StringBuilder sb = new StringBuilder(s.length());
boolean upperCase = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == SEPARATOR) {
upperCase = true;
} else if (upperCase) {
sb.append(Character.toUpperCase(c));
upperCase = false;
} else {
sb.append(c);
}
}
return sb.toString();
}
@SuppressWarnings("unchecked")
public static <T> T cast(Object obj) {
return (T) obj;
}
}
2、实体类
1、CustomerSelPojo
用户的实体类。
package com.rabbitSystem.carrot.pojo.sel;
import lombok.Data;
@Data
public class CustomerSelPojo {
// 用户信息
private String id;
private String account;
private String role;
private String roleName;
private String headPortraitName;
private byte[] headPortraitFile; // 头像存储的是二进制文件
private String nickName;
private String password;
private String salt;
private String birthday;
private String age;
private String sex;
private String safetyProblem1Id;
private String safetyProblem2Id;
private String safetyProblem3Id;
private String safetyProblem1Answer;
private String safetyProblem2Answer;
private String safetyProblem3Answer;
private String createTime;
private String updateTime;
private String isDelete;
// 功能
private int i; // 注册时新增个数
private int totalCount; //用户总数量
private String currentPage; //当前页码
private String pageSize; //每页条数
private String searchValue; //用于页面的自由搜索条件
private String image; //用于接收前端传回来的图片
}
2、RolePojo
角色的实体类。
package com.rabbitSystem.carrot.pojo.sel;
import lombok.Data;
@Data
public class RolePojo {
private String id;
private String roleName;
private String createTime;
private String updateTime;
private String isDelete;
}
3、SafetyProblemPojo
安全问题实体类。
package com.rabbitSystem.carrot.pojo.sel;
import lombok.Data;
@Data
public class SafetyProblemPojo {
private String id;
private String problem;
private String createTime;
private String updateTime;
private String isDelete;
private int i;
}
(另外三个就不展示了,主要就用的这三个pojo里面的)
3、控制层
1、CustomerController
用户操作的控制层。
package com.rabbitSystem.carrot.controller;
import com.rabbitSystem.carrot.common.core.AjaxResult;
import com.rabbitSystem.carrot.pojo.sel.CustomerSelPojo;
import com.rabbitSystem.carrot.service.CustomerService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
@RestController
@RequestMapping("/CustomerController")
/**
* 用户的控制层
*/
public class CustomerController {
/**
* 调用业务访问层对象
*/
@Resource
private CustomerService customerService;
/**
* GetMapping:处理 GET 请求(查询一般用 get 请求)
* 查询所有用户信息的方法
*/
@PostMapping("/selCustomers")
public AjaxResult selCustomers(@RequestBody CustomerSelPojo customerSelPojo) {
// 获取客户数据,支持分页
List<CustomerSelPojo> customerSelPojoList = customerService.selCustomers(customerSelPojo);
// 返回结果
String msg = customerSelPojoList != null ? "查询成功!" : "查询失败!";
return new AjaxResult(customerSelPojoList != null ? AjaxResult.Type.SUCCESS : AjaxResult.Type.ERROR, msg, customerSelPojoList);
}
/**
* 查询所有用户数量的方法
* @return
*/
@PostMapping("/selCustomersCount")
public AjaxResult selCustomersCount(@RequestBody CustomerSelPojo customerSelPojo) {
int customersCount = customerService.selCustomersCount(customerSelPojo);
String msg;
if (customersCount != 0){
msg = "查询成功!";
}else {
msg = "查询失败!";
}
return new AjaxResult(customersCount != 0 ? AjaxResult.Type.SUCCESS : AjaxResult.Type.ERROR, msg, customersCount);
}
/**
* 根据id删除用户的方法
* @return
*/
@GetMapping("/delCustomerById/{id}")
private AjaxResult delCustomerById(@PathVariable("id") String id){
int customersCount = customerService.delCustomerById(id);
String msg;
if (customersCount > 0){
msg = "删除成功!";
}else {
msg = "删除失败!";
}
return new AjaxResult(customersCount > 0 ? AjaxResult.Type.SUCCESS : AjaxResult.Type.ERROR, msg, customersCount);
}
/**
* 上传用户的头像的方法
* @return
*/
@PostMapping("/uploadHeadPortrait")
public AjaxResult uploadHeadPortrait(@RequestParam("image") MultipartFile file,@RequestParam("id") String id) {
if (file.isEmpty()) {
return new AjaxResult(AjaxResult.Type.ERROR, "失败", ResponseEntity.badRequest().body("请上传一个文件"));
} else {
try {
CustomerSelPojo customerSelPojo = new CustomerSelPojo();
customerSelPojo.setId(id);
customerSelPojo.setHeadPortraitName(file.getOriginalFilename());
// 如果要存储文件内容,可以考虑使用数据库的 BLOB 类型
byte[] fileBytes = file.getBytes();
// 直接存储字节数组,或进行其他处理
customerSelPojo.setHeadPortraitFile(fileBytes);
// 保存到数据库,假设你有 save 方法
int i = customerService.uploadHeadPortrait(customerSelPojo); // 需要实现实际保存逻辑
if (i == 1){
return new AjaxResult(ResponseEntity.status(200).body(customerSelPojo.getHeadPortraitFile()));
// return new AjaxResult(ResponseEntity.ok("文件上传成功: " + customerSelPojo.getHeadPortraitFile()));
}
return new AjaxResult(ResponseEntity.status(500).body("文件上传失败"));
} catch (IOException e) {
e.printStackTrace();
return new AjaxResult(ResponseEntity.status(500).body("文件上传失败"));
}
}
}
/**
* 修改用户信息的方法
* @return
*/
@PostMapping("/updInformation")
public AjaxResult updInformation(@RequestBody CustomerSelPojo customerSelPojo) {
int i = customerService.updInformation(customerSelPojo);
String msg = "";
if (i == 1){
msg = "修改成功!";
}else {
msg = "修改失败";
}
return new AjaxResult(i > 0 ? AjaxResult.Type.SUCCESS : AjaxResult.Type.ERROR, msg);
}
/**
* 修改用户安全问题的方法
* @return
*/
@PostMapping("/updSafetyProblem")
public AjaxResult updSafetyProblem(@RequestBody CustomerSelPojo customerSelPojo) {
int i = customerService.updSafetyProblem(customerSelPojo);
String msg = "";
if (i == 1){
msg = "修改成功!";
}else {
msg = "修改失败";
}
return new AjaxResult(i > 0 ? AjaxResult.Type.SUCCESS : AjaxResult.Type.ERROR, msg);
}
}
2、HomePageController
主页面加载用户信息的控制层。
package com.rabbitSystem.carrot.controller;
import com.rabbitSystem.carrot.common.core.AjaxResult;
import com.rabbitSystem.carrot.pojo.sel.CustomerSelPojo;
import com.rabbitSystem.carrot.service.CustomerService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/HomePageController")
public class HomePageController {
@Resource
private CustomerService customerService;
/**
* 根据账号加载用户的所有信息
* @param account
* @return
*/
@GetMapping("/loadCustomerInformation/{account}")
private AjaxResult loadCustomerInformation(@PathVariable("account") String account){
CustomerSelPojo customerSelPojo = customerService.selCustomer(account);
String msg = "";
if (customerSelPojo != null){
customerSelPojo.setI(1);
msg = "查询成功";
}else {
customerSelPojo.setI(0);
msg = "查询失败";
}
return new AjaxResult(customerSelPojo.getI() == 1 ? AjaxResult.Type.SUCCESS : AjaxResult.Type.ERROR,msg,customerSelPojo);
}
}
3、LoginAndRegisterController
注册登录的控制层。
package com.rabbitSystem.carrot.controller;
import com.rabbitSystem.carrot.common.core.AjaxResult;
import com.rabbitSystem.carrot.pojo.sel.CustomerSelPojo;
import com.rabbitSystem.carrot.service.LoginAndRegisterService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* 标识控制层
* @RestController 组合注解, @Controller + @ResponseBody ,
* 在类头部指定@RestController注解,就表示该控制器下所有的方法均以 json格式类型从服务端响应给客户端(前台)
*/
@RestController
/**
* 处理 HTTP 请求
*/
@RequestMapping("/LoginAndRegisterController")
/**
* 注册登录控制层
*/
public class LoginAndRegisterController {
@Resource
private LoginAndRegisterService loginAndRegisterService;
/**
* PostMapping:处理 Post 请求(插入一般用 post 请求)
* 用户注册的方法
*/
@PostMapping("/register")
public AjaxResult register(@RequestBody CustomerSelPojo customerSelPojo){
customerSelPojo = loginAndRegisterService.register(customerSelPojo);
String msg;
if (customerSelPojo.getI()>0){
msg = "注册成功,您的账号为:"+ customerSelPojo.getAccount();
}else {
msg = "注册失败,请重试!";
}
return new AjaxResult(customerSelPojo.getI() > 0 ? AjaxResult.Type.SUCCESS : AjaxResult.Type.ERROR,msg, customerSelPojo);
}
/**
* 登录
* @param customerSelPojo
* @return
*/
@PostMapping("/login")
public AjaxResult login(@RequestBody CustomerSelPojo customerSelPojo){
customerSelPojo = loginAndRegisterService.login(customerSelPojo);
String msg;
if (customerSelPojo.getI()== 0){
msg = "当前账号不存在!";
}else if (customerSelPojo.getI()== 1){
msg = "登录成功!";
}else {
msg = "密码错误!";
}
return new AjaxResult(customerSelPojo.getI() == 1 ? AjaxResult.Type.SUCCESS : AjaxResult.Type.ERROR,msg, customerSelPojo);
}
}
4、RetrievePasswordController
忘记密码的控制层。
package com.rabbitSystem.carrot.controller;
import com.rabbitSystem.carrot.common.core.AjaxResult;
import com.rabbitSystem.carrot.pojo.sel.CustomerSelPojo;
import com.rabbitSystem.carrot.service.CustomerService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController
@RequestMapping("/RetrievePasswordController")
public class RetrievePasswordController {
@Resource
private CustomerService customerService;
/**
* 找回密码校验账号和安全问题
* @param customerSelPojo
* @return
*/
@PostMapping("/retrievePassword")
public AjaxResult selCustomers(@RequestBody CustomerSelPojo customerSelPojo){
String safetyProblem1Id = customerSelPojo.getSafetyProblem1Id();
String safetyProblem2Id = customerSelPojo.getSafetyProblem2Id();
String safetyProblem3Id = customerSelPojo.getSafetyProblem3Id();
String safetyProblem1Answer = customerSelPojo.getSafetyProblem1Answer();
String safetyProblem2Answer = customerSelPojo.getSafetyProblem2Answer();
String safetyProblem3Answer = customerSelPojo.getSafetyProblem3Answer();
customerSelPojo = customerService.selCustomer(customerSelPojo.getAccount());
if (customerSelPojo == null){
return new AjaxResult(AjaxResult.Type.ERROR,"该账号不存在!");
}else if (safetyProblem1Id.equals(customerSelPojo.getSafetyProblem1Id())
&& safetyProblem2Id.equals(customerSelPojo.getSafetyProblem2Id())
&& safetyProblem3Id.equals(customerSelPojo.getSafetyProblem3Id())
&& safetyProblem1Answer.equals(customerSelPojo.getSafetyProblem1Answer())
&& safetyProblem2Answer.equals(customerSelPojo.getSafetyProblem2Answer())
&& safetyProblem3Answer.equals(customerSelPojo.getSafetyProblem3Answer())){
return new AjaxResult(AjaxResult.Type.SUCCESS,"校验成功!", customerSelPojo);
}else {
return new AjaxResult(AjaxResult.Type.ERROR,"您选择的问题及答案不正确!");
}
}
/**
* 根据账号修改密码
* @param customerSelPojo
* @return
*/
@PostMapping("/updPassword")
public AjaxResult updPassword(@RequestBody CustomerSelPojo customerSelPojo){
int i = customerService.updPassword(customerSelPojo);
String msg = "";
if (i==1){
msg = "修改成功";
}else {
msg = "修改失败";
}
return new AjaxResult( i == 1 ? AjaxResult.Type.SUCCESS : AjaxResult.Type.ERROR,msg);
}
}
5、RoleController
角色控制层。
package com.rabbitSystem.carrot.controller;
import com.rabbitSystem.carrot.common.core.AjaxResult;
import com.rabbitSystem.carrot.pojo.sel.RolePojo;
import com.rabbitSystem.carrot.service.RoleService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* 角色控制层
*/
@RestController
@RequestMapping("/RoleController")
public class RoleController {
@Resource
private RoleService roleService;
/**
* 查询所有的角色
* @return
*/
@GetMapping("/selRoles")
public AjaxResult selRoles(){
List<RolePojo> rolePojoList = roleService.selRoles();
String msg;
if (rolePojoList != null){
msg = "查询成功";
}else {
msg = "查询失败";
}
return new AjaxResult(rolePojoList != null ? AjaxResult.Type.SUCCESS : AjaxResult.Type.ERROR,msg,rolePojoList);
}
}
6、SafetyProblemController
安全问题控制层。
package com.rabbitSystem.carrot.controller;
import com.rabbitSystem.carrot.common.core.AjaxResult;
import com.rabbitSystem.carrot.pojo.SafetyProblem;
import com.rabbitSystem.carrot.service.SafetyProblemService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* 安全问题控制层
*/
@RestController
@RequestMapping("/SafetyProblemController")
public class SafetyProblemController {
@Resource
private SafetyProblemService safetyProblemService;
/**
* PostMapping:处理 Post 请求(插入一般用 post 请求)
* 用户注册的方法
*/
@GetMapping("/selSafetyProblems")
public AjaxResult selSafetyProblems(){
List<SafetyProblem> safetyProblemList = safetyProblemService.selSafetyProblems();
int i = 0;
if (safetyProblemList != null){
i = 1;
}
String msg = i == 0 ? "查询失败" : "查询成功";
return new AjaxResult(i > 0 ? AjaxResult.Type.SUCCESS : AjaxResult.Type.ERROR,msg, safetyProblemList);
}
}
4、业务层
1、CustomerService 及 CustomerImpl
用户业务层。
package com.rabbitSystem.carrot.service;
import com.rabbitSystem.carrot.pojo.sel.CustomerSelPojo;
import java.util.List;
public interface CustomerService {
/**
* 查询所有用户信息
* @return
*/
List<CustomerSelPojo> selCustomers(CustomerSelPojo customerSelPojo);
/**
* 根据账号查询个人用户信息
* @param account
* @return
*/
CustomerSelPojo selCustomer(String account);
/**
* 根据账号修改密码
* @param customerSelPojo
* @return
*/
int updPassword(CustomerSelPojo customerSelPojo);
/**
* 查询所有用户数
* @return
*/
int selCustomersCount(CustomerSelPojo customerSelPojo);
/**
* 根据id删除用户
* @param id
* @return
*/
int delCustomerById(String id);
/**
* 上传头像
* @param customerSelPojo
* @return
*/
int uploadHeadPortrait(CustomerSelPojo customerSelPojo);
/**
* 修改个人信息
* @param customerSelPojo
* @return
*/
int updInformation(CustomerSelPojo customerSelPojo);
/**
* 修改个人安全问题
* @param customerSelPojo
* @return
*/
int updSafetyProblem(CustomerSelPojo customerSelPojo);
}
用户业务层实现类。
package com.rabbitSystem.carrot.service.impl;
import com.rabbitSystem.carrot.common.utils.PasswordUtils;
import com.rabbitSystem.carrot.mapper.CustomerMapper;
import com.rabbitSystem.carrot.pojo.sel.CustomerSelPojo;
import com.rabbitSystem.carrot.service.CustomerService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class CustomerImpl implements CustomerService {
//创建数据访问层对象
@Resource
private CustomerMapper customerMapper;
@Override
public List<CustomerSelPojo> selCustomers(CustomerSelPojo customerSelPojo) {
return customerMapper.selCustomers(customerSelPojo);
}
@Override
public CustomerSelPojo selCustomer(String account) {
CustomerSelPojo customerSelPojo= customerMapper.selCustomer(account);
return customerSelPojo;
}
@Override
public int updPassword(CustomerSelPojo customerSelPojo) {
String password = customerSelPojo.getPassword();
// 密码加密
String salt = PasswordUtils.getSalt();
String encPass = PasswordUtils.encode(password, salt);
customerSelPojo.setPassword(encPass);
customerSelPojo.setSalt(salt);
int i = customerMapper.updPassword(customerSelPojo);
return i;
}
@Override
public int selCustomersCount(CustomerSelPojo customerSelPojo) {
return customerMapper.selCustomersCount(customerSelPojo);
}
@Override
public int delCustomerById(String id) {
return customerMapper.delCustomerById(id);
}
@Override
public int uploadHeadPortrait(CustomerSelPojo customerSelPojo) {
return customerMapper.uploadHeadPortrait(customerSelPojo);
}
@Override
public int updInformation(CustomerSelPojo customerSelPojo) {
return customerMapper.updInformation(customerSelPojo);
}
@Override
public int updSafetyProblem(CustomerSelPojo customerSelPojo) {
return customerMapper.updSafetyProblem(customerSelPojo);
}
}
2、LoginAndRegisterService 及 LoginAndRegisterImpl
登录注册业务层。
package com.rabbitSystem.carrot.service;
import com.rabbitSystem.carrot.pojo.sel.CustomerSelPojo;
public interface LoginAndRegisterService {
CustomerSelPojo register(CustomerSelPojo customerSelPojo);
CustomerSelPojo login(CustomerSelPojo customerSelPojo);
}
登录注册实现类。
package com.rabbitSystem.carrot.service.impl;
import com.rabbitSystem.carrot.common.utils.PasswordUtils;
import com.rabbitSystem.carrot.common.utils.SnowflakeIdWorker;
import com.rabbitSystem.carrot.mapper.LoginAndRegisterMapper;
import com.rabbitSystem.carrot.pojo.sel.CustomerSelPojo;
import com.rabbitSystem.carrot.service.CustomerService;
import com.rabbitSystem.carrot.service.LoginAndRegisterService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Random;
@Service
public class LoginAndRegisterImpl implements LoginAndRegisterService {
@Resource
private CustomerService customerService;
@Resource
private LoginAndRegisterMapper loginAndRegisterMapper;
@Override
public CustomerSelPojo register(CustomerSelPojo customerSelPojo) {
// 生成用户id
String userId = SnowflakeIdWorker.getId("00001");
customerSelPojo.setId(userId);
// 密码加密
String salt = PasswordUtils.getSalt();
String encPass = PasswordUtils.encode(customerSelPojo.getPassword(), salt);
customerSelPojo.setPassword(encPass);
customerSelPojo.setSalt(salt);
// 生成账号
Random random = new Random();
String account;
do {
int randomNumber = random.nextInt(1000000000);
account = String.format("%09d", randomNumber);
} while (customerService.selCustomer(account) != null);
customerSelPojo.setAccount(account);
int i = loginAndRegisterMapper.register(customerSelPojo);
customerSelPojo.setI(i);
return customerSelPojo;
}
@Override
public CustomerSelPojo login(CustomerSelPojo customerSelPojo) {
String account = customerSelPojo.getAccount();
String password = customerSelPojo.getPassword();
CustomerSelPojo primitiveUser = customerService.selCustomer(account);
if (primitiveUser == null) {
customerSelPojo.setI(0);
} else {
/**
* 根据查找用户名返回的盐和密码,跟前端传回来的进行校验
*/
if (PasswordUtils.matches(primitiveUser.getSalt(), password, primitiveUser.getPassword())) {
customerSelPojo.setI(1);
}else{
customerSelPojo.setI(2);
}
}
return customerSelPojo;
}
}
3、RoleService 及 RoleImpl
角色业务层。
package com.rabbitSystem.carrot.service;
import com.rabbitSystem.carrot.pojo.sel.RolePojo;
import java.util.List;
public interface RoleService {
/**
* 查询所有的角色
* @return
*/
List<RolePojo> selRoles();
}
角色业务层实现类。
package com.rabbitSystem.carrot.service.impl;
import com.rabbitSystem.carrot.mapper.RoleMapper;
import com.rabbitSystem.carrot.pojo.sel.RolePojo;
import com.rabbitSystem.carrot.service.RoleService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class RoleImpl implements RoleService {
@Resource
private RoleMapper roleMapper;
@Override
public List<RolePojo> selRoles() {
return roleMapper.selRoles();
}
}
4、SafetyProblemService 及 SafetyProblemImpl
安全问题业务层。
package com.rabbitSystem.carrot.service;
import com.rabbitSystem.carrot.pojo.SafetyProblem;
import java.util.List;
public interface SafetyProblemService {
/**
* 查序所有的安全问题
* @return
*/
List<SafetyProblem> selSafetyProblems();
}
安全问题业务层实现类。
package com.rabbitSystem.carrot.service.impl;
import com.rabbitSystem.carrot.mapper.SafetyProblemMapper;
import com.rabbitSystem.carrot.pojo.SafetyProblem;
import com.rabbitSystem.carrot.service.SafetyProblemService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class SafetyProblemImpl implements SafetyProblemService {
@Resource
private SafetyProblemMapper safetyProblemMapper;
@Override
public List<SafetyProblem> selSafetyProblems() {
List<SafetyProblem> safetyProblemList= safetyProblemMapper.selSafetyProblems();
return safetyProblemList;
}
}
5、映射层
1、CustomerMapper
用户的映射层。
package com.rabbitSystem.carrot.mapper;
import com.rabbitSystem.carrot.pojo.sel.CustomerSelPojo;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CustomerMapper {
List<CustomerSelPojo> selCustomers(CustomerSelPojo customerSelPojo);
CustomerSelPojo selCustomer(String account);
int updPassword(CustomerSelPojo customerSelPojo);
int selCustomersCount(CustomerSelPojo customerSelPojo);
int delCustomerById(String id);
int uploadHeadPortrait(CustomerSelPojo customerSelPojo);
int updInformation(CustomerSelPojo customerSelPojo);
int updSafetyProblem(CustomerSelPojo customerSelPojo);
}
2、LoginAndRegisterMapper
登录注册映射层。
package com.rabbitSystem.carrot.mapper;
import com.rabbitSystem.carrot.pojo.sel.CustomerSelPojo;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface LoginAndRegisterMapper {
int register(CustomerSelPojo customerSelPojo);
}
3、RoleMapper
角色映射层。
package com.rabbitSystem.carrot.mapper;
import com.rabbitSystem.carrot.pojo.sel.RolePojo;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface RoleMapper {
List<RolePojo> selRoles();
}
4、SafetyProblemMapper
安全问题映射层。
package com.rabbitSystem.carrot.mapper;
import com.rabbitSystem.carrot.pojo.SafetyProblem;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface SafetyProblemMapper {
List<SafetyProblem> selSafetyProblems();
}
6、持久层
1、CustomerMapper.xml
用户持久层。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rabbitSystem.carrot.mapper.CustomerMapper">
<resultMap id="customerResultMap" type="com.rabbitSystem.carrot.pojo.sel.CustomerSelPojo">
<result property="id" column="ID"/>
<result property="account" column="ACCOUNT"/>
<result property="role" column="ROLE"/>
<result property="roleName" column="ROLE_NAME"/>
<result property="headPortraitName" column="HEAD_PORTRAIT_NAME"/>
<result property="headPortraitFile" column="HEAD_PORTRAIT_FILE"/>
<result property="nickName" column="NICK_NAME"/>
<result property="password" column="PASSWORD"/>
<result property="salt" column="SALT"/>
<result property="birthday" column="BIRTHDAY"/>
<result property="age" column="AGE"/>
<result property="sex" column="SEX"/>
<result property="safetyProblem1Id" column="SAFETY_PROBLEM1_ID"/>
<result property="safetyProblem2Id" column="SAFETY_PROBLEM2_ID"/>
<result property="safetyProblem3Id" column="SAFETY_PROBLEM3_ID"/>
<result property="safetyProblem1Answer" column="SAFETY_PROBLEM1_ANSWER"/>
<result property="safetyProblem2Answer" column="SAFETY_PROBLEM2_ANSWER"/>
<result property="safetyProblem3Answer" column="SAFETY_PROBLEM3_ANSWER"/>
<result property="createTime" column="CREATE_TIME"/>
<result property="updateTime" column="UPDATE_TIME"/>
<result property="isDelete" column="IS_DELETE"/>
</resultMap>
<select id="selCustomers" resultMap="customerResultMap">
SELECT T.ID, T.ACCOUNT, R.ROLE_NAME, T.HEAD_PORTRAIT_NAME,T.HEAD_PORTRAIT_FILE, T.NICK_NAME, T.PASSWORD, T.SALT,
TO_CHAR(T.BIRTHDAY, 'YYYY-MM-DD') AS BIRTHDAY,T.AGE, T.SEX, T.SAFETY_PROBLEM1_ID, T.SAFETY_PROBLEM2_ID,
T.SAFETY_PROBLEM3_ID, T.SAFETY_PROBLEM1_ANSWER,T.SAFETY_PROBLEM2_ANSWER, T.SAFETY_PROBLEM3_ANSWER,
T.CREATE_TIME, T.UPDATE_TIME, T.IS_DELETE
FROM CUSTOMER T
LEFT JOIN ROLE R ON R.ID = T.ROLE
<where>
<if test="searchValue != '' and searchValue != null">
AND (T.ACCOUNT LIKE '%' || #{searchValue} || '%' OR R.ROLE_NAME LIKE '%' || #{searchValue} || '%'
OR T.NICK_NAME LIKE '%' || #{searchValue} || '%' OR T.AGE LIKE '%' || #{searchValue} || '%'
OR T.SEX LIKE '%' || #{searchValue} || '%')
</if>
<if test="sex != '' and sex != null">
AND T.SEX = #{sex}
</if>
<if test="role != '' and role != null">
AND T.ROLE = #{role}
</if>
AND T.IS_DELETE = 'N'
</where>
ORDER BY T.CREATE_TIME,T.ACCOUNT
<if test="currentPage != '' and currentPage != null and pageSize != '' and pageSize != null">
OFFSET (#{currentPage} - 1) * #{pageSize} ROWS
FETCH FIRST #{pageSize} ROWS ONLY
</if>
</select>
<select id="selCustomer" resultType="com.rabbitSystem.carrot.pojo.sel.CustomerSelPojo">
select T.ID id,T.ACCOUNT account,T.ROLE role,T.HEAD_PORTRAIT_NAME headPortraitName,
T.HEAD_PORTRAIT_FILE headPortraitFile,T.NICK_NAME nickName,T.PASSWORD password,T.SALT salt,
TO_CHAR(T.BIRTHDAY, 'YYYY-MM-DD') AS birthday,T.AGE age,T.SEX sex,T.SAFETY_PROBLEM1_ID safetyProblem1Id,
T.SAFETY_PROBLEM2_ID safetyProblem2Id,T.SAFETY_PROBLEM3_ID safetyProblem3Id,
T.SAFETY_PROBLEM1_ANSWER safetyProblem1Answer,T.SAFETY_PROBLEM2_ANSWER safetyProblem2Answer,
T.SAFETY_PROBLEM3_ANSWER safetyProblem3Answer,
T.CREATE_TIME createTime,T.UPDATE_TIME updateTime,T.IS_DELETE isDelete
FROM CUSTOMER T
LEFT JOIN ROLE R ON R.ID = T.ROLE
WHERE T.ACCOUNT = #{ACCOUNT}
</select>
<update id="updPassword">
UPDATE CUSTOMER T
SET T.PASSWORD = #{password},T.SALT = #{salt}
WHERE T.ACCOUNT = #{account}
</update>
<select id="selCustomersCount" resultType="int">
SELECT COUNT(*)
FROM CUSTOMER T
LEFT JOIN ROLE R ON R.ID = T.ROLE
<where>
<if test="searchValue != '' and searchValue != null">
AND (T.ACCOUNT LIKE '%' || #{searchValue} || '%' OR R.ROLE_NAME LIKE '%' || #{searchValue} || '%'
OR T.NICK_NAME LIKE '%' || #{searchValue} || '%' OR T.AGE LIKE '%' || #{searchValue} || '%'
OR T.SEX LIKE '%' || #{searchValue} || '%')
</if>
<if test="sex != '' and sex != null">
AND T.SEX = #{sex}
</if>
<if test="role != '' and role != null">
AND T.ROLE = #{role}
</if>
AND T.IS_DELETE = 'N'
</where>
</select>
<update id="delCustomerById">
UPDATE CUSTOMER T
SET T.IS_DELETE = 'Y'
WHERE T.ID = #{id}
</update>
<update id="uploadHeadPortrait">
UPDATE CUSTOMER T
SET T.HEAD_PORTRAIT_NAME = #{headPortraitName},T.HEAD_PORTRAIT_FILE = #{headPortraitFile}
WHERE T.ID = #{id}
</update>
<update id="updInformation">
update CUSTOMER T
<trim prefix="SET" suffixOverrides=",">
<if test="nickName != null">T.NICK_NAME = #{nickName},</if>
<if test="sex != null">T.SEX = #{sex},</if>
<if test="age != null">T.AGE = #{age},</if>
<if test="birthday != null">T.BIRTHDAY = TO_DATE(#{birthday}, 'YYYY-MM-DD'),</if>
</trim>
where T.ACCOUNT = #{account}
</update>
<update id="updSafetyProblem">
update CUSTOMER T
SET T.SAFETY_PROBLEM1_ID = #{safetyProblem1Id},
T.SAFETY_PROBLEM2_ID = #{safetyProblem2Id},
T.SAFETY_PROBLEM3_ID = #{safetyProblem3Id},
T.SAFETY_PROBLEM1_ANSWER = #{safetyProblem1Answer},
T.SAFETY_PROBLEM2_ANSWER = #{safetyProblem2Answer},
T.SAFETY_PROBLEM3_ANSWER = #{safetyProblem3Answer}
WHERE T.ACCOUNT = #{account}
</update>
</mapper>
2、LoginAndRegisterMapper.xml
登录注册持久层
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rabbitSystem.carrot.mapper.LoginAndRegisterMapper">
<insert id="register">
INSERT INTO CUSTOMER T (
T.ID, T.ACCOUNT, T.NICK_NAME, T.PASSWORD, T.SALT,
T.BIRTHDAY, T.AGE, T.SEX,
T.SAFETY_PROBLEM1_ID, T.SAFETY_PROBLEM2_ID, T.SAFETY_PROBLEM3_ID,
T.SAFETY_PROBLEM1_ANSWER, T.SAFETY_PROBLEM2_ANSWER, T.SAFETY_PROBLEM3_ANSWER
) VALUES (
#{id}, #{account}, #{nickName}, #{password}, #{salt},
TO_DATE(#{birthday}, 'YYYY-MM-DD'), #{age}, #{sex},
#{safetyProblem1Id}, #{safetyProblem2Id}, #{safetyProblem3Id},
#{safetyProblem1Answer}, #{safetyProblem2Answer}, #{safetyProblem3Answer}
)
</insert>
</mapper>
3、RoleMapper.xml
角色持久层。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rabbitSystem.carrot.mapper.RoleMapper">
<resultMap id="roleResultMap" type="com.rabbitSystem.carrot.pojo.sel.RolePojo">
<result property="id" column="ID"/>
<result property="roleName" column="ROLE_NAME"/>
<result property="createTime" column="CREATE_TIME"/>
<result property="updateTime" column="UPDATE_TIME"/>
<result property="isDelete" column="IS_DELETE"/>
</resultMap>
<select id="selRoles" resultMap="roleResultMap">
SELECT T.ID,T.ROLE_NAME,T.CREATE_TIME,T.UPDATE_TIME,T.IS_DELETE FROM ROLE T
</select>
</mapper>
4、SafetyProblemMapper.xml
安全问题持久层。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rabbitSystem.carrot.mapper.RoleMapper">
<resultMap id="roleResultMap" type="com.rabbitSystem.carrot.pojo.sel.RolePojo">
<result property="id" column="ID"/>
<result property="roleName" column="ROLE_NAME"/>
<result property="createTime" column="CREATE_TIME"/>
<result property="updateTime" column="UPDATE_TIME"/>
<result property="isDelete" column="IS_DELETE"/>
</resultMap>
<select id="selRoles" resultMap="roleResultMap">
SELECT T.ID,T.ROLE_NAME,T.CREATE_TIME,T.UPDATE_TIME,T.IS_DELETE FROM ROLE T
</select>
</mapper>
四、前端配置文件相关
1、index.html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>兔子系统</title>
</head>
<body>
<div id="app" style="margin: 0;padding: 0;width: 100%;height: 100vh;"></div>
</body>
<style>
body{
margin: 0;
padding: 0;
width: 100%;
height: 100vh;
}
</style>
</html>
2、HelloWorld.vue
<template>
<div class="hello" id="app">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
3、index.js
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import App from '@/App';
import CustomerPage from '@/module/rabbit-system/views/CustomerPage';
import LoginPage from '@/module/rabbit-system/views/LoginPage';
import DataScreen from '@/module/rabbit-system/views/DataScreen';
import HomePage from '@/module/rabbit-system/views/HomePage';
import RegisterPage from '@/module/rabbit-system/views/RegisterPage';
import ForgetPasswordPage from '@/module/rabbit-system/views/RetrievePassword';
import NotFound from '@/NotFoundPage';
// 检查用户是否已登录
function isAuthenticated() {
return !!sessionStorage.getItem('account'); // 假设存储用户信息的键为 'account'
}
const routes = [
{ path: '/', component: App },
{ path: '/CustomerPage', component: CustomerPage, meta: { requiresAuth: true } },// 添加认证需要
{ path: '/LoginPage', component: LoginPage },
{ path: '/RegisterPage', component: RegisterPage },
{ path: '/ForgetPasswordPage', component: ForgetPasswordPage },
{ path: '/DataScreen', component: DataScreen, meta: { requiresAuth: true } }, // 添加认证需要
{ path: '/HomePage', component: HomePage, meta: { requiresAuth: true } }, // 添加认证需要
{ path: '/:pathMatch(.*)*', component: NotFound },
];
const router = createRouter({
history: createWebHistory(),
routes,
});
// 全局前置守卫
router.beforeEach((to, from, next) => {
// 检查目标路由是否需要认证
if (to.matched.some(record => record.meta.requiresAuth)) {
// 如果目标路由需要认证且用户未认证
if (!isAuthenticated()) {
next('/LoginPage'); // 重定向到登录页面
} else {
next(); // 允许访问目标路由
}
} else {
next(); // 允许访问无认证要求的路由
}
});
export default router;
4、App.vue
<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
5、main.js
import { createApp } from 'vue';
import RabbitSystemVueApp from './RabbitSystemVueApp'; // 引入主 Vue 组件
import router from './router'; // 引入路由配置
import ElementPlus from 'element-plus'; // 引入 Element Plus UI 库
import 'element-plus/dist/index.css'; // 引入 Element Plus 的样式文件
import zhCn from 'element-plus/es/locale/lang/zh-cn'; // 引入中文语言包
// 创建 Vue 应用实例
const app = createApp(RabbitSystemVueApp);
// 使用插件
app.use(router); // 注册路由插件
app.use(ElementPlus, { locale: zhCn }); // 注册 Element Plus 插件并引用中文包
// 挂载应用到 DOM 元素
app.mount('#app'); // 将 Vue 应用挂载到 id 为 'app' 的 DOM 元素上
6、NotFoundPage.vue
<!-- src/components/NotFound.vue -->
<template>
<div class="not-found">
<div class="container">
<h1>404</h1>
<p>哎呀!我们找不到你要的页面。</p>
<router-link class="home-link" to="/LoginPage">返回首页</router-link>
</div>
</div>
</template>
<script setup>
</script>
<style scoped>
.not-found {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
background: linear-gradient(135deg, #61E7FFFF, #d7a1b2);
color: #fff;
font-family: 'Arial', sans-serif;
}
.container {
text-align: center;
background: rgba(0, 0, 0, 0.6);
border-radius: 10px;
padding: 40px;
max-width: 500px;
width: 100%;
}
h1 {
font-size: 120px;
margin: 0;
font-weight: bold;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
p {
font-size: 18px;
margin: 20px 0;
line-height: 1.5;
}
.home-link {
display: inline-block;
padding: 10px 20px;
margin-top: 20px;
font-size: 16px;
color: #fff;
background-color: #42b983;
border-radius: 5px;
text-decoration: none;
transition: background-color 0.3s, transform 0.3s;
}
.home-link:hover {
background-color: #359d77;
transform: scale(1.05);
}
.home-link:focus {
outline: none;
}
</style>
7、RabbitSystemVueApp.vue
<template>
<router-view>
</router-view> <!-- 渲染匹配的路由组件 -->
</template>
<script>
</script>
<style scoped>
</style>
8、vue.config.js
const { defineConfig } = require('@vue/cli-service');
const { DefinePlugin } = require('webpack');
const path = require('path');
module.exports = defineConfig({
// 配置开发服务器
devServer: {
port: 8082, // 设置开发服务器的端口号为 8082
},
// 是否转译依赖(依赖于 babel)
transpileDependencies: true,
// 配置公共路径,根据环境变量设置不同的路径
publicPath: process.env.NODE_ENV === 'production' ? '/myapp/' : '/',
// 配置 webpack
configureWebpack: {
plugins: [
new DefinePlugin({
// 定义全局常量,用于在生产环境中关闭 Vue 的生产模式水合不匹配的详细信息
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: JSON.stringify(false)
}),
],
},
});
9、package.json
执行命令:npm init -y 使用默认值快速生成
五、前端代码
1、工具
安装xlsx库 npm install xlsx //用来导出用户信息 Excel 表格的。
1、utils.js 工具js
放一些通用的方法
// 批量处理数字英文转换(传入的是用户信息集合)
export function processCustomerData(customers) {
customers.forEach(customer => {
// 处理 sex
if (customer.sex === "0") {
customer.sex = "女";
} else if (customer.sex === "1") {
customer.sex = "男";
} else if (customer.sex === "2") {
customer.sex = "保密";
}
// 处理 isDelete
if (customer.isDelete === "N") {
customer.isDelete = "是";
} else if (customer.isDelete === "Y") {
customer.isDelete = "否";
}
});
return customers;
}
// 根据生日获得年龄
export function getAgeByBirthday(birthday) {
if (!birthday) return null; // 如果生日未选择则返回 null
const today = new Date();
const birthDate = new Date(birthday);
let age = today.getFullYear() - birthDate.getFullYear();
const monthDiff = today.getMonth() - birthDate.getMonth();
// 如果生日还未到,年龄减 1
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
return age;
}
// 匹配中文英文和数字
export function isValidInput(str) {
// 正则表达式:允许中文、英文和数字
const pattern = /^[\u4E00-\u9FA5A-Za-z0-9]+$/;
return pattern.test(str);
}
//计算字符串的长度
export function charsLengthUtil(str){
//中文长度
let cLength = 0;
//英文长度
let yLength = 0;
// 匹配中文
const pattern = /[\u4E00-\u9FA5]/;
// 计量字符串总长度
let allLength;
for (let i = 0; i < str.length; i++) {
const char = str[i];
if (pattern.test(char)) {
cLength += 1;
} else {
yLength += 0.5;
}
}
allLength = cLength + yLength;
return allLength;
}
// 将选择的日期格式化为 YYYY-MM-DD
export function dateFormatConversion(birthday){
// 获取选择的日期
const localDate = new Date(birthday); // 假设 updBirthday.value 是 Date 对象
// 获取 UTC 日期部分
const utcDate = new Date(Date.UTC(localDate.getFullYear(), localDate.getMonth(), localDate.getDate()));
// 格式化为 YYYY-MM-DD
const formattedBirthday = utcDate.toISOString().split('T')[0];
return formattedBirthday
}
2、页面
1、LoginPage.vue
登录页面。
<template>
<div v-loading = "loading" style="background: linear-gradient(to bottom, green, orange);width: 100%; height: 100vh; position: fixed;">
<div style="display: inline-block;top: 200px;margin-top: 100px">
<span style="font-size: 60px;color: #FFA500;">欢迎登录兔子系统</span>
<div style="margin-top: 150px">
账号:<el-input v-model="account" style="width: 240px" placeholder="请输入账号" />
</div>
<div style="margin-top: 10px">
密码:<el-input v-model="password" type="password" style="width: 240px" placeholder="请输入密码" show-password/>
</div>
<div class="mb-4" style="margin-top: 10px">
<el-button type="primary" @click="register">注册</el-button>
<el-button type="success" @click="login">登录</el-button>
</div>
<div style="margin-top: 10px">
<el-link type="danger" style="font-size: 10px" @click="forgetPassword">忘记密码?</el-link>
</div>
</div>
</div>
</template>
<script>
import { ref } from 'vue'
import router from "@/router";
import {loginJS} from "@/module/rabbit-system/api/registerAndLogin";
import {ElMessageBox} from "element-plus";
export default {
name: 'LoginPage',
setup() {
const form ={
account: "",
password: "",
};
// 定义一个 delay 函数,用于处理异步延迟
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 定义响应式变量
const loading = ref(false);
const account = ref("");
const password = ref("");
const register = () => {
// router.replace('/RegisterPage'); 浏览器可以返回,不销毁当前页面
router.replace('/RegisterPage'); // 浏览器不可以返回,销毁当前页面
};
// 一个提示弹窗的方法
const popUpWindows = (msg) => {
ElMessageBox.alert(msg, '温馨提示', {
confirmButtonText: '确认',
})
};
// 登录按钮
const login = async() => {
if (account.value.length === 0){
popUpWindows("请输入您的账号");
}else if (password.value.length === 0){
popUpWindows("请输入您的密码");
}else {
form.account = account.value;
form.password = password.value;
const accountSessionStorage = account.value;
// 开始登录
loading.value = true;
const data = await loginJS(form);
await delay(1000);
if (data.data.i === 0){
loading.value = false;
popUpWindows(data.msg);
}else if (data.data.i === 1){
loading.value = false;
// 存储 sessionStorage ,他是 Web 存储机制,用于在用户会话期间存储数据,就是当前页面在就在,页面关闭就没了,可以存储一些用户信息等
sessionStorage.setItem('account', accountSessionStorage); // 将用户的账号存储到 sessionStorage
await router.push('/HomePage');
popUpWindows(data.msg);
}else {
loading.value = false;
popUpWindows(data.msg);
}
}
};
// 忘记密码
const forgetPassword = () => {
router.replace('/ForgetPasswordPage');
};
return {
loading,
account,
password,
login,
register,
popUpWindows,
forgetPassword,
};
}
}
</script>
<style scoped>
/* 你的样式代码 */
</style>
2、RegisterPage.vue
注册页面。
<template>
<div v-loading = "loading" style="background: linear-gradient(to top, green, orange);width: 100%; height: 100vh; position: fixed;">
<div style="display: inline-block;top: 200px; left: 200px;position: fixed;">
<span style="font-size: 60px;color: #FFA500;">欢迎注册兔子系统</span>
</div>
<div style="margin-left: 600px;">
<div style="margin-top: 100px">
<el-avatar :src="require('@/assets/兔子来了.jpeg')" style="width: 100px;height: 100px"/>
</div>
<div style="margin-top: 20px">
请输入昵称:<el-input v-model="nickName" style="width: 240px" placeholder="请输入昵称" />
</div>
<div style="margin-top: 10px">
请选择性别:<el-radio-group v-model="sex"> <!--v-model="sex" 用于双向绑定-->
<el-radio value="1" border style="width: 74px;">男</el-radio>
<el-radio value="0" border style="width: 74px;margin-left: -20px">女</el-radio>
<el-radio value="2" border style="width: 74px;margin-left: -20px">保密</el-radio>
</el-radio-group>
</div>
<div style="margin-top: 10px">
请选择生日:<el-date-picker
type="date"
placeholder="出生日期"
style="width: 240px"
v-model="birthday"
/>
</div>
<div style="margin-top: 10px">
请输入密码:<el-input
v-model="password1"
style="width: 240px"
type="password"
placeholder="请输入密码"
show-password
/>
</div>
<div style="margin-top: 10px">
请输入密码:<el-input
v-model="password2"
style="width: 240px"
type="password"
placeholder="请输入密码"
show-password
/>
</div>
<div style="margin-top: 5px;">
<!-- v-if:当你需要根据条件动态地添加或移除元素时,使用 v-if。它会完全从 DOM 中移除元素,不会占用空间。-->
<el-text v-if="password1 !== password2" type="danger">两次输入的密码不一致!</el-text>
</div>
<div style="margin-top: 10px;">
<el-dropdown split-button trigger="click" @command="(command) => customerSafetyPlus(command)">
<span style="width: 274px">{{ safetyProblem1 }}</span>
<span style="width: 274px;display: none">{{ safetyProblem1Id }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="'1'+'/'+item.problem+'/'+item.id" v-for="item in questions" :key = "item">
{{ item.problem }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div>
<el-input v-model="safetyProblem1Answer" v-if="safetyProblem1!='请选择安全问题1'" style="width: 338px;margin-top: 5px" placeholder="请输入安全问题的答案" />
</div>
<div style="margin-top: 10px;">
<el-dropdown split-button trigger="click" @command="(command) => customerSafetyPlus(command)">
<span style="width: 274px">{{ safetyProblem2 }}</span>
<span style="width: 274px;display: none">{{ safetyProblem2Id }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="'2'+'/'+item.problem+'/'+item.id" v-for="item in questions" :key = "item">
{{ item.problem }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div>
<el-input v-model="safetyProblem2Answer" v-if="safetyProblem2!='请选择安全问题2'" style="width: 338px;margin-top: 5px" placeholder="请输入安全问题的答案" />
</div>
<div style="margin-top: 10px;">
<el-dropdown split-button trigger="click" @command="(command) => customerSafetyPlus(command)">
<span style="width: 274px">{{ safetyProblem3 }}</span>
<span style="width: 274px;display: none">{{ safetyProblem3Id }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="'3'+'/'+item.problem+'/'+item.id" v-for="item in questions" :key = "item">
{{ item.problem }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div>
<el-input v-model="safetyProblem3Answer" v-if="safetyProblem3!='请选择安全问题3'" style="width: 338px;margin-top: 5px" placeholder="请输入安全问题的答案" />
</div>
<div class="mb-4" style="margin-top: 10px;">
<!-- :style="{ width: '340px' }" 设置按钮宽度 -->
<el-button type="primary" @click="goBack">返回</el-button>
<el-button type="primary" @click="register">注册</el-button>
</div>
</div>
</div>
</template>
<script>
import {onMounted, ref, } from 'vue';
import router from "@/router";
import {ElMessageBox} from "element-plus";
import {registerJS, selSafetyProblemsJS} from "@/module/rabbit-system/api/registerAndLogin";
import {isValidInput, charsLengthUtil, getAgeByBirthday} from "@/module/uitls"
export default {
name: 'LoginPage',
setup() {
// 页面一打开就执行
onMounted(() => {
loadProblems();
});
const form ={
nickName: "",
sex: "",
age: "",
birthday: "",
password: "",
safetyProblem1Id: "",
safetyProblem2Id: "",
safetyProblem3Id: "",
safetyProblem1Answer: "",
safetyProblem2Answer: "",
safetyProblem3Answer: "",
};
// 定义响应式变量
const loading = ref(false);
const nickName = ref("");
// v-model="sex" 用于双向绑定 ,这里是给组件赋一个默认的值
const sex = ref('')
const birthday = ref("");
const password1 = ref("");
const password2 = ref("");
let safetyProblem1 = ref('请选择安全问题1'); // 确保在这里声明 safetyProblem1
let safetyProblem2 = ref('请选择安全问题2');
let safetyProblem3 = ref('请选择安全问题3');
let safetyProblem1Id = ref('');
let safetyProblem2Id = ref('');
let safetyProblem3Id = ref('');
const safetyProblem1Answer = ref("");
const safetyProblem2Answer = ref("");
const safetyProblem3Answer = ref("");
// let 可以在下面的函数内部重新赋值
let message;
// 一个提示弹窗的方法
const popUpWindows = (msg) => {
ElMessageBox.alert(msg, '温馨提示', {
confirmButtonText: '确认',
})
};
// 定义一个 delay 函数,用于处理异步延迟
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 重置表单数据
const resetForm = () => {
nickName.value = "";
sex.value = "";
birthday.value = "";
password1.value = "";
password2.value = "";
safetyProblem1.value = '请选择安全问题1';
safetyProblem2.value = '请选择安全问题2';
safetyProblem3.value = '请选择安全问题3';
safetyProblem1Id.value = '';
safetyProblem2Id.value = '';
safetyProblem3Id.value = '';
safetyProblem1Answer.value = "";
safetyProblem2Answer.value = "";
safetyProblem3Answer.value = "";
};
// 加载安全问题
let questions = ref([]);
const loadProblems = async () => {
try {
const response = await selSafetyProblemsJS();
const dd = response.data.data;
questions.value = dd;
} catch (error) {
console.error('获取问题失败:', error);
}
};
// 选择安全问题后将安全问题及id渲染到标签里
const customerSafetyPlus = (command) => {
const [order,problem, id] = command.split('/');
if (order === '1'){
safetyProblem1Id.value = id;
safetyProblem1.value = problem;
}else if (order === '2'){
safetyProblem2Id.value = id;
safetyProblem2.value = problem;
}else if (order === '3'){
safetyProblem3Id.value = id;
safetyProblem3.value = problem;
}
}
// 返回按钮
const goBack = () => {
router.replace('/LoginPage');
};
// 注册按钮
const register = async() => {
if (nickName.value == "" || sex.value == "" || birthday.value == "" || password1.value == "" || password2.value == "") {
message = "请完善用户信息!";
popUpWindows(message);
}else if (isValidInput(nickName.value) === false) {
message = "昵称请使用中文英文和数字!";
popUpWindows(message);
} else if (charsLengthUtil(nickName.value) > 7) {
message = "昵称不得超过7个字符长度!";
popUpWindows(message);
} else if (password1.value.length < 6 || password2.value.length < 6 || password2.value.length > 20 || password2.value.length > 20) {
message = "密码长度不得小于6位,不得大于20位!";
popUpWindows(message);
} else if (password1.value !== password2.value) {
message = "两次输入的密码不一致!";
popUpWindows(message);
} else if (getAgeByBirthday(birthday.value) < 18 && getAgeByBirthday(birthday.value) != null) {
message = "年龄必须大于18岁!";
popUpWindows(message);
} else if (safetyProblem1.value == '请选择安全问题1' || safetyProblem2.value == '请选择安全问题2' || safetyProblem3.value == '请选择安全问题3') {
message = "请选择3个安全问题!";
popUpWindows(message);
} else if (safetyProblem1Answer.value.length == 0) {
message = "请回答第一个问题!";
popUpWindows(message);
} else if (safetyProblem2Answer.value.length == 0) {
message = "请回答第二个问题!";
popUpWindows(message);
} else if (safetyProblem3Answer.value.length == 0) {
message = "请回答第三个问题!";
popUpWindows(message);
}else if (safetyProblem1Answer.value.length > 20 || safetyProblem2Answer.value.length > 20 || safetyProblem3Answer.value.length > 20) {
message = "答案字数不得超过20个字!";
popUpWindows(message);
} else if (isValidInput(safetyProblem1Answer.value) === false || isValidInput(safetyProblem2Answer.value) === false || isValidInput(safetyProblem3Answer.value) === false) {
message = "昵称请使用中文英文和数字!";
popUpWindows(message);
}else {
// 获取选择的日期
const localDate = new Date(birthday.value); // 假设 updBirthday.value 是 Date 对象
// 获取 UTC 日期部分
const utcDate = new Date(Date.UTC(localDate.getFullYear(), localDate.getMonth(), localDate.getDate()));
// 格式化为 YYYY-MM-DD
const formattedBirthday = utcDate.toISOString().split('T')[0];
form.nickName= nickName.value;
form.sex= sex.value;
form.age= getAgeByBirthday(birthday.value);
form.birthday = formattedBirthday;
form.password= password1.value;
form.safetyProblem1Id= safetyProblem1Id.value;
form.safetyProblem2Id= safetyProblem2Id.value;
form.safetyProblem3Id= safetyProblem3Id.value;
form.safetyProblem1Answer= safetyProblem1Answer.value;
form.safetyProblem2Answer= safetyProblem2Answer.value;
form.safetyProblem3Answer= safetyProblem3Answer.value;
// 开始注册
loading.value = true;
const data = await registerJS(form);
await delay(1000);
loading.value = false;
if (data.data.i > 0){
popUpWindows(data.msg);
await router.replace('/LoginPage');
}else {
popUpWindows(data.msg);
resetForm(); // 清空表单数据
}
}
loading.value = false;
};
return {
loading,
questions,
nickName,
sex,
birthday,
password1,
password2,
safetyProblem1,
safetyProblem2,
safetyProblem3,
safetyProblem1Id,
safetyProblem2Id,
safetyProblem3Id,
safetyProblem1Answer,
safetyProblem2Answer,
safetyProblem3Answer,
goBack,
register,
popUpWindows,
customerSafetyPlus
};
}
}
</script>
<style scoped>
/* 你的样式代码 */
</style>
3、RetrievePassword.vue
忘记密码页面
<template>
<div v-loading = "loading" style="background: linear-gradient(to top, green, orange);width: 100%; height: 100vh; position: fixed;">
<div style="display: inline-block;top: 200px; left: 200px;position: fixed;">
<span style="font-size: 60px;color: #FFA500;">欢迎注册兔子系统</span>
</div>
<div style="margin-left: 600px;">
<div style="margin-top: 100px">
<el-avatar :src="require('@/assets/兔子来了.jpeg')" style="width: 100px;height: 100px"/>
</div>
<div style="margin-top: 20px">
请输入昵称:<el-input v-model="nickName" style="width: 240px" placeholder="请输入昵称" />
</div>
<div style="margin-top: 10px">
请选择性别:<el-radio-group v-model="sex"> <!--v-model="sex" 用于双向绑定-->
<el-radio value="1" border style="width: 74px;">男</el-radio>
<el-radio value="0" border style="width: 74px;margin-left: -20px">女</el-radio>
<el-radio value="2" border style="width: 74px;margin-left: -20px">保密</el-radio>
</el-radio-group>
</div>
<div style="margin-top: 10px">
请选择生日:<el-date-picker
type="date"
placeholder="出生日期"
style="width: 240px"
v-model="birthday"
/>
</div>
<div style="margin-top: 10px">
请输入密码:<el-input
v-model="password1"
style="width: 240px"
type="password"
placeholder="请输入密码"
show-password
/>
</div>
<div style="margin-top: 10px">
请输入密码:<el-input
v-model="password2"
style="width: 240px"
type="password"
placeholder="请输入密码"
show-password
/>
</div>
<div style="margin-top: 5px;">
<!-- v-if:当你需要根据条件动态地添加或移除元素时,使用 v-if。它会完全从 DOM 中移除元素,不会占用空间。-->
<el-text v-if="password1 !== password2" type="danger">两次输入的密码不一致!</el-text>
</div>
<div style="margin-top: 10px;">
<el-dropdown split-button trigger="click" @command="(command) => customerSafetyPlus(command)">
<span style="width: 274px">{{ safetyProblem1 }}</span>
<span style="width: 274px;display: none">{{ safetyProblem1Id }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="'1'+'/'+item.problem+'/'+item.id" v-for="item in questions" :key = "item">
{{ item.problem }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div>
<el-input v-model="safetyProblem1Answer" v-if="safetyProblem1!='请选择安全问题1'" style="width: 338px;margin-top: 5px" placeholder="请输入安全问题的答案" />
</div>
<div style="margin-top: 10px;">
<el-dropdown split-button trigger="click" @command="(command) => customerSafetyPlus(command)">
<span style="width: 274px">{{ safetyProblem2 }}</span>
<span style="width: 274px;display: none">{{ safetyProblem2Id }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="'2'+'/'+item.problem+'/'+item.id" v-for="item in questions" :key = "item">
{{ item.problem }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div>
<el-input v-model="safetyProblem2Answer" v-if="safetyProblem2!='请选择安全问题2'" style="width: 338px;margin-top: 5px" placeholder="请输入安全问题的答案" />
</div>
<div style="margin-top: 10px;">
<el-dropdown split-button trigger="click" @command="(command) => customerSafetyPlus(command)">
<span style="width: 274px">{{ safetyProblem3 }}</span>
<span style="width: 274px;display: none">{{ safetyProblem3Id }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="'3'+'/'+item.problem+'/'+item.id" v-for="item in questions" :key = "item">
{{ item.problem }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div>
<el-input v-model="safetyProblem3Answer" v-if="safetyProblem3!='请选择安全问题3'" style="width: 338px;margin-top: 5px" placeholder="请输入安全问题的答案" />
</div>
<div class="mb-4" style="margin-top: 10px;">
<!-- :style="{ width: '340px' }" 设置按钮宽度 -->
<el-button type="primary" @click="goBack">返回</el-button>
<el-button type="primary" @click="register">注册</el-button>
</div>
</div>
</div>
</template>
<script>
import {onMounted, ref, } from 'vue';
import router from "@/router";
import {ElMessageBox} from "element-plus";
import {registerJS, selSafetyProblemsJS} from "@/module/rabbit-system/api/registerAndLogin";
import {isValidInput, charsLengthUtil, getAgeByBirthday} from "@/module/uitls"
export default {
name: 'LoginPage',
setup() {
// 页面一打开就执行
onMounted(() => {
loadProblems();
});
const form ={
nickName: "",
sex: "",
age: "",
birthday: "",
password: "",
safetyProblem1Id: "",
safetyProblem2Id: "",
safetyProblem3Id: "",
safetyProblem1Answer: "",
safetyProblem2Answer: "",
safetyProblem3Answer: "",
};
// 定义响应式变量
const loading = ref(false);
const nickName = ref("");
// v-model="sex" 用于双向绑定 ,这里是给组件赋一个默认的值
const sex = ref('')
const birthday = ref("");
const password1 = ref("");
const password2 = ref("");
let safetyProblem1 = ref('请选择安全问题1'); // 确保在这里声明 safetyProblem1
let safetyProblem2 = ref('请选择安全问题2');
let safetyProblem3 = ref('请选择安全问题3');
let safetyProblem1Id = ref('');
let safetyProblem2Id = ref('');
let safetyProblem3Id = ref('');
const safetyProblem1Answer = ref("");
const safetyProblem2Answer = ref("");
const safetyProblem3Answer = ref("");
// let 可以在下面的函数内部重新赋值
let message;
// 一个提示弹窗的方法
const popUpWindows = (msg) => {
ElMessageBox.alert(msg, '温馨提示', {
confirmButtonText: '确认',
})
};
// 定义一个 delay 函数,用于处理异步延迟
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 重置表单数据
const resetForm = () => {
nickName.value = "";
sex.value = "";
birthday.value = "";
password1.value = "";
password2.value = "";
safetyProblem1.value = '请选择安全问题1';
safetyProblem2.value = '请选择安全问题2';
safetyProblem3.value = '请选择安全问题3';
safetyProblem1Id.value = '';
safetyProblem2Id.value = '';
safetyProblem3Id.value = '';
safetyProblem1Answer.value = "";
safetyProblem2Answer.value = "";
safetyProblem3Answer.value = "";
};
// 加载安全问题
let questions = ref([]);
const loadProblems = async () => {
try {
const response = await selSafetyProblemsJS();
const dd = response.data.data;
questions.value = dd;
} catch (error) {
console.error('获取问题失败:', error);
}
};
// 选择安全问题后将安全问题及id渲染到标签里
const customerSafetyPlus = (command) => {
const [order,problem, id] = command.split('/');
if (order === '1'){
safetyProblem1Id.value = id;
safetyProblem1.value = problem;
}else if (order === '2'){
safetyProblem2Id.value = id;
safetyProblem2.value = problem;
}else if (order === '3'){
safetyProblem3Id.value = id;
safetyProblem3.value = problem;
}
}
// 返回按钮
const goBack = () => {
router.replace('/LoginPage');
};
// 注册按钮
const register = async() => {
if (nickName.value == "" || sex.value == "" || birthday.value == "" || password1.value == "" || password2.value == "") {
message = "请完善用户信息!";
popUpWindows(message);
}else if (isValidInput(nickName.value) === false) {
message = "昵称请使用中文英文和数字!";
popUpWindows(message);
} else if (charsLengthUtil(nickName.value) > 7) {
message = "昵称不得超过7个字符长度!";
popUpWindows(message);
} else if (password1.value.length < 6 || password2.value.length < 6 || password2.value.length > 20 || password2.value.length > 20) {
message = "密码长度不得小于6位,不得大于20位!";
popUpWindows(message);
} else if (password1.value !== password2.value) {
message = "两次输入的密码不一致!";
popUpWindows(message);
} else if (getAgeByBirthday(birthday.value) < 18 && getAgeByBirthday(birthday.value) != null) {
message = "年龄必须大于18岁!";
popUpWindows(message);
} else if (safetyProblem1.value == '请选择安全问题1' || safetyProblem2.value == '请选择安全问题2' || safetyProblem3.value == '请选择安全问题3') {
message = "请选择3个安全问题!";
popUpWindows(message);
} else if (safetyProblem1Answer.value.length == 0) {
message = "请回答第一个问题!";
popUpWindows(message);
} else if (safetyProblem2Answer.value.length == 0) {
message = "请回答第二个问题!";
popUpWindows(message);
} else if (safetyProblem3Answer.value.length == 0) {
message = "请回答第三个问题!";
popUpWindows(message);
}else if (safetyProblem1Answer.value.length > 20 || safetyProblem2Answer.value.length > 20 || safetyProblem3Answer.value.length > 20) {
message = "答案字数不得超过20个字!";
popUpWindows(message);
} else if (isValidInput(safetyProblem1Answer.value) === false || isValidInput(safetyProblem2Answer.value) === false || isValidInput(safetyProblem3Answer.value) === false) {
message = "昵称请使用中文英文和数字!";
popUpWindows(message);
}else {
// 获取选择的日期
const localDate = new Date(birthday.value); // 假设 updBirthday.value 是 Date 对象
// 获取 UTC 日期部分
const utcDate = new Date(Date.UTC(localDate.getFullYear(), localDate.getMonth(), localDate.getDate()));
// 格式化为 YYYY-MM-DD
const formattedBirthday = utcDate.toISOString().split('T')[0];
form.nickName= nickName.value;
form.sex= sex.value;
form.age= getAgeByBirthday(birthday.value);
form.birthday = formattedBirthday;
form.password= password1.value;
form.safetyProblem1Id= safetyProblem1Id.value;
form.safetyProblem2Id= safetyProblem2Id.value;
form.safetyProblem3Id= safetyProblem3Id.value;
form.safetyProblem1Answer= safetyProblem1Answer.value;
form.safetyProblem2Answer= safetyProblem2Answer.value;
form.safetyProblem3Answer= safetyProblem3Answer.value;
// 开始注册
loading.value = true;
const data = await registerJS(form);
await delay(1000);
loading.value = false;
if (data.data.i > 0){
popUpWindows(data.msg);
await router.replace('/LoginPage');
}else {
popUpWindows(data.msg);
resetForm(); // 清空表单数据
}
}
loading.value = false;
};
return {
loading,
questions,
nickName,
sex,
birthday,
password1,
password2,
safetyProblem1,
safetyProblem2,
safetyProblem3,
safetyProblem1Id,
safetyProblem2Id,
safetyProblem3Id,
safetyProblem1Answer,
safetyProblem2Answer,
safetyProblem3Answer,
goBack,
register,
popUpWindows,
customerSafetyPlus
};
}
}
</script>
<style scoped>
/* 你的样式代码 */
</style>
4、HomePage.vue
登陆后的主页面。
<template>
<div v-loading = "loading" style="background: gray; width: 100%; height: 100vh; position: fixed;">
<div style="background-color: green; width: 100%; height: 80px; display: flex; align-items: center; justify-content: space-between;">
<h1 style=" width: 140px; display: block; border: none; outline: none;margin-left: 50px;color: white">兔子系统</h1>
<div style="width: 800px;height: 70px;background-color: orange;display: flex; flex-direction: column;border-radius:15px">
<el-text style="color: red;margin-top: 5px">公告:</el-text>
<el-text style="align-self: flex-start;text-align: left;text-indent: 2em;color: white;margin-left: 10px;margin-right: 10px">我会让时间去证明,只有爱才能够战胜一切武力,只有爱才能够填补人们心中无穷无尽的欲望黑洞,也只有爱才能让这个世界达到永恒的和平。</el-text>
</div>
<div style="display: flex; align-items: center;cursor: pointer" @click="personalInformationShow = true">
<img v-if="headPortraitFile"
:src="`${headPortraitFile}`"
alt="头像"
style="width: 60px; height: 60px; border-radius: 50%;margin-right: 10px" />
<div style="display: flex; flex-direction: column; margin-right: 20px;">
<el-text style="align-self: flex-start;color: white">昵称:{{ nickName }}</el-text>
<el-text style="align-self: flex-start;color: white">账号:{{ account }}</el-text>
</div>
</div>
</div>
<div style="width: 100%; height: 100vh; position: fixed; display: flex; margin-top: 5px;left: 0;">
<div style="background-color: green; width: 200px; height: 100%; margin: 0; display: inline-block;">
<el-row class="tac"> <!-- 行组件 -->
<el-col :span="12"> <!-- 列组件 -->
<el-menu style="width: 200px;" :unique-opened="true"> <!-- 单个垂直菜单组件 --> <!-- unique-opened="true" 始终最多只有一个菜单是展开的-->
<el-sub-menu index="1">
<template #title>
<span>系统管理</span>
</template>
<el-menu-item index="1-1" @click="customerManagement">用户管理</el-menu-item>
</el-sub-menu>
<!-- <el-sub-menu index="2">-->
<!-- <template #title>-->
<!-- <span>即时通讯</span>-->
<!-- </template>-->
<!-- <el-menu-item index="2-1">好友</el-menu-item>-->
<!-- <el-menu-item index="2-2">聊天</el-menu-item>-->
<!-- </el-sub-menu>-->
</el-menu>
</el-col>
</el-row>
</div>
<div style="background-color: gray; width: 100%; height: 100vh; margin-left: 5px;">
<iframe id="iframe" style="background-color: white; width: 100%; height: 100vh; border: none;" src="/DataScreen"></iframe>
</div>
</div>
</div>
<!-- 个人信息的遮罩层 -->
<el-drawer v-model="personalInformationShow" :with-header="false" style="display: flex; flex-direction: column; height: 100%;background-color: #42b983">
<div style="flex: 1; display: flex; flex-direction: column; justify-content: flex-start;">
<div style="display: flex; align-items: center;">
<img v-if="headPortraitFile"
:src="`${headPortraitFile}`"
alt="头像"
style="width: 60px; height: 60px; border-radius: 50%;margin-right: 10px" />
<div style="display: flex; flex-direction: column; margin-right: 20px;">
<el-text style="align-self: flex-start;">昵称:{{ nickName }}</el-text>
<el-text style="align-self: flex-start;">账号:{{ account }}</el-text>
</div>
</div>
<div style="display: flex; flex-direction: column; margin-right: 10px;">
<el-text style="align-self: flex-start;margin-top: 5px">年龄:{{ age }}</el-text>
<el-text style="align-self: flex-start;margin-top: 5px">出生日期:{{ birthday }}</el-text>
<el-text style="align-self: flex-start;margin-top: 5px">性别:{{ sex }}</el-text>
<el-text style="align-self: flex-start;margin-top: 5px">注册时间:{{ createTime }}</el-text>
</div>
</div>
<div style="height: 70%"></div>
<div style="padding: 10px; text-align: center; background-color: #359d77 ;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); border-radius: 5px;">
<el-button type="primary" style="width: 180px" @click="setting()">设置</el-button>
<el-button type="primary" style="width: 180px" @click="logOut()">退出登录</el-button>
</div>
</el-drawer>
<!-- 设置页面 -->
<el-dialog :close-on-click-modal="false" v-model="settingShow" width="200px" style="margin-top: 120px" append-to-body><!--要支持动态绑定就在前面加个 : ,如::title="detailTile"-->
<span style="font-size: 20px">设置</span>
<div>
<el-button @click="updPersonalHeadPortrait()" style="margin-top: 10px">更换头像</el-button>
</div>
<div>
<el-button @click="updPersonalInformation()" style="margin-top: 10px">修改个人信息</el-button>
</div>
<div>
<el-button @click="updPassword()" style="margin-top: 10px">修改密码</el-button>
</div>
<div>
<el-button @click="updSafetyProblem()" style="margin-top: 10px">修改安全问题</el-button>
</div>
<div style="text-align: right;margin-top: 15px">
<el-button @click="cancelShow()">取消</el-button>
</div>
</el-dialog>
<!-- 替换头像页面 -->
<el-dialog :close-on-click-modal="false" v-model="updPersonalHeadPortraitShow" width="500" style="margin-top: 120px" append-to-body>
<span style="font-size: 20px">更换头像</span>
<div style="display: flex; align-items: center;">
<img v-if="headPortraitFile"
:src="`${headPortraitFile}`"
alt="头像"
style="width: 100px; height: 100px;margin-left: 10px;margin-top: 10px;border-radius: 50%" />
<!-- <el-avatar> 组件用于展示用户的头像或图片。在你的代码中,它的作用如下:
src 属性:这个属性绑定到 headPortraitFile,表示头像的图片源。当用户选择了一张图片后,headPortraitFile 会更新为该图片的 URL,从而使 <el-avatar> 显示用户选择的图片。
style 属性:设置了头像的宽度和高度为 100px,确保头像在界面上以合适的尺寸显示。
v-if="headPortraitFile":这个指令确保只有在 headPortraitFile(图片的src) 存在时,头像才会显示。这意味着在用户未选择图片之前,头像不会占用界面空间。 -->
<div style="margin-top: 5px;margin-left: 10px">
<div>
<input type="file" @change="selectHeadPortrait" accept="image/*" style="width: 68px;"/>
<span v-if="selectedFileName" style="margin-left: 5px">{{ selectedFileName }}</span>
<!-- 允许用户选择本地的图片,并且用户只能选择图片文件 -->
</div>
<div style="margin-top: 10px">
<button @click="uploadHeadPortrait" v-if="updHeadPortraitFile">替换头像</button>
</div>
</div>
</div>
<div style="text-align: right;margin-top: 10px;margin-right: 14px">
<el-button @click="cancelShow()">取消</el-button>
</div>
</el-dialog>
<!-- 修改个人信息页面 -->
<el-dialog :close-on-click-modal="false" v-model="updPersonalInformationShow" width="604" style="margin-top: 120px" append-to-body><!--要支持动态绑定就在前面加个 : ,如::title="detailTile"-->
<span style="font-size: 20px">修改个人信息</span>
<div style="margin-top: 20px;margin-left: 10px">
账号:<el-input v-model="updAccount" style="width: 220px;margin-right: 20px" disabled/>
昵称:<el-input v-model="updNickName" style="width: 220px;"/>
</div>
<div style="margin-top: 10px;margin-left: 10px">
性别:<el-radio-group v-model="updSex" style="margin-right: 20px"> <!--v-model="sex" 用于双向绑定-->
<el-radio value="0" border style="width: 60px;">女</el-radio>
<el-radio value="1" border style="width: 60px;margin-left: -20px">男</el-radio>
<el-radio value="2" border style="width: 80px;margin-left: -20px">保密</el-radio>
</el-radio-group>
生日:<el-date-picker type="date" style="width: 220px;" v-model="updBirthday"/>
</div>
<div style="text-align: right;margin-top: 10px;margin-right: 14px">
<el-button @click="cancelShow()">取消</el-button>
<el-button @click="updInformation()">提交</el-button>
</div>
</el-dialog>
<!-- 安全问题校验页面 -->
<el-dialog :close-on-click-modal="false" v-model="safetyProblemVerifyShow" width="370px" style="margin-top: 120px" append-to-body>
<span style="font-size: 20px">请选择并回答原来的安全问题</span>
<div style="margin-top: 10px;">
<el-dropdown split-button trigger="click" @command="(command) => customerSafetyPlus(command)">
<span style="width: 274px">{{ safetyProblem1 }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="'1'+'/'+item.problem+'/'+item.id" v-for="item in questions" :key="item.id">
{{ item.problem }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div>
<el-input v-model="safetyProblem1Answer" v-if="safetyProblem1!='请选择安全问题1'" style="width: 338px;margin-top: 5px" placeholder="请输入安全问题的答案" />
</div>
<div style="margin-top: 10px;">
<el-dropdown split-button trigger="click" @command="(command) => customerSafetyPlus(command)">
<span style="width: 274px">{{ safetyProblem2 }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="'2'+'/'+item.problem+'/'+item.id" v-for="item in questions" :key = "item.id">
{{ item.problem }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div>
<el-input v-model="safetyProblem2Answer" v-if="safetyProblem2!='请选择安全问题2'" style="width: 338px;margin-top: 5px" placeholder="请输入安全问题的答案" />
</div>
<div style="margin-top: 10px;">
<el-dropdown split-button trigger="click" @command="(command) => customerSafetyPlus(command)">
<span style="width: 274px">{{ safetyProblem3 }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="'3'+'/'+item.problem+'/'+item.id" v-for="item in questions" :key = "item.id">
{{ item.problem }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div>
<el-input v-model="safetyProblem3Answer" v-if="safetyProblem3!='请选择安全问题3'" style="width: 338px;margin-top: 5px" placeholder="请输入安全问题的答案" />
</div>
<div class="mb-4" style="margin-top: 10px;text-align: center;">
<el-button type="primary" @click="cancelShow">取消</el-button>
<el-button type="primary" @click="verify">开始校验</el-button>
</div>
</el-dialog>
<!--修改密码页面-->
<el-dialog v-model="updPasswordForm" title="请输入您的新密码" width="250" style="margin-top: 30vh;" :close-on-click-modal="false"> <!-- close-on-click-modal 控制点击外部是否关闭-->
<el-input v-model="newPassword1" type="password" autocomplete="off" placeholder="请输入您的新密码"/><!-- autocomplete="off" 防止自动填充 -->
<el-input v-model="newPassword2" type="password" autocomplete="off" placeholder="请再次输入您的新密码" style="margin-top: 5px"/><!-- autocomplete="off" 防止自动填充 -->
<div style="margin-top: 5px;">
<el-text v-if="newPassword1 !== newPassword2" type="danger">两次输入的密码不一致!</el-text>
</div>
<el-button style="margin-top: 10px" @click="updPasswordForm = false">取消</el-button>
<el-button style="margin-top: 10px" @click="updPasswordSubmit">确定</el-button>
</el-dialog>
<!--修改安全问题页面-->
<el-dialog :close-on-click-modal="false" v-model="updSafetyProblemVerifyShow" width="370px" style="margin-top: 120px" append-to-body>
<span style="font-size: 20px">请重新选择安全问题并输入答案</span>
<div style="margin-top: 10px;">
<el-dropdown split-button trigger="click" @command="(command) => customerSafetyPlus(command)">
<span style="width: 274px">{{ safetyProblem1 }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="'1'+'/'+item.problem+'/'+item.id" v-for="item in questions" :key = "item.id">
{{ item.problem }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div>
<el-input v-model="safetyProblem1Answer" v-if="safetyProblem1!='请选择安全问题1'" style="width: 338px;margin-top: 5px" placeholder="请输入安全问题的答案" />
</div>
<div style="margin-top: 10px;">
<el-dropdown split-button trigger="click" @command="(command) => customerSafetyPlus(command)">
<span style="width: 274px">{{ safetyProblem2 }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="'2'+'/'+item.problem+'/'+item.id" v-for="item in questions" :key = "item.id">
{{ item.problem }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div>
<el-input v-model="safetyProblem2Answer" v-if="safetyProblem2!='请选择安全问题2'" style="width: 338px;margin-top: 5px" placeholder="请输入安全问题的答案" />
</div>
<div style="margin-top: 10px;">
<el-dropdown split-button trigger="click" @command="(command) => customerSafetyPlus(command)">
<span style="width: 274px">{{ safetyProblem3 }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="'3'+'/'+item.problem+'/'+item.id" v-for="item in questions" :key = "item.id">
{{ item.problem }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div>
<el-input v-model="safetyProblem3Answer" v-if="safetyProblem3!='请选择安全问题3'" style="width: 338px;margin-top: 5px" placeholder="请输入安全问题的答案" />
</div>
<div class="mb-4" style="margin-top: 10px;text-align: center;">
<!-- :style="{ width: '340px' }" 设置按钮宽度 -->
<el-button type="primary" @click="cancelShow">取消</el-button>
<el-button type="primary" @click="updSafetyProblemSubmit()">提交</el-button>
</div>
</el-dialog>
<!--退出登录提示-->
<el-dialog :close-on-click-modal="false" v-model="logOutShow" width="370px" style="margin-top: 300px" append-to-body>
<span style="font-size: 20px">确定退出吗?</span>
<div class="mb-4" style="margin-top: 10px;text-align: center;">
<el-button @click="cancelShow">取消</el-button>
<el-button @click="logOutSubmit">确定</el-button>
</div>
</el-dialog>
</template>
<script>
import { onMounted, ref} from "vue";
import {loadCustomerInformationJS} from "../api/home"
import {ElMessageBox} from "element-plus";
import {uploadHeadPortraitJS,updInformationJS,updSafetyProblemJS} from "../api/customer"
import {selSafetyProblemsJS} from "@/module/rabbit-system/api/registerAndLogin";
import {retrievePasswordJs, updPasswordJS} from "@/module/rabbit-system/api/retrievePassword";
import router from "@/router";
import {charsLengthUtil, dateFormatConversion, getAgeByBirthday, isValidInput} from "@/module/uitls";
export default {
name: "HomePage",
setup(){
const loading = ref(false); //加载
const personalInformationShow = ref(false); // 控制个人信息抽屉弹窗
const settingShow = ref(false); // 控制个人信息设置弹窗
const updPersonalInformationShow = ref(false); //控制修改个人信息的页面
const updPersonalHeadPortraitShow = ref(false);
const selectedFile = ref(null);
const headPortraitFile = ref(""); //原始头像
const updHeadPortraitFile = ref(false); //替换头像时的控制替换头像按钮是否显示
const selectedFileName = ref(''); // 用于存储选择的文件名
let nickName = ref("");
let updNickName = ref("");
let account = ref("");
let updAccount = ref("");
let age = ref("");
let sex = ref("");
let updSex = ref("");
let createTime = ref("");
let birthday = ref("");
let updBirthday = ref("");
let safetyProblemVerifyShow = ref(false);
let safetyProblem1 = ref('请选择安全问题1');
let safetyProblem2 = ref('请选择安全问题2');
let safetyProblem3 = ref('请选择安全问题3');
let safetyProblem1Id = ref('');
let safetyProblem2Id = ref('');
let safetyProblem3Id = ref('');
const safetyProblem1Answer = ref("");
const safetyProblem2Answer = ref("");
const safetyProblem3Answer = ref("");
const newPassword1 = ref("");
const newPassword2 = ref("");
const updPasswordForm = ref(false);
const isShow = ref("");
const updSafetyProblemVerifyShow = ref(false);
const logOutShow = ref(false);
// 一个提示弹窗的方法
const popUpWindows = async (msg) => {
await ElMessageBox.alert(msg, '温馨提示', {
confirmButtonText: '确认',
});
};
// 页面一打开就执行的方法
onMounted(() => {
loadCustomerInformation(sessionStorage.getItem("account"));
});
// 加载用户的所有数据
const loadCustomerInformation = async(sessionAccount) => {
loading.value = true;
const data = await loadCustomerInformationJS(sessionAccount);
if (data.code === 200){
const customer = data.data;
nickName.value = customer.nickName;
account.value = customer.account;
age.value = customer.age;
if (customer.sex==='0'){
sex.value = "女";
}else if(customer.sex==='1') {
sex.value = "男";
}else {
sex.value = "保密";
}
if (customer.headPortraitFile == null){
headPortraitFile.value = require('@/assets/兔子来了.jpeg');
sessionStorage.setItem('headPortraitFile',require('@/assets/兔子来了.jpeg'));
}else {
headPortraitFile.value = "data:image/jpeg;base64," + customer.headPortraitFile;
sessionStorage.setItem('headPortraitFile',"data:image/jpeg;base64," + customer.headPortraitFile);
}
createTime.value = customer.createTime;
birthday.value = customer.birthday;
sessionStorage.setItem('id', customer.id);
loading.value = false;
}else {
loading.value = false;
await popUpWindows(data.msg);
}
};
// 点击用户管理
const customerManagement = async() => {
const iframe = document.getElementById('iframe');
iframe.src = "/CustomerPage";
};
// 用户信息的设置
const setting = async() => {
personalInformationShow.value = false;
settingShow.value = true;
};
// 退出登录提示
const logOut = async() => {
personalInformationShow.value = false;
logOutShow.value = true;
}
// 退出登录确定按钮
const logOutSubmit = async() => {
// 清除所有的 sessionStorage
sessionStorage.clear();
await router.replace('/LoginPage');
}
// 取消用户信息设置的页面
const cancelShow = async() => {
headPortraitFile.value = sessionStorage.getItem("headPortraitFile");
updPersonalInformationShow.value = false;
updPersonalHeadPortraitShow.value = false;
settingShow.value = false;
safetyProblemVerifyShow.value = false;
updPasswordForm.value = false;
updSafetyProblemVerifyShow.value = false;
logOutShow.value = false;
await emptyForm();
};
// 修改用户信息
const updPersonalInformation = async() => {
updBirthday.value = birthday.value;
updAccount.value = account.value;
if (sex.value === "女"){
updSex.value = "0";
}else if(sex.value=== "男") {
updSex.value = "1";
}else {
updSex.value = "2";
}
updNickName.value = nickName.value;
settingShow.value = false;
updPersonalInformationShow.value = true;
};
// 修改用户密码
const updPassword = async() => {
isShow.value = "3";
await loadProblems();
settingShow.value = false;
safetyProblemVerifyShow.value = true;
};
// 修改用户安全问题
const updSafetyProblem = async() => {
isShow.value = "4";
await loadProblems();
settingShow.value = false;
safetyProblemVerifyShow.value = true;
};
// 更换头像
const updPersonalHeadPortrait = async() => {
updHeadPortraitFile.value = false;
settingShow.value = false;
updPersonalHeadPortraitShow.value = true;
};
// 选择头像的方法
// 一个常见的问题,浏览器在选择相同文件时不会触发 change 事件。为了处理这种情况,可以在文件选择之前手动重置输入框的值。这可以通过将输入框的 value 设置为一个空字符串来实现。
const selectHeadPortrait = (event) => {
const files = event.target.files;
if (files.length === 0) { //未选择任何文件
return;
}
const validExtensions = ['jpg', 'jpeg', 'png', 'gif'];
for (let i = 0; i < files.length; i++) {
const file = files[i];
const fileExtension = file.name.split('.').pop().toLowerCase();
if (validExtensions.includes(fileExtension)) {
updHeadPortraitFile.value = true;
selectedFile.value = file;
headPortraitFile.value = URL.createObjectURL(file);
selectedFileName.value = file.name; // 保存文件名
} else {
popUpWindows(`${file.name} 不是有效的图片文件`);
}
}
// 重置 input 的值
event.target.value = '';
};
// 上传头像(替换头像)
const MAX_SIZE = 40 * 1024 * 1024; // 40 MB
const uploadHeadPortrait = async () => {
const formData = new FormData();
if (selectedFile.value.size > MAX_SIZE) {
await popUpWindows(`${selectedFile.value.name} 文件大小超过限制`);
}else {
loading.value = true;
const id = sessionStorage.getItem("id");
formData.append("id", id);
formData.append('image', selectedFile.value);
try {
const response = await uploadHeadPortraitJS(formData);
console.log(response);
if (response.statusCodeValue === 200){
updPersonalHeadPortraitShow.value = false;
// 更新保存用户头像文件的 session
sessionStorage.setItem("headPortraitFile","data:image/jpeg;base64," + response.body) ;
loading.value = false;
await popUpWindows("替换成功");
}
} catch (error) {
loading.value = false;
console.error('上传头像失败:', error);
}
}
};
// 修改个人信息
const updInformation = async () => {
if (updNickName.value === ''){
await popUpWindows("昵称不能为空");
}else if (isValidInput(updNickName.value) === false) {
await popUpWindows("昵称请使用中文英文和数字!");
}else if (charsLengthUtil(updNickName.value) > 7) {
await popUpWindows("昵称不得超过7个字符长度!");
}else if (getAgeByBirthday(updBirthday.value) < 18 &&getAgeByBirthday(updBirthday.value) != null) {
await popUpWindows("年龄必须大于18岁!");
}else {
loading.value = true;
const information ={
account: "",
nickName: "",
sex: "",
birthday: "",
age: ""
};
information.account = updAccount.value;
information.nickName = updNickName.value;
information.sex = updSex.value;
if (updBirthday.value === birthday.value){
information.birthday = null;
information.age = null;
}else{
information.birthday= dateFormatConversion(updBirthday.value);
information.age = getAgeByBirthday(updBirthday.value);
}
const data = await updInformationJS(information);
if (data.code == 200){
loading.value = false;
nickName.value = updNickName.value;
if (updSex.value === "0") {
sex.value = "女";
} else if (updSex.value === "1") {
sex.value = "男";
} else if (updSex.value === "2") {
sex.value = "保密";
}
birthday.value = dateFormatConversion(updBirthday.value);
age.value = getAgeByBirthday(updBirthday.value);
updPersonalInformationShow.value = false;
await popUpWindows(data.msg);
}else {
loading.value = false;
await popUpWindows(data.msg);
}
}
}
// 加载安全问题
let questions = ref([]);
const loadProblems = async () => {
try {
const response = await selSafetyProblemsJS();
const dd = response.data.data;
questions.value = dd;
} catch (error) {
console.error('获取问题失败:', error);
}
};
// 选择安全问题后将安全问题及id渲染到标签里
const customerSafetyPlus = (command) => {
const [order,problem, id] = command.split('/');
if (order === '1'){
safetyProblem1Id.value = id;
safetyProblem1.value = problem;
}else if (order === '2'){
safetyProblem2Id.value = id;
safetyProblem2.value = problem;
}else if (order === '3'){
safetyProblem3Id.value = id;
safetyProblem3.value = problem;
}
}
// 安全问题校验
const verify = async () => {
if (safetyProblem1.value == '请选择安全问题1' || safetyProblem2.value == '请选择安全问题2' || safetyProblem3.value == '请选择安全问题3') {
await popUpWindows("请选择3个安全问题!");
} else if (safetyProblem1Answer.value.length == 0) {
await popUpWindows("请回答第一个问题!");
} else if (safetyProblem2Answer.value.length == 0) {
await popUpWindows("请回答第二个问题!");
} else if (safetyProblem3Answer.value.length == 0) {
await popUpWindows("请回答第三个问题!");
} else {
loading.value = true;
const verifyForm ={
account: "",
safetyProblem1Id: "",
safetyProblem2Id: "",
safetyProblem3Id: "",
safetyProblem1Answer: "",
safetyProblem2Answer: "",
safetyProblem3Answer: "",
password:"",
};
verifyForm.account= account.value;
verifyForm.safetyProblem1Id= safetyProblem1Id.value;
verifyForm.safetyProblem2Id= safetyProblem2Id.value;
verifyForm.safetyProblem3Id= safetyProblem3Id.value;
verifyForm.safetyProblem1Answer= safetyProblem1Answer.value;
verifyForm.safetyProblem2Answer= safetyProblem2Answer.value;
verifyForm.safetyProblem3Answer= safetyProblem3Answer.value;
const data = await retrievePasswordJs(verifyForm);
if (data.code == 200){
safetyProblemVerifyShow.value = false;
loading.value = false;
await emptyForm();
await popUpWindows(data.msg);
if (isShow.value === "3"){
updPasswordForm.value = true;
}else if (isShow.value === "4"){
updSafetyProblemVerifyShow.value = true;
}
}else {
loading.value = false;
await popUpWindows(data.msg);
}
}
}
// 清空表单数据
const emptyForm = async () => {
safetyProblem1.value = "请选择安全问题1";
safetyProblem2.value = "请选择安全问题2";
safetyProblem3.value = "请选择安全问题3";
safetyProblem1Id.value = "";
safetyProblem2Id.value = "";
safetyProblem3Id.value = "";
safetyProblem1Answer.value = "";
safetyProblem2Answer.value = "";
safetyProblem3Answer.value = "";
}
// 修改密码
const updPasswordSubmit = async () => {
if (newPassword1.value != newPassword2.value){
await popUpWindows("两次输入的密码不一致!");
}else if (newPassword1.value.length < 6 || newPassword1.value.length > 20){
await popUpWindows("密码的长度应该在6到20之间!");
}else {
loading.value = true;
const accountAndPassword ={
account: "",
password:"",
};
accountAndPassword.account = account.value;
accountAndPassword.password = newPassword1.value;
const data = await updPasswordJS(accountAndPassword);
newPassword1.value = "";
newPassword2.value = "";
updPasswordForm.value = false;
loading.value = false;
if (data.code == 200){
await popUpWindows(data.msg);
}else {
await popUpWindows(data.msg);
}
}
};
// 修改安全问题
const updSafetyProblemSubmit = async () => {
loading.value = true;
const newSafetyProblem ={
account: "",
safetyProblem1Id: "",
safetyProblem2Id: "",
safetyProblem3Id: "",
safetyProblem1Answer: "",
safetyProblem2Answer: "",
safetyProblem3Answer: "",
};
newSafetyProblem.account = account.value;
newSafetyProblem.safetyProblem1Id = safetyProblem1Id.value;
newSafetyProblem.safetyProblem2Id = safetyProblem2Id.value;
newSafetyProblem.safetyProblem3Id = safetyProblem3Id.value;
newSafetyProblem.safetyProblem1Answer = safetyProblem1Answer.value;
newSafetyProblem.safetyProblem2Answer = safetyProblem2Answer.value;
newSafetyProblem.safetyProblem3Answer = safetyProblem3Answer.value;
const data = await updSafetyProblemJS(newSafetyProblem);
if (data.code == 200){
updSafetyProblemVerifyShow.value = false;
await emptyForm();
await popUpWindows(data.msg);
}else {
await popUpWindows(data.msg);
}
loading.value = false;
}
return {
loading,
account,
updAccount,
nickName,
updNickName,
age,
birthday,
updBirthday,
sex,
updSex,
createTime,
customerManagement,
personalInformationShow,
settingShow,
setting,
updPersonalInformationShow,
updPersonalHeadPortraitShow,
updPersonalInformation,
updPersonalHeadPortrait,
cancelShow,
selectHeadPortrait,
selectedFile,
uploadHeadPortrait,
headPortraitFile,
selectedFileName,
updHeadPortraitFile,
updInformation,
questions,
updPassword,
safetyProblemVerifyShow,
safetyProblem1,
safetyProblem2,
safetyProblem3,
safetyProblem1Id,
safetyProblem2Id,
safetyProblem3Id,
safetyProblem1Answer,
safetyProblem2Answer,
safetyProblem3Answer,
customerSafetyPlus,
verify,
updPasswordForm,
newPassword1,
newPassword2,
updPasswordSubmit,
updSafetyProblem,
updSafetyProblemVerifyShow,
updSafetyProblemSubmit,
logOut,
logOutShow,
logOutSubmit,
};
}
}
</script>
<style scoped>
/* 这里可以添加其他样式 */
</style>
5、DataScreen.vue
首页面展示。
<template>
大屏数据展示
</template>
<script>
export default {
name: "DataScreen"
}
</script>
<style scoped>
</style>
6、CustomerPage.vue
用户管理页面
<template>
<div>
<el-form :inline="true" style="text-align: left;margin-left: 10px;position: relative; top: 10px;"> <!--:inline="true" :把表格里的元素都放在同一排-->
<el-form-item prop="condition" label="搜索">
<el-input v-model="searchValue" placeholder="请输入搜索条件"/>
</el-form-item>
<el-form-item label="角色">
<el-select v-model="selectedRole" placeholder="请选择" :style="{width: '120px'}"> <!-- 修改v-model绑定项 selectedRole: null, // 用于存储选中的角色 -->
<el-option
v-for="role in selectRole"
:key="role.key"
:label="role.name"
:value="role.value"
/>
</el-select>
</el-form-item>
<el-form-item label="性别">
<el-select v-model="selectedSex" placeholder="请选择" :style="{width: '90px'}"> <!-- 修改v-model绑定项 -->
<el-option
v-for="sex in selectSex"
:key="sex.key"
:label="sex.name"
:value="sex.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="search()">搜索</el-button>
<el-button @click="reset()">重置</el-button>
</el-form-item>
</el-form>
<div>
<el-row>
<el-col :span="1.5">
<el-button @click="exportExcel()" type="success" plain style="width: 100px;margin-left: 10px">导出所有用户</el-button>
</el-col>
</el-row>
</div>
<!-- 用户列表表格 -->
<el-table v-loading="loading" :data="customerList" style="width: calc(100% - 20px);height: 510px;left: 10px;right: 10px;top: 10px;border: 1px solid gray;" :style="{ overflow: 'auto' }" scrollbar-always-on >
<el-table-column fixed="left" type="index" label="序号" align="center" width="60px"/>
<el-table-column label="id" align="center" width="100px" prop="id" v-if="false"/>
<!-- 使用 v-if 确保 headPortraitFile 存在,避免在没有头像时出现空白的 img 标签-->
<el-table-column width="100" label="头像" align="center">
<template #default="scope">
<img v-if="scope.row.headPortraitFile"
:src="scope.row.headPortraitFile"
alt="头像"
style="width: 30px; height: 30px; border-radius: 50%; vertical-align: middle;" />
</template>
</el-table-column>
<el-table-column label="账号" align="center" width="100px" prop="account"/>
<el-table-column label="角色" align="center" width="100px" prop="roleName"/>
<el-table-column label="昵称" align="center" width="165px" prop="nickName"/>
<el-table-column label="出生日期" align="center" width="100px" prop="birthday"/>
<el-table-column label="年龄" align="center" width="60px" prop="age"/>
<el-table-column label="性别" align="center" width="60px" prop="sex"/>
<el-table-column label="创建时间" align="center" width="160px" prop="createTime"/>
<el-table-column label="更新时间" align="center" width="160px" prop="updateTime"/>
<el-table-column label="更新时间" align="center" width="160px" prop="updateTime"/>
<el-table-column label="更新时间" align="center" width="160px" prop="updateTime"/>
<el-table-column label="更新时间" align="center" width="160px" prop="updateTime"/>
<el-table-column label="操作" fixed="right" min-width="140px" align="center">
<template #default="obj">
<el-button size="small" @click="details(obj.row)">
详情
</el-button>
<el-button
size="small"
type="danger"
@click="delCustomer(obj.row)"
>
删除
</el-button><!--@click="delCustomer(obj.$index, obj.row)" obj.$index:序号;obj.row:这一行的内容-->
</template>
</el-table-column>
</el-table>
<!-- 控制页数和分页 -->
<div class="demo-pagination-block" style="margin-top: 20px;display: flex; justify-content: center;">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 50, 100, 200]"
layout="total, sizes, prev, pager, next, jumper"
:total="customersCount"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
<!-- 详情弹窗 -->
<el-dialog :close-on-click-modal="false" :title="detailTile" v-model="detailsShow" width="622px" style="margin-top: 50px" append-to-body><!--要支持动态绑定就在前面加个 : ,如::title="detailTile"-->
<div style="display: flex; justify-content: center; align-items: center;">
<img v-if="headPortraitFile"
:src="headPortraitFile"
alt="头像"
@click="viewImage(headPortraitFile)"
style="width: 100px; height: 100px; border-radius: 50%; cursor: pointer" />
</div>
<div style="margin-top: 25px">
账号:<el-input v-model="account" style="width: 240px;margin-right: 20px" disabled/>
角色:<el-input v-model="roleName" style="width: 240px;" disabled/>
</div>
<div style="margin-top: 10px">
账号:<el-input v-model="nickName" style="width: 240px;margin-right: 20px" disabled/>
生日:<el-input v-model="birthday" style="width: 240px;" disabled/>
</div>
<div style="margin-top: 10px">
年龄:<el-input v-model="age" style="width: 240px;margin-right: 20px" disabled/>
性别:<el-input v-model="sex" style="width: 240px;" disabled/>
</div>
<div style="margin-top: 10px">
创建时间:<el-input v-model="createTime" style="width: 212px;margin-right: 20px" disabled/>
更新时间:<el-input v-model="updateTime" style="width: 212px;" disabled/>
</div>
<div style="margin-top: 15px; display: flex; justify-content: flex-end;margin-right: 4px">
<el-button @click="detailsShow = false" type="primary" plain>取消</el-button>
</div>
</el-dialog>
<!-- 下面是放大容器和图片(点击头像用的) -->
<div v-if="isLightboxVisible"
style="position: fixed; top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0, 0, 0, 0.8); display: flex;
justify-content: center; align-items: center; z-index: 1000;"
@click="closeImage">
<img
:src="imageShow"
style="max-width: 70%; max-height: 70%; border-radius: 10px;margin-top: -80px"
/>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
import { selCustomersJs, selCustomersCountJS, delCustomerById } from '@/module/rabbit-system/api/customer';
import { selRolesJS } from '@/module/rabbit-system/api/role';
import { processCustomerData } from '@/module/uitls';
import {ElMessageBox} from "element-plus";
import * as XLSX from 'xlsx';
export default {
name: 'CustomerPage',
setup() {
const isLightboxVisible = ref(false);
const imageShow = ref('');
// 点击头像变大,展示头像的方法
const viewImage = (image) => {
detailsShow.value = false;
imageShow.value = image;
isLightboxVisible.value = true;
};
// 点其他地方头像变小
const closeImage = () => {
detailsShow.value = true;
isLightboxVisible.value = false;
};
// 返回分页所需数据的表单
const pagingForm ={
currentPage: "",
pageSize: "",
searchValue:"",
sex:"",
role:"",
};
const loading = ref(true); //是否进入加载的控制
const customerList = ref([]);
const currentPage = ref(1); // 当前页码
let pageSize = ref(10); // 每页显示的条目数
let customersCount = ref(0); // 总条目数
let headPortraitFile = ref("");
let nickName = ref("");
let detailsShow = ref(false);
let selectedSex = ref("");
let selectedRole = ref("");
let selectRole = ref([]);
let searchValue = ref(""); //自定义搜索条件
//详情里的信息
let detailTile = ref("");
let account = ref("");
let roleName = ref("");
let birthday = ref("");
let age = ref("");
let sex = ref("");
let createTime = ref("");
let updateTime = ref("");
// 页面加载时调用方法
onMounted(() => {
selCustomersVue();
selRolesVue();
});
// 加载角色筛选条件信息
const selRolesVue = async() => {
const data = await selRolesJS();
// 转换为目标格式
selectRole.value = data.data.map((role, index) => ({
key: index, // 使用当前索引作为 key
name: role.roleName, // 使用 roleName 作为 name
value: role.id // 使用 id 作为 value
}));
// 添加额外的选项
selectRole.value.push({ key: selectRole.value.length, name: '',value: ''});
// console.log(selectRole.value);
}
// 搜索按钮
const search = async() => {
loading.value = true;
pagingForm.currentPage = currentPage.value;
pagingForm.pageSize = pageSize.value;
pagingForm.sex = selectedSex.value;
pagingForm.role = selectedRole.value;
pagingForm.searchValue = searchValue.value;
const response = await selCustomersJs(pagingForm);
// 批量处理数据
const customers = processCustomerData(response.data.data);
// 遍历所有的元素,如果没有换过头像的,就默认一个头像,换过的就用换的头像
customers.forEach(customer => {
if (customer.headPortraitFile == null) {
customer.headPortraitFile = require('@/assets/兔子来了.jpeg');
} else {
// 需要将 headPortrait 的二进制值格式化为一个完整的 Base64 图片 URL。
// 假设你的数据中的 headPortraitFile 字段存储的是一个 Base64 编码的图片数据
customer.headPortraitFile = "data:image/jpeg;base64," + customer.headPortraitFile;
}
});
customerList.value = customers;
customersCount.value = await selCustomersCount(pagingForm);
loading.value = false;
}
// 重置按钮
const reset = async() => {
loading.value = true;
pagingForm.currentPage = currentPage.value;
pagingForm.pageSize = pageSize.value;
pagingForm.sex = "";
pagingForm.role = "";
pagingForm.searchValue = "";
selectedSex.value = "";
selectedRole.value = "";
searchValue.value = "";
const response = await selCustomersJs(pagingForm);
// 批量处理数据
const customers = processCustomerData(response.data.data);
// 遍历所有的元素,如果没有换过头像的,就默认一个头像,换过的就用换的头像
customers.forEach(customer => {
if (customer.headPortraitFile == null) {
customer.headPortraitFile = require('@/assets/兔子来了.jpeg');
} else {
// 需要将 headPortrait 的二进制值格式化为一个完整的 Base64 图片 URL。
// 假设你的数据中的 headPortraitFile 字段存储的是一个 Base64 编码的图片数据
customer.headPortraitFile = "data:image/jpeg;base64," + customer.headPortraitFile;
}
});
customerList.value = customers;
customersCount.value = await selCustomersCount(pagingForm);
loading.value = false;
}
// 导出所有用户的 excel 按钮操作
const exportExcel = async() => {
// 清空查询所有用户的条件(包括分页,即所有数据)
pagingForm.currentPage = "";
pagingForm.pageSize = "";
pagingForm.searchValue = "";
pagingForm.sex = "";
pagingForm.role = "";
const allCustomerList = await selCustomersJs(pagingForm);
// 处理一下数据
const allCustomerListPlus = processCustomerData(allCustomerList.data.data);
const exportCustomers = allCustomerListPlus.map((customer, index) => ({
序号: index + 1, // 生成从1开始的序号
账号: customer.account,
角色: customer.roleName,
昵称: customer.nickName,
出生日期: customer.birthday,
年龄: customer.age,
性别: customer.sex,
创建时间: customer.createTime,
更新时间: customer.updateTime
}));
// 将数据转换为工作表
const ws = XLSX.utils.json_to_sheet(exportCustomers);
// 设置列的宽度
ws['!cols'] = [
{ wch: 5 }, // 序号宽度
{ wch: 10 }, // 账号宽度
{ wch: 11 }, // 角色宽度
{ wch: 20 }, // 昵称宽度
{ wch: 11 }, // 出生日期宽度
{ wch: 5 }, // 年龄宽度
{ wch: 5 }, // 性别宽度
{ wch: 21 }, // 创建时间宽度
{ wch: 21 }, // 更新时间宽度
];
// 创建一个新的工作簿
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, '数据');
// 获取当前时间
// 获取当前年份、月份和日期
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从0开始
const day = String(now.getDate()).padStart(2, '0');
const formattedDate = `${year}${month}${day}`; // 输出格式:YYYYMMDD
// 获取当前时间(小时、分钟、秒)
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
const formattedTime = `${hours}${minutes}${seconds}`; // 输出格式:HHmmss
// 最终样式
const formattedDateTime = `${formattedDate}${formattedTime}`; // 输出格式:YYYYMMDDHHmmss
// 导出文件
XLSX.writeFile(wb, "用户信息导出"+formattedDateTime+".xlsx");
}
// 计算所有用户的数量
const selCustomersCount = async (pagingForm) => {
const customersCount = await selCustomersCountJS(pagingForm);
return customersCount;
}
// 获取用户数据
const selCustomersVue = async () => {
loading.value = true;
try {
pagingForm.currentPage = currentPage.value;
pagingForm.pageSize = pageSize.value;
const response = await selCustomersJs(pagingForm);
// 批量处理数据
const customers = processCustomerData(response.data.data);
// 遍历所有的元素,如果没有换过头像的,就默认一个头像,换过的就用换的头像
customers.forEach(customer => {
if (customer.headPortraitFile == null) {
customer.headPortraitFile = require('@/assets/兔子来了.jpeg');
} else {
// 需要将 headPortrait 的二进制值格式化为一个完整的 Base64 图片 URL。
// 假设你的数据中的 headPortraitFile 字段存储的是一个 Base64 编码的图片数据
customer.headPortraitFile = "data:image/jpeg;base64," + customer.headPortraitFile;
}
});
customerList.value = customers;
customersCount.value = await selCustomersCount(pagingForm);
} catch (error) {
console.error('Error fetching customers:', error);
} finally {
loading.value = false;
}
};
// 每页显示的用户数量
const handleSizeChange = async (newSize) => {
pageSize.value = newSize; // 更新每页条目数
currentPage.value = 1; // 重置为第一页
await selCustomersVue(); // 重新获取数据
}
// 前往多少页
const handleCurrentChange = async (newPage) => {
currentPage.value = newPage; // 更新当前页码
await selCustomersVue(); // 重新获取数据
}
// 详情
const details = async (row) => {
detailTile.value = "用户:"+row.nickName;
account.value = row.account;
nickName.value = row.nickName;
roleName.value = row.roleName;
birthday.value = row.birthday;
age.value = row.age;
sex.value = row.sex;
headPortraitFile.value = row.headPortraitFile;
createTime.value = row.createTime;
updateTime.value = row.updateTime;
detailsShow.value = true; // 直接赋值为布尔值 // 更新当前页码
}
// 删除用户
const delCustomer = async (row) => {
// 弹窗
ElMessageBox.confirm(
"确认删除账号"+row.account+"吗?", // 消息内容
'温馨提示', // 标题
{
confirmButtonText: '确认', // 确认按钮文本
cancelButtonText: '取消', // 取消按钮文本
type: 'warning', // 提示类型,可以是 'warning'、'success'、'info'、'error'
closeOnClickModal: false, // 点其他地方不关闭弹窗
customStyle:{
top: '-150px'
}
}
)
.then(async () => {
// 用户点击确认
loading.value = true;
const id = await row.id;
const data = await delCustomerById(id);
if (data.code === 200) {
await selCustomersVue();
loading.value = false;
await ElMessageBox.alert("删除成功", '温馨提示', {
confirmButtonText: '确认',
customStyle: {
top: '-150px'
}
})
} else {
loading.value = false;
await ElMessageBox.alert("删除失败", '温馨提示', {
confirmButtonText: '确认',
customStyle: {
top: '-150px'
}
})
}
})
.catch(() => {
// 用户点击取消
});
}
return {
loading,
customerList,
pageSize,
customersCount,
currentPage,
handleSizeChange,
handleCurrentChange,
headPortraitFile,
delCustomer,
nickName,
details,
account,
detailsShow,
detailTile,
roleName,
birthday,
age,
sex,
createTime,
updateTime,
selectedSex,
selectSex: [ // 性别列表
{ key: 0,name: '女' ,value: '0'},
{ key: 1,name: '男' ,value: '1'},
{ key: 2,name: '保密' ,value: '2'},
{ key: 3,name: '' ,value: ''},
],
selectedRole,
selectRole,
searchValue,
reset,
exportExcel,
search,
isLightboxVisible,
imageShow,
viewImage,
closeImage
};
},
};
</script>
<style scoped>
/* 添加样式 */
</style>
3、跟后端交互的 js
1、registerAndLogin.js
登陆注册相关的js。
import axios from 'axios';
// 创建 axios 实例
const instance = axios.create({
baseURL: 'http://localhost:8081', // 设置请求的基础 URL
timeout: 10000, // 请求超时时间(毫秒)
});
// 使用 Axios 发送请求后,你会得到一个 Promise 对象。这个对象的 .then 方法可以用来处理请求的结果。当你收到响应数据时,它会被封装在 response 对象中。你可以从这个对象中提取你需要的数据。
// 定义一个异步函数,用于注册
export async function registerJS(formData) {
try {
// 发起 POST 请求,进行注册
const response = await instance.post('/LoginAndRegisterController/register', formData);
// 提取并返回响应中的数据
if (response.status === 200) {
return response.data; // 返回 '123456' 或其他响应数据
} else {
throw new Error(`Unexpected status code: ${response.status}`);
}
} catch (error) {
// 捕获并处理请求错误
console.error('Error fetching register:', error.message); // 打印错误信息到控制台
throw error; // 重新抛出错误,以便调用者可以进一步处理
}
}
// 登录
export async function loginJS(formData) {
try {
// 发起 POST 请求,进行注册
const response = await instance.post('/LoginAndRegisterController/login', formData);
// 提取并返回响应中的数据
if (response.status === 200) {
return response.data; // 返回 '123456' 或其他响应数据
} else {
throw new Error(`Unexpected status code: ${response.status}`);
}
} catch (error) {
// 捕获并处理请求错误
console.error('Error fetching register:', error.message); // 打印错误信息到控制台
throw error; // 重新抛出错误,以便调用者可以进一步处理
}
}
// 查询安全问题
export async function selSafetyProblemsJS() {
try {
const response = await instance.get('/SafetyProblemController/selSafetyProblems', {
headers: {
'Content-Type': 'application/json', // 设置请求头的内容类型为 JSON
}
});
return response; // // 假设返回的数据格式是 [{ command: 'problem1', label: '问题1、你的母亲姓什么?' }, ...]
} catch (error) {
// 捕获并处理请求错误
console.error('Error fetching register:', error.message); // 打印错误信息到控制台
throw error; // 重新抛出错误,以便调用者可以进一步处理
}
}
2、retrievePassword.js
忘记密码的js。
import axios from 'axios';
// 创建 axios 实例
const instance = axios.create({
baseURL: 'http://localhost:8081', // 设置请求的基础 URL
timeout: 10000, // 请求超时时间(毫秒)
});
// 定义一个异步函数,校验输入的账号和安全问题是否正确
export async function retrievePasswordJs(formData) {
try {
// 发起 POST 请求,进行注册
const response = await instance.post('/RetrievePasswordController/retrievePassword', formData);
// 提取并返回响应中的数据
if (response.status === 200) {
return response.data; // 返回 '123456' 或其他响应数据
} else {
throw new Error(`Unexpected status code: ${response.status}`);
}
} catch (error) {
// 捕获并处理请求错误
console.error('Error fetching register:', error.message); // 打印错误信息到控制台
throw error; // 重新抛出错误,以便调用者可以进一步处理
}
}
//修改密码
export async function updPasswordJS(formData) {
try {
// 发起 POST 请求,进行注册
const response = await instance.post('/RetrievePasswordController/updPassword', formData);
// 提取并返回响应中的数据
if (response.status === 200) {
return response.data; // 返回 '123456' 或其他响应数据
} else {
throw new Error(`Unexpected status code: ${response.status}`);
}
} catch (error) {
// 捕获并处理请求错误
console.error('Error fetching register:', error.message); // 打印错误信息到控制台
throw error; // 重新抛出错误,以便调用者可以进一步处理
}
}
3、home.js
主页面的 js。
import axios from 'axios';
// 创建 axios 实例
const instance = axios.create({
baseURL: 'http://localhost:8081', // 设置请求的基础 URL
timeout: 10000, // 请求超时时间(毫秒)
});
export async function loadCustomerInformationJS(account) {
try {
const response = await instance.get(`/HomePageController/loadCustomerInformation/${account}`);
if (response.status === 200) {
return response.data;
} else {
throw new Error(`Unexpected status code: ${response.status}`);
}
} catch (error) {
console.error('Error fetching customer information:', error.message);
throw error;
}
}
4、customer.js
用户相关的 js。
import axios from 'axios'; // 导入 axios 库,用于发起 HTTP 请求
// 创建 axios 实例
const instance = axios.create({
baseURL: 'http://localhost:8081', // 设置请求的基础 URL
timeout: 10000, // 请求超时时间(毫秒)
});
// 定义一个异步函数,用于获取客户数据
export async function selCustomersJs(form) {
try {
// 发起 GET 请求,获取客户数据
const response = await instance.post('/CustomerController/selCustomers',form);
// 返回响应的数据
return response;
} catch (error) {
console.error('Error fetching customers:', error);
throw error;
}
}
// 定义一个异步函数,用于获取客户的总数量
export async function selCustomersCountJS(form) {
try {
// 发起 GET 请求,获取客户数据
const response = await instance.post(`/CustomerController/selCustomersCount`,form);
// 返回响应的数据
return response.data.data;
} catch (error) {
console.error('Error fetching customers:', error);
throw error;
}
}
// 根据id删除用户
export async function delCustomerById(id) {
try {
// 发起 GET 请求,获取客户数据
const response = await instance.get(`/CustomerController/delCustomerById/${id}`);
// 返回响应的数据
return response.data;
} catch (error) {
console.error('Error fetching customers:', error);
throw error;
}
}
// 定义一个异步函数,用于上传头像
export async function uploadHeadPortraitJS(formData) {
try {
// 发起 GET 请求,获取客户数据
const response = await instance.post(`/CustomerController/uploadHeadPortrait`,formData);
// 返回响应的数据
return response.data.data;
} catch (error) {
console.error('Error fetching customers:', error);
throw error;
}
}
// 定义一个异步函数,修改个人信息
export async function updInformationJS(formData) {
try {
// 发起 GET 请求,获取客户数据
const response = await instance.post(`/CustomerController/updInformation`,formData);
// 返回响应的数据
return response.data;
} catch (error) {
console.error('Error fetching customers:', error);
throw error;
}
}
// 定义一个异步函数,修改个人安全问题
export async function updSafetyProblemJS(formData) {
try {
// 发起 GET 请求,获取客户数据
const response = await instance.post(`/CustomerController/updSafetyProblem`,formData);
// 返回响应的数据
return response.data;
} catch (error) {
console.error('Error fetching customers:', error);
throw error;
}
}
5、role.js
角色相关的 js。
import axios from 'axios';
// 创建 axios 实例
const instance = axios.create({
baseURL: 'http://localhost:8081', // 设置请求的基础 URL
timeout: 10000, // 请求超时时间(毫秒)
});
export async function selRolesJS() {
try {
const response = await instance.get(`/RoleController/selRoles`);
if (response.status === 200) {
return response.data;
} else {
throw new Error(`Unexpected status code: ${response.status}`);
}
} catch (error) {
console.error('Error fetching customer information:', error.message);
throw error;
}
}
这些大致就是全部的代码了,有疑问的话可以在评论区讨论,如果写法上有不妥或者更好的写法也可以告知,感谢。
结束...