parent
8439d436b5
commit
efc25bd26a
|
@ -2,20 +2,21 @@
|
|||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile default="true" name="Default" enabled="true" />
|
||||
<profile name="Maven default annotation processors profile" enabled="true">
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<module name="Online-code-evaluation-system" />
|
||||
<module name="oj-spring-boot" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
<bytecodeTargetLevel>
|
||||
<module name="Online-code-evaluation-system" target="17" />
|
||||
<module name="oj-spring-boot" target="1.8" />
|
||||
</bytecodeTargetLevel>
|
||||
</component>
|
||||
<component name="JavacSettings">
|
||||
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
|
||||
<module name="Online-code-evaluation-system" options="-parameters" />
|
||||
<module name="oj-spring-boot" options="-parameters" />
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="@localhost" uuid="6aaf03f9-1117-4cce-acf3-09911a21dc79">
|
||||
<driver-ref>mysql.8</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:mysql://localhost:3306</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
|
@ -7,5 +7,5 @@
|
|||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="openjdk-22" project-jdk-type="JavaSDK" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
|
||||
</project>
|
|
@ -2,8 +2,8 @@
|
|||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/oj-spring-boot/Online-code-evaluation-system.iml" filepath="$PROJECT_DIR$/oj-spring-boot/Online-code-evaluation-system.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/XJ-OJ.iml" filepath="$PROJECT_DIR$/.idea/XJ-OJ.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/oj-spring-boot/oj-spring-boot.iml" filepath="$PROJECT_DIR$/oj-spring-boot/oj-spring-boot.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
64
README.md
64
README.md
|
@ -1,4 +1,4 @@
|
|||
# 在线代码测评系统(第二次测试)
|
||||
# 在线代码测评系统
|
||||
|
||||
### 开发环境
|
||||
|
||||
|
@ -19,32 +19,14 @@
|
|||
|
||||

|
||||
|
||||
```
|
||||
视图层包括网站主站点和后台管理系统,主站点负责向用户提供服务,是用户看到的系统页面。用户可以在上面进行登录注册、个人信息管理、查看题目、编写代码、提交代码、查看测评状态、查看排名、参加竞赛、查看竞赛结果以及发布文章等主要操作。后台管理系统是专门给系统管理员使用的管理后台,管理员可以在上面进行用户管理、题目管理、竞赛管理、日志管理以及查看系统运行状态等操作。视图层通过Axios与后端接口进行数据交互。
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
网络接口层的主要职责是接收用户的请求,根据请求参数的不同,做出不同的响应,响应数据的格式是JSON数据。
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
业务层包括用户服务、题目服务、竞赛服务、代码服务、测评服务、文件服务、文章服务等功能模块。业务层主要是实现系统功能的代码部分,通过数据映射实现和数据层的交互,从而实现数据的持久化。
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
数据层是系统最底层,也是最重要的一层,系统中所有的数据均保存在数据层的MySQL服务和Redis服务中。
|
||||
```
|
||||
|
||||
- 视图层包括网站主站点和后台管理系统,主站点负责向用户提供服务,是用户看到的系统页面。用户可以在上面进行登录注册、个人信息管理、查看题目、编写代码、提交代码、查看测评状态、查看排名、参加竞赛、查看竞赛结果以及发布文章等主要操作。后台管理系统是专门给系统管理员使用的管理后台,管理员可以在上面进行用户管理、题目管理、竞赛管理、日志管理以及查看系统运行状态等操作。视图层通过Ajax与后端接口进行数据交互。
|
||||
- 网络接口层的主要职责是接收用户的请求,根据请求参数的不同,做出不同的响应,响应数据的格式是JSON数据。
|
||||
- 业务层包括用户服务、题目服务、竞赛服务、代码服务、测评服务、文件服务、文章服务等功能模块。业务层主要是实现系统功能的代码部分,通过数据映射实现和数据层的交互,从而实现数据的持久化。
|
||||
- 数据层是系统最底层,也是最重要的一层,系统中所有的数据均保存在数据层的MySQL服务和Redis服务中。
|
||||
|
||||
### 系统总体设计
|
||||
|
||||
```
|
||||
本系统主要分为用户和管理员两个主要的模块。用户模块主要包括登录注册、个人中心、题目浏览、代码评测、竞赛参与、文章发布等多个子功能模块。管理员的功能模块包括用户管理、题目管理、测评管理、竞赛管理、文章管理等。本系统详细的功能模块划分图如图所示。
|
||||
```
|
||||
|
||||
|
||||

|
||||
|
||||
|
@ -58,7 +40,7 @@ Compile Error:编译错误。用户提交的代码中有语法错误,无法
|
|||
|
||||
Partial Accepted:部分测评用例通过。表面用户提交的源代码可以通过部分测评用例,还有一部分测评用例无法通过,需要用户考虑其他的可能性。
|
||||
|
||||
Accepted:通过。用户提交的源代码经过测试后通过了所有的测评用例,表面用户解决了该题目。
|
||||
Accepted:通过。用户提交的源代码经过测试后通过了所有的测评用例,表明用户解决了该题目。
|
||||
|
||||
Wrong Answer:答案错误。表示用户提交的源代码的输出结果错误,没有通过任何一个测评用例。
|
||||
|
||||
|
@ -92,8 +74,6 @@ System Error:系统错误。在进行代码测评时,测评机器发送错
|
|||
|
||||

|
||||
|
||||
###### 图4.1 用户注册页面
|
||||
|
||||
2) 个人中心
|
||||
|
||||
个人中心主要用于向用户展示自己的个人信息。同时用户还可以进行个人资料的编辑和重置密码等操作。如图所示是系统个人中心页面。
|
||||
|
@ -155,3 +135,35 @@ System Error:系统错误。在进行代码测评时,测评机器发送错
|
|||
管理员可以对文章进行管理。实现了查看文章列表、删除文章等功能。文章管理页面如图所示。
|
||||
|
||||

|
||||
|
||||
# 开发过程
|
||||
|
||||
## 整体架构
|
||||
|
||||
- 后端服务
|
||||
- 后端管理界面
|
||||
- 前端显示界面
|
||||
|
||||
### 后端服务
|
||||
|
||||
#### 技术选型
|
||||
|
||||
- SpringBoot
|
||||
- MySql
|
||||
- Redis
|
||||
|
||||
### 后端管理界面
|
||||
|
||||
#### 技术选型
|
||||
|
||||
- Html、Css、 JavaScript
|
||||
- thymeleaf
|
||||
- 组件库 [Layui](https://layui.dev/docs/2/base.html)
|
||||
|
||||
### 前端显示界面
|
||||
|
||||
#### 技术选型
|
||||
|
||||
- Vue
|
||||
- 组件库 [Element-plus](https://element-plus.org/zh-CN/component/overview.html)
|
||||
- 文本编辑器 TODO
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -14,7 +14,16 @@
|
|||
Date: 30/11/2023 13:19:41
|
||||
*/
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
# 先创建数据库
|
||||
# CREATE DATABASE XJ_OJ;
|
||||
# USE XJ_OJ;
|
||||
|
||||
# 如果是MySql8.0以上的版本遇到报错可以尝试将 utf8_general_ci替换为utf8mb4_0900_ai_ci
|
||||
# 同时将utf8替换为utf8mb4
|
||||
|
||||
|
||||
SET NAMES utf8;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- ----------------------------
|
||||
|
@ -46,11 +55,11 @@ CREATE TABLE `code` (
|
|||
`id` int NOT NULL AUTO_INCREMENT COMMENT '代码id',
|
||||
`user_id` int NOT NULL,
|
||||
`problem_id` int NOT NULL,
|
||||
`code_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||
`code_path` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
|
||||
`create_time` datetime NULL DEFAULT NULL,
|
||||
`language` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||
`language` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of code
|
||||
|
@ -62,13 +71,13 @@ CREATE TABLE `code` (
|
|||
DROP TABLE IF EXISTS `contest`;
|
||||
CREATE TABLE `contest` (
|
||||
`id` int NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
|
||||
`start_time` datetime NULL DEFAULT NULL,
|
||||
`end_time` datetime NULL DEFAULT NULL,
|
||||
`num` int NULL DEFAULT NULL,
|
||||
`status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||
`status` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of contest
|
||||
|
@ -85,7 +94,7 @@ CREATE TABLE `contest_problem` (
|
|||
`submit_num` int NOT NULL DEFAULT 0 COMMENT '提交数',
|
||||
`solved_num` int NOT NULL DEFAULT 0 COMMENT '通过数',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of contest_problem
|
||||
|
@ -102,7 +111,7 @@ CREATE TABLE `contest_user` (
|
|||
`submit_num` int NOT NULL DEFAULT 0,
|
||||
`solved_num` int NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of contest_user
|
||||
|
@ -117,14 +126,14 @@ CREATE TABLE `evaluation` (
|
|||
`user_id` int NOT NULL,
|
||||
`problem_id` int NOT NULL,
|
||||
`create_time` datetime NOT NULL,
|
||||
`language` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
|
||||
`language` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
|
||||
`passed_test_case_num` int NOT NULL DEFAULT 0,
|
||||
`all_test_case_num` int NOT NULL DEFAULT 0,
|
||||
`error` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL,
|
||||
`error` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
|
||||
`is_passed` int NOT NULL DEFAULT 0,
|
||||
`status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||
`status` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of evaluation
|
||||
|
@ -136,12 +145,12 @@ CREATE TABLE `evaluation` (
|
|||
DROP TABLE IF EXISTS `file`;
|
||||
CREATE TABLE `file` (
|
||||
`id` int NOT NULL,
|
||||
`original_filename` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||
`new_filename` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||
`url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||
`original_filename` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
|
||||
`new_filename` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
|
||||
`url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
|
||||
`date` datetime NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of file
|
||||
|
@ -203,10 +212,10 @@ DROP TABLE IF EXISTS `test_case`;
|
|||
CREATE TABLE `test_case` (
|
||||
`id` int NOT NULL AUTO_INCREMENT,
|
||||
`problem_id` int NOT NULL,
|
||||
`input` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||
`output` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||
`input` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
|
||||
`output` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of test_case
|
||||
|
|
|
@ -8,15 +8,15 @@
|
|||
<version>2.7.6</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>top.weiyuexin</groupId>
|
||||
<artifactId>Online-code-evaluation-system</artifactId>
|
||||
<groupId>com.cjb666</groupId>
|
||||
<artifactId>XJ-OJ</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<name>Online-code-evaluation-system</name>
|
||||
<description>Online-code-evaluation-system</description>
|
||||
<name>XJ-OJ</name>
|
||||
<description>XJ-OJ</description>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package top.weiyuexin.config;
|
||||
|
||||
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("/**")
|
||||
// 允许发送 Cookie
|
||||
.allowCredentials(true)
|
||||
// 放行哪些域名(必须用 patterns,否则 * 会和 allowCredentials 冲突)
|
||||
.allowedOriginPatterns("*")
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||
.allowedHeaders("*")
|
||||
.exposedHeaders("*");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package top.weiyuexin.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import top.weiyuexin.interceptor.LoginInterceptor;
|
||||
|
||||
@Configuration
|
||||
public class InterceptorConfig implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new LoginInterceptor())
|
||||
.addPathPatterns("/index.html/**");
|
||||
}
|
||||
}
|
|
@ -34,11 +34,12 @@ public class AdminController {
|
|||
@Autowired
|
||||
private ArticleService articleService;
|
||||
|
||||
@GetMapping("/login")
|
||||
@GetMapping(value = {"/","/login"})
|
||||
public String login() {
|
||||
return "user/login";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 管理员登录
|
||||
*
|
||||
|
@ -46,13 +47,13 @@ public class AdminController {
|
|||
* @param password
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/admin/login.do/{username}/{password}")
|
||||
@PostMapping("/adminLogin")
|
||||
@ResponseBody
|
||||
public R adminLogin(@PathVariable("username") String username,
|
||||
@PathVariable("password") String password) {
|
||||
public R adminLogin(String username, String password) {
|
||||
User user = new User();
|
||||
user.setUsername(username);
|
||||
user.setPassword(password);
|
||||
System.out.println(user);
|
||||
// 2、校验用户名和密码是否正确
|
||||
user.setPassword(DigestUtil.md5Hex(user.getPassword()));
|
||||
User queriedUser = userService.getByNameAndPasswordAndIsAdmin(user.getUsername(), user.getPassword());
|
||||
|
@ -108,7 +109,7 @@ public class AdminController {
|
|||
}
|
||||
user.setPassword(DigestUtil.md5Hex(user.getPassword()));
|
||||
user.setRegisterTime(Time.CurrentTime());
|
||||
user.setPhoto("https://img.weiyuexin.top/img/picgo/2023/04/27/20230427183111.png");
|
||||
user.setPhoto("");
|
||||
if (userService.save(user)) {
|
||||
return R.success("用户添加成功");
|
||||
} else {
|
||||
|
|
|
@ -103,7 +103,7 @@ public class ArticleController {
|
|||
/**
|
||||
* 删除文章
|
||||
*
|
||||
* @param article
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
|
|
|
@ -117,7 +117,7 @@ public class UserController {
|
|||
user.setIntroduction("暂无介绍");
|
||||
user.setPassword(DigestUtil.md5Hex(user.getPassword()));
|
||||
user.setRegisterTime(Time.CurrentTime());
|
||||
user.setPhoto("https://img.weiyuexin.top/img/picgo/2023/04/27/20230427183111.png");
|
||||
user.setPhoto("");
|
||||
if (userService.save(user)) {
|
||||
return R.success("注册成功");
|
||||
} else {
|
||||
|
@ -173,8 +173,9 @@ public class UserController {
|
|||
* @param user
|
||||
* @return
|
||||
*/
|
||||
@PutMapping("")
|
||||
public R updateUser(User user) {
|
||||
@PutMapping("/modify")
|
||||
public R updateUser(@RequestBody User user) {
|
||||
System.out.println( user);
|
||||
if (user.getPassword() != null) {
|
||||
user.setPassword(DigestUtil.md5Hex(user.getPassword()));
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ public class ContestCron {
|
|||
@Autowired
|
||||
private ContestService contestService;
|
||||
|
||||
@Scheduled(fixedRate = 1000)
|
||||
// @Scheduled(fixedRate = 1000)
|
||||
public void updateContestStatus() throws ParseException {
|
||||
List<Contest> contests = contestService.getAllNewContest();
|
||||
for (int i = 0; i < contests.size(); i++) {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package top.weiyuexin.interceptor;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
@ -14,15 +16,32 @@ import javax.servlet.http.HttpServletResponse;
|
|||
* @Email: 3022422894@qq.com
|
||||
* @Date: 2023/4/27 17:19
|
||||
*/
|
||||
|
||||
@Component
|
||||
public class LoginInterceptor implements HandlerInterceptor {
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
|
||||
//未登录,跳转到登录页
|
||||
if (request.getCookies() == null || true) {
|
||||
response.sendRedirect("/login");
|
||||
Cookie[] cookies = request.getCookies();
|
||||
String username=null;
|
||||
String password=null;
|
||||
for (Cookie cookie : cookies) {
|
||||
if (cookie.getName().equals("username")){
|
||||
username=cookie.getValue();
|
||||
}
|
||||
if (cookie.getName().equals("password")){
|
||||
password=cookie.getValue();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
System.out.println(username+" "+password);
|
||||
if (username==null || password==null){
|
||||
response.sendRedirect("/login");
|
||||
return false;
|
||||
}
|
||||
// //未登录,跳转到登录页
|
||||
// if (request.getCookies() == null || true) {
|
||||
// response.sendRedirect("/login");
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -8,6 +8,7 @@ import org.springframework.stereotype.Service;
|
|||
import top.weiyuexin.pojo.vo.R;
|
||||
import top.weiyuexin.service.EmailService;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
|
@ -38,6 +39,7 @@ public class EmailServiceImpl implements EmailService {
|
|||
//调用HuTool中的发送验证码的方法,发送验证码
|
||||
try {
|
||||
MailUtil.send(email,title,emailCodeContent,false);
|
||||
// MailUtil.sendText(email,title,emailCodeContent, (File) null);
|
||||
// 将验证码保存到Redis,并设置过期时间为5分钟
|
||||
redisTemplate.opsForValue().set("emailCode:"+email, String.valueOf(emailCode),60*5, TimeUnit.SECONDS);
|
||||
return R.success("验证码发送成功!");
|
||||
|
|
|
@ -3,18 +3,20 @@ server:
|
|||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://116.222.21.125:3306/onlineoj?useUnicode=true&characterEncoding=UTF-8&useJDBC49CompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8
|
||||
url: jdbc:mysql://localhost:3306/xj_oj?useUnicode=true&characterEncoding=UTF-8&useJDBC49CompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8
|
||||
username: root
|
||||
password: weiyuexin
|
||||
password: filwy3344
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
# 配置servlet的多部分上传文件限制
|
||||
servlet:
|
||||
multipart:
|
||||
# 设置请求的最大大小,超过此大小的请求将被拒绝
|
||||
max-request-size: 100MB
|
||||
# 设置单个上传文件的最大大小,超过此大小的文件将被拒绝
|
||||
max-file-size: 100MB
|
||||
redis:
|
||||
host: 116.222.21.125
|
||||
host: 192.168.200.130
|
||||
port: 6379
|
||||
password: weiyuexin
|
||||
lettuce:
|
||||
pool:
|
||||
# 最大阻塞等待时间,负数表示没有限制
|
||||
|
@ -39,11 +41,11 @@ spring:
|
|||
pathmatch:
|
||||
matching-strategy: ant_path_matcher
|
||||
tencent:
|
||||
secretId: AKID
|
||||
secretKey: 1qlUzIOJ8
|
||||
bucket: ap-beijing
|
||||
bucketName: wyx-130
|
||||
path: https://img.weiyuexin.top
|
||||
secretId: AKIDRZbP5Zu1zmy45rTrRtAD8rBNgrvrb7Uo
|
||||
secretKey: HMDxVr471jGesvsfHkMuInJm6OWnxueB
|
||||
bucket: ap-chengdu
|
||||
bucketName: xj-oj-1329750222
|
||||
path: https://xj-oj-1329750222.cos.ap-chengdu.myqcloud.com
|
||||
qianzui: img
|
||||
qianzui-file: file
|
||||
|
||||
|
@ -76,17 +78,15 @@ knife4j:
|
|||
basic:
|
||||
username: root
|
||||
password: root
|
||||
enable: false
|
||||
enable: true
|
||||
openapi:
|
||||
title: 在线代码测评系统官方文档
|
||||
description: "在线代码测评系统 API 文档"
|
||||
email: 3022422894@qq.com
|
||||
concat: YuexinWei
|
||||
url: https://blog.weiyuexin.top
|
||||
email: 2948429338@qq.com
|
||||
concat: ChenJiabin
|
||||
url: https://127.0.0.1:8080/doc.html
|
||||
version: v4.0.2
|
||||
license: Apache 2.0
|
||||
license-url: https://blog.weiyuexin.top
|
||||
terms-of-service-url: https://blog.weiyuexin.top
|
||||
group:
|
||||
controller:
|
||||
group-name: 接口
|
||||
|
|
|
@ -3,8 +3,12 @@ host = smtp.qq.com
|
|||
# 邮件服务器的SMTP端口,可选,默认25
|
||||
port = 587
|
||||
# 发件人(必须正确,否则发送失败)
|
||||
from = wyxweiyuexin@qq.com
|
||||
from = 2948429338@qq.com
|
||||
# 用户名,默认为发件人邮箱前缀
|
||||
user = wyxweiyuexin
|
||||
user = 2948429338@qq.com
|
||||
# 密码(注意,某些邮箱需要为SMTP服务单独设置授权码,详情查看相关帮助)
|
||||
pass = brsrxrunyqmxdeje
|
||||
pass = rfvewkybntywdhfe
|
||||
#使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展
|
||||
starttlsEnable = true
|
||||
# 需要设置为false 否则QQ邮箱测试邮件发送报错
|
||||
sslEnable = false
|
|
@ -1,5 +1,4 @@
|
|||
body {
|
||||
background-image: url(https://img.weiyuexin.top/img/picgo/2023/04/09/20230409173204.jpg);
|
||||
background-position: 14px 14px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
|
|
|
@ -61,12 +61,12 @@
|
|||
<a href="javascript:;" data-check-screen="full"><i class="fa fa-arrows-alt"></i></a>
|
||||
</li>
|
||||
<li class="layui-nav-item layuimini-setting">
|
||||
<a href="javascript:;" id="username"></a>
|
||||
<a href="javascript:;" id="username">admin</a>
|
||||
<dl class="layui-nav-child">
|
||||
<dd>
|
||||
<a href="javascript:;" layuimini-content-href="page/user-setting.html" data-title="基本资料"
|
||||
data-icon="fa fa-gears">基本资料<span class="layui-badge-dot"></span></a>
|
||||
</dd>
|
||||
<!-- <dd>-->
|
||||
<!-- <a href="javascript:;" layuimini-content-href="page/user-setting.html" data-title="基本资料"-->
|
||||
<!-- data-icon="fa fa-gears">基本资料<span class="layui-badge-dot"></span></a>-->
|
||||
<!-- </dd>-->
|
||||
<dd>
|
||||
<a href="javascript:;" layuimini-content-href="page/user-password.html"
|
||||
data-title="修改密码"
|
||||
|
@ -76,7 +76,7 @@
|
|||
<hr>
|
||||
</dd>
|
||||
<dd>
|
||||
<a href="/user/logout" class="login-out">退出登录</a>
|
||||
<a class="login-out">退出登录</a>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
|
@ -163,12 +163,25 @@
|
|||
'layuimini-onepage.99php.cn',
|
||||
],
|
||||
});
|
||||
//
|
||||
// $('.login-out').on("click", function () {
|
||||
// layer.msg('退出登录成功', function () {
|
||||
// window.location = '/login';
|
||||
// });
|
||||
// });
|
||||
|
||||
// 设置或删除Cookie
|
||||
function setCookie(name, value, days) {
|
||||
var expires = '';
|
||||
if (days) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
expires = '; expires=' + date.toUTCString();
|
||||
}
|
||||
document.cookie = name + '=' + (value || '') + expires + '; path=/';
|
||||
}
|
||||
|
||||
$('.login-out').on("click", function () {
|
||||
setCookie('username', '', -1);
|
||||
setCookie('password', '', -1);
|
||||
layer.msg('退出登录成功', function () {
|
||||
window.location = '/login';
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,58 +1,175 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
|
||||
<title>登录-HENU-OJ-后台管理系统</title>
|
||||
<link type="favicon" rel="shortcut icon" href="../images/pns.png">
|
||||
<!-- 引入Jquery -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
|
||||
<!-- 引入Layui.css -->
|
||||
<link rel="stylesheet" th:href="@{/lib/layui-v2.6.3/css/layui.css}">
|
||||
<!-- 引入Layui.js -->
|
||||
<script th:src="@{/lib/layui-v2.6.3/layui.js}" /></script>
|
||||
<script th:src="@{/js/login.js}"></script>
|
||||
<link rel="stylesheet" th:href="@{/css/login.css}">
|
||||
<meta charset="UTF-8">
|
||||
<title>后台管理-登陆</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta http-equiv="Access-Control-Allow-Origin" content="*">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<link rel="stylesheet" href="../lib/layui-v2.6.3/css/layui.css" media="all">
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://cdn.staticfile.org/html5shiv/r29/html5.min.js"></script>
|
||||
<script src="https://cdn.staticfile.org/respond.js/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
<style>
|
||||
html, body {width: 100%;height: 100%;overflow: hidden}
|
||||
body {background: #1E9FFF;}
|
||||
body:after {content:'';background-repeat:no-repeat;background-size:cover;-webkit-filter:blur(3px);-moz-filter:blur(3px);-o-filter:blur(3px);-ms-filter:blur(3px);filter:blur(3px);position:absolute;top:0;left:0;right:0;bottom:0;z-index:-1;}
|
||||
.layui-container {width: 100%;height: 100%;overflow: hidden}
|
||||
.admin-login-background {width:360px;height:300px;position:absolute;left:50%;top:40%;margin-left:-180px;margin-top:-100px;}
|
||||
.logo-title {text-align:center;letter-spacing:2px;padding:14px 0;}
|
||||
.logo-title h1 {color:#1E9FFF;font-size:25px;font-weight:bold;}
|
||||
.login-form {background-color:#fff;border:1px solid #fff;border-radius:3px;padding:14px 20px;box-shadow:0 0 8px #eeeeee;}
|
||||
.login-form .layui-form-item {position:relative;}
|
||||
.login-form .layui-form-item label {position:absolute;left:1px;top:1px;width:38px;line-height:36px;text-align:center;color:#d2d2d2;}
|
||||
.login-form .layui-form-item input {padding-left:36px;}
|
||||
.captcha {width:60%;display:inline-block;}
|
||||
.captcha-img {display:inline-block;width:34%;float:right;}
|
||||
.captcha-img img {height:34px;border:1px solid #e6e6e6;height:36px;width:100%;}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="layui-form" id="loginForm">
|
||||
<div class="layui-form-item">
|
||||
<h1>后 台 管 理 系 统 </h1>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<span class="decrib">账号:</span>
|
||||
<input type="text" name="username" placeholder="请输入管理员账号" autocomplete="off" class="layui-input" id="username" required="">
|
||||
<div class="layui-container">
|
||||
<div class="admin-login-background">
|
||||
<div class="layui-form login-form">
|
||||
<form class="layui-form" action="">
|
||||
<div class="layui-form-item logo-title">
|
||||
<h1>LayuiMini后台登录</h1>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-icon layui-icon-username" for="username"></label>
|
||||
<input type="text" name="username" lay-verify="required|account" placeholder="用户名或者邮箱" autocomplete="off" class="layui-input" value="">
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-icon layui-icon-password" for="password"></label>
|
||||
<input type="password" name="password" lay-verify="required|password" placeholder="密码" autocomplete="off" class="layui-input" value="">
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-icon layui-icon-vercode" for="captcha"></label>
|
||||
<input type="text" name="captcha" lay-verify="required|captcha" placeholder="图形验证码" autocomplete="off" class="layui-input verification captcha" value="">
|
||||
<div class="captcha-img">
|
||||
<img id="captchaPic" src="../images/captcha.jpg">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<input type="checkbox" name="rememberMe" value="true" lay-skin="primary" title="记住密码">
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<button class="layui-btn layui-btn layui-btn-normal layui-btn-fluid" lay-submit="" lay-filter="login">登 入</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<span class="decrib">密码:</span>
|
||||
<input type="password" name="password" placeholder="请输入密码" autocomplete="off" class="layui-input" id="password" required="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<span class="decrib">验证:</span>
|
||||
<input type="text" placeholder="请输入验证码" class="input-val" autocomplete="off" required="">
|
||||
<canvas id="canvas" width="100" height="44">
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block login">
|
||||
<button class="layui-btn layui-btn-bypercent-left btn" id="submit">登 录</button>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<!--<span class="forget"><a th:href="@{/user/forget}">忘记密码?</a></span>-->
|
||||
</div>
|
||||
</body>
|
||||
<script src="../lib/jquery-3.4.1/jquery-3.4.1.min.js" charset="utf-8"></script>
|
||||
<script src="../lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
|
||||
<script src="../lib/jq-module/jquery.particleground.min.js" charset="utf-8"></script>
|
||||
<script>
|
||||
layui.use(['form'], function () {
|
||||
var form = layui.form,
|
||||
layer = layui.layer;
|
||||
|
||||
</html>
|
||||
// 登录过期的时候,跳出ifram框架
|
||||
if (top.location != self.location) top.location = self.location;
|
||||
|
||||
// 粒子线条背景
|
||||
$(document).ready(function(){
|
||||
$('.layui-container').particleground({
|
||||
dotColor:'#7ec7fd',
|
||||
lineColor:'#7ec7fd'
|
||||
});
|
||||
});
|
||||
|
||||
// 读取Cookie中的用户名和密码
|
||||
function readCookies() {
|
||||
var username = getCookie('username');
|
||||
var password = getCookie('password');
|
||||
|
||||
if (username && password) {
|
||||
$('input[name="username"]').val(username);
|
||||
$('input[name="password"]').val(password);
|
||||
$('input[name="rememberMe"]').prop('checked', true);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置或删除Cookie
|
||||
function setCookie(name, value, days) {
|
||||
var expires = '';
|
||||
if (days) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
expires = '; expires=' + date.toUTCString();
|
||||
}
|
||||
document.cookie = name + '=' + (value || '') + expires + '; path=/';
|
||||
}
|
||||
|
||||
function getCookie(name) {
|
||||
var nameEQ = name + '=';
|
||||
var ca = document.cookie.split(';');
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
|
||||
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 进行登录操作
|
||||
form.on('submit(login)', function (data) {
|
||||
data = data.field;
|
||||
if (data.username == '') {
|
||||
layer.msg('用户名不能为空');
|
||||
return false;
|
||||
}
|
||||
if (data.password == '') {
|
||||
layer.msg('密码不能为空');
|
||||
return false;
|
||||
}
|
||||
if (data.captcha.toUpperCase() !== 'xszg'.toUpperCase()) {
|
||||
layer.msg('验证码不正确');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置或删除Cookie
|
||||
if (data.rememberMe === 'true') {
|
||||
setCookie('username', data.username, 30);
|
||||
setCookie('password', data.password, 30);
|
||||
} else {
|
||||
setCookie('username', '', -1);
|
||||
setCookie('password', '', -1);
|
||||
}
|
||||
|
||||
// 使用 jQuery 发送 AJAX 请求
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/adminLogin',
|
||||
data: {
|
||||
username: data.username,
|
||||
password: data.password
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function (response) {
|
||||
if (response.code===200) {
|
||||
layer.msg('登录成功', function () {
|
||||
window.location = '../index.html';
|
||||
});
|
||||
} else {
|
||||
layer.msg('登录失败: ' + response.msg);
|
||||
}
|
||||
},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
layer.msg('登录失败: ' + textStatus);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
// 初始化读取Cookie
|
||||
readCookies();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,24 +1,29 @@
|
|||
# oj-vue
|
||||
|
||||
## Project setup
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="link">
|
||||
Copyright © 2023 <a href="http://github.com/weiyuexin" target="_blank" class="author-link">Ginkgo</a>
|
||||
Copyright © 2023 <a href="" target="_blank" class="author-link">Ginkgo</a>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
|
@ -41,7 +41,7 @@ export default {
|
|||
<style scoped>
|
||||
.footer {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
height: 10%;
|
||||
background-color: #fff;
|
||||
margin-top: 30px;
|
||||
position: fixed;
|
||||
|
@ -58,14 +58,12 @@ export default {
|
|||
}
|
||||
|
||||
.logo {
|
||||
height: 100%;
|
||||
margin-top: 20px;
|
||||
height: 5%;
|
||||
}
|
||||
|
||||
.logo img {
|
||||
position: relative;
|
||||
top: 13%;
|
||||
width: 22%;
|
||||
width: 15%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
@ -74,6 +72,6 @@ export default {
|
|||
text-align: center;
|
||||
color: #000;
|
||||
font-size: 18px;
|
||||
margin-top: 100px;
|
||||
margin-top: 5%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="nav">
|
||||
<router-link :to="`/`" ><img src="@/assets/logo.png" class="logoImg"/></router-link>
|
||||
<router-link :to="`/`"><img src="@/assets/logo.png" class="logoImg"/></router-link>
|
||||
<el-menu :default-active="activeIndex" router active-text-color="#409EFF" class="oj-navbar" mode="horizontal"
|
||||
@select="handleSelect">
|
||||
<el-menu-item index="/">
|
||||
|
@ -53,16 +53,28 @@
|
|||
</el-menu-item>
|
||||
<div class="account" v-if="JSON.stringify(user) !== '{}'">
|
||||
<el-dropdown>
|
||||
<span class="el-dropdown-link">
|
||||
<el-avatar
|
||||
:src="user.photo"
|
||||
/>
|
||||
{{user.username}}<el-icon class="el-icon--right"><arrow-down /></el-icon>
|
||||
</span>
|
||||
<span class="el-dropdown-link">
|
||||
<el-avatar
|
||||
:src="user.photo"
|
||||
/>
|
||||
{{ user.username }}<el-icon class="el-icon--right"><arrow-down/></el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item><router-link to="/user"><el-icon><Avatar /></el-icon>个人中心</router-link></el-dropdown-item>
|
||||
<el-dropdown-item @click="logout"><el-icon><CircleClose /></el-icon>退出登录</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<router-link to="/user">
|
||||
<el-icon>
|
||||
<Avatar/>
|
||||
</el-icon>
|
||||
个人中心
|
||||
</router-link>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="logout">
|
||||
<el-icon>
|
||||
<CircleClose/>
|
||||
</el-icon>
|
||||
退出登录
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
@ -74,6 +86,7 @@
|
|||
<script>
|
||||
import Cookies from 'js-cookie'
|
||||
import {ElMessage} from "element-plus";
|
||||
import router from "@/router";
|
||||
|
||||
|
||||
export default {
|
||||
|
@ -94,11 +107,14 @@ export default {
|
|||
// 判断是否登录
|
||||
check() {
|
||||
// console.log(JSON.parse(Cookies.get('user'))!==null)
|
||||
//const user = JSON.parse(Cookies.get('user'))
|
||||
if (Cookies.get('user')!==undefined && Cookies.get('user')!==null){
|
||||
this.user = JSON.parse(Cookies.get('user'))
|
||||
}
|
||||
console.log(this.user)
|
||||
// const user = Cookies.get('user')
|
||||
// this.user = JSON.parse(user)
|
||||
},
|
||||
logout(){
|
||||
logout() {
|
||||
Cookies.remove('user')
|
||||
this.$router.go(0);
|
||||
window.location.reload();
|
||||
|
@ -125,11 +141,12 @@ export default {
|
|||
align-items: center;
|
||||
font-size: 18px;
|
||||
}
|
||||
.el-menu-item:hover{
|
||||
background-color: white!important;
|
||||
|
||||
.el-menu-item:hover {
|
||||
background-color: white !important;
|
||||
}
|
||||
|
||||
.nav{
|
||||
.nav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
|
@ -139,6 +156,7 @@ export default {
|
|||
border-bottom: solid 1px #EFF3F5;
|
||||
padding-left: 5%;
|
||||
}
|
||||
|
||||
.oj-navbar {
|
||||
width: 85%;
|
||||
z-index: 999;
|
||||
|
@ -149,7 +167,8 @@ export default {
|
|||
.logo {
|
||||
width: 130px !important;
|
||||
}
|
||||
.logoImg{
|
||||
|
||||
.logoImg {
|
||||
height: 60px;
|
||||
width: 5%;
|
||||
float: left;
|
||||
|
|
|
@ -62,6 +62,8 @@
|
|||
import {ElMessage} from "element-plus";
|
||||
import axios from "axios";
|
||||
import Cookies from 'js-cookie'
|
||||
import router from "@/router";
|
||||
|
||||
export default {
|
||||
// eslint-disable-next-line vue/multi-word-component-names
|
||||
name: "Login",
|
||||
|
@ -107,9 +109,7 @@ export default {
|
|||
|
||||
Cookies.set('user', JSON.stringify(response.data.data),'7d')
|
||||
//跳转到首页
|
||||
window.setTimeout(function () {
|
||||
location.href = "/";
|
||||
}, 2000);
|
||||
router.push("/");
|
||||
} else {
|
||||
ElMessage({
|
||||
message: response.data.msg,
|
||||
|
|
|
@ -1,184 +1,202 @@
|
|||
<template>
|
||||
<div class="main">
|
||||
<NavBar></NavBar>
|
||||
<el-tabs
|
||||
v-model="activeName"
|
||||
@tab-click="handleClick"
|
||||
class="demo-tabs"
|
||||
tab-position="left"
|
||||
:stretch="true"
|
||||
>
|
||||
<el-tab-pane label="个人信息">
|
||||
<el-descriptions
|
||||
class="margin-top"
|
||||
:column="1"
|
||||
size="10px"
|
||||
border
|
||||
>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<user/>
|
||||
</el-icon>
|
||||
用户名
|
||||
</div>
|
||||
</template>
|
||||
{{ user.username }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<Male />
|
||||
</el-icon>
|
||||
性别
|
||||
</div>
|
||||
</template>
|
||||
{{user.sex}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<View />
|
||||
</el-icon>
|
||||
个人介绍
|
||||
</div>
|
||||
</template>
|
||||
{{user.introduction}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<Cellphone />
|
||||
</el-icon>
|
||||
电子邮箱
|
||||
</div>
|
||||
</template>
|
||||
{{user.email}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<Memo />
|
||||
</el-icon>
|
||||
提交次数
|
||||
</div>
|
||||
</template>
|
||||
{{user.submitNum}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<DataAnalysis />
|
||||
</el-icon>
|
||||
解题总数
|
||||
</div>
|
||||
</template>
|
||||
{{user.solvedNum}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<office-building/>
|
||||
</el-icon>
|
||||
学校
|
||||
</div>
|
||||
</template>
|
||||
{{user.school}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<Finished />
|
||||
</el-icon>
|
||||
注册时间
|
||||
</div>
|
||||
</template>
|
||||
{{user.registerTime}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<Refresh />
|
||||
</el-icon>
|
||||
上次登录时间
|
||||
</div>
|
||||
</template>
|
||||
{{user.accessTime}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<PieChart />
|
||||
</el-icon>
|
||||
默认使用的语言
|
||||
</div>
|
||||
</template>
|
||||
{{user.language}}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="修改密码" class="changePass">
|
||||
<el-form
|
||||
label-position="top"
|
||||
label-width="100px"
|
||||
style="max-width: 460px"
|
||||
>
|
||||
<el-form-item label="请输入旧密码">
|
||||
<el-input type="password" show-password v-model="password.oldpass" />
|
||||
</el-form-item>
|
||||
<el-form-item label="请输入新密码">
|
||||
<el-input type="password" show-password v-model="password.newpass1" />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码">
|
||||
<el-input type="password" v-model="password.newpass2" />
|
||||
</el-form-item>
|
||||
<el-button type="primary" @click="change">
|
||||
确认修改
|
||||
</el-button>
|
||||
</el-form>
|
||||
<div style="padding-top:7%;padding-bottom:10%;width:80%;margin-left: 10%;background-color: #FFFFFF;">
|
||||
<el-tabs
|
||||
class="demo-tabs"
|
||||
tab-position="left"
|
||||
:stretch="true"
|
||||
v-model="activeName"
|
||||
@tab-click="handleClick"
|
||||
>
|
||||
<el-tab-pane label="个人信息">
|
||||
<el-descriptions
|
||||
class="margin-top"
|
||||
:column="1"
|
||||
size="10px"
|
||||
border
|
||||
>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<user/>
|
||||
</el-icon>
|
||||
用户名
|
||||
</div>
|
||||
</template>
|
||||
{{ user.username }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<Male />
|
||||
</el-icon>
|
||||
性别
|
||||
</div>
|
||||
</template>
|
||||
{{user.sex}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<View />
|
||||
</el-icon>
|
||||
个人介绍
|
||||
</div>
|
||||
</template>
|
||||
{{user.introduction}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<Cellphone />
|
||||
</el-icon>
|
||||
电子邮箱
|
||||
</div>
|
||||
</template>
|
||||
{{user.email}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<Memo />
|
||||
</el-icon>
|
||||
提交次数
|
||||
</div>
|
||||
</template>
|
||||
{{user.submitNum}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<DataAnalysis />
|
||||
</el-icon>
|
||||
解题总数
|
||||
</div>
|
||||
</template>
|
||||
{{user.solvedNum}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<office-building/>
|
||||
</el-icon>
|
||||
学校
|
||||
</div>
|
||||
</template>
|
||||
{{user.school}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<Finished />
|
||||
</el-icon>
|
||||
注册时间
|
||||
</div>
|
||||
</template>
|
||||
{{user.registerTime}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<Refresh />
|
||||
</el-icon>
|
||||
上次登录时间
|
||||
</div>
|
||||
</template>
|
||||
{{user.accessTime}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
<el-icon class="iconStyle">
|
||||
<PieChart />
|
||||
</el-icon>
|
||||
默认使用的语言
|
||||
</div>
|
||||
</template>
|
||||
{{user.language}}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="修改密码" class="changePass">
|
||||
<el-form
|
||||
label-position="top"
|
||||
label-width="100px"
|
||||
style="max-width: 460px"
|
||||
>
|
||||
<el-form-item label="请输入旧密码">
|
||||
<el-input type="password" show-password v-model="password.oldpass" />
|
||||
</el-form-item>
|
||||
<el-form-item label="请输入新密码">
|
||||
<el-input type="password" show-password v-model="password.newpass1" />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码">
|
||||
<el-input type="password" v-model="password.newpass2" />
|
||||
</el-form-item>
|
||||
<el-button type="primary" @click="change">
|
||||
确认修改
|
||||
</el-button>
|
||||
</el-form>
|
||||
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="个人资料设置" class="changePass">
|
||||
<el-form
|
||||
label-position="top"
|
||||
label-width="100px"
|
||||
style="max-width: 460px"
|
||||
>
|
||||
<el-form-item label="头像">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action="http://localhost:8080/cos/upload"
|
||||
:show-file-list="false"
|
||||
:on-success="handleAvatarSuccess"
|
||||
:before-upload="beforeAvatarUpload"
|
||||
>
|
||||
<img w-full v-if="user.photo" :src="user.photo" class="avatar" />
|
||||
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-radio-group v-model="sex">
|
||||
<el-radio :label="1" size="large">男</el-radio>
|
||||
<el-radio :label="0" size="large">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="简介">
|
||||
<el-input type="textarea" v-model="user.introduction" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学校">
|
||||
<el-input type="text" v-model="user.school" />
|
||||
</el-form-item>
|
||||
<el-form-item label="默认使用语言">
|
||||
<el-select v-model="user.language" class="m-2" placeholder="Select">
|
||||
<el-option
|
||||
v-for="item in languages"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-button type="primary" @click="changeAccount">
|
||||
确认修改
|
||||
</el-button>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="个人资料设置" class="changePass">
|
||||
<el-form
|
||||
label-position="top"
|
||||
label-width="100px"
|
||||
style="max-width: 460px"
|
||||
>
|
||||
<el-form-item label="用户名">
|
||||
<el-input type="text" show-password v-model="password.oldpass" />
|
||||
</el-form-item>
|
||||
<el-form-item label="简介">
|
||||
<el-input type="textarea" show-password v-model="password.oldpass" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学校">
|
||||
<el-input type="text" show-password v-model="password.oldpass" />
|
||||
</el-form-item>
|
||||
<el-form-item label="默认使用语言">
|
||||
<el-select v-model="language" class="m-2" placeholder="Select">
|
||||
<el-option
|
||||
v-for="item in languages"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-button type="primary" @click="changeAccount">
|
||||
确认修改
|
||||
</el-button>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<Footer></Footer>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -227,7 +245,8 @@ export default {
|
|||
value: 'JavaScript',
|
||||
label: 'JavaScript',
|
||||
},
|
||||
]
|
||||
],
|
||||
sex:"",
|
||||
};
|
||||
},
|
||||
components: {
|
||||
|
@ -235,16 +254,60 @@ export default {
|
|||
Footer
|
||||
},
|
||||
methods: {
|
||||
handleAvatarSuccess( response){
|
||||
this.user.photo = response.data.url
|
||||
},
|
||||
|
||||
beforeAvatarUpload(rawFile) {
|
||||
console.log(rawFile.type)
|
||||
if (rawFile.type !== 'image/jpeg' && rawFile.type !== 'image/png') {
|
||||
ElMessage.error('Avatar picture must be JPG format!')
|
||||
return false
|
||||
} else if (rawFile.size / 1024 / 1024 > 2) {
|
||||
ElMessage.error('Avatar picture size can not exceed 2MB!')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
getUser() {
|
||||
const user = Cookies.get('user')
|
||||
this.user = JSON.parse(user)
|
||||
console.log(user)
|
||||
if (user!==null){
|
||||
axios.get("/api/user/"+this.user.id).then(response => {
|
||||
this.user=response.data.data
|
||||
if (this.user.sex==="男"){
|
||||
this.sex=1
|
||||
}else{
|
||||
this.sex=0
|
||||
}
|
||||
})
|
||||
}
|
||||
console.log(this.user)
|
||||
},
|
||||
changeAccount(){
|
||||
ElMessage({
|
||||
message: '修改成功',
|
||||
type: 'success',
|
||||
axios.put("/api/user/modify",{
|
||||
id:this.user.id,
|
||||
sex:this.sex===1?"男":"女",
|
||||
introduction:this.user.introduction,
|
||||
school:this.user.school,
|
||||
language:this.user.language,
|
||||
photo:this.user.photo
|
||||
}).then(response => {
|
||||
if (response.data.code===200){
|
||||
ElMessage({
|
||||
message: '修改成功',
|
||||
type: 'success',
|
||||
})
|
||||
this.getUser()
|
||||
}else{
|
||||
ElMessage({
|
||||
message: response.data.msg,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
},
|
||||
change(){
|
||||
if (this.password.oldpass===""){
|
||||
|
@ -298,86 +361,30 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
.main {
|
||||
background-color: #EFF3F5;
|
||||
}
|
||||
.avatar-uploader .avatar {
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__item {
|
||||
color: #7E7C77;
|
||||
height: 60px;
|
||||
}
|
||||
.avatar-uploader .el-upload {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__item.is-active {
|
||||
color: #15cbf3;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
::v-deep .el-icon-arrow-left {
|
||||
color: #7E7C77;
|
||||
}
|
||||
|
||||
::v-deep .el-icon-arrow-right {
|
||||
color: #7E7C77;
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__nav-wrap::after {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__active-bar {
|
||||
background-color: #15cbf3;
|
||||
}
|
||||
.iconStyle{
|
||||
margin-right: 10px;
|
||||
}
|
||||
.changePass{
|
||||
padding-top: 50px;
|
||||
padding-left: 50px;
|
||||
}
|
||||
.demo-tabs {
|
||||
margin-top: 60px;
|
||||
margin-left: 100px;
|
||||
margin-right: 100px;
|
||||
z-index: 999;
|
||||
height: 490px;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.el-descriptions {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.cell-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.margin-top {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.main .el-tabs__item {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.el-tabs__active-bar {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.demo-tabs > .el-tabs__content {
|
||||
padding: 32px;
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.el-tabs--right .el-tabs__content,
|
||||
.el-tabs--left .el-tabs__content {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tab {
|
||||
background-color: red;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue