mirror of
https://github.com/continew-org/continew-admin.git
synced 2025-09-20 03:00:53 +08:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
2b22c598eb | |||
4a8af1f72d | |||
b0b1127b5b | |||
![]() |
104f69e8a0 | ||
76f04dd38f | |||
b632c18399 | |||
026247f677 | |||
8743ed14d9 | |||
39b0b9a48e | |||
9376d6fd5f | |||
efbcb9b39d | |||
165effedb9 | |||
f3fabea7dd | |||
2d2a7e7c8e | |||
18c54a74fc | |||
9b2a924184 | |||
e6f7429fa3 | |||
48de2e85e0 | |||
5968f402ed | |||
a623acd4a5 | |||
9f25925d46 | |||
331491dd5d | |||
fe9201427e | |||
8b955a0b1b | |||
d4aedaabc8 |
26
CHANGELOG.md
26
CHANGELOG.md
@@ -1,4 +1,26 @@
|
||||
# v1.0.0 (2023-03-26)
|
||||
## v1.0.1 (2023-08-17)
|
||||
|
||||
### 💎 功能优化
|
||||
|
||||
- 优化根据 ID 查询用户昵称方法 ([4a8af1f](https://github.com/Charles7c/continew-admin/commit/4a8af1f72d9249afa1c013e08674f492f453b020))
|
||||
- 优化 BaseController 中部分权限码的使用 ([b0b1127](https://github.com/Charles7c/continew-admin/commit/b0b1127b5bd39e9bc431e9fa9c86201bbc18e891))
|
||||
- 优化分页总记录数数据类型 ([76f04dd](https://github.com/Charles7c/continew-admin/commit/76f04dd38f90aad6abf82d2dccba031d4d9108cf))
|
||||
- 优化通用查询注解解析器 ([a623acd](https://github.com/Charles7c/continew-admin/commit/a623acd4a5529ae42898ec359f595716acc5bab8)) ([b632c18](https://github.com/Charles7c/continew-admin/commit/b632c183994ac71382180a38bf7bdb7a6315c1e6))
|
||||
- 优化数据库表结构中部分类型长度 ([f3fabea](https://github.com/Charles7c/continew-admin/commit/f3fabea7dd736d94badecbc08091eec6274f5fb7))
|
||||
- 使用常量优化部分魔法值 ([e6f7429](https://github.com/Charles7c/continew-admin/commit/e6f7429fa30cbc87c03a073a53b6f7df24d33d8d))
|
||||
- 优化部分 Properties 用法 ([48de2e8](https://github.com/Charles7c/continew-admin/commit/48de2e85e0fbf60f10769cd3529f79ac3c531e92))
|
||||
|
||||
### 🐛 问题修复
|
||||
|
||||
- 修复获取字典参数为空时的判断条件 ([#6](https://github.com/Charles7c/continew-admin/pull/6)) ([104f69e](https://github.com/Charles7c/continew-admin/commit/104f69e8a09ce36163f6f9680b2d8d61bb45f11a))
|
||||
- 完善查询用户数据权限 ([026247f](https://github.com/Charles7c/continew-admin/commit/026247f677110ae199124a67c68503729cbaec92))
|
||||
- 解决 IDE 报 Delete ␍ eslint(prettier/prettier) 警告的问题 ([8743ed1](https://github.com/Charles7c/continew-admin/commit/8743ed14d927ab52814ed5f5f166afaa7a6b78b2))
|
||||
- 修复分页查询条件默认值未生效的问题 ([2d2a7e7](https://github.com/Charles7c/continew-admin/commit/2d2a7e7c8e31763ac3ea514d8a92c3938376dd3a))
|
||||
- 完善各模块事务注解 ([18c54a7](https://github.com/Charles7c/continew-admin/commit/18c54a74fc6ff0650ff53eeadc094d7e1df0b0a5))
|
||||
- 修复邮箱健康检查报错问题并优化部分配置写法 ([5968f40](https://github.com/Charles7c/continew-admin/commit/5968f402ed478244d36f5825373190ed00d8c1f1))
|
||||
- 完善各模块参数校验 ([8b955a0](https://github.com/Charles7c/continew-admin/commit/8b955a0b1bde4e8959fc0dfbc11a326d9eec0b45))
|
||||
|
||||
## v1.0.0 (2023-03-26)
|
||||
|
||||
### ✨ 新特性
|
||||
|
||||
@@ -7,4 +29,4 @@
|
||||
* 部门管理:可配置系统组织架构,树形表格展示
|
||||
* 菜单管理:已实现菜单动态路由,后端可配置化,支持多级菜单
|
||||
* 在线用户:管理当前登录用户,可一键踢下线
|
||||
* 日志管理:提供在线用户监控、登录日志监控、操作日志监控和系统日志监控等监控功能
|
||||
* 日志管理:提供在线用户监控、登录日志监控、操作日志监控和系统日志监控等监控功能
|
||||
|
40
README.md
40
README.md
@@ -1,13 +1,13 @@
|
||||
# ContiNew Admin 中后台管理框架
|
||||
|
||||
[](https://github.com/Charles7c/continew-admin/blob/dev/LICENSE)
|
||||

|
||||

|
||||
[](https://github.com/Charles7c/continew-admin)
|
||||
[](https://github.com/Charles7c/continew-admin)
|
||||
[](https://gitee.com/Charles7c/continew-admin)
|
||||
[](https://gitee.com/Charles7c/continew-admin)
|
||||
|
||||
📚 [在线文档](https://doc.charles7c.top) | ✨ [提交需求](https://doc.charles7c.top/require) | 🚀 [演示地址](https://cnadmin.charles7c.top)(账号/密码:admin/admin123)
|
||||
📚 [在线文档](https://doc.charles7c.top) | ✨ [提交需求](https://doc.charles7c.top/require.html) | 🚀 [演示地址](https://cnadmin.charles7c.top)(账号/密码:admin/admin123)
|
||||
|
||||
## 简介
|
||||
|
||||
@@ -36,39 +36,7 @@ ContiNew Admin 中后台管理框架/脚手架,Continue New Admin,持续以
|
||||
|
||||
## 系统功能
|
||||
|
||||
> v1.x 开发和 v2.x 开发同步进行中。小步快跑,快速迭代。
|
||||
>
|
||||
> 详细进度请查看 [GitHub Project](https://github.com/Charles7c/continew-admin/projects)
|
||||
|
||||
**v2.0.0:** :fire: 升级并适配 Spring Boot 3.x。
|
||||
|
||||
- [ ] 依赖升级:升级并适配 Spring Boot 3.x
|
||||
- [ ] 依赖升级:其他依赖升级
|
||||
- [ ] 计划对接 [FlowLong](https://gitee.com/aizuda/flowlong) 纯国产工作流引擎
|
||||
- [ ] 其他需求汇集中...
|
||||
|
||||
**v1.2.0:** 第三方服务支持。
|
||||
|
||||
- [ ] 文件管理:提供 OSS 及本地文件管理
|
||||
- [ ] 支持第三方登录
|
||||
- [ ] 支持 SMS
|
||||
- [x] 文档:整理使用文档,创建文档站点
|
||||
- [ ] 依赖升级:升级至最新 Spring Boot 2.x 版本
|
||||
- [ ] 依赖升级:其他依赖升级
|
||||
- [ ] 其他需求汇集中...
|
||||
|
||||
**v1.1.0:** 丰富中后台管理框架/脚手架的基础功能。
|
||||
|
||||
- [ ] 代码生成:高灵活度生成前后端代码,减少大量重复的工作任务
|
||||
- [ ] 公告管理:提供系统公告的发布和维护功能
|
||||
- [ ] 通知管理:提供系统通知管理,设为已读、未读
|
||||
- [ ] 仪表盘优化:各区块数据动态渲染
|
||||
- [ ] 网站配置:支持配置系统网站标题、网站 Logo、favicon、版权信息等
|
||||
- [ ] 依赖升级:升级至最新 Spring Boot 2.x 版本
|
||||
- [ ] 依赖升级:其他依赖升级
|
||||
- [ ] 其他需求汇集中...
|
||||
|
||||
**v1.0.0:** 初步完成中后台管理框架/脚手架的基础功能。
|
||||
> 更多功能和优化正在赶来💦,最新项目计划和进展请关注 [GitHub Project](https://github.com/Charles7c/continew-admin/projects)
|
||||
|
||||
- 用户管理:提供用户的相关配置,新增用户后,默认密码为 123456
|
||||
- 角色管理:对权限与菜单进行分配,可根据部门设置角色的数据权限
|
||||
@@ -380,7 +348,7 @@ yarn dev
|
||||
|
||||
💬 非常欢迎各位小伙伴儿在 Issues、Discussions 中进行交流探讨~
|
||||
|
||||
💬 也欢迎各位小伙伴儿扫码加作者好友(请备注:cnadmin),作者拉你进群,现有作者(群主)及 7 位群友,随意聊聊技术、提提需求,吐吐槽~
|
||||
💬 也欢迎各位小伙伴儿扫码加作者好友(请备注:cnadmin),作者拉你进群,随意聊聊技术、提提需求,吐吐槽~
|
||||
|
||||
<div align="left">
|
||||
<img src="https://s1.ax1x.com/2023/03/09/ppnhe0A.jpg" alt="二维码" width="200" />
|
||||
|
@@ -16,6 +16,8 @@
|
||||
|
||||
package top.charles7c.cnadmin.common.base;
|
||||
|
||||
import static top.charles7c.cnadmin.common.annotation.CrudRequestMapping.Api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@@ -75,8 +77,8 @@ public abstract class BaseController<S extends BaseService<V, D, Q, C>, V, D, Q,
|
||||
@Operation(summary = "分页查询列表")
|
||||
@ResponseBody
|
||||
@GetMapping
|
||||
protected R<PageDataVO<V>> page(@Validated Q query, @Validated PageQuery pageQuery) {
|
||||
this.checkPermission("list");
|
||||
public R<PageDataVO<V>> page(@Validated Q query, @Validated PageQuery pageQuery) {
|
||||
this.checkPermission(Api.LIST);
|
||||
PageDataVO<V> pageDataVO = baseService.page(query, pageQuery);
|
||||
return R.ok(pageDataVO);
|
||||
}
|
||||
@@ -93,8 +95,8 @@ public abstract class BaseController<S extends BaseService<V, D, Q, C>, V, D, Q,
|
||||
@Operation(summary = "查询树列表")
|
||||
@ResponseBody
|
||||
@GetMapping("/tree")
|
||||
protected R<List<Tree<Long>>> tree(@Validated Q query, @Validated SortQuery sortQuery) {
|
||||
this.checkPermission("list");
|
||||
public R<List<Tree<Long>>> tree(@Validated Q query, @Validated SortQuery sortQuery) {
|
||||
this.checkPermission(Api.LIST);
|
||||
List<Tree<Long>> list = baseService.tree(query, sortQuery, false);
|
||||
return R.ok(list);
|
||||
}
|
||||
@@ -111,8 +113,8 @@ public abstract class BaseController<S extends BaseService<V, D, Q, C>, V, D, Q,
|
||||
@Operation(summary = "查询列表")
|
||||
@ResponseBody
|
||||
@GetMapping("/list")
|
||||
protected R<List<V>> list(@Validated Q query, @Validated SortQuery sortQuery) {
|
||||
this.checkPermission("list");
|
||||
public R<List<V>> list(@Validated Q query, @Validated SortQuery sortQuery) {
|
||||
this.checkPermission(Api.LIST);
|
||||
List<V> list = baseService.list(query, sortQuery);
|
||||
return R.ok(list);
|
||||
}
|
||||
@@ -128,8 +130,8 @@ public abstract class BaseController<S extends BaseService<V, D, Q, C>, V, D, Q,
|
||||
@Parameter(name = "id", description = "ID", in = ParameterIn.PATH)
|
||||
@ResponseBody
|
||||
@GetMapping("/{id}")
|
||||
protected R<D> get(@PathVariable Long id) {
|
||||
this.checkPermission("list");
|
||||
public R<D> get(@PathVariable Long id) {
|
||||
this.checkPermission(Api.LIST);
|
||||
D detail = baseService.get(id);
|
||||
return R.ok(detail);
|
||||
}
|
||||
@@ -144,8 +146,8 @@ public abstract class BaseController<S extends BaseService<V, D, Q, C>, V, D, Q,
|
||||
@Operation(summary = "新增数据")
|
||||
@ResponseBody
|
||||
@PostMapping
|
||||
protected R<Long> add(@Validated(BaseRequest.Add.class) @RequestBody C request) {
|
||||
this.checkPermission("add");
|
||||
public R<Long> add(@Validated(BaseRequest.Add.class) @RequestBody C request) {
|
||||
this.checkPermission(Api.ADD);
|
||||
Long id = baseService.add(request);
|
||||
return R.ok("新增成功", id);
|
||||
}
|
||||
@@ -162,8 +164,8 @@ public abstract class BaseController<S extends BaseService<V, D, Q, C>, V, D, Q,
|
||||
@Operation(summary = "修改数据")
|
||||
@ResponseBody
|
||||
@PutMapping("/{id}")
|
||||
protected R update(@Validated(BaseRequest.Update.class) @RequestBody C request, @PathVariable Long id) {
|
||||
this.checkPermission("update");
|
||||
public R update(@Validated(BaseRequest.Update.class) @RequestBody C request, @PathVariable Long id) {
|
||||
this.checkPermission(Api.UPDATE);
|
||||
baseService.update(request, id);
|
||||
return R.ok("修改成功");
|
||||
}
|
||||
@@ -179,8 +181,8 @@ public abstract class BaseController<S extends BaseService<V, D, Q, C>, V, D, Q,
|
||||
@Parameter(name = "ids", description = "ID 列表", in = ParameterIn.PATH)
|
||||
@ResponseBody
|
||||
@DeleteMapping("/{ids}")
|
||||
protected R delete(@PathVariable List<Long> ids) {
|
||||
this.checkPermission("delete");
|
||||
public R delete(@PathVariable List<Long> ids) {
|
||||
this.checkPermission(Api.DELETE);
|
||||
baseService.delete(ids);
|
||||
return R.ok("删除成功");
|
||||
}
|
||||
@@ -197,21 +199,21 @@ public abstract class BaseController<S extends BaseService<V, D, Q, C>, V, D, Q,
|
||||
*/
|
||||
@Operation(summary = "导出数据")
|
||||
@GetMapping("/export")
|
||||
protected void export(@Validated Q query, @Validated SortQuery sortQuery, HttpServletResponse response) {
|
||||
this.checkPermission("export");
|
||||
public void export(@Validated Q query, @Validated SortQuery sortQuery, HttpServletResponse response) {
|
||||
this.checkPermission(Api.EXPORT);
|
||||
baseService.export(query, sortQuery, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限认证
|
||||
* 根据 API 类型进行权限验证
|
||||
*
|
||||
* @param subPermission
|
||||
* 部分权限码
|
||||
* @param api
|
||||
* API 类型
|
||||
*/
|
||||
private void checkPermission(String subPermission) {
|
||||
private void checkPermission(Api api) {
|
||||
CrudRequestMapping crudRequestMapping = this.getClass().getDeclaredAnnotation(CrudRequestMapping.class);
|
||||
String path = crudRequestMapping.value();
|
||||
String permissionPrefix = String.join(StringConsts.COLON, StrUtil.splitTrim(path, StringConsts.SLASH));
|
||||
StpUtil.checkPermission(String.format("%s:%s", permissionPrefix, subPermission));
|
||||
StpUtil.checkPermission(String.format("%s:%s", permissionPrefix, api.name().toLowerCase()));
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,10 @@ import top.charles7c.cnadmin.common.model.dto.LoginUser;
|
||||
import top.charles7c.cnadmin.common.model.dto.RoleDTO;
|
||||
import top.charles7c.cnadmin.common.util.helper.LoginHelper;
|
||||
|
||||
import net.sf.jsqlparser.expression.*;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.Function;
|
||||
import net.sf.jsqlparser.expression.LongValue;
|
||||
import net.sf.jsqlparser.expression.Parenthesis;
|
||||
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
|
||||
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
|
||||
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
|
||||
@@ -45,7 +48,7 @@ import net.sf.jsqlparser.statement.select.SelectExpressionItem;
|
||||
import net.sf.jsqlparser.statement.select.SubSelect;
|
||||
|
||||
/**
|
||||
* 数据权限处理器实现类
|
||||
* 数据权限处理器实现
|
||||
* <p>
|
||||
* 来源:<a href="https://gitee.com/baomidou/mybatis-plus/issues/I37I90">DataPermissionInterceptor 如何使用?</a>
|
||||
* </p>
|
||||
|
@@ -16,9 +16,8 @@
|
||||
|
||||
package top.charles7c.cnadmin.common.config.properties;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
|
||||
@@ -29,8 +28,7 @@ import cn.hutool.extra.spring.SpringUtil;
|
||||
* @author Charles7c
|
||||
* @since 2022/12/21 20:21
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class RsaProperties {
|
||||
|
||||
/** 私钥 */
|
||||
|
@@ -20,11 +20,11 @@ import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
@@ -40,10 +40,9 @@ import top.charles7c.cnadmin.common.util.ExceptionUtils;
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
@EnableConfigurationProperties(ThreadPoolProperties.class)
|
||||
public class ThreadPoolConfiguration {
|
||||
|
||||
private final ThreadPoolProperties threadPoolProperties;
|
||||
/** 核心(最小)线程数 = CPU 核心数 + 1 */
|
||||
private final int corePoolSize = Runtime.getRuntime().availableProcessors() + 1;
|
||||
|
||||
@@ -52,7 +51,7 @@ public class ThreadPoolConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true")
|
||||
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
|
||||
public ThreadPoolTaskExecutor threadPoolTaskExecutor(ThreadPoolProperties threadPoolProperties) {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
// 核心(最小)线程数
|
||||
executor.setCorePoolSize(corePoolSize);
|
||||
|
@@ -19,7 +19,6 @@ package top.charles7c.cnadmin.common.config.threadpool;
|
||||
import lombok.Data;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 线程池配置属性
|
||||
@@ -29,7 +28,6 @@ import org.springframework.stereotype.Component;
|
||||
* @since 2022/12/23 23:06
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "thread-pool")
|
||||
public class ThreadPoolProperties {
|
||||
|
||||
|
@@ -19,6 +19,8 @@ package top.charles7c.cnadmin.common.constant;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import cn.hutool.core.lang.RegexPool;
|
||||
|
||||
/**
|
||||
* 正则相关常量
|
||||
*
|
||||
@@ -26,10 +28,25 @@ import lombok.NoArgsConstructor;
|
||||
* @since 2023/1/10 20:06
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class RegExpConsts {
|
||||
public class RegexConsts implements RegexPool {
|
||||
|
||||
/**
|
||||
* 密码正则(必须包含字母和数字的组合,可以使用特殊字符,长度在6-32之间)
|
||||
* 用户名正则(长度为 4 到 16 位,可以包含字母、数字,下划线,以字母开头)
|
||||
*/
|
||||
public static final String USERNAME = "^[a-zA-Z][a-zA-Z0-9_]{3,15}$";
|
||||
|
||||
/**
|
||||
* 密码正则(长度为 6 到 32 位,可以包含字母、数字、下划线,特殊字符,同时包含字母和数字)
|
||||
*/
|
||||
public static final String PASSWORD = "^(?=.*\\d)(?=.*[a-z]).{6,32}$";
|
||||
|
||||
/**
|
||||
* 通用编码正则(长度为 2 到 16 位,可以包含字母、数字,下划线,以字母开头)
|
||||
*/
|
||||
public static final String GENERAL_CODE = "^[a-zA-Z][a-zA-Z0-9_]{1,15}$";
|
||||
|
||||
/**
|
||||
* 通用名称正则(长度为 1 到 20 位,可以包含中文、字母、数字、下划线,短横线)
|
||||
*/
|
||||
public static final String GENERAL_NAME = "^[\\u4e00-\\u9fa5a-zA-Z0-9_-]{1,20}$";
|
||||
}
|
@@ -45,6 +45,11 @@ public class StringConsts implements StrPool {
|
||||
*/
|
||||
public static final String ASTERISK = "*";
|
||||
|
||||
/**
|
||||
* 问号
|
||||
*/
|
||||
public static final String QUESTION_MARK = "?";
|
||||
|
||||
/**
|
||||
* 中文逗号
|
||||
*/
|
||||
|
@@ -47,30 +47,24 @@ import cn.hutool.core.util.StrUtil;
|
||||
public class PageQuery extends SortQuery {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
/** 默认页码:1 */
|
||||
private static final int DEFAULT_PAGE = 1;
|
||||
/** 默认每页条数:10 */
|
||||
private static final int DEFAULT_SIZE = 10;
|
||||
|
||||
/**
|
||||
* 页码
|
||||
*/
|
||||
@Schema(description = "页码")
|
||||
@Min(value = 1, message = "页码最小值为 {value}")
|
||||
private Integer page;
|
||||
private Integer page = DEFAULT_PAGE;
|
||||
|
||||
/**
|
||||
* 每页条数
|
||||
*/
|
||||
@Schema(description = "每页条数")
|
||||
@Range(min = 1, max = 1000, message = "每页条数(取值范围 {min}-{max})")
|
||||
private Integer size;
|
||||
|
||||
/** 默认页码:1 */
|
||||
private static final int DEFAULT_PAGE = 1;
|
||||
/** 默认每页条数:10 */
|
||||
private static final int DEFAULT_SIZE = 10;
|
||||
|
||||
public PageQuery(Integer page, Integer size) {
|
||||
this.setPage(page);
|
||||
this.setSize(size);
|
||||
}
|
||||
private Integer size = DEFAULT_SIZE;
|
||||
|
||||
/**
|
||||
* 基于分页查询条件转换为 MyBatis Plus 分页条件
|
||||
@@ -92,12 +86,4 @@ public class PageQuery extends SortQuery {
|
||||
}
|
||||
return mybatisPage;
|
||||
}
|
||||
|
||||
public void setPage(Integer page) {
|
||||
this.page = page == null ? DEFAULT_PAGE : page;
|
||||
}
|
||||
|
||||
public void setSize(Integer size) {
|
||||
this.size = size == null ? DEFAULT_SIZE : size;
|
||||
}
|
||||
}
|
||||
|
@@ -55,7 +55,7 @@ public class PageDataVO<V> implements Serializable {
|
||||
* 总记录数
|
||||
*/
|
||||
@Schema(description = "总记录数")
|
||||
private Long total;
|
||||
private int total;
|
||||
|
||||
/**
|
||||
* 基于 MyBatis Plus 分页数据构建分页信息,并将源数据转换为指定类型数据
|
||||
@@ -76,7 +76,7 @@ public class PageDataVO<V> implements Serializable {
|
||||
}
|
||||
PageDataVO<V> pageDataVO = new PageDataVO<>();
|
||||
pageDataVO.setList(BeanUtil.copyToList(page.getRecords(), targetClass));
|
||||
pageDataVO.setTotal(page.getTotal());
|
||||
pageDataVO.setTotal((int) page.getTotal());
|
||||
return pageDataVO;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ public class PageDataVO<V> implements Serializable {
|
||||
}
|
||||
PageDataVO<V> pageDataVO = new PageDataVO<>();
|
||||
pageDataVO.setList(page.getRecords());
|
||||
pageDataVO.setTotal(page.getTotal());
|
||||
pageDataVO.setTotal((int) page.getTotal());
|
||||
return pageDataVO;
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ public class PageDataVO<V> implements Serializable {
|
||||
return pageDataVO;
|
||||
}
|
||||
|
||||
pageDataVO.setTotal((long)list.size());
|
||||
pageDataVO.setTotal(list.size());
|
||||
// 对列表数据进行分页
|
||||
int fromIndex = (page - 1) * size;
|
||||
int toIndex = page * size + size;
|
||||
|
@@ -25,6 +25,8 @@ import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import top.charles7c.cnadmin.common.constant.StringConsts;
|
||||
|
||||
/**
|
||||
* 异常工具类
|
||||
*
|
||||
@@ -99,7 +101,7 @@ public class ExceptionUtils {
|
||||
* @return /
|
||||
*/
|
||||
public static String exToBlank(ExSupplier<String> exSupplier) {
|
||||
return exToDefault(exSupplier, "");
|
||||
return exToDefault(exSupplier, StringConsts.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -26,12 +26,13 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import top.charles7c.cnadmin.common.annotation.Query;
|
||||
import top.charles7c.cnadmin.common.exception.BadRequestException;
|
||||
import top.charles7c.cnadmin.common.util.ReflectUtils;
|
||||
import top.charles7c.cnadmin.common.util.validate.ValidationUtils;
|
||||
|
||||
/**
|
||||
* 查询助手
|
||||
@@ -100,6 +101,10 @@ public class QueryHelper {
|
||||
|
||||
// 解析查询条件
|
||||
parse(queryAnnotation, field.getName(), fieldValue, queryWrapper);
|
||||
} catch (BadRequestException e) {
|
||||
log.error("Build query occurred an validation error: {}. Query: {}, Field: {}.", e.getMessage(), query,
|
||||
field, e);
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.error("Build query occurred an error: {}. Query: {}, Field: {}.", e.getMessage(), query, field, e);
|
||||
} finally {
|
||||
@@ -140,9 +145,9 @@ public class QueryHelper {
|
||||
// 如果没有单独指定属性名,就和使用该注解的属性的名称一致
|
||||
// 注意:数据库规范中列采用下划线连接法命名,程序规范中变量采用驼峰法命名
|
||||
String property = queryAnnotation.property();
|
||||
fieldName = StrUtil.blankToDefault(property, fieldName);
|
||||
String columnName = StrUtil.toUnderlineCase(fieldName);
|
||||
switch (queryAnnotation.type()) {
|
||||
String columnName = StrUtil.toUnderlineCase(StrUtil.blankToDefault(property, fieldName));
|
||||
Query.Type queryType = queryAnnotation.type();
|
||||
switch (queryType) {
|
||||
case EQUAL:
|
||||
queryWrapper.eq(columnName, fieldValue);
|
||||
break;
|
||||
@@ -163,6 +168,7 @@ public class QueryHelper {
|
||||
break;
|
||||
case BETWEEN:
|
||||
List<Object> between = new ArrayList<>((List<Object>)fieldValue);
|
||||
ValidationUtils.throwIf(between.size() != 2, "[{}] 必须是一个范围", fieldName);
|
||||
queryWrapper.between(columnName, between.get(0), between.get(1));
|
||||
break;
|
||||
case LEFT_LIKE:
|
||||
@@ -175,14 +181,12 @@ public class QueryHelper {
|
||||
queryWrapper.likeRight(columnName, fieldValue);
|
||||
break;
|
||||
case IN:
|
||||
if (CollUtil.isNotEmpty((List<Object>)fieldValue)) {
|
||||
queryWrapper.in(columnName, (List<Object>)fieldValue);
|
||||
}
|
||||
ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", fieldName);
|
||||
queryWrapper.in(columnName, (List<Object>)fieldValue);
|
||||
break;
|
||||
case NOT_IN:
|
||||
if (CollUtil.isNotEmpty((List<Object>)fieldValue)) {
|
||||
queryWrapper.notIn(columnName, (List<Object>)fieldValue);
|
||||
}
|
||||
ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", fieldName);
|
||||
queryWrapper.notIn(columnName, (List<Object>)fieldValue);
|
||||
break;
|
||||
case IS_NULL:
|
||||
queryWrapper.isNull(columnName);
|
||||
@@ -191,7 +195,7 @@ public class QueryHelper {
|
||||
queryWrapper.isNotNull(columnName);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
throw new IllegalArgumentException(String.format("暂不支持 [%s] 查询类型", queryType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import top.charles7c.cnadmin.common.constant.StringConsts;
|
||||
import top.charles7c.cnadmin.common.exception.ServiceException;
|
||||
|
||||
/**
|
||||
@@ -52,8 +53,8 @@ public class CheckUtils extends Validator {
|
||||
* 字段值
|
||||
*/
|
||||
public static void throwIfNotExists(Object obj, String entityName, String fieldName, Object fieldValue) {
|
||||
String message =
|
||||
String.format("%s 为 [%s] 的 %s 记录已不存在", fieldName, fieldValue, StrUtil.replace(entityName, "DO", ""));
|
||||
String message = String.format("%s 为 [%s] 的 %s 记录已不存在", fieldName, fieldValue,
|
||||
StrUtil.replace(entityName, "DO", StringConsts.EMPTY));
|
||||
throwIfNull(obj, message, EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
|
@@ -47,6 +47,7 @@ import cn.hutool.http.HttpStatus;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
|
||||
import top.charles7c.cnadmin.auth.model.request.LoginRequest;
|
||||
import top.charles7c.cnadmin.common.constant.StringConsts;
|
||||
import top.charles7c.cnadmin.common.constant.SysConsts;
|
||||
import top.charles7c.cnadmin.common.model.dto.LogContext;
|
||||
import top.charles7c.cnadmin.common.util.ExceptionUtils;
|
||||
@@ -170,7 +171,8 @@ public class LogInterceptor implements HandlerInterceptor {
|
||||
// (本框架代码规范)例如:@Tag(name = "部门管理 API") -> 部门管理
|
||||
if (classTag != null) {
|
||||
String name = classTag.name();
|
||||
logDO.setModule(StrUtil.isNotBlank(name) ? name.replace("API", "").trim() : "请在该接口类上指定所属模块");
|
||||
logDO
|
||||
.setModule(StrUtil.isNotBlank(name) ? name.replace("API", StringConsts.EMPTY).trim() : "请在该接口类上指定所属模块");
|
||||
}
|
||||
// 例如:@Log(module = "部门管理") -> 部门管理
|
||||
if (classLog != null && StrUtil.isNotBlank(classLog.module())) {
|
||||
@@ -213,7 +215,7 @@ public class LogInterceptor implements HandlerInterceptor {
|
||||
*/
|
||||
private void logRequest(LogDO logDO, HttpServletRequest request) {
|
||||
logDO.setRequestUrl(StrUtil.isBlank(request.getQueryString()) ? request.getRequestURL().toString()
|
||||
: request.getRequestURL().append("?").append(request.getQueryString()).toString());
|
||||
: request.getRequestURL().append(StringConsts.QUESTION_MARK).append(request.getQueryString()).toString());
|
||||
logDO.setRequestMethod(request.getMethod());
|
||||
logDO.setRequestHeaders(this.desensitize(ServletUtil.getHeaderMap(request)));
|
||||
String requestBody = this.getRequestBody(request);
|
||||
|
@@ -28,6 +28,7 @@ import org.springdoc.api.annotations.ParameterObject;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import top.charles7c.cnadmin.common.annotation.Query;
|
||||
import top.charles7c.cnadmin.common.constant.StringConsts;
|
||||
|
||||
/**
|
||||
* 登录日志查询条件
|
||||
@@ -54,6 +55,6 @@ public class LoginLogQuery implements Serializable {
|
||||
*/
|
||||
@Schema(description = "登录时间")
|
||||
@Query(type = Query.Type.BETWEEN)
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = StringConsts.NORM_DATE_TIME_PATTERN)
|
||||
private List<Date> createTime;
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@ import org.springdoc.api.annotations.ParameterObject;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import top.charles7c.cnadmin.common.annotation.Query;
|
||||
import top.charles7c.cnadmin.common.constant.StringConsts;
|
||||
|
||||
/**
|
||||
* 操作日志查询条件
|
||||
@@ -61,7 +62,7 @@ public class OperationLogQuery implements Serializable {
|
||||
*/
|
||||
@Schema(description = "操作时间")
|
||||
@Query(type = Query.Type.BETWEEN)
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = StringConsts.NORM_DATE_TIME_PATTERN)
|
||||
private List<Date> createTime;
|
||||
|
||||
/**
|
||||
|
@@ -28,6 +28,7 @@ import org.springdoc.api.annotations.ParameterObject;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import top.charles7c.cnadmin.common.annotation.Query;
|
||||
import top.charles7c.cnadmin.common.constant.StringConsts;
|
||||
|
||||
/**
|
||||
* 系统日志查询条件
|
||||
@@ -47,6 +48,6 @@ public class SystemLogQuery implements Serializable {
|
||||
*/
|
||||
@Schema(description = "创建时间")
|
||||
@Query(type = Query.Type.BETWEEN)
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = StringConsts.NORM_DATE_TIME_PATTERN)
|
||||
private List<Date> createTime;
|
||||
}
|
||||
|
@@ -49,7 +49,7 @@ import top.charles7c.cnadmin.monitor.model.vo.*;
|
||||
import top.charles7c.cnadmin.monitor.service.LogService;
|
||||
|
||||
/**
|
||||
* 系统日志业务实现类
|
||||
* 系统日志业务实现
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2022/12/23 20:12
|
||||
|
@@ -147,7 +147,7 @@ public class SaTokenRedisDaoImpl implements SaTokenDao {
|
||||
|
||||
@Override
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
|
||||
Collection<String> keys = RedisUtils.keys(prefix + "*" + keyword + "*");
|
||||
Collection<String> keys = RedisUtils.keys(String.format("%s*%s*", prefix, keyword));
|
||||
List<String> list = new ArrayList<>(keys);
|
||||
return SaFoxUtil.searchList(list, start, size, sortType);
|
||||
}
|
||||
|
@@ -27,6 +27,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import org.springdoc.api.annotations.ParameterObject;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import top.charles7c.cnadmin.common.constant.StringConsts;
|
||||
|
||||
/**
|
||||
* 在线用户查询条件
|
||||
*
|
||||
@@ -50,6 +52,6 @@ public class OnlineUserQuery implements Serializable {
|
||||
* 登录时间
|
||||
*/
|
||||
@Schema(description = "登录时间")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = StringConsts.NORM_DATE_TIME_PATTERN)
|
||||
private List<Date> loginTime;
|
||||
}
|
||||
|
@@ -53,7 +53,7 @@ import top.charles7c.cnadmin.system.service.RoleService;
|
||||
import top.charles7c.cnadmin.system.service.UserService;
|
||||
|
||||
/**
|
||||
* 登录业务实现类
|
||||
* 登录业务实现
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2022/12/21 21:49
|
||||
|
@@ -41,7 +41,7 @@ import top.charles7c.cnadmin.common.model.vo.PageDataVO;
|
||||
import top.charles7c.cnadmin.common.util.helper.LoginHelper;
|
||||
|
||||
/**
|
||||
* 在线用户业务实现类
|
||||
* 在线用户业务实现
|
||||
*
|
||||
* @author Charles7c
|
||||
* @author Lion Li(RuoYi-Vue-Plus)
|
||||
|
@@ -30,7 +30,7 @@ import top.charles7c.cnadmin.system.service.MenuService;
|
||||
import top.charles7c.cnadmin.system.service.RoleService;
|
||||
|
||||
/**
|
||||
* 权限业务实现类
|
||||
* 权限业务实现
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/3/2 20:40
|
||||
|
@@ -16,6 +16,16 @@
|
||||
|
||||
package top.charles7c.cnadmin.system.mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
|
||||
import top.charles7c.cnadmin.common.annotation.DataPermission;
|
||||
import top.charles7c.cnadmin.common.base.BaseMapper;
|
||||
import top.charles7c.cnadmin.system.model.entity.UserDO;
|
||||
|
||||
@@ -25,4 +35,33 @@ import top.charles7c.cnadmin.system.model.entity.UserDO;
|
||||
* @author Charles7c
|
||||
* @since 2022/12/22 21:47
|
||||
*/
|
||||
public interface UserMapper extends BaseMapper<UserDO> {}
|
||||
public interface UserMapper extends BaseMapper<UserDO> {
|
||||
|
||||
@Override
|
||||
@DataPermission
|
||||
List<UserDO> selectList(@Param(Constants.WRAPPER) Wrapper<UserDO> queryWrapper);
|
||||
|
||||
@Override
|
||||
@DataPermission
|
||||
<P extends IPage<UserDO>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<UserDO> queryWrapper);
|
||||
|
||||
/**
|
||||
* 根据用户名查询
|
||||
*
|
||||
* @param username
|
||||
* 用户名
|
||||
* @return 用户信息
|
||||
*/
|
||||
@Select("SELECT * FROM `sys_user` WHERE `username` = #{username}")
|
||||
UserDO selectByUsername(@Param("username") String username);
|
||||
|
||||
/**
|
||||
* 根据 ID 查询昵称
|
||||
*
|
||||
* @param id
|
||||
* ID
|
||||
* @return 昵称
|
||||
*/
|
||||
@Select("SELECT `nickname` FROM `sys_user` WHERE `id` = #{id}")
|
||||
String selectNicknameById(@Param("id") Long id);
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@ import org.springdoc.api.annotations.ParameterObject;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import top.charles7c.cnadmin.common.annotation.Query;
|
||||
import top.charles7c.cnadmin.common.constant.StringConsts;
|
||||
|
||||
/**
|
||||
* 用户查询条件
|
||||
@@ -61,7 +62,7 @@ public class UserQuery implements Serializable {
|
||||
*/
|
||||
@Schema(description = "创建时间")
|
||||
@Query(type = Query.Type.BETWEEN)
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = StringConsts.NORM_DATE_TIME_PATTERN)
|
||||
private List<Date> createTime;
|
||||
|
||||
/**
|
||||
|
@@ -18,6 +18,7 @@ package top.charles7c.cnadmin.system.model.request;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -26,6 +27,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import top.charles7c.cnadmin.common.base.BaseRequest;
|
||||
import top.charles7c.cnadmin.common.constant.RegexConsts;
|
||||
import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum;
|
||||
|
||||
/**
|
||||
@@ -52,6 +54,7 @@ public class DeptRequest extends BaseRequest {
|
||||
*/
|
||||
@Schema(description = "部门名称")
|
||||
@NotBlank(message = "部门名称不能为空")
|
||||
@Pattern(regexp = RegexConsts.GENERAL_NAME, message = "部门名称长度为 1 到 20 位,可以包含中文、字母、数字、下划线,短横线")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
|
@@ -18,12 +18,14 @@ package top.charles7c.cnadmin.system.model.request;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import top.charles7c.cnadmin.common.base.BaseRequest;
|
||||
import top.charles7c.cnadmin.common.constant.RegexConsts;
|
||||
import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum;
|
||||
import top.charles7c.cnadmin.common.enums.MenuTypeEnum;
|
||||
|
||||
@@ -57,6 +59,7 @@ public class MenuRequest extends BaseRequest {
|
||||
*/
|
||||
@Schema(description = "菜单标题")
|
||||
@NotBlank(message = "菜单标题不能为空")
|
||||
@Pattern(regexp = RegexConsts.GENERAL_NAME, message = "菜单标题长度为 1 到 20 位,可以包含中文、字母、数字、下划线,短横线")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
|
@@ -21,6 +21,7 @@ import java.util.List;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -29,6 +30,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import top.charles7c.cnadmin.common.base.BaseRequest;
|
||||
import top.charles7c.cnadmin.common.constant.RegexConsts;
|
||||
import top.charles7c.cnadmin.common.enums.DataScopeEnum;
|
||||
import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum;
|
||||
|
||||
@@ -49,12 +51,15 @@ public class RoleRequest extends BaseRequest {
|
||||
*/
|
||||
@Schema(description = "角色名称")
|
||||
@NotBlank(message = "角色名称不能为空")
|
||||
@Pattern(regexp = RegexConsts.GENERAL_NAME, message = "角色名称长度为 1 到 20 位,可以包含中文、字母、数字、下划线,短横线")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 角色编码
|
||||
*/
|
||||
@Schema(description = "角色编码")
|
||||
@NotBlank(message = "角色编码不能为空")
|
||||
@Pattern(regexp = RegexConsts.GENERAL_CODE, message = "角色编码长度为 2 到 16 位,可以包含字母、数字,下划线,以字母开头")
|
||||
private String code;
|
||||
|
||||
/**
|
||||
|
@@ -20,13 +20,13 @@ import java.io.Serializable;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import top.charles7c.cnadmin.common.constant.RegexConsts;
|
||||
import top.charles7c.cnadmin.common.enums.GenderEnum;
|
||||
|
||||
/**
|
||||
@@ -46,7 +46,7 @@ public class UpdateBasicInfoRequest implements Serializable {
|
||||
*/
|
||||
@Schema(description = "昵称")
|
||||
@NotBlank(message = "昵称不能为空")
|
||||
@Length(max = 32, message = "昵称长度不能超过 {max} 个字符")
|
||||
@Pattern(regexp = RegexConsts.GENERAL_NAME, message = "昵称长度为 1 到 20 位,可以包含中文、字母、数字、下划线,短横线")
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
|
@@ -27,7 +27,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import cn.hutool.core.lang.RegexPool;
|
||||
import top.charles7c.cnadmin.common.constant.RegexConsts;
|
||||
|
||||
/**
|
||||
* 修改邮箱信息
|
||||
@@ -46,7 +46,7 @@ public class UpdateEmailRequest implements Serializable {
|
||||
*/
|
||||
@Schema(description = "新邮箱")
|
||||
@NotBlank(message = "新邮箱不能为空")
|
||||
@Pattern(regexp = RegexPool.EMAIL, message = "邮箱格式错误")
|
||||
@Pattern(regexp = RegexConsts.EMAIL, message = "邮箱格式错误")
|
||||
private String newEmail;
|
||||
|
||||
/**
|
||||
|
@@ -18,7 +18,10 @@ package top.charles7c.cnadmin.system.model.request;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.constraints.*;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -26,9 +29,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import cn.hutool.core.lang.RegexPool;
|
||||
|
||||
import top.charles7c.cnadmin.common.base.BaseRequest;
|
||||
import top.charles7c.cnadmin.common.constant.RegexConsts;
|
||||
import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum;
|
||||
import top.charles7c.cnadmin.common.enums.GenderEnum;
|
||||
|
||||
@@ -49,27 +51,29 @@ public class UserRequest extends BaseRequest {
|
||||
*/
|
||||
@Schema(description = "用户名")
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@Pattern(regexp = RegexConsts.USERNAME, message = "用户名长度为 4 到 16 位,可以包含字母、数字,下划线,以字母开头")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
@Schema(description = "昵称")
|
||||
@Length(max = 32, message = "昵称长度不能超过 {max} 个字符")
|
||||
@NotBlank(message = "昵称不能为空")
|
||||
@Pattern(regexp = RegexConsts.GENERAL_NAME, message = "昵称长度为 1 到 20 位,可以包含中文、字母、数字、下划线,短横线")
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
@Schema(description = "邮箱")
|
||||
@Pattern(regexp = RegexPool.EMAIL, message = "邮箱格式错误")
|
||||
@Pattern(regexp = RegexConsts.EMAIL, message = "邮箱格式错误")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 手机号码
|
||||
*/
|
||||
@Schema(description = "手机号码")
|
||||
@Pattern(regexp = RegexPool.MOBILE, message = "手机号码格式错误")
|
||||
@Pattern(regexp = RegexConsts.MOBILE, message = "手机号码格式错误")
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
@@ -83,12 +87,14 @@ public class UserRequest extends BaseRequest {
|
||||
* 所属部门
|
||||
*/
|
||||
@Schema(description = "所属部门")
|
||||
@NotNull(message = "所属部门不能为空")
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 所属角色
|
||||
*/
|
||||
@Schema(description = "所属角色")
|
||||
@NotEmpty(message = "所属角色不能为空")
|
||||
private List<Long> roleIds;
|
||||
|
||||
/**
|
||||
|
@@ -38,13 +38,12 @@ public interface RoleDeptService {
|
||||
boolean save(List<Long> deptIds, Long roleId);
|
||||
|
||||
/**
|
||||
* 根据角色 ID 查询
|
||||
* 根据角色 ID 删除
|
||||
*
|
||||
* @param roleId
|
||||
* 角色 ID
|
||||
* @return 部门 ID 列表
|
||||
* @param roleIds
|
||||
* 角色 ID 列表
|
||||
*/
|
||||
List<Long> listDeptIdByRoleId(Long roleId);
|
||||
void deleteByRoleIds(List<Long> roleIds);
|
||||
|
||||
/**
|
||||
* 根据部门 ID 删除
|
||||
@@ -55,10 +54,11 @@ public interface RoleDeptService {
|
||||
void deleteByDeptIds(List<Long> deptIds);
|
||||
|
||||
/**
|
||||
* 根据角色 ID 删除
|
||||
* 根据角色 ID 查询
|
||||
*
|
||||
* @param roleIds
|
||||
* 角色 ID 列表
|
||||
* @param roleId
|
||||
* 角色 ID
|
||||
* @return 部门 ID 列表
|
||||
*/
|
||||
void deleteByRoleIds(List<Long> roleIds);
|
||||
List<Long> listDeptIdByRoleId(Long roleId);
|
||||
}
|
@@ -37,6 +37,14 @@ public interface RoleMenuService {
|
||||
*/
|
||||
boolean save(List<Long> menuIds, Long roleId);
|
||||
|
||||
/**
|
||||
* 根据角色 ID 删除
|
||||
*
|
||||
* @param roleIds
|
||||
* 角色 ID 列表
|
||||
*/
|
||||
void deleteByRoleIds(List<Long> roleIds);
|
||||
|
||||
/**
|
||||
* 根据角色 ID 查询
|
||||
*
|
||||
@@ -45,12 +53,4 @@ public interface RoleMenuService {
|
||||
* @return 菜单 ID 列表
|
||||
*/
|
||||
List<Long> listMenuIdByRoleIds(List<Long> roleIds);
|
||||
|
||||
/**
|
||||
* 根据角色 ID 删除
|
||||
*
|
||||
* @param roleIds
|
||||
* 角色 ID 列表
|
||||
*/
|
||||
void deleteByRoleIds(List<Long> roleIds);
|
||||
}
|
@@ -38,13 +38,12 @@ public interface UserRoleService {
|
||||
boolean save(List<Long> roleIds, Long userId);
|
||||
|
||||
/**
|
||||
* 根据角色 ID 列表查询
|
||||
* 根据用户 ID 删除
|
||||
*
|
||||
* @param roleIds
|
||||
* 角色 ID 列表
|
||||
* @return 总记录数
|
||||
* @param userIds
|
||||
* 用户 ID 列表
|
||||
*/
|
||||
Long countByRoleIds(List<Long> roleIds);
|
||||
void deleteByUserIds(List<Long> userIds);
|
||||
|
||||
/**
|
||||
* 根据用户 ID 查询
|
||||
@@ -56,10 +55,11 @@ public interface UserRoleService {
|
||||
List<Long> listRoleIdByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 根据用户 ID 删除
|
||||
* 根据角色 ID 列表查询
|
||||
*
|
||||
* @param userIds
|
||||
* 用户 ID 列表
|
||||
* @param roleIds
|
||||
* 角色 ID 列表
|
||||
* @return 总记录数
|
||||
*/
|
||||
void deleteByUserIds(List<Long> userIds);
|
||||
Long countByRoleIds(List<Long> roleIds);
|
||||
}
|
@@ -23,6 +23,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
import top.charles7c.cnadmin.common.base.BaseService;
|
||||
import top.charles7c.cnadmin.system.model.entity.UserDO;
|
||||
import top.charles7c.cnadmin.system.model.query.UserQuery;
|
||||
import top.charles7c.cnadmin.system.model.request.UpdateBasicInfoRequest;
|
||||
import top.charles7c.cnadmin.system.model.request.UpdateUserRoleRequest;
|
||||
import top.charles7c.cnadmin.system.model.request.UserRequest;
|
||||
import top.charles7c.cnadmin.system.model.vo.UserDetailVO;
|
||||
@@ -47,6 +48,16 @@ public interface UserService extends BaseService<UserVO, UserDetailVO, UserQuery
|
||||
*/
|
||||
String uploadAvatar(MultipartFile avatar, Long id);
|
||||
|
||||
/**
|
||||
* 修改基础信息
|
||||
*
|
||||
* @param request
|
||||
* 修改信息
|
||||
* @param id
|
||||
* ID
|
||||
*/
|
||||
void updateBasicInfo(UpdateBasicInfoRequest request, Long id);
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*
|
||||
|
@@ -48,7 +48,7 @@ import top.charles7c.cnadmin.system.service.RoleDeptService;
|
||||
import top.charles7c.cnadmin.system.service.UserService;
|
||||
|
||||
/**
|
||||
* 部门业务实现类
|
||||
* 部门业务实现
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/1/22 17:55
|
||||
|
@@ -36,7 +36,7 @@ import top.charles7c.cnadmin.system.model.vo.MenuVO;
|
||||
import top.charles7c.cnadmin.system.service.MenuService;
|
||||
|
||||
/**
|
||||
* 菜单业务实现类
|
||||
* 菜单业务实现
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/2/15 20:30
|
||||
|
@@ -22,6 +22,7 @@ import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
|
||||
@@ -30,7 +31,7 @@ import top.charles7c.cnadmin.system.model.entity.RoleDeptDO;
|
||||
import top.charles7c.cnadmin.system.service.RoleDeptService;
|
||||
|
||||
/**
|
||||
* 角色和部门业务实现类
|
||||
* 角色和部门业务实现
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/2/19 10:47
|
||||
@@ -42,6 +43,7 @@ public class RoleDeptServiceImpl implements RoleDeptService {
|
||||
private final RoleDeptMapper roleDeptMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean save(List<Long> deptIds, Long roleId) {
|
||||
// 检查是否有变更
|
||||
List<Long> oldDeptIdList = roleDeptMapper.lambdaQuery().select(RoleDeptDO::getDeptId)
|
||||
@@ -58,17 +60,19 @@ public class RoleDeptServiceImpl implements RoleDeptService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> listDeptIdByRoleId(Long roleId) {
|
||||
return roleDeptMapper.selectDeptIdByRoleId(roleId);
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteByRoleIds(List<Long> roleIds) {
|
||||
roleDeptMapper.lambdaUpdate().in(RoleDeptDO::getRoleId, roleIds).remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteByDeptIds(List<Long> deptIds) {
|
||||
roleDeptMapper.lambdaUpdate().in(RoleDeptDO::getDeptId, deptIds).remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteByRoleIds(List<Long> roleIds) {
|
||||
roleDeptMapper.lambdaUpdate().in(RoleDeptDO::getRoleId, roleIds).remove();
|
||||
public List<Long> listDeptIdByRoleId(Long roleId) {
|
||||
return roleDeptMapper.selectDeptIdByRoleId(roleId);
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
|
||||
@@ -31,7 +32,7 @@ import top.charles7c.cnadmin.system.model.entity.RoleMenuDO;
|
||||
import top.charles7c.cnadmin.system.service.RoleMenuService;
|
||||
|
||||
/**
|
||||
* 角色和菜单业务实现类
|
||||
* 角色和菜单业务实现
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/2/19 10:43
|
||||
@@ -43,6 +44,7 @@ public class RoleMenuServiceImpl implements RoleMenuService {
|
||||
private final RoleMenuMapper roleMenuMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean save(List<Long> menuIds, Long roleId) {
|
||||
// 检查是否有变更
|
||||
List<Long> oldMenuIdList = roleMenuMapper.lambdaQuery().select(RoleMenuDO::getMenuId)
|
||||
@@ -58,6 +60,12 @@ public class RoleMenuServiceImpl implements RoleMenuService {
|
||||
return roleMenuMapper.insertBatch(roleMenuList);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteByRoleIds(List<Long> roleIds) {
|
||||
roleMenuMapper.lambdaUpdate().in(RoleMenuDO::getRoleId, roleIds).remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> listMenuIdByRoleIds(List<Long> roleIds) {
|
||||
if (CollUtil.isEmpty(roleIds)) {
|
||||
@@ -65,9 +73,4 @@ public class RoleMenuServiceImpl implements RoleMenuService {
|
||||
}
|
||||
return roleMenuMapper.selectMenuIdByRoleIds(roleIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteByRoleIds(List<Long> roleIds) {
|
||||
roleMenuMapper.lambdaUpdate().in(RoleMenuDO::getRoleId, roleIds).remove();
|
||||
}
|
||||
}
|
||||
|
@@ -47,7 +47,7 @@ import top.charles7c.cnadmin.system.model.vo.RoleVO;
|
||||
import top.charles7c.cnadmin.system.service.*;
|
||||
|
||||
/**
|
||||
* 角色业务实现类
|
||||
* 角色业务实现
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/2/8 23:17
|
||||
|
@@ -22,6 +22,7 @@ import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
|
||||
@@ -30,7 +31,7 @@ import top.charles7c.cnadmin.system.model.entity.UserRoleDO;
|
||||
import top.charles7c.cnadmin.system.service.UserRoleService;
|
||||
|
||||
/**
|
||||
* 用户和角色业务实现类
|
||||
* 用户和角色业务实现
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/2/20 21:30
|
||||
@@ -42,6 +43,7 @@ public class UserRoleServiceImpl implements UserRoleService {
|
||||
private final UserRoleMapper userRoleMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean save(List<Long> roleIds, Long userId) {
|
||||
// 检查是否有变更
|
||||
List<Long> oldRoleIdList = userRoleMapper.lambdaQuery().select(UserRoleDO::getRoleId)
|
||||
@@ -58,8 +60,9 @@ public class UserRoleServiceImpl implements UserRoleService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countByRoleIds(List<Long> roleIds) {
|
||||
return userRoleMapper.lambdaQuery().in(UserRoleDO::getRoleId, roleIds).count();
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteByUserIds(List<Long> userIds) {
|
||||
userRoleMapper.lambdaUpdate().in(UserRoleDO::getUserId, userIds).remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,7 +71,7 @@ public class UserRoleServiceImpl implements UserRoleService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteByUserIds(List<Long> userIds) {
|
||||
userRoleMapper.lambdaUpdate().in(UserRoleDO::getUserId, userIds).remove();
|
||||
public Long countByRoleIds(List<Long> roleIds) {
|
||||
return userRoleMapper.lambdaQuery().in(UserRoleDO::getRoleId, roleIds).count();
|
||||
}
|
||||
}
|
||||
|
@@ -52,14 +52,18 @@ import top.charles7c.cnadmin.common.util.validate.CheckUtils;
|
||||
import top.charles7c.cnadmin.system.mapper.UserMapper;
|
||||
import top.charles7c.cnadmin.system.model.entity.UserDO;
|
||||
import top.charles7c.cnadmin.system.model.query.UserQuery;
|
||||
import top.charles7c.cnadmin.system.model.request.UpdateBasicInfoRequest;
|
||||
import top.charles7c.cnadmin.system.model.request.UpdateUserRoleRequest;
|
||||
import top.charles7c.cnadmin.system.model.request.UserRequest;
|
||||
import top.charles7c.cnadmin.system.model.vo.UserDetailVO;
|
||||
import top.charles7c.cnadmin.system.model.vo.UserVO;
|
||||
import top.charles7c.cnadmin.system.service.*;
|
||||
import top.charles7c.cnadmin.system.service.DeptService;
|
||||
import top.charles7c.cnadmin.system.service.RoleService;
|
||||
import top.charles7c.cnadmin.system.service.UserRoleService;
|
||||
import top.charles7c.cnadmin.system.service.UserService;
|
||||
|
||||
/**
|
||||
* 用户业务实现类
|
||||
* 用户业务实现
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2022/12/21 21:49
|
||||
@@ -181,6 +185,14 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserVO,
|
||||
return newAvatar;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateBasicInfo(UpdateBasicInfoRequest request, Long id) {
|
||||
super.getById(id);
|
||||
baseMapper.lambdaUpdate().set(UserDO::getNickname, request.getNickname())
|
||||
.set(UserDO::getGender, request.getGender()).eq(UserDO::getId, id).update();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updatePassword(String oldPassword, String newPassword, Long id) {
|
||||
@@ -208,6 +220,7 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserVO,
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void resetPassword(Long id) {
|
||||
UserDO user = super.getById(id);
|
||||
user.setPassword(SecureUtils.md5Salt(SysConsts.DEFAULT_PASSWORD, id.toString()));
|
||||
@@ -216,6 +229,7 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserVO,
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateRole(UpdateUserRoleRequest request, Long id) {
|
||||
super.getById(id);
|
||||
// 保存用户和角色关联
|
||||
@@ -224,7 +238,7 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserVO,
|
||||
|
||||
@Override
|
||||
public UserDO getByUsername(String username) {
|
||||
return baseMapper.lambdaQuery().eq(UserDO::getUsername, username).one();
|
||||
return baseMapper.selectByUsername(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -234,7 +248,7 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserVO,
|
||||
|
||||
@Override
|
||||
public String getNicknameById(Long id) {
|
||||
return super.getById(id).getNickname();
|
||||
return baseMapper.selectNicknameById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -6,4 +6,5 @@ module.exports = {
|
||||
quoteProps: 'consistent',
|
||||
htmlWhitespaceSensitivity: 'strict',
|
||||
vueIndentScriptAndStyle: true,
|
||||
endOfLine: 'auto',
|
||||
};
|
||||
|
150
continew-admin-ui/components.d.ts
vendored
150
continew-admin-ui/components.d.ts
vendored
@@ -1,84 +1,84 @@
|
||||
// generated by unplugin-vue-components
|
||||
// We suggest you to commit this file into source control
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
import '@vue/runtime-core'
|
||||
import '@vue/runtime-core';
|
||||
|
||||
export {}
|
||||
export {};
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
AAffix: typeof import('@arco-design/web-vue')['Affix']
|
||||
AAlert: typeof import('@arco-design/web-vue')['Alert']
|
||||
AAvatar: typeof import('@arco-design/web-vue')['Avatar']
|
||||
ABadge: typeof import('@arco-design/web-vue')['Badge']
|
||||
ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb']
|
||||
ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem']
|
||||
AButton: typeof import('@arco-design/web-vue')['Button']
|
||||
AButtonGroup: typeof import('@arco-design/web-vue')['ButtonGroup']
|
||||
ACard: typeof import('@arco-design/web-vue')['Card']
|
||||
ACardMeta: typeof import('@arco-design/web-vue')['CardMeta']
|
||||
ACarousel: typeof import('@arco-design/web-vue')['Carousel']
|
||||
ACarouselItem: typeof import('@arco-design/web-vue')['CarouselItem']
|
||||
ACheckbox: typeof import('@arco-design/web-vue')['Checkbox']
|
||||
ACol: typeof import('@arco-design/web-vue')['Col']
|
||||
AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider']
|
||||
ADescriptions: typeof import('@arco-design/web-vue')['Descriptions']
|
||||
ADescriptionsItem: typeof import('@arco-design/web-vue')['DescriptionsItem']
|
||||
ADivider: typeof import('@arco-design/web-vue')['Divider']
|
||||
ADoption: typeof import('@arco-design/web-vue')['Doption']
|
||||
ADrawer: typeof import('@arco-design/web-vue')['Drawer']
|
||||
ADropdown: typeof import('@arco-design/web-vue')['Dropdown']
|
||||
AForm: typeof import('@arco-design/web-vue')['Form']
|
||||
AFormItem: typeof import('@arco-design/web-vue')['FormItem']
|
||||
AGrid: typeof import('@arco-design/web-vue')['Grid']
|
||||
AGridItem: typeof import('@arco-design/web-vue')['GridItem']
|
||||
AInput: typeof import('@arco-design/web-vue')['Input']
|
||||
AInputNumber: typeof import('@arco-design/web-vue')['InputNumber']
|
||||
AInputPassword: typeof import('@arco-design/web-vue')['InputPassword']
|
||||
AInputSearch: typeof import('@arco-design/web-vue')['InputSearch']
|
||||
ALayout: typeof import('@arco-design/web-vue')['Layout']
|
||||
ALayoutContent: typeof import('@arco-design/web-vue')['LayoutContent']
|
||||
ALayoutFooter: typeof import('@arco-design/web-vue')['LayoutFooter']
|
||||
ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider']
|
||||
ALink: typeof import('@arco-design/web-vue')['Link']
|
||||
AList: typeof import('@arco-design/web-vue')['List']
|
||||
AListItem: typeof import('@arco-design/web-vue')['ListItem']
|
||||
AListItemMeta: typeof import('@arco-design/web-vue')['ListItemMeta']
|
||||
AMenu: typeof import('@arco-design/web-vue')['Menu']
|
||||
AMenuItem: typeof import('@arco-design/web-vue')['MenuItem']
|
||||
AModal: typeof import('@arco-design/web-vue')['Modal']
|
||||
AOption: typeof import('@arco-design/web-vue')['Option']
|
||||
APopconfirm: typeof import('@arco-design/web-vue')['Popconfirm']
|
||||
APopover: typeof import('@arco-design/web-vue')['Popover']
|
||||
ARadio: typeof import('@arco-design/web-vue')['Radio']
|
||||
ARadioGroup: typeof import('@arco-design/web-vue')['RadioGroup']
|
||||
ARangePicker: typeof import('@arco-design/web-vue')['RangePicker']
|
||||
AResult: typeof import('@arco-design/web-vue')['Result']
|
||||
ARow: typeof import('@arco-design/web-vue')['Row']
|
||||
ASelect: typeof import('@arco-design/web-vue')['Select']
|
||||
ASkeleton: typeof import('@arco-design/web-vue')['Skeleton']
|
||||
ASkeletonLine: typeof import('@arco-design/web-vue')['SkeletonLine']
|
||||
ASpace: typeof import('@arco-design/web-vue')['Space']
|
||||
ASpin: typeof import('@arco-design/web-vue')['Spin']
|
||||
AStatistic: typeof import('@arco-design/web-vue')['Statistic']
|
||||
AStep: typeof import('@arco-design/web-vue')['Step']
|
||||
ASteps: typeof import('@arco-design/web-vue')['Steps']
|
||||
ASubMenu: typeof import('@arco-design/web-vue')['SubMenu']
|
||||
ASwitch: typeof import('@arco-design/web-vue')['Switch']
|
||||
ATable: typeof import('@arco-design/web-vue')['Table']
|
||||
ATableColumn: typeof import('@arco-design/web-vue')['TableColumn']
|
||||
ATabPane: typeof import('@arco-design/web-vue')['TabPane']
|
||||
ATabs: typeof import('@arco-design/web-vue')['Tabs']
|
||||
ATag: typeof import('@arco-design/web-vue')['Tag']
|
||||
ATextarea: typeof import('@arco-design/web-vue')['Textarea']
|
||||
ATooltip: typeof import('@arco-design/web-vue')['Tooltip']
|
||||
ATree: typeof import('@arco-design/web-vue')['Tree']
|
||||
ATreeSelect: typeof import('@arco-design/web-vue')['TreeSelect']
|
||||
ATypographyParagraph: typeof import('@arco-design/web-vue')['TypographyParagraph']
|
||||
ATypographyText: typeof import('@arco-design/web-vue')['TypographyText']
|
||||
ATypographyTitle: typeof import('@arco-design/web-vue')['TypographyTitle']
|
||||
AUpload: typeof import('@arco-design/web-vue')['Upload']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
AAffix: typeof import('@arco-design/web-vue')['Affix'];
|
||||
AAlert: typeof import('@arco-design/web-vue')['Alert'];
|
||||
AAvatar: typeof import('@arco-design/web-vue')['Avatar'];
|
||||
ABadge: typeof import('@arco-design/web-vue')['Badge'];
|
||||
ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb'];
|
||||
ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem'];
|
||||
AButton: typeof import('@arco-design/web-vue')['Button'];
|
||||
AButtonGroup: typeof import('@arco-design/web-vue')['ButtonGroup'];
|
||||
ACard: typeof import('@arco-design/web-vue')['Card'];
|
||||
ACardMeta: typeof import('@arco-design/web-vue')['CardMeta'];
|
||||
ACarousel: typeof import('@arco-design/web-vue')['Carousel'];
|
||||
ACarouselItem: typeof import('@arco-design/web-vue')['CarouselItem'];
|
||||
ACheckbox: typeof import('@arco-design/web-vue')['Checkbox'];
|
||||
ACol: typeof import('@arco-design/web-vue')['Col'];
|
||||
AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider'];
|
||||
ADescriptions: typeof import('@arco-design/web-vue')['Descriptions'];
|
||||
ADescriptionsItem: typeof import('@arco-design/web-vue')['DescriptionsItem'];
|
||||
ADivider: typeof import('@arco-design/web-vue')['Divider'];
|
||||
ADoption: typeof import('@arco-design/web-vue')['Doption'];
|
||||
ADrawer: typeof import('@arco-design/web-vue')['Drawer'];
|
||||
ADropdown: typeof import('@arco-design/web-vue')['Dropdown'];
|
||||
AForm: typeof import('@arco-design/web-vue')['Form'];
|
||||
AFormItem: typeof import('@arco-design/web-vue')['FormItem'];
|
||||
AGrid: typeof import('@arco-design/web-vue')['Grid'];
|
||||
AGridItem: typeof import('@arco-design/web-vue')['GridItem'];
|
||||
AInput: typeof import('@arco-design/web-vue')['Input'];
|
||||
AInputNumber: typeof import('@arco-design/web-vue')['InputNumber'];
|
||||
AInputPassword: typeof import('@arco-design/web-vue')['InputPassword'];
|
||||
AInputSearch: typeof import('@arco-design/web-vue')['InputSearch'];
|
||||
ALayout: typeof import('@arco-design/web-vue')['Layout'];
|
||||
ALayoutContent: typeof import('@arco-design/web-vue')['LayoutContent'];
|
||||
ALayoutFooter: typeof import('@arco-design/web-vue')['LayoutFooter'];
|
||||
ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider'];
|
||||
ALink: typeof import('@arco-design/web-vue')['Link'];
|
||||
AList: typeof import('@arco-design/web-vue')['List'];
|
||||
AListItem: typeof import('@arco-design/web-vue')['ListItem'];
|
||||
AListItemMeta: typeof import('@arco-design/web-vue')['ListItemMeta'];
|
||||
AMenu: typeof import('@arco-design/web-vue')['Menu'];
|
||||
AMenuItem: typeof import('@arco-design/web-vue')['MenuItem'];
|
||||
AModal: typeof import('@arco-design/web-vue')['Modal'];
|
||||
AOption: typeof import('@arco-design/web-vue')['Option'];
|
||||
APopconfirm: typeof import('@arco-design/web-vue')['Popconfirm'];
|
||||
APopover: typeof import('@arco-design/web-vue')['Popover'];
|
||||
ARadio: typeof import('@arco-design/web-vue')['Radio'];
|
||||
ARadioGroup: typeof import('@arco-design/web-vue')['RadioGroup'];
|
||||
ARangePicker: typeof import('@arco-design/web-vue')['RangePicker'];
|
||||
AResult: typeof import('@arco-design/web-vue')['Result'];
|
||||
ARow: typeof import('@arco-design/web-vue')['Row'];
|
||||
ASelect: typeof import('@arco-design/web-vue')['Select'];
|
||||
ASkeleton: typeof import('@arco-design/web-vue')['Skeleton'];
|
||||
ASkeletonLine: typeof import('@arco-design/web-vue')['SkeletonLine'];
|
||||
ASpace: typeof import('@arco-design/web-vue')['Space'];
|
||||
ASpin: typeof import('@arco-design/web-vue')['Spin'];
|
||||
AStatistic: typeof import('@arco-design/web-vue')['Statistic'];
|
||||
AStep: typeof import('@arco-design/web-vue')['Step'];
|
||||
ASteps: typeof import('@arco-design/web-vue')['Steps'];
|
||||
ASubMenu: typeof import('@arco-design/web-vue')['SubMenu'];
|
||||
ASwitch: typeof import('@arco-design/web-vue')['Switch'];
|
||||
ATable: typeof import('@arco-design/web-vue')['Table'];
|
||||
ATableColumn: typeof import('@arco-design/web-vue')['TableColumn'];
|
||||
ATabPane: typeof import('@arco-design/web-vue')['TabPane'];
|
||||
ATabs: typeof import('@arco-design/web-vue')['Tabs'];
|
||||
ATag: typeof import('@arco-design/web-vue')['Tag'];
|
||||
ATextarea: typeof import('@arco-design/web-vue')['Textarea'];
|
||||
ATooltip: typeof import('@arco-design/web-vue')['Tooltip'];
|
||||
ATree: typeof import('@arco-design/web-vue')['Tree'];
|
||||
ATreeSelect: typeof import('@arco-design/web-vue')['TreeSelect'];
|
||||
ATypographyParagraph: typeof import('@arco-design/web-vue')['TypographyParagraph'];
|
||||
ATypographyText: typeof import('@arco-design/web-vue')['TypographyText'];
|
||||
ATypographyTitle: typeof import('@arco-design/web-vue')['TypographyTitle'];
|
||||
AUpload: typeof import('@arco-design/web-vue')['Upload'];
|
||||
RouterLink: typeof import('vue-router')['RouterLink'];
|
||||
RouterView: typeof import('vue-router')['RouterView'];
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "continew-admin-ui",
|
||||
"description": "ContiNew Admin 中后台管理框架,Continue New Admin,持续以最新流行技术栈构建,拥抱变化,迭代优化。",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"private": true,
|
||||
"author": "Charles7c",
|
||||
"license": "Apache-2.0",
|
||||
@@ -11,6 +11,7 @@
|
||||
"report": "cross-env REPORT=true npm run build",
|
||||
"preview": "npm run build && vite preview --host",
|
||||
"type:check": "vue-tsc --noEmit --skipLibCheck",
|
||||
"lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx --fix",
|
||||
"lint-staged": "npx lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
|
@@ -1,12 +1,26 @@
|
||||
<template>
|
||||
<a-layout-footer class="footer">
|
||||
{{ `Copyright © 2022-${new Date().getFullYear()}` }}
|
||||
<a href="https://blog.charles7c.top/about/me" target="_blank" rel="noopenner noreferrer">Charles7c</a>
|
||||
<a
|
||||
href="https://blog.charles7c.top/about/me"
|
||||
target="_blank"
|
||||
rel="noopenner noreferrer"
|
||||
>Charles7c</a
|
||||
>
|
||||
<span> ⋅ </span>
|
||||
<a href="https://github.com/Charles7c/continew-admin" target="_blank" rel="noopenner noreferrer">{{ $t('title') }}</a>
|
||||
v1.0.0
|
||||
<a
|
||||
href="https://github.com/Charles7c/continew-admin"
|
||||
target="_blank"
|
||||
rel="noopenner noreferrer"
|
||||
>{{ $t('title') }}</a
|
||||
> v1.0.0
|
||||
<span> ⋅ </span>
|
||||
<a href="https://beian.miit.gov.cn" target="_blank" rel="noopenner noreferrer">津ICP备2022005864号-2</a>
|
||||
<a
|
||||
href="https://beian.miit.gov.cn"
|
||||
target="_blank"
|
||||
rel="noopenner noreferrer"
|
||||
>津ICP备2022005864号-2</a
|
||||
>
|
||||
</a-layout-footer>
|
||||
</template>
|
||||
|
||||
|
@@ -160,7 +160,10 @@
|
||||
:size="32"
|
||||
:style="{ marginRight: '8px', cursor: 'pointer' }"
|
||||
>
|
||||
<img alt="avatar" :src="getAvatar(loginStore.avatar, loginStore.gender)" />
|
||||
<img
|
||||
alt="avatar"
|
||||
:src="getAvatar(loginStore.avatar, loginStore.gender)"
|
||||
/>
|
||||
</a-avatar>
|
||||
<template #content>
|
||||
<a-doption>
|
||||
|
@@ -2,10 +2,14 @@
|
||||
<div class="header-operation-right">
|
||||
<a-button-group>
|
||||
<a-tooltip :content="showQuery ? '隐藏搜索栏' : '显示搜索栏'">
|
||||
<a-button @click="toggleSearch"><template #icon><icon-search /></template></a-button>
|
||||
<a-button @click="toggleSearch"
|
||||
><template #icon><icon-search /></template
|
||||
></a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip content="刷新">
|
||||
<a-button @click="handleRefresh"><template #icon><icon-refresh /></template></a-button>
|
||||
<a-button @click="handleRefresh"
|
||||
><template #icon><icon-refresh /></template
|
||||
></a-button>
|
||||
</a-tooltip>
|
||||
</a-button-group>
|
||||
</div>
|
||||
@@ -42,4 +46,4 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
<style scoped lang="less"></style>
|
||||
|
4
continew-admin-ui/src/hooks/axios.d.ts
vendored
4
continew-admin-ui/src/hooks/axios.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
import axios, { Axios, AxiosResponse, AxiosRequestConfig } from 'axios';
|
||||
|
||||
declare module "axios" {
|
||||
declare module 'axios' {
|
||||
interface AxiosResponse<T = any> {
|
||||
success: boolean; // 是否成功
|
||||
code: number; // 状态码
|
||||
@@ -9,4 +9,4 @@ declare module "axios" {
|
||||
data: T; // 返回数据
|
||||
}
|
||||
export function create(config?: AxiosRequestConfig): AxiosInstance;
|
||||
}
|
||||
}
|
||||
|
@@ -15,7 +15,8 @@ const LIST: AppRouteRecordRaw = {
|
||||
{
|
||||
path: 'search-table', // The midline path complies with SEO specifications
|
||||
name: 'SearchTable',
|
||||
component: () => import('@/views/arco-design/list/search-table/index.vue'),
|
||||
component: () =>
|
||||
import('@/views/arco-design/list/search-table/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.list.searchTable',
|
||||
requiresAuth: true,
|
||||
|
@@ -15,7 +15,8 @@ const VISUALIZATION: AppRouteRecordRaw = {
|
||||
{
|
||||
path: 'data-analysis',
|
||||
name: 'DataAnalysis',
|
||||
component: () => import('@/views/arco-design/visualization/data-analysis/index.vue'),
|
||||
component: () =>
|
||||
import('@/views/arco-design/visualization/data-analysis/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.visualization.dataAnalysis',
|
||||
requiresAuth: true,
|
||||
@@ -26,7 +27,9 @@ const VISUALIZATION: AppRouteRecordRaw = {
|
||||
path: 'multi-dimension-data-analysis',
|
||||
name: 'MultiDimensionDataAnalysis',
|
||||
component: () =>
|
||||
import('@/views/arco-design/visualization/multi-dimension-data-analysis/index.vue'),
|
||||
import(
|
||||
'@/views/arco-design/visualization/multi-dimension-data-analysis/index.vue'
|
||||
),
|
||||
meta: {
|
||||
locale: 'menu.visualization.multiDimensionDataAnalysis',
|
||||
requiresAuth: true,
|
||||
@@ -36,7 +39,8 @@ const VISUALIZATION: AppRouteRecordRaw = {
|
||||
{
|
||||
path: 'monitor',
|
||||
name: 'Monitor',
|
||||
component: () => import('@/views/arco-design/visualization/monitor/index.vue'),
|
||||
component: () =>
|
||||
import('@/views/arco-design/visualization/monitor/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.dashboard.monitor',
|
||||
requiresAuth: true,
|
||||
|
@@ -6,7 +6,7 @@ const useDictStore = defineStore('dict', {
|
||||
actions: {
|
||||
// 获取字典
|
||||
getDict(_name: string) {
|
||||
if (_name === null && _name === '') {
|
||||
if (_name == null || _name === '') {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
|
@@ -2,7 +2,10 @@ import Unknown from '../assets/images/avatar/unknown.png';
|
||||
import Male from '../assets/images/avatar/male.png';
|
||||
import Female from '../assets/images/avatar/female.png';
|
||||
|
||||
export default function getAvatar(avatar: string | undefined, gender: number | undefined) {
|
||||
export default function getAvatar(
|
||||
avatar: string | undefined,
|
||||
gender: number | undefined
|
||||
) {
|
||||
if (avatar) {
|
||||
const baseUrl = import.meta.env.VITE_API_BASE_URL;
|
||||
return `${baseUrl}/avatar/${avatar}`;
|
||||
|
@@ -167,7 +167,9 @@
|
||||
<div>
|
||||
<a-checkbox
|
||||
v-model="item.checked"
|
||||
@change="handleChange($event, item as TableColumnData, index)"
|
||||
@change="
|
||||
handleChange($event, item as TableColumnData, index)
|
||||
"
|
||||
>
|
||||
</a-checkbox>
|
||||
</div>
|
||||
|
@@ -55,12 +55,7 @@
|
||||
{{ $t('login.form.rememberMe') }}
|
||||
</a-checkbox>
|
||||
</div>
|
||||
<a-button
|
||||
:loading="loading"
|
||||
type="primary"
|
||||
long
|
||||
html-type="submit"
|
||||
>
|
||||
<a-button :loading="loading" type="primary" long html-type="submit">
|
||||
{{ $t('login.form.login') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
|
@@ -69,4 +69,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@@ -1,6 +1,7 @@
|
||||
export default {
|
||||
'login.form.title': 'Login to ContiNew Admin',
|
||||
'login.form.subTitle': 'Continue to build the latest popular technology stack in the background management framework',
|
||||
'login.form.subTitle':
|
||||
'Continue to build the latest popular technology stack in the background management framework',
|
||||
|
||||
'login.form.placeholder.username': 'Please enter username',
|
||||
'login.form.placeholder.password': 'Please enter password',
|
||||
@@ -27,5 +28,6 @@ export default {
|
||||
'login.banner.subSlogan2':
|
||||
'Rich basic functions, low threshold to use, enterprise rapid development scaffolding',
|
||||
'login.banner.slogan3': 'The code is standard and open source and free',
|
||||
'login.banner.subSlogan3': 'The backend code is fully compliant with the Alibaba coding specification, and the frontend code is strictly ESLint checked',
|
||||
'login.banner.subSlogan3':
|
||||
'The backend code is fully compliant with the Alibaba coding specification, and the frontend code is strictly ESLint checked',
|
||||
};
|
||||
|
@@ -21,9 +21,11 @@ export default {
|
||||
'login.form.logout.success': '退出成功',
|
||||
|
||||
'login.banner.slogan1': '中后台管理框架',
|
||||
'login.banner.subSlogan1': 'Continue New Admin,持续以最新流行技术栈构建,拥抱变化,迭代优化',
|
||||
'login.banner.subSlogan1':
|
||||
'Continue New Admin,持续以最新流行技术栈构建,拥抱变化,迭代优化',
|
||||
'login.banner.slogan2': '内置了常见问题的解决方案',
|
||||
'login.banner.subSlogan2': '基础功能丰富,使用门槛低,企业级快速开发脚手架',
|
||||
'login.banner.slogan3': '代码规范且开源免费',
|
||||
'login.banner.subSlogan3': '后端代码完全遵循阿里巴巴编码规范,前端代码使用严格的 ESLint 检查',
|
||||
'login.banner.subSlogan3':
|
||||
'后端代码完全遵循阿里巴巴编码规范,前端代码使用严格的 ESLint 检查',
|
||||
};
|
||||
|
@@ -61,7 +61,9 @@
|
||||
<a-table-column title="登录行为" data-index="description" />
|
||||
<a-table-column title="登录状态" align="center">
|
||||
<template #cell="{ record }">
|
||||
<a-tag v-if="record.status === 1" color="green"><span class="circle pass" />成功</a-tag>
|
||||
<a-tag v-if="record.status === 1" color="green"
|
||||
><span class="circle pass" />成功</a-tag
|
||||
>
|
||||
<a-tooltip v-else :content="record.errorMsg">
|
||||
<a-tag color="red" style="cursor: pointer">
|
||||
<span class="circle fail" />失败
|
||||
@@ -88,7 +90,9 @@
|
||||
} from '@/api/monitor/log';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const { SuccessFailureStatusEnum } = proxy.useDict('SuccessFailureStatusEnum');
|
||||
const { SuccessFailureStatusEnum } = proxy.useDict(
|
||||
'SuccessFailureStatusEnum'
|
||||
);
|
||||
|
||||
const loginLogList = ref<LoginLogRecord[]>([]);
|
||||
const total = ref(0);
|
||||
|
@@ -72,7 +72,9 @@
|
||||
<a-table-column title="所属模块" data-index="module" />
|
||||
<a-table-column title="操作状态" align="center">
|
||||
<template #cell="{ record }">
|
||||
<a-tag v-if="record.status === 1" color="green"><span class="circle pass" />成功</a-tag>
|
||||
<a-tag v-if="record.status === 1" color="green"
|
||||
><span class="circle pass" />成功</a-tag
|
||||
>
|
||||
<a-tooltip v-else :content="record.errorMsg">
|
||||
<a-tag color="red" style="cursor: pointer">
|
||||
<span class="circle fail" />失败
|
||||
@@ -98,7 +100,9 @@
|
||||
} from '@/api/monitor/log';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const { SuccessFailureStatusEnum } = proxy.useDict('SuccessFailureStatusEnum');
|
||||
const { SuccessFailureStatusEnum } = proxy.useDict(
|
||||
'SuccessFailureStatusEnum'
|
||||
);
|
||||
|
||||
const operationLogList = ref<OperationLogRecord[]>([]);
|
||||
const total = ref(0);
|
||||
|
@@ -50,15 +50,27 @@
|
||||
</a-table-column>
|
||||
<a-table-column title="状态码" align="center">
|
||||
<template #cell="{ record }">
|
||||
<a-tag v-if="record.statusCode >= 400" color="red">{{ record.statusCode }}</a-tag>
|
||||
<a-tag v-else-if="record.statusCode === 200" color="green">{{ record.statusCode }}</a-tag>
|
||||
<a-tag v-if="record.statusCode >= 400" color="red">{{
|
||||
record.statusCode
|
||||
}}</a-tag>
|
||||
<a-tag v-else-if="record.statusCode === 200" color="green">{{
|
||||
record.statusCode
|
||||
}}</a-tag>
|
||||
<a-tag v-else color="orange">{{ record.statusCode }}</a-tag>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="请求方式" align="center" data-index="requestMethod" />
|
||||
<a-table-column
|
||||
title="请求方式"
|
||||
align="center"
|
||||
data-index="requestMethod"
|
||||
/>
|
||||
<a-table-column title="请求 URI">
|
||||
<template #cell="{ record }">
|
||||
<span :title="decodeURIComponent(record.requestUrl)">{{ record.requestUrl.match(/(\w+):\/\/([^/:]+)(:\d*)?([^#|\?|\n]*)(\?.*)?/)[4] }}</span>
|
||||
<span :title="decodeURIComponent(record.requestUrl)">{{
|
||||
record.requestUrl.match(
|
||||
/(\w+):\/\/([^/:]+)(:\d*)?([^#|\?|\n]*)(\?.*)?/
|
||||
)[4]
|
||||
}}</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="客户端 IP" data-index="clientIp" />
|
||||
@@ -66,18 +78,33 @@
|
||||
<a-table-column title="浏览器" data-index="browser" />
|
||||
<a-table-column title="请求耗时">
|
||||
<template #cell="{ record }">
|
||||
<a-tag v-if="record.elapsedTime > 500" color="red">{{ record.elapsedTime }} ms</a-tag>
|
||||
<a-tag v-else-if="record.elapsedTime > 200" color="orange">{{ record.elapsedTime }} ms</a-tag>
|
||||
<a-tag v-if="record.elapsedTime > 500" color="red"
|
||||
>{{ record.elapsedTime }} ms</a-tag
|
||||
>
|
||||
<a-tag v-else-if="record.elapsedTime > 200" color="orange"
|
||||
>{{ record.elapsedTime }} ms</a-tag
|
||||
>
|
||||
<a-tag v-else color="green">{{ record.elapsedTime }} ms</a-tag>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="创建时间" data-index="createTime" />
|
||||
<a-table-column title="操作" align="center">
|
||||
<template #cell="{ record }">
|
||||
<a-button type="text" size="small" title="查看详情" @click="toDetail(record.id)">
|
||||
<a-button
|
||||
type="text"
|
||||
size="small"
|
||||
title="查看详情"
|
||||
@click="toDetail(record.id)"
|
||||
>
|
||||
<template #icon><icon-eye /></template>详情
|
||||
</a-button>
|
||||
<a-button v-if="record.exceptionDetail" type="text" size="small" title="查看异常详情" @click="toExceptionDetail(record)">
|
||||
<a-button
|
||||
v-if="record.exceptionDetail"
|
||||
type="text"
|
||||
size="small"
|
||||
title="查看异常详情"
|
||||
@click="toExceptionDetail(record)"
|
||||
>
|
||||
<template #icon><icon-bug /></template>异常
|
||||
</a-button>
|
||||
</template>
|
||||
@@ -126,7 +153,9 @@
|
||||
<a-tag v-else-if="systemLog.elapsedTime > 200" color="orange">
|
||||
{{ systemLog.elapsedTime }} ms
|
||||
</a-tag>
|
||||
<a-tag v-else color="green">{{ systemLog.elapsedTime }} ms</a-tag>
|
||||
<a-tag v-else color="green"
|
||||
>{{ systemLog.elapsedTime }} ms</a-tag
|
||||
>
|
||||
</span>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="创建时间">
|
||||
@@ -136,14 +165,23 @@
|
||||
<span v-else>{{ systemLog.createTime }}</span>
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
<a-descriptions title="协议信息" :column="2" bordered style="margin-top: 25px">
|
||||
<a-descriptions
|
||||
title="协议信息"
|
||||
:column="2"
|
||||
bordered
|
||||
style="margin-top: 25px"
|
||||
>
|
||||
<a-descriptions-item label="状态码">
|
||||
<a-skeleton v-if="loading" :animation="true">
|
||||
<a-skeleton-line :rows="1" />
|
||||
</a-skeleton>
|
||||
<span v-else>
|
||||
<a-tag v-if="systemLog.statusCode >= 400" color="red">{{ systemLog.statusCode }}</a-tag>
|
||||
<a-tag v-else-if="systemLog.statusCode === 200" color="green">{{ systemLog.statusCode }}</a-tag>
|
||||
<a-tag v-if="systemLog.statusCode >= 400" color="red">{{
|
||||
systemLog.statusCode
|
||||
}}</a-tag>
|
||||
<a-tag v-else-if="systemLog.statusCode === 200" color="green">{{
|
||||
systemLog.statusCode
|
||||
}}</a-tag>
|
||||
<a-tag v-else color="orange">{{ systemLog.statusCode }}</a-tag>
|
||||
</span>
|
||||
</a-descriptions-item>
|
||||
@@ -168,7 +206,8 @@
|
||||
v-if="systemLog.responseBody"
|
||||
:path="'res'"
|
||||
:data="JSON.parse(systemLog.responseBody)"
|
||||
:show-length="true" />
|
||||
:show-length="true"
|
||||
/>
|
||||
<span v-else>无</span>
|
||||
</a-space>
|
||||
</a-descriptions-item>
|
||||
@@ -181,7 +220,8 @@
|
||||
v-if="systemLog.responseHeaders"
|
||||
:path="'res'"
|
||||
:data="JSON.parse(systemLog.responseHeaders)"
|
||||
:show-length="true" />
|
||||
:show-length="true"
|
||||
/>
|
||||
<span v-else>无</span>
|
||||
</a-space>
|
||||
</a-descriptions-item>
|
||||
@@ -194,7 +234,8 @@
|
||||
v-if="systemLog.requestBody"
|
||||
:path="'res'"
|
||||
:data="JSON.parse(systemLog.requestBody)"
|
||||
:show-length="true" />
|
||||
:show-length="true"
|
||||
/>
|
||||
<span v-else>无</span>
|
||||
</a-space>
|
||||
</a-descriptions-item>
|
||||
@@ -206,7 +247,8 @@
|
||||
<VueJsonPretty
|
||||
v-if="systemLog.requestHeaders"
|
||||
:data="JSON.parse(systemLog.requestHeaders)"
|
||||
:show-length="true" />
|
||||
:show-length="true"
|
||||
/>
|
||||
<span v-else>无</span>
|
||||
</a-space>
|
||||
</a-descriptions-item>
|
||||
|
@@ -12,7 +12,7 @@
|
||||
v-model="queryParams.nickname"
|
||||
placeholder="输入用户昵称搜索"
|
||||
allow-clear
|
||||
style="width: 150px;"
|
||||
style="width: 150px"
|
||||
@press-enter="handleQuery"
|
||||
/>
|
||||
</a-form-item>
|
||||
@@ -59,22 +59,34 @@
|
||||
</a-table-column>
|
||||
<a-table-column title="用户昵称">
|
||||
<template #cell="{ record }">
|
||||
{{ record.nickname }}({{record.username}})
|
||||
{{ record.nickname }}({{ record.username }})
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="登录 IP" data-index="clientIp" />
|
||||
<a-table-column title="登录地点" data-index="location" />
|
||||
<a-table-column title="浏览器" data-index="browser" />
|
||||
<a-table-column title="登录时间" data-index="loginTime" />
|
||||
<a-table-column v-if="checkPermission(['monitor:online:user:delete'])" title="操作" align="center">
|
||||
<a-table-column
|
||||
v-if="checkPermission(['monitor:online:user:delete'])"
|
||||
title="操作"
|
||||
align="center"
|
||||
>
|
||||
<template #cell="{ record }">
|
||||
<a-popconfirm content="确定要强退该用户吗?" type="warning" @ok="handleKickout(record.token)">
|
||||
<a-popconfirm
|
||||
content="确定要强退该用户吗?"
|
||||
type="warning"
|
||||
@ok="handleKickout(record.token)"
|
||||
>
|
||||
<a-button
|
||||
v-permission="['monitor:online:user:delete']"
|
||||
type="text"
|
||||
size="small"
|
||||
:disabled="currentToken === record.token"
|
||||
:title="currentToken === record.token ? '不能强退当前登录用户' : '强退'"
|
||||
:title="
|
||||
currentToken === record.token
|
||||
? '不能强退当前登录用户'
|
||||
: '强退'
|
||||
"
|
||||
>
|
||||
<template #icon><icon-delete /></template>强退
|
||||
</a-button>
|
||||
@@ -124,12 +136,14 @@
|
||||
*/
|
||||
const getList = (params: OnlineUserParam = { ...queryParams.value }) => {
|
||||
loading.value = true;
|
||||
listOnlineUser(params).then((res) => {
|
||||
onlineUserList.value = res.data.list;
|
||||
total.value = res.data.total;
|
||||
}).finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
listOnlineUser(params)
|
||||
.then((res) => {
|
||||
onlineUserList.value = res.data.list;
|
||||
total.value = res.data.total;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
getList();
|
||||
|
||||
|
@@ -113,23 +113,19 @@
|
||||
<template #columns>
|
||||
<a-table-column title="部门名称">
|
||||
<template #cell="{ record }">
|
||||
<a-link @click="toDetail(record.id)">{{
|
||||
record.name
|
||||
}}</a-link>
|
||||
<a-link @click="toDetail(record.id)">{{ record.name }}</a-link>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column
|
||||
title="部门排序"
|
||||
align="center"
|
||||
data-index="sort"
|
||||
/>
|
||||
<a-table-column title="部门排序" align="center" data-index="sort" />
|
||||
<a-table-column title="状态" align="center" data-index="status">
|
||||
<template #cell="{ record }">
|
||||
<a-switch
|
||||
v-model="record.status"
|
||||
:checked-value="1"
|
||||
:unchecked-value="2"
|
||||
:disabled="record.disabled || !checkPermission(['system:dept:update'])"
|
||||
:disabled="
|
||||
record.disabled || !checkPermission(['system:dept:update'])
|
||||
"
|
||||
@change="handleChangeStatus(record)"
|
||||
/>
|
||||
</template>
|
||||
@@ -188,7 +184,11 @@
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form ref="formRef" :model="form" :rules="rules" size="large">
|
||||
<a-form-item label="上级部门" field="parentId" :disabled="form.disabled">
|
||||
<a-form-item
|
||||
label="上级部门"
|
||||
field="parentId"
|
||||
:disabled="form.disabled"
|
||||
>
|
||||
<a-tree-select
|
||||
v-model="form.parentId"
|
||||
:data="treeData"
|
||||
@@ -527,7 +527,10 @@
|
||||
}
|
||||
} else if (record.children) {
|
||||
record.children.forEach((r) => {
|
||||
rowKeys.splice(rowKeys.findIndex((key: number | undefined) => key === r.id), 1);
|
||||
rowKeys.splice(
|
||||
rowKeys.findIndex((key: number | undefined) => key === r.id),
|
||||
1
|
||||
);
|
||||
proxy.$refs.tableRef.select(r.id, false);
|
||||
if (r.children) {
|
||||
handleSelect(rowKeys, rowKey, r);
|
||||
|
@@ -307,21 +307,13 @@
|
||||
<a-radio :value="false">否</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="form.type === 2"
|
||||
label="是否缓存"
|
||||
field="isCache"
|
||||
>
|
||||
<a-form-item v-if="form.type === 2" label="是否缓存" field="isCache">
|
||||
<a-radio-group v-model="form.isCache" type="button">
|
||||
<a-radio :value="true">是</a-radio>
|
||||
<a-radio :value="false">否</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="form.type !== 3"
|
||||
label="是否隐藏"
|
||||
field="isHidden"
|
||||
>
|
||||
<a-form-item v-if="form.type !== 3" label="是否隐藏" field="isHidden">
|
||||
<a-radio-group v-model="form.isHidden" type="button">
|
||||
<a-radio :value="true">是</a-radio>
|
||||
<a-radio :value="false">否</a-radio>
|
||||
|
@@ -118,33 +118,31 @@
|
||||
<a-table-column title="ID" data-index="id" />
|
||||
<a-table-column title="角色名称" data-index="name" :width="130">
|
||||
<template #cell="{ record }">
|
||||
<a-link @click="toDetail(record.id)">{{
|
||||
record.name
|
||||
}}</a-link>
|
||||
<a-link @click="toDetail(record.id)">{{ record.name }}</a-link>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="角色编码" data-index="code" />
|
||||
<a-table-column title="数据权限" :width="130">
|
||||
<template #cell="{ record }">
|
||||
<span v-if="record.dataScope === 1">全部数据权限</span>
|
||||
<span v-else-if="record.dataScope === 2">本部门及以下数据权限</span>
|
||||
<span v-else-if="record.dataScope === 2"
|
||||
>本部门及以下数据权限</span
|
||||
>
|
||||
<span v-else-if="record.dataScope === 3">本部门数据权限</span>
|
||||
<span v-else-if="record.dataScope === 4">仅本人数据权限</span>
|
||||
<span v-else>自定义数据权限</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column
|
||||
title="角色排序"
|
||||
align="center"
|
||||
data-index="sort"
|
||||
/>
|
||||
<a-table-column title="角色排序" align="center" data-index="sort" />
|
||||
<a-table-column title="状态" align="center" data-index="status">
|
||||
<template #cell="{ record }">
|
||||
<a-switch
|
||||
v-model="record.status"
|
||||
:checked-value="1"
|
||||
:unchecked-value="2"
|
||||
:disabled="record.disabled || !checkPermission(['system:role:update'])"
|
||||
:disabled="
|
||||
record.disabled || !checkPermission(['system:role:update'])
|
||||
"
|
||||
@change="handleChangeStatus(record)"
|
||||
/>
|
||||
</template>
|
||||
@@ -204,7 +202,11 @@
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form ref="formRef" :model="form" :rules="rules" size="large">
|
||||
<a-alert v-if="!form.disabled" type="warning" style="margin-bottom: 15px;">
|
||||
<a-alert
|
||||
v-if="!form.disabled"
|
||||
type="warning"
|
||||
style="margin-bottom: 15px"
|
||||
>
|
||||
变更角色编码、功能权限或数据权限后,关联在线用户会自动下线!
|
||||
</a-alert>
|
||||
<fieldset>
|
||||
@@ -212,7 +214,11 @@
|
||||
<a-form-item label="角色名称" field="name">
|
||||
<a-input v-model="form.name" placeholder="请输入角色名称" />
|
||||
</a-form-item>
|
||||
<a-form-item label="角色编码" field="code" :disabled="form.disabled">
|
||||
<a-form-item
|
||||
label="角色编码"
|
||||
field="code"
|
||||
:disabled="form.disabled"
|
||||
>
|
||||
<a-input v-model="form.code" placeholder="请输入角色编码" />
|
||||
</a-form-item>
|
||||
<a-form-item label="角色排序" field="sort">
|
||||
@@ -239,8 +245,16 @@
|
||||
<legend>功能权限</legend>
|
||||
<a-form-item label="功能权限" :disabled="form.disabled">
|
||||
<a-space style="margin-top: 2px">
|
||||
<a-checkbox v-model="menuExpandAll" @change="handleExpandAll('menu')">展开/折叠</a-checkbox>
|
||||
<a-checkbox v-model="menuCheckAll" @change="handleCheckAll('menu')">全选/全不选</a-checkbox>
|
||||
<a-checkbox
|
||||
v-model="menuExpandAll"
|
||||
@change="handleExpandAll('menu')"
|
||||
>展开/折叠</a-checkbox
|
||||
>
|
||||
<a-checkbox
|
||||
v-model="menuCheckAll"
|
||||
@change="handleCheckAll('menu')"
|
||||
>全选/全不选</a-checkbox
|
||||
>
|
||||
<a-checkbox v-model="menuCheckStrictly">父子联动</a-checkbox>
|
||||
</a-space>
|
||||
<template #extra>
|
||||
@@ -259,17 +273,33 @@
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>数据权限</legend>
|
||||
<a-form-item label="数据权限" field="dataScope" :disabled="form.disabled">
|
||||
<a-form-item
|
||||
label="数据权限"
|
||||
field="dataScope"
|
||||
:disabled="form.disabled"
|
||||
>
|
||||
<a-select
|
||||
v-model="form.dataScope"
|
||||
:options="DataScopeEnum"
|
||||
placeholder="请选择数据权限"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="form.dataScope === 5" label="权限范围" :disabled="form.disabled">
|
||||
<a-form-item
|
||||
v-if="form.dataScope === 5"
|
||||
label="权限范围"
|
||||
:disabled="form.disabled"
|
||||
>
|
||||
<a-space style="margin-top: 2px">
|
||||
<a-checkbox v-model="deptExpandAll" @change="handleExpandAll('dept')">展开/折叠</a-checkbox>
|
||||
<a-checkbox v-model="deptCheckAll" @change="handleCheckAll('dept')">全选/全不选</a-checkbox>
|
||||
<a-checkbox
|
||||
v-model="deptExpandAll"
|
||||
@change="handleExpandAll('dept')"
|
||||
>展开/折叠</a-checkbox
|
||||
>
|
||||
<a-checkbox
|
||||
v-model="deptCheckAll"
|
||||
@change="handleCheckAll('dept')"
|
||||
>全选/全不选</a-checkbox
|
||||
>
|
||||
<a-checkbox v-model="deptCheckStrictly">父子联动</a-checkbox>
|
||||
</a-space>
|
||||
<template #extra>
|
||||
@@ -328,7 +358,9 @@
|
||||
</a-skeleton>
|
||||
<span v-else>
|
||||
<span v-if="role.dataScope === 1">全部数据权限</span>
|
||||
<span v-else-if="role.dataScope === 2">本部门及以下数据权限</span>
|
||||
<span v-else-if="role.dataScope === 2"
|
||||
>本部门及以下数据权限</span
|
||||
>
|
||||
<span v-else-if="role.dataScope === 3">本部门数据权限</span>
|
||||
<span v-else-if="role.dataScope === 4">仅本人数据权限</span>
|
||||
<span v-else>自定义数据权限</span>
|
||||
@@ -464,7 +496,21 @@
|
||||
form: {} as RoleRecord,
|
||||
// 表单验证规则
|
||||
rules: {
|
||||
name: [{ required: true, message: '请输入角色名称' }],
|
||||
name: [
|
||||
{ required: true, message: '请输入角色名称' },
|
||||
{
|
||||
match: /^[\u4e00-\u9fa5a-zA-Z0-9_-]{1,20}$/,
|
||||
message:
|
||||
'长度为 1 到 20 位,可以包含中文、字母、数字、下划线,短横线',
|
||||
},
|
||||
],
|
||||
code: [
|
||||
{ required: true, message: '请输入角色编码' },
|
||||
{
|
||||
match: /^[a-zA-Z][a-zA-Z0-9_]{1,15}$/,
|
||||
message: '长度为 2 到 16 位,可以包含字母、数字,下划线,以字母开头',
|
||||
},
|
||||
],
|
||||
dataScope: [{ required: true, message: '请选择数据权限' }],
|
||||
sort: [{ required: true, message: '请输入角色排序' }],
|
||||
},
|
||||
|
@@ -8,21 +8,30 @@
|
||||
size="large"
|
||||
class="form"
|
||||
>
|
||||
<a-form-item :label="$t('userCenter.basicInfo.form.label.username')" disabled>
|
||||
<a-form-item
|
||||
:label="$t('userCenter.basicInfo.form.label.username')"
|
||||
disabled
|
||||
>
|
||||
<a-input
|
||||
v-model="form.username"
|
||||
:placeholder="$t('userCenter.basicInfo.form.placeholder.username')"
|
||||
max-length="50"
|
||||
max-length="16"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('userCenter.basicInfo.form.label.nickname')" field="nickname">
|
||||
<a-form-item
|
||||
:label="$t('userCenter.basicInfo.form.label.nickname')"
|
||||
field="nickname"
|
||||
>
|
||||
<a-input
|
||||
v-model="form.nickname"
|
||||
:placeholder="$t('userCenter.basicInfo.form.placeholder.nickname')"
|
||||
max-length="32"
|
||||
max-length="20"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('userCenter.basicInfo.form.label.gender')" field="gender">
|
||||
<a-form-item
|
||||
:label="$t('userCenter.basicInfo.form.label.gender')"
|
||||
field="gender"
|
||||
>
|
||||
<a-radio-group v-model="form.gender">
|
||||
<a-radio :value="1">男</a-radio>
|
||||
<a-radio :value="2">女</a-radio>
|
||||
@@ -70,12 +79,20 @@
|
||||
required: true,
|
||||
message: t('userCenter.basicInfo.form.error.required.username'),
|
||||
},
|
||||
{
|
||||
match: /^[a-zA-Z][a-zA-Z0-9_]{3,15}$/,
|
||||
message: t('userCenter.basicInfo.form.error.match.username'),
|
||||
},
|
||||
],
|
||||
nickname: [
|
||||
{
|
||||
required: true,
|
||||
message: t('userCenter.basicInfo.form.error.required.nickname'),
|
||||
},
|
||||
{
|
||||
match: /^[\u4e00-\u9fa5a-zA-Z0-9_-]{1,20}$/,
|
||||
message: t('userCenter.basicInfo.form.error.match.nickname'),
|
||||
},
|
||||
],
|
||||
};
|
||||
}),
|
||||
|
@@ -29,7 +29,9 @@
|
||||
<a-table-column title="所属模块" data-index="module" />
|
||||
<a-table-column title="操作状态" align="center">
|
||||
<template #cell="{ record }">
|
||||
<a-tag v-if="record.status === 1" color="green"><span class="circle pass" />成功</a-tag>
|
||||
<a-tag v-if="record.status === 1" color="green"
|
||||
><span class="circle pass" />成功</a-tag
|
||||
>
|
||||
<a-tooltip v-else :content="record.errorMsg">
|
||||
<a-tag color="red" style="cursor: pointer">
|
||||
<span class="circle fail" />失败
|
||||
|
@@ -8,14 +8,25 @@
|
||||
<template #description>
|
||||
<div class="content">
|
||||
<a-typography-paragraph v-if="loginStore.email">
|
||||
{{ $t('userCenter.securitySettings.updateEmail.placeholder.success.email') }}:{{ loginStore.email }}
|
||||
{{
|
||||
$t(
|
||||
'userCenter.securitySettings.updateEmail.placeholder.success.email'
|
||||
)
|
||||
}}:{{ loginStore.email }}
|
||||
</a-typography-paragraph>
|
||||
<a-typography-paragraph v-else class="tip">
|
||||
{{ $t('userCenter.securitySettings.updateEmail.placeholder.error.email') }}
|
||||
{{
|
||||
$t(
|
||||
'userCenter.securitySettings.updateEmail.placeholder.error.email'
|
||||
)
|
||||
}}
|
||||
</a-typography-paragraph>
|
||||
</div>
|
||||
<div class="operation">
|
||||
<a-link :title="$t('userCenter.securitySettings.button.update')" @click="toUpdate">
|
||||
<a-link
|
||||
:title="$t('userCenter.securitySettings.button.update')"
|
||||
@click="toUpdate"
|
||||
>
|
||||
{{ $t('userCenter.securitySettings.button.update') }}
|
||||
</a-link>
|
||||
</div>
|
||||
@@ -30,17 +41,35 @@
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form ref="formRef" :model="form" :rules="rules" size="large">
|
||||
<a-form-item :label="$t('userCenter.securitySettings.updateEmail.form.label.newEmail')" field="newEmail">
|
||||
<a-form-item
|
||||
:label="
|
||||
$t('userCenter.securitySettings.updateEmail.form.label.newEmail')
|
||||
"
|
||||
field="newEmail"
|
||||
>
|
||||
<a-input
|
||||
v-model="form.newEmail"
|
||||
:placeholder="$t('userCenter.securitySettings.updateEmail.form.placeholder.newEmail')"
|
||||
:placeholder="
|
||||
$t(
|
||||
'userCenter.securitySettings.updateEmail.form.placeholder.newEmail'
|
||||
)
|
||||
"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('userCenter.securitySettings.updateEmail.form.label.captcha')" field="captcha">
|
||||
<a-form-item
|
||||
:label="
|
||||
$t('userCenter.securitySettings.updateEmail.form.label.captcha')
|
||||
"
|
||||
field="captcha"
|
||||
>
|
||||
<a-input
|
||||
v-model="form.captcha"
|
||||
:placeholder="$t('userCenter.securitySettings.updateEmail.form.placeholder.captcha')"
|
||||
:placeholder="
|
||||
$t(
|
||||
'userCenter.securitySettings.updateEmail.form.placeholder.captcha'
|
||||
)
|
||||
"
|
||||
max-length="6"
|
||||
allow-clear
|
||||
style="width: 80%"
|
||||
@@ -55,10 +84,21 @@
|
||||
{{ captchaBtnName }}
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('userCenter.securitySettings.updateEmail.form.label.currentPassword')" field="currentPassword">
|
||||
<a-form-item
|
||||
:label="
|
||||
$t(
|
||||
'userCenter.securitySettings.updateEmail.form.label.currentPassword'
|
||||
)
|
||||
"
|
||||
field="currentPassword"
|
||||
>
|
||||
<a-input-password
|
||||
v-model="form.currentPassword"
|
||||
:placeholder="$t('userCenter.securitySettings.updateEmail.form.placeholder.currentPassword')"
|
||||
:placeholder="
|
||||
$t(
|
||||
'userCenter.securitySettings.updateEmail.form.placeholder.currentPassword'
|
||||
)
|
||||
"
|
||||
max-length="32"
|
||||
allow-clear
|
||||
/>
|
||||
@@ -85,7 +125,9 @@
|
||||
const captchaLoading = ref(false);
|
||||
const captchaDisable = ref(false);
|
||||
const visible = ref(false);
|
||||
const captchaBtnNameKey = ref('userCenter.securitySettings.updateEmail.form.sendCaptcha');
|
||||
const captchaBtnNameKey = ref(
|
||||
'userCenter.securitySettings.updateEmail.form.sendCaptcha'
|
||||
);
|
||||
const captchaBtnName = computed(() => t(captchaBtnNameKey.value));
|
||||
|
||||
// 表单数据
|
||||
@@ -98,24 +140,48 @@
|
||||
const rules = computed((): Record<string, FieldRule[]> => {
|
||||
return {
|
||||
newEmail: [
|
||||
{ required: true, message: t('userCenter.securitySettings.updateEmail.form.error.required.newEmail') },
|
||||
{ type: 'email', message: t('userCenter.securitySettings.updateEmail.form.error.match.newEmail') },
|
||||
{
|
||||
required: true,
|
||||
message: t(
|
||||
'userCenter.securitySettings.updateEmail.form.error.required.newEmail'
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'email',
|
||||
message: t(
|
||||
'userCenter.securitySettings.updateEmail.form.error.match.newEmail'
|
||||
),
|
||||
},
|
||||
{
|
||||
validator: (value, callback) => {
|
||||
if (value === loginStore.email) {
|
||||
callback(t('userCenter.securitySettings.updateEmail.form.error.validator.newEmail'))
|
||||
callback(
|
||||
t(
|
||||
'userCenter.securitySettings.updateEmail.form.error.validator.newEmail'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
callback()
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
captcha: [
|
||||
{ required: true, message: t('userCenter.securitySettings.updateEmail.form.error.required.captcha') }
|
||||
{
|
||||
required: true,
|
||||
message: t(
|
||||
'userCenter.securitySettings.updateEmail.form.error.required.captcha'
|
||||
),
|
||||
},
|
||||
],
|
||||
currentPassword: [
|
||||
{ required: true, message: t('userCenter.securitySettings.updateEmail.form.error.required.currentPassword') }
|
||||
]
|
||||
{
|
||||
required: true,
|
||||
message: t(
|
||||
'userCenter.securitySettings.updateEmail.form.error.required.currentPassword'
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
@@ -125,9 +191,10 @@
|
||||
const resetCaptcha = () => {
|
||||
window.clearInterval(captchaTimer.value);
|
||||
captchaTime.value = 60;
|
||||
captchaBtnNameKey.value = 'userCenter.securitySettings.updateEmail.form.sendCaptcha';
|
||||
captchaBtnNameKey.value =
|
||||
'userCenter.securitySettings.updateEmail.form.sendCaptcha';
|
||||
captchaDisable.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 发送验证码
|
||||
@@ -137,28 +204,37 @@
|
||||
proxy.$refs.formRef.validateField('newEmail', (valid: any) => {
|
||||
if (!valid) {
|
||||
captchaLoading.value = true;
|
||||
captchaBtnNameKey.value = 'userCenter.securitySettings.updateEmail.form.loading.sendCaptcha';
|
||||
captchaBtnNameKey.value =
|
||||
'userCenter.securitySettings.updateEmail.form.loading.sendCaptcha';
|
||||
getMailCaptcha({
|
||||
email: form.newEmail
|
||||
}).then((res) => {
|
||||
captchaLoading.value = false;
|
||||
captchaDisable.value = true;
|
||||
captchaBtnNameKey.value = `${t('userCenter.securitySettings.updateEmail.form.reSendCaptcha')}(${captchaTime.value -= 1}s)`;
|
||||
captchaTimer.value = window.setInterval(() => {
|
||||
captchaTime.value -= 1;
|
||||
captchaBtnNameKey.value = `${t('userCenter.securitySettings.updateEmail.form.reSendCaptcha')}(${captchaTime.value}s)`;
|
||||
if (captchaTime.value < 0) {
|
||||
window.clearInterval(captchaTimer.value);
|
||||
captchaTime.value = 60;
|
||||
captchaBtnNameKey.value = t('userCenter.securitySettings.updateEmail.form.reSendCaptcha');
|
||||
captchaDisable.value = false;
|
||||
}
|
||||
}, 1000);
|
||||
proxy.$message.success(res.msg);
|
||||
}).catch(() => {
|
||||
resetCaptcha();
|
||||
captchaLoading.value = false;
|
||||
});
|
||||
email: form.newEmail,
|
||||
})
|
||||
.then((res) => {
|
||||
captchaLoading.value = false;
|
||||
captchaDisable.value = true;
|
||||
captchaBtnNameKey.value = `${t(
|
||||
'userCenter.securitySettings.updateEmail.form.reSendCaptcha'
|
||||
)}(${(captchaTime.value -= 1)}s)`;
|
||||
captchaTimer.value = window.setInterval(() => {
|
||||
captchaTime.value -= 1;
|
||||
captchaBtnNameKey.value = `${t(
|
||||
'userCenter.securitySettings.updateEmail.form.reSendCaptcha'
|
||||
)}(${captchaTime.value}s)`;
|
||||
if (captchaTime.value < 0) {
|
||||
window.clearInterval(captchaTimer.value);
|
||||
captchaTime.value = 60;
|
||||
captchaBtnNameKey.value = t(
|
||||
'userCenter.securitySettings.updateEmail.form.reSendCaptcha'
|
||||
);
|
||||
captchaDisable.value = false;
|
||||
}
|
||||
}, 1000);
|
||||
proxy.$message.success(res.msg);
|
||||
})
|
||||
.catch(() => {
|
||||
resetCaptcha();
|
||||
captchaLoading.value = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@@ -8,10 +8,18 @@
|
||||
<template #description>
|
||||
<div class="content">
|
||||
<a-typography-paragraph v-if="loginStore.phone">
|
||||
{{ $t('userCenter.securitySettings.updatePhone.placeholder.success.phone') }}:{{ loginStore.phone }}
|
||||
{{
|
||||
$t(
|
||||
'userCenter.securitySettings.updatePhone.placeholder.success.phone'
|
||||
)
|
||||
}}:{{ loginStore.phone }}
|
||||
</a-typography-paragraph>
|
||||
<a-typography-paragraph v-else class="tip">
|
||||
{{ $t('userCenter.securitySettings.updatePhone.placeholder.error.phone') }}
|
||||
{{
|
||||
$t(
|
||||
'userCenter.securitySettings.updatePhone.placeholder.error.phone'
|
||||
)
|
||||
}}
|
||||
</a-typography-paragraph>
|
||||
</div>
|
||||
<div class="operation">
|
||||
|
@@ -8,14 +8,25 @@
|
||||
<template #description>
|
||||
<div class="content">
|
||||
<a-typography-paragraph v-if="loginStore.pwdResetTime">
|
||||
{{ $t('userCenter.securitySettings.updatePwd.placeholder.success.password') }}
|
||||
{{
|
||||
$t(
|
||||
'userCenter.securitySettings.updatePwd.placeholder.success.password'
|
||||
)
|
||||
}}
|
||||
</a-typography-paragraph>
|
||||
<a-typography-paragraph v-else class="tip">
|
||||
{{ $t('userCenter.securitySettings.updatePwd.placeholder.error.password') }}
|
||||
{{
|
||||
$t(
|
||||
'userCenter.securitySettings.updatePwd.placeholder.error.password'
|
||||
)
|
||||
}}
|
||||
</a-typography-paragraph>
|
||||
</div>
|
||||
<div class="operation">
|
||||
<a-link :title="$t('userCenter.securitySettings.button.update')" @click="toUpdate">
|
||||
<a-link
|
||||
:title="$t('userCenter.securitySettings.button.update')"
|
||||
@click="toUpdate"
|
||||
>
|
||||
{{ $t('userCenter.securitySettings.button.update') }}
|
||||
</a-link>
|
||||
</div>
|
||||
@@ -30,26 +41,53 @@
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form ref="formRef" :model="form" :rules="rules" size="large">
|
||||
<a-form-item :label="$t('userCenter.securitySettings.updatePwd.form.label.oldPassword')" field="oldPassword">
|
||||
<a-form-item
|
||||
:label="
|
||||
$t('userCenter.securitySettings.updatePwd.form.label.oldPassword')
|
||||
"
|
||||
field="oldPassword"
|
||||
>
|
||||
<a-input-password
|
||||
v-model="form.oldPassword"
|
||||
:placeholder="$t('userCenter.securitySettings.updatePwd.form.placeholder.oldPassword')"
|
||||
:placeholder="
|
||||
$t(
|
||||
'userCenter.securitySettings.updatePwd.form.placeholder.oldPassword'
|
||||
)
|
||||
"
|
||||
max-length="32"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('userCenter.securitySettings.updatePwd.form.label.newPassword')" field="newPassword">
|
||||
<a-form-item
|
||||
:label="
|
||||
$t('userCenter.securitySettings.updatePwd.form.label.newPassword')
|
||||
"
|
||||
field="newPassword"
|
||||
>
|
||||
<a-input-password
|
||||
v-model="form.newPassword"
|
||||
:placeholder="$t('userCenter.securitySettings.updatePwd.form.placeholder.newPassword')"
|
||||
:placeholder="
|
||||
$t(
|
||||
'userCenter.securitySettings.updatePwd.form.placeholder.newPassword'
|
||||
)
|
||||
"
|
||||
max-length="32"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('userCenter.securitySettings.updatePwd.form.label.rePassword')" field="rePassword">
|
||||
<a-form-item
|
||||
:label="
|
||||
$t('userCenter.securitySettings.updatePwd.form.label.rePassword')
|
||||
"
|
||||
field="rePassword"
|
||||
>
|
||||
<a-input-password
|
||||
v-model="form.rePassword"
|
||||
:placeholder="$t('userCenter.securitySettings.updatePwd.form.placeholder.rePassword')"
|
||||
:placeholder="
|
||||
$t(
|
||||
'userCenter.securitySettings.updatePwd.form.placeholder.rePassword'
|
||||
)
|
||||
"
|
||||
max-length="32"
|
||||
allow-clear
|
||||
/>
|
||||
@@ -81,31 +119,61 @@
|
||||
// 表单验证规则
|
||||
const rules = computed((): Record<string, FieldRule[]> => {
|
||||
return {
|
||||
oldPassword: [{ required: true, message: t('userCenter.securitySettings.updatePwd.form.error.required.oldPassword') }],
|
||||
oldPassword: [
|
||||
{
|
||||
required: true,
|
||||
message: t(
|
||||
'userCenter.securitySettings.updatePwd.form.error.required.oldPassword'
|
||||
),
|
||||
},
|
||||
],
|
||||
newPassword: [
|
||||
{ required: true, message: t('userCenter.securitySettings.updatePwd.form.error.required.newPassword') },
|
||||
{ match: /^(?=.*\d)(?=.*[a-z]).{6,32}$/, message: t('userCenter.securitySettings.updatePwd.form.error.match.newPassword') },
|
||||
{
|
||||
required: true,
|
||||
message: t(
|
||||
'userCenter.securitySettings.updatePwd.form.error.required.newPassword'
|
||||
),
|
||||
},
|
||||
{
|
||||
match: /^(?=.*\d)(?=.*[a-z]).{6,32}$/,
|
||||
message: t(
|
||||
'userCenter.securitySettings.updatePwd.form.error.match.newPassword'
|
||||
),
|
||||
},
|
||||
{
|
||||
validator: (value, callback) => {
|
||||
if (value === form.oldPassword) {
|
||||
callback(t('userCenter.securitySettings.updatePwd.form.error.validator.newPassword'))
|
||||
callback(
|
||||
t(
|
||||
'userCenter.securitySettings.updatePwd.form.error.validator.newPassword'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
callback()
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
rePassword: [
|
||||
{ required: true, message: t('userCenter.securitySettings.updatePwd.form.error.required.rePassword') },
|
||||
{
|
||||
required: true,
|
||||
message: t(
|
||||
'userCenter.securitySettings.updatePwd.form.error.required.rePassword'
|
||||
),
|
||||
},
|
||||
{
|
||||
validator: (value, callback) => {
|
||||
if (value !== form.newPassword) {
|
||||
callback(t('userCenter.securitySettings.updatePwd.form.error.validator.rePassword'))
|
||||
callback(
|
||||
t(
|
||||
'userCenter.securitySettings.updatePwd.form.error.validator.rePassword'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
callback()
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
@@ -25,7 +25,11 @@ export default {
|
||||
'userCenter.basicInfo.form.placeholder.nickname': 'Please enter nickname',
|
||||
|
||||
'userCenter.basicInfo.form.error.required.username': 'Please enter username',
|
||||
'userCenter.basicInfo.form.error.match.username':
|
||||
'Username are 4 to 16 characters long and can contain letters, numbers, underscores, and start with a letter',
|
||||
'userCenter.basicInfo.form.error.required.nickname': 'Please enter nickname',
|
||||
'userCenter.basicInfo.form.error.match.nickname':
|
||||
'Nickname are 1 to 20 digits long and can contain Chinese, letters, numbers, underscores, dashes',
|
||||
|
||||
'userCenter.basicInfo.form.save': 'Save',
|
||||
'userCenter.basicInfo.form.save.success': 'Save success',
|
||||
@@ -34,55 +38,81 @@ export default {
|
||||
// security-settings
|
||||
// update-pwd
|
||||
'userCenter.securitySettings.updatePwd.label.password': 'Login Password',
|
||||
'userCenter.securitySettings.updatePwd.placeholder.success.password': 'Has been set',
|
||||
'userCenter.securitySettings.updatePwd.placeholder.success.password':
|
||||
'Has been set',
|
||||
'userCenter.securitySettings.updatePwd.placeholder.error.password':
|
||||
'You have not set a password yet. The password must contain at least six letters, digits, and special characters except Spaces.',
|
||||
|
||||
'userCenter.securitySettings.updatePwd.modal.title': 'Update login password',
|
||||
'userCenter.securitySettings.updatePwd.form.label.oldPassword': 'Old password',
|
||||
'userCenter.securitySettings.updatePwd.form.label.newPassword': 'New password',
|
||||
'userCenter.securitySettings.updatePwd.form.label.rePassword': 'Confirm password',
|
||||
'userCenter.securitySettings.updatePwd.form.label.oldPassword':
|
||||
'Old password',
|
||||
'userCenter.securitySettings.updatePwd.form.label.newPassword':
|
||||
'New password',
|
||||
'userCenter.securitySettings.updatePwd.form.label.rePassword':
|
||||
'Confirm password',
|
||||
|
||||
'userCenter.securitySettings.updatePwd.form.placeholder.oldPassword': 'Please enter old password',
|
||||
'userCenter.securitySettings.updatePwd.form.placeholder.newPassword': 'Password contains 6 to 32 digits and letters',
|
||||
'userCenter.securitySettings.updatePwd.form.placeholder.rePassword': 'Please enter new password again',
|
||||
'userCenter.securitySettings.updatePwd.form.placeholder.oldPassword':
|
||||
'Please enter old password',
|
||||
'userCenter.securitySettings.updatePwd.form.placeholder.newPassword':
|
||||
'Password contains 6 to 32 digits and letters',
|
||||
'userCenter.securitySettings.updatePwd.form.placeholder.rePassword':
|
||||
'Please enter new password again',
|
||||
|
||||
'userCenter.securitySettings.updatePwd.form.error.required.oldPassword': 'Please enter old password',
|
||||
'userCenter.securitySettings.updatePwd.form.error.required.newPassword': 'Please enter new password',
|
||||
'userCenter.securitySettings.updatePwd.form.error.match.newPassword': 'Password contains 6 to 32 digits and letters',
|
||||
'userCenter.securitySettings.updatePwd.form.error.validator.newPassword': 'New password cannot be the same as the old password',
|
||||
'userCenter.securitySettings.updatePwd.form.error.required.rePassword': 'Please enter new password again',
|
||||
'userCenter.securitySettings.updatePwd.form.error.validator.rePassword': 'Two passwords are different',
|
||||
'userCenter.securitySettings.updatePwd.form.error.required.oldPassword':
|
||||
'Please enter old password',
|
||||
'userCenter.securitySettings.updatePwd.form.error.required.newPassword':
|
||||
'Please enter new password',
|
||||
'userCenter.securitySettings.updatePwd.form.error.match.newPassword':
|
||||
'Password contains 6 to 32 digits and letters',
|
||||
'userCenter.securitySettings.updatePwd.form.error.validator.newPassword':
|
||||
'New password cannot be the same as the old password',
|
||||
'userCenter.securitySettings.updatePwd.form.error.required.rePassword':
|
||||
'Please enter new password again',
|
||||
'userCenter.securitySettings.updatePwd.form.error.validator.rePassword':
|
||||
'Two passwords are different',
|
||||
|
||||
// update-phone
|
||||
'userCenter.securitySettings.updatePhone.label.phone': 'Phone',
|
||||
'userCenter.securitySettings.updatePhone.placeholder.success.phone': 'Has been bound',
|
||||
'userCenter.securitySettings.updatePhone.placeholder.success.phone':
|
||||
'Has been bound',
|
||||
'userCenter.securitySettings.updatePhone.placeholder.error.phone':
|
||||
'You have not set a phone yet. The phone binding can be used to retrieve passwords and receive notifications and SMS login.',
|
||||
|
||||
// update-email
|
||||
'userCenter.securitySettings.updateEmail.label.email': 'Email',
|
||||
'userCenter.securitySettings.updateEmail.placeholder.success.email': 'Has been bound',
|
||||
'userCenter.securitySettings.updateEmail.placeholder.success.email':
|
||||
'Has been bound',
|
||||
'userCenter.securitySettings.updateEmail.placeholder.error.email':
|
||||
'You have not set a mailbox yet. The mailbox binding can be used to retrieve passwords and receive notifications.',
|
||||
|
||||
'userCenter.securitySettings.updateEmail.modal.title': 'Update email',
|
||||
'userCenter.securitySettings.updateEmail.form.label.newEmail': 'New email',
|
||||
'userCenter.securitySettings.updateEmail.form.label.captcha': 'Captcha',
|
||||
'userCenter.securitySettings.updateEmail.form.label.currentPassword': 'Current password',
|
||||
'userCenter.securitySettings.updateEmail.form.label.currentPassword':
|
||||
'Current password',
|
||||
'userCenter.securitySettings.updateEmail.form.sendCaptcha': 'Send captcha',
|
||||
'userCenter.securitySettings.updateEmail.form.reSendCaptcha': 'Resend captcha',
|
||||
'userCenter.securitySettings.updateEmail.form.loading.sendCaptcha': 'Sending...',
|
||||
'userCenter.securitySettings.updateEmail.form.reSendCaptcha':
|
||||
'Resend captcha',
|
||||
'userCenter.securitySettings.updateEmail.form.loading.sendCaptcha':
|
||||
'Sending...',
|
||||
|
||||
'userCenter.securitySettings.updateEmail.form.placeholder.newEmail': 'Please enter new email',
|
||||
'userCenter.securitySettings.updateEmail.form.placeholder.captcha': 'Please enter email captcha',
|
||||
'userCenter.securitySettings.updateEmail.form.placeholder.currentPassword': 'Please enter current password',
|
||||
'userCenter.securitySettings.updateEmail.form.placeholder.newEmail':
|
||||
'Please enter new email',
|
||||
'userCenter.securitySettings.updateEmail.form.placeholder.captcha':
|
||||
'Please enter email captcha',
|
||||
'userCenter.securitySettings.updateEmail.form.placeholder.currentPassword':
|
||||
'Please enter current password',
|
||||
|
||||
'userCenter.securitySettings.updateEmail.form.error.required.newEmail': 'Please enter new email',
|
||||
'userCenter.securitySettings.updateEmail.form.error.match.newEmail': 'Please enter the correct email',
|
||||
'userCenter.securitySettings.updateEmail.form.error.validator.newEmail': 'New email cannot be the same as the old email',
|
||||
'userCenter.securitySettings.updateEmail.form.error.required.captcha': 'Please enter email captcha',
|
||||
'userCenter.securitySettings.updateEmail.form.error.required.currentPassword': 'Please enter current password',
|
||||
'userCenter.securitySettings.updateEmail.form.error.required.newEmail':
|
||||
'Please enter new email',
|
||||
'userCenter.securitySettings.updateEmail.form.error.match.newEmail':
|
||||
'Please enter the correct email',
|
||||
'userCenter.securitySettings.updateEmail.form.error.validator.newEmail':
|
||||
'New email cannot be the same as the old email',
|
||||
'userCenter.securitySettings.updateEmail.form.error.required.captcha':
|
||||
'Please enter email captcha',
|
||||
'userCenter.securitySettings.updateEmail.form.error.required.currentPassword':
|
||||
'Please enter current password',
|
||||
|
||||
'userCenter.securitySettings.button.update': 'Update',
|
||||
};
|
||||
|
@@ -25,7 +25,11 @@ export default {
|
||||
'userCenter.basicInfo.form.placeholder.nickname': '请输入昵称',
|
||||
|
||||
'userCenter.basicInfo.form.error.required.username': '请输入用户名',
|
||||
'userCenter.basicInfo.form.error.match.username':
|
||||
'长度为 4 到 16 位,可以包含字母、数字,下划线,以字母开头',
|
||||
'userCenter.basicInfo.form.error.required.nickname': '请输入昵称',
|
||||
'userCenter.basicInfo.form.error.match.nickname':
|
||||
'长度为 1 到 20 位,可以包含中文、字母、数字、下划线,短横线',
|
||||
|
||||
'userCenter.basicInfo.form.save': '保存',
|
||||
'userCenter.basicInfo.form.save.success': '保存成功',
|
||||
@@ -34,7 +38,8 @@ export default {
|
||||
// security-settings
|
||||
// update-pwd
|
||||
'userCenter.securitySettings.updatePwd.label.password': '登录密码',
|
||||
'userCenter.securitySettings.updatePwd.placeholder.success.password': '已设置',
|
||||
'userCenter.securitySettings.updatePwd.placeholder.success.password':
|
||||
'已设置',
|
||||
'userCenter.securitySettings.updatePwd.placeholder.error.password':
|
||||
'您暂未设置密码,密码至少6位字符,支持数字、字母和除空格外的特殊字符。',
|
||||
|
||||
@@ -43,16 +48,25 @@ export default {
|
||||
'userCenter.securitySettings.updatePwd.form.label.newPassword': '新密码',
|
||||
'userCenter.securitySettings.updatePwd.form.label.rePassword': '确认新密码',
|
||||
|
||||
'userCenter.securitySettings.updatePwd.form.placeholder.oldPassword': '请输入当前密码',
|
||||
'userCenter.securitySettings.updatePwd.form.placeholder.newPassword': '密码长度 6 到 32 位,同时包含数字和字母',
|
||||
'userCenter.securitySettings.updatePwd.form.placeholder.rePassword': '请再次输入新密码',
|
||||
'userCenter.securitySettings.updatePwd.form.placeholder.oldPassword':
|
||||
'请输入当前密码',
|
||||
'userCenter.securitySettings.updatePwd.form.placeholder.newPassword':
|
||||
'长度为 6 到 32 位,同时包含字母和数字',
|
||||
'userCenter.securitySettings.updatePwd.form.placeholder.rePassword':
|
||||
'请再次输入新密码',
|
||||
|
||||
'userCenter.securitySettings.updatePwd.form.error.required.oldPassword': '请输入当前密码',
|
||||
'userCenter.securitySettings.updatePwd.form.error.required.newPassword': '请输入新密码',
|
||||
'userCenter.securitySettings.updatePwd.form.error.match.newPassword': '密码长度 6 到 32 位,同时包含数字和字母',
|
||||
'userCenter.securitySettings.updatePwd.form.error.validator.newPassword': '新密码不能与当前密码相同',
|
||||
'userCenter.securitySettings.updatePwd.form.error.required.rePassword': '请再次输入新密码',
|
||||
'userCenter.securitySettings.updatePwd.form.error.validator.rePassword': '两次输入的密码不一致',
|
||||
'userCenter.securitySettings.updatePwd.form.error.required.oldPassword':
|
||||
'请输入当前密码',
|
||||
'userCenter.securitySettings.updatePwd.form.error.required.newPassword':
|
||||
'请输入新密码',
|
||||
'userCenter.securitySettings.updatePwd.form.error.match.newPassword':
|
||||
'长度为 6 到 32 位,同时包含字母和数字',
|
||||
'userCenter.securitySettings.updatePwd.form.error.validator.newPassword':
|
||||
'新密码不能与当前密码相同',
|
||||
'userCenter.securitySettings.updatePwd.form.error.required.rePassword':
|
||||
'请再次输入新密码',
|
||||
'userCenter.securitySettings.updatePwd.form.error.validator.rePassword':
|
||||
'两次输入的密码不一致',
|
||||
|
||||
// update-phone
|
||||
'userCenter.securitySettings.updatePhone.label.phone': '安全手机',
|
||||
@@ -69,20 +83,30 @@ export default {
|
||||
'userCenter.securitySettings.updateEmail.modal.title': '修改邮箱',
|
||||
'userCenter.securitySettings.updateEmail.form.label.newEmail': '新邮箱',
|
||||
'userCenter.securitySettings.updateEmail.form.label.captcha': '验证码',
|
||||
'userCenter.securitySettings.updateEmail.form.label.currentPassword': '当前密码',
|
||||
'userCenter.securitySettings.updateEmail.form.label.currentPassword':
|
||||
'当前密码',
|
||||
'userCenter.securitySettings.updateEmail.form.sendCaptcha': '发送验证码',
|
||||
'userCenter.securitySettings.updateEmail.form.reSendCaptcha': '重新发送',
|
||||
'userCenter.securitySettings.updateEmail.form.loading.sendCaptcha': '发送中...',
|
||||
'userCenter.securitySettings.updateEmail.form.loading.sendCaptcha':
|
||||
'发送中...',
|
||||
|
||||
'userCenter.securitySettings.updateEmail.form.placeholder.newEmail': '请输入新邮箱',
|
||||
'userCenter.securitySettings.updateEmail.form.placeholder.captcha': '请输入邮箱验证码',
|
||||
'userCenter.securitySettings.updateEmail.form.placeholder.currentPassword': '请输入当前密码',
|
||||
'userCenter.securitySettings.updateEmail.form.placeholder.newEmail':
|
||||
'请输入新邮箱',
|
||||
'userCenter.securitySettings.updateEmail.form.placeholder.captcha':
|
||||
'请输入邮箱验证码',
|
||||
'userCenter.securitySettings.updateEmail.form.placeholder.currentPassword':
|
||||
'请输入当前密码',
|
||||
|
||||
'userCenter.securitySettings.updateEmail.form.error.required.newEmail': '请输入新邮箱',
|
||||
'userCenter.securitySettings.updateEmail.form.error.match.newEmail': '请输入正确的邮箱',
|
||||
'userCenter.securitySettings.updateEmail.form.error.validator.newEmail': '新邮箱不能与当前邮箱相同',
|
||||
'userCenter.securitySettings.updateEmail.form.error.required.captcha': '请输入邮箱验证码',
|
||||
'userCenter.securitySettings.updateEmail.form.error.required.currentPassword': '请输入当前密码',
|
||||
'userCenter.securitySettings.updateEmail.form.error.required.newEmail':
|
||||
'请输入新邮箱',
|
||||
'userCenter.securitySettings.updateEmail.form.error.match.newEmail':
|
||||
'请输入正确的邮箱',
|
||||
'userCenter.securitySettings.updateEmail.form.error.validator.newEmail':
|
||||
'新邮箱不能与当前邮箱相同',
|
||||
'userCenter.securitySettings.updateEmail.form.error.required.captcha':
|
||||
'请输入邮箱验证码',
|
||||
'userCenter.securitySettings.updateEmail.form.error.required.currentPassword':
|
||||
'请输入当前密码',
|
||||
|
||||
'userCenter.securitySettings.button.update': '修改',
|
||||
};
|
||||
|
@@ -3,7 +3,14 @@
|
||||
<Breadcrumb :items="['menu.system', 'menu.system.user.list']" />
|
||||
<a-card class="general-card" :title="$t('menu.system.user.list')">
|
||||
<a-row>
|
||||
<a-col :xs="9" :sm="6" :md="5" :lg="4" :xl="4" style="margin-right: 10px">
|
||||
<a-col
|
||||
:xs="9"
|
||||
:sm="6"
|
||||
:md="5"
|
||||
:lg="4"
|
||||
:xl="4"
|
||||
style="margin-right: 10px"
|
||||
>
|
||||
<a-input-search
|
||||
v-model="deptName"
|
||||
placeholder="输入部门名称搜索"
|
||||
@@ -157,13 +164,16 @@
|
||||
<a-table-column title="头像" align="center">
|
||||
<template #cell="{ record }">
|
||||
<a-avatar>
|
||||
<img :src="getAvatar(record.avatar, record.gender)" alt="头像" />
|
||||
<img
|
||||
:src="getAvatar(record.avatar, record.gender)"
|
||||
alt="头像"
|
||||
/>
|
||||
</a-avatar>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="联系方式" :width="170">
|
||||
<template #cell="{ record }">
|
||||
{{ record.email }}<br v-if="record.email && record.phone">
|
||||
{{ record.email }}<br v-if="record.email && record.phone" />
|
||||
{{ record.phone }}
|
||||
</template>
|
||||
</a-table-column>
|
||||
@@ -173,7 +183,10 @@
|
||||
v-model="record.status"
|
||||
:checked-value="1"
|
||||
:unchecked-value="2"
|
||||
:disabled="record.disabled || !checkPermission(['system:user:update'])"
|
||||
:disabled="
|
||||
record.disabled ||
|
||||
!checkPermission(['system:user:update'])
|
||||
"
|
||||
@change="handleChangeStatus(record)"
|
||||
/>
|
||||
</template>
|
||||
@@ -186,7 +199,7 @@
|
||||
</a-table-column>
|
||||
<a-table-column title="创建人/创建时间" :width="175">
|
||||
<template #cell="{ record }">
|
||||
{{ record.createUserString }}<br>
|
||||
{{ record.createUserString }}<br />
|
||||
{{ record.createTime }}
|
||||
</template>
|
||||
</a-table-column>
|
||||
@@ -240,7 +253,9 @@
|
||||
size="small"
|
||||
title="重置密码"
|
||||
>
|
||||
<template #icon><svg-icon icon-class="privacy" /></template>
|
||||
<template #icon
|
||||
><svg-icon icon-class="privacy"
|
||||
/></template>
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
<a-button
|
||||
@@ -251,7 +266,9 @@
|
||||
:disabled="record.disabled"
|
||||
@click="toUpdateRole(record.id)"
|
||||
>
|
||||
<template #icon><svg-icon icon-class="reference" /></template>
|
||||
<template #icon
|
||||
><svg-icon icon-class="reference"
|
||||
/></template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-table-column>
|
||||
@@ -325,7 +342,11 @@
|
||||
style="width: 431px"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="所属角色" field="roleIds" :disabled="form.disabled">
|
||||
<a-form-item
|
||||
label="所属角色"
|
||||
field="roleIds"
|
||||
:disabled="form.disabled"
|
||||
>
|
||||
<a-select
|
||||
v-model="form.roleIds"
|
||||
:options="roleOptions"
|
||||
@@ -554,8 +575,8 @@
|
||||
rules: {
|
||||
username: [{ required: true, message: '请输入用户名' }],
|
||||
nickname: [{ required: true, message: '请输入昵称' }],
|
||||
roleIds: [{ required: true, message: '请选择所属角色' }],
|
||||
deptId: [{ required: true, message: '请选择所属部门' }],
|
||||
roleIds: [{ required: true, message: '请选择所属角色' }],
|
||||
},
|
||||
});
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
@@ -34,6 +34,9 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
|
||||
import top.charles7c.cnadmin.common.config.properties.ContiNewAdminProperties;
|
||||
|
||||
@@ -73,10 +76,16 @@ public class ContiNewAdminApplication implements ApplicationRunner {
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
String hostAddress = InetAddress.getLocalHost().getHostAddress();
|
||||
Integer port = serverProperties.getPort();
|
||||
String contextPath = serverProperties.getServlet().getContextPath();
|
||||
String baseUrl = URLUtil.normalize(String.format("%s:%s%s", hostAddress, port, contextPath));
|
||||
log.info("------------------------------------------------------");
|
||||
log.info("{} backend service started successfully.", properties.getName());
|
||||
log.info("后端 API 地址:http://{}:{}", hostAddress, serverProperties.getPort());
|
||||
log.info("后端 API 文档:http://{}:{}/doc.html", hostAddress, serverProperties.getPort());
|
||||
log.info("后端 API 地址:{}", baseUrl);
|
||||
Boolean docEnabled = Convert.toBool(SpringUtil.getProperty("springdoc.swagger-ui.enabled"));
|
||||
if (Boolean.TRUE.equals(docEnabled)) {
|
||||
log.info("后端 API 文档:{}/doc.html", baseUrl);
|
||||
}
|
||||
log.info("------------------------------------------------------");
|
||||
}
|
||||
}
|
||||
|
@@ -36,16 +36,18 @@ import com.wf.captcha.base.Captcha;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.lang.RegexPool;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
|
||||
import top.charles7c.cnadmin.common.config.properties.CaptchaProperties;
|
||||
import top.charles7c.cnadmin.common.config.properties.ContiNewAdminProperties;
|
||||
import top.charles7c.cnadmin.common.constant.CacheConsts;
|
||||
import top.charles7c.cnadmin.common.constant.RegexConsts;
|
||||
import top.charles7c.cnadmin.common.model.vo.CaptchaVO;
|
||||
import top.charles7c.cnadmin.common.model.vo.R;
|
||||
import top.charles7c.cnadmin.common.util.*;
|
||||
import top.charles7c.cnadmin.common.util.MailUtils;
|
||||
import top.charles7c.cnadmin.common.util.RedisUtils;
|
||||
import top.charles7c.cnadmin.common.util.TemplateUtils;
|
||||
import top.charles7c.cnadmin.common.util.validate.CheckUtils;
|
||||
|
||||
/**
|
||||
@@ -86,7 +88,7 @@ public class CaptchaController {
|
||||
@Operation(summary = "获取邮箱验证码", description = "发送验证码到指定邮箱")
|
||||
@GetMapping("/mail")
|
||||
public R getMailCaptcha(
|
||||
@NotBlank(message = "邮箱不能为空") @Pattern(regexp = RegexPool.EMAIL, message = "邮箱格式错误") String email)
|
||||
@NotBlank(message = "邮箱不能为空") @Pattern(regexp = RegexConsts.EMAIL, message = "邮箱格式错误") String email)
|
||||
throws MessagingException {
|
||||
String limitKeyPrefix = CacheConsts.LIMIT_KEY_PREFIX;
|
||||
String captchaKeyPrefix = CacheConsts.CAPTCHA_KEY_PREFIX;
|
||||
|
@@ -52,14 +52,14 @@ public class MenuController extends BaseController<MenuService, MenuVO, MenuVO,
|
||||
|
||||
@Override
|
||||
@SaCheckPermission("system:menu:add")
|
||||
protected R<Long> add(@Validated(BaseRequest.Add.class) @RequestBody MenuRequest request) {
|
||||
public R<Long> add(@Validated(BaseRequest.Add.class) @RequestBody MenuRequest request) {
|
||||
this.checkPath(request);
|
||||
return super.add(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SaCheckPermission("system:menu:update")
|
||||
protected R update(@Validated(BaseRequest.Update.class) @RequestBody MenuRequest request, @PathVariable Long id) {
|
||||
public R update(@Validated(BaseRequest.Update.class) @RequestBody MenuRequest request, @PathVariable Long id) {
|
||||
this.checkPath(request);
|
||||
return super.update(request, id);
|
||||
}
|
||||
|
@@ -27,11 +27,10 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
|
||||
import top.charles7c.cnadmin.common.constant.CacheConsts;
|
||||
import top.charles7c.cnadmin.common.constant.RegExpConsts;
|
||||
import top.charles7c.cnadmin.common.constant.RegexConsts;
|
||||
import top.charles7c.cnadmin.common.model.vo.R;
|
||||
import top.charles7c.cnadmin.common.util.ExceptionUtils;
|
||||
import top.charles7c.cnadmin.common.util.RedisUtils;
|
||||
@@ -41,7 +40,6 @@ import top.charles7c.cnadmin.common.util.validate.ValidationUtils;
|
||||
import top.charles7c.cnadmin.system.model.request.UpdateBasicInfoRequest;
|
||||
import top.charles7c.cnadmin.system.model.request.UpdateEmailRequest;
|
||||
import top.charles7c.cnadmin.system.model.request.UpdatePasswordRequest;
|
||||
import top.charles7c.cnadmin.system.model.request.UserRequest;
|
||||
import top.charles7c.cnadmin.system.model.vo.AvatarVO;
|
||||
import top.charles7c.cnadmin.system.service.UserService;
|
||||
|
||||
@@ -73,9 +71,7 @@ public class UserCenterController {
|
||||
@Operation(summary = "修改基础信息", description = "修改用户基础信息")
|
||||
@PatchMapping("/basic/info")
|
||||
public R updateBasicInfo(@Validated @RequestBody UpdateBasicInfoRequest updateBasicInfoRequest) {
|
||||
UserRequest userRequest = new UserRequest();
|
||||
BeanUtil.copyProperties(updateBasicInfoRequest, userRequest);
|
||||
userService.update(userRequest, LoginHelper.getUserId());
|
||||
userService.updateBasicInfo(updateBasicInfoRequest, LoginHelper.getUserId());
|
||||
return R.ok("修改成功");
|
||||
}
|
||||
|
||||
@@ -88,7 +84,8 @@ public class UserCenterController {
|
||||
String rawNewPassword =
|
||||
ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updatePasswordRequest.getNewPassword()));
|
||||
ValidationUtils.throwIfBlank(rawNewPassword, "新密码解密失败");
|
||||
ValidationUtils.throwIf(!ReUtil.isMatch(RegExpConsts.PASSWORD, rawNewPassword), "密码长度 6 到 32 位,同时包含数字和字母");
|
||||
ValidationUtils.throwIf(!ReUtil.isMatch(RegexConsts.PASSWORD, rawNewPassword),
|
||||
"密码长度为 6 到 32 位,可以包含字母、数字、下划线,特殊字符,同时包含字母和数字");
|
||||
|
||||
// 修改密码
|
||||
userService.updatePassword(rawOldPassword, rawNewPassword, LoginHelper.getUserId());
|
||||
|
@@ -53,7 +53,7 @@ public class UserController extends BaseController<UserService, UserVO, UserDeta
|
||||
|
||||
@Override
|
||||
@SaCheckPermission("system:user:add")
|
||||
protected R<Long> add(@Validated(BaseRequest.Add.class) @RequestBody UserRequest request) {
|
||||
public R<Long> add(@Validated(BaseRequest.Add.class) @RequestBody UserRequest request) {
|
||||
Long id = baseService.add(request);
|
||||
return R.ok(String.format("新增成功,请牢记默认密码:%s", SysConsts.DEFAULT_PASSWORD), id);
|
||||
}
|
||||
|
@@ -4,45 +4,44 @@ server:
|
||||
port: 8000
|
||||
|
||||
--- ### 数据源配置
|
||||
spring:
|
||||
datasource:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
## 动态数据源配置(可配多主多从:m1、s1...、纯粹多库:mysql、oracle...、混合配置:m1、s1、oracle...)
|
||||
dynamic:
|
||||
# 是否启用 P6Spy(SQL 性能分析组件,默认 false,该插件有性能损耗,不建议生产环境使用)
|
||||
p6spy: true
|
||||
# 设置默认的数据源或者数据源组(默认 master)
|
||||
primary: master
|
||||
# 严格匹配数据源(true 未匹配到指定数据源时抛异常;false 使用默认数据源;默认 false)
|
||||
strict: false
|
||||
datasource:
|
||||
# 主库配置(可配多个,构成多主)
|
||||
master:
|
||||
url: jdbc:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false
|
||||
username: ${DB_USER:root}
|
||||
password: ${DB_PWD:123456}
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
# 从库配置(可配多个,构成多从)
|
||||
slave_1:
|
||||
url: jdbc:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false
|
||||
username:
|
||||
password:
|
||||
lazy: true
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
type: ${spring.datasource.type}
|
||||
# Hikari 连接池配置(完整配置请参阅:https://github.com/brettwooldridge/HikariCP)
|
||||
hikari:
|
||||
# 最大连接数量(默认 10,根据实际环境调整)
|
||||
# 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒
|
||||
max-pool-size: 20
|
||||
# 获取连接超时时间(默认 30000 毫秒,30 秒)
|
||||
connection-timeout: 30000
|
||||
# 空闲连接最大存活时间(默认 600000 毫秒,10 分钟)
|
||||
idle-timeout: 600000
|
||||
# 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime(默认 0,禁用)
|
||||
keepaliveTime: 30000
|
||||
# 连接最大生存时间(默认 1800000 毫秒,30 分钟)
|
||||
max-lifetime: 1800000
|
||||
spring.datasource:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
## 动态数据源配置(可配多主多从:m1、s1...、纯粹多库:mysql、oracle...、混合配置:m1、s1、oracle...)
|
||||
dynamic:
|
||||
# 是否启用 P6Spy(SQL 性能分析组件,默认 false,该插件有性能损耗,不建议生产环境使用)
|
||||
p6spy: true
|
||||
# 设置默认的数据源或者数据源组(默认 master)
|
||||
primary: master
|
||||
# 严格匹配数据源(true 未匹配到指定数据源时抛异常;false 使用默认数据源;默认 false)
|
||||
strict: false
|
||||
datasource:
|
||||
# 主库配置(可配多个,构成多主)
|
||||
master:
|
||||
url: jdbc:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false
|
||||
username: ${DB_USER:root}
|
||||
password: ${DB_PWD:123456}
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
# 从库配置(可配多个,构成多从)
|
||||
slave_1:
|
||||
url: jdbc:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false
|
||||
username:
|
||||
password:
|
||||
lazy: true
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
type: ${spring.datasource.type}
|
||||
# Hikari 连接池配置(完整配置请参阅:https://github.com/brettwooldridge/HikariCP)
|
||||
hikari:
|
||||
# 最大连接数量(默认 10,根据实际环境调整)
|
||||
# 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒
|
||||
max-pool-size: 20
|
||||
# 获取连接超时时间(默认 30000 毫秒,30 秒)
|
||||
connection-timeout: 30000
|
||||
# 空闲连接最大存活时间(默认 600000 毫秒,10 分钟)
|
||||
idle-timeout: 600000
|
||||
# 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime(默认 0,禁用)
|
||||
keepaliveTime: 30000
|
||||
# 连接最大生存时间(默认 1800000 毫秒,30 分钟)
|
||||
max-lifetime: 1800000
|
||||
|
||||
--- ### Liquibase 配置
|
||||
spring.liquibase:
|
||||
@@ -68,21 +67,20 @@ spring:
|
||||
ssl: false
|
||||
|
||||
--- ### 邮件配置
|
||||
spring:
|
||||
mail:
|
||||
# 根据需要更换
|
||||
host: smtp.126.com
|
||||
port: 465
|
||||
username: 你的邮箱
|
||||
password: 你的邮箱授权码
|
||||
default-encoding: utf-8
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
auth: true
|
||||
socketFactory:
|
||||
class: javax.net.ssl.SSLSocketFactory
|
||||
port: 465
|
||||
spring.mail:
|
||||
# 根据需要更换
|
||||
host: smtp.126.com
|
||||
port: 465
|
||||
username: 你的邮箱
|
||||
password: 你的邮箱授权码
|
||||
default-encoding: utf-8
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
auth: true
|
||||
socketFactory:
|
||||
class: javax.net.ssl.SSLSocketFactory
|
||||
port: 465
|
||||
|
||||
--- ### 验证码配置
|
||||
captcha:
|
||||
@@ -109,26 +107,24 @@ captcha:
|
||||
# 模板路径
|
||||
templatePath: mail/captcha.ftl
|
||||
|
||||
--- ### 安全配置
|
||||
security:
|
||||
# 排除路径配置
|
||||
excludes:
|
||||
# 静态资源
|
||||
- /*.html
|
||||
- /**/*.html
|
||||
- /**/*.css
|
||||
- /**/*.js
|
||||
- /webSocket/**
|
||||
# 接口文档相关资源
|
||||
- /favicon.ico
|
||||
- /doc.html
|
||||
- /webjars/**
|
||||
- /swagger-ui/**
|
||||
- /swagger-resources/**
|
||||
- /*/api-docs/**
|
||||
# 本地存储资源
|
||||
- /avatar/**
|
||||
- /file/**
|
||||
--- ### 安全配置-排除路径配置
|
||||
security.excludes:
|
||||
# 静态资源
|
||||
- /*.html
|
||||
- /**/*.html
|
||||
- /**/*.css
|
||||
- /**/*.js
|
||||
- /webSocket/**
|
||||
# 接口文档相关资源
|
||||
- /favicon.ico
|
||||
- /doc.html
|
||||
- /webjars/**
|
||||
- /swagger-ui/**
|
||||
- /swagger-resources/**
|
||||
- /*/api-docs/**
|
||||
# 本地存储资源
|
||||
- /avatar/**
|
||||
- /file/**
|
||||
|
||||
--- ### 非对称加密配置(例如:密码加密传输,前端公钥加密,后端私钥解密;在线生成 RSA 密钥对:http://web.chacuo.net/netrsakeypair)
|
||||
rsa:
|
||||
@@ -141,14 +137,13 @@ springdoc:
|
||||
enabled: true
|
||||
|
||||
--- ### 文件上传配置
|
||||
spring:
|
||||
servlet:
|
||||
multipart:
|
||||
enabled: true
|
||||
# 单文件上传大小限制
|
||||
max-file-size: 10MB
|
||||
# 单次总上传文件大小限制
|
||||
max-request-size: 20MB
|
||||
spring.servlet:
|
||||
multipart:
|
||||
enabled: true
|
||||
# 单文件上传大小限制
|
||||
max-file-size: 10MB
|
||||
# 单次总上传文件大小限制
|
||||
max-request-size: 20MB
|
||||
|
||||
--- ### 本地存储配置
|
||||
local-storage:
|
||||
|
@@ -4,45 +4,44 @@ server:
|
||||
port: 18000
|
||||
|
||||
--- ### 数据源配置
|
||||
spring:
|
||||
datasource:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
## 动态数据源配置(可配多主多从:m1、s1...、纯粹多库:mysql、oracle...、混合配置:m1、s1、oracle...)
|
||||
dynamic:
|
||||
# 是否启用 P6Spy(SQL 性能分析组件,默认 false,该插件有性能损耗,不建议生产环境使用)
|
||||
p6spy: false
|
||||
# 设置默认的数据源或者数据源组(默认 master)
|
||||
primary: master
|
||||
# 严格匹配数据源(true 未匹配到指定数据源时抛异常;false 使用默认数据源;默认 false)
|
||||
strict: false
|
||||
datasource:
|
||||
# 主库配置(可配多个,构成多主)
|
||||
master:
|
||||
url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false
|
||||
username: ${DB_USER:root}
|
||||
password: ${DB_PWD:123456}
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
# 从库配置(可配多个,构成多从)
|
||||
slave_1:
|
||||
url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false
|
||||
username:
|
||||
password:
|
||||
lazy: true
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
type: ${spring.datasource.type}
|
||||
# Hikari 连接池配置(完整配置请参阅:https://github.com/brettwooldridge/HikariCP)
|
||||
hikari:
|
||||
# 最大连接数量(默认 10,根据实际环境调整)
|
||||
# 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒
|
||||
max-pool-size: 20
|
||||
# 获取连接超时时间(默认 30000 毫秒,30 秒)
|
||||
connection-timeout: 30000
|
||||
# 空闲连接最大存活时间(默认 600000 毫秒,10 分钟)
|
||||
idle-timeout: 600000
|
||||
# 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime(默认 0,禁用)
|
||||
keepaliveTime: 30000
|
||||
# 连接最大生存时间(默认 1800000 毫秒,30 分钟)
|
||||
max-lifetime: 1800000
|
||||
spring.datasource:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
## 动态数据源配置(可配多主多从:m1、s1...、纯粹多库:mysql、oracle...、混合配置:m1、s1、oracle...)
|
||||
dynamic:
|
||||
# 是否启用 P6Spy(SQL 性能分析组件,默认 false,该插件有性能损耗,不建议生产环境使用)
|
||||
p6spy: false
|
||||
# 设置默认的数据源或者数据源组(默认 master)
|
||||
primary: master
|
||||
# 严格匹配数据源(true 未匹配到指定数据源时抛异常;false 使用默认数据源;默认 false)
|
||||
strict: false
|
||||
datasource:
|
||||
# 主库配置(可配多个,构成多主)
|
||||
master:
|
||||
url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false
|
||||
username: ${DB_USER:root}
|
||||
password: ${DB_PWD:123456}
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
# 从库配置(可配多个,构成多从)
|
||||
slave_1:
|
||||
url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false
|
||||
username:
|
||||
password:
|
||||
lazy: true
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
type: ${spring.datasource.type}
|
||||
# Hikari 连接池配置(完整配置请参阅:https://github.com/brettwooldridge/HikariCP)
|
||||
hikari:
|
||||
# 最大连接数量(默认 10,根据实际环境调整)
|
||||
# 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒
|
||||
max-pool-size: 20
|
||||
# 获取连接超时时间(默认 30000 毫秒,30 秒)
|
||||
connection-timeout: 30000
|
||||
# 空闲连接最大存活时间(默认 600000 毫秒,10 分钟)
|
||||
idle-timeout: 600000
|
||||
# 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime(默认 0,禁用)
|
||||
keepaliveTime: 30000
|
||||
# 连接最大生存时间(默认 1800000 毫秒,30 分钟)
|
||||
max-lifetime: 1800000
|
||||
|
||||
--- ### Liquibase 配置
|
||||
spring.liquibase:
|
||||
@@ -68,21 +67,20 @@ spring:
|
||||
ssl: false
|
||||
|
||||
--- ### 邮件配置
|
||||
spring:
|
||||
mail:
|
||||
# 根据需要更换
|
||||
host: smtp.126.com
|
||||
port: 465
|
||||
username: 你的邮箱
|
||||
password: 你的邮箱授权码
|
||||
default-encoding: utf-8
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
auth: true
|
||||
socketFactory:
|
||||
class: javax.net.ssl.SSLSocketFactory
|
||||
port: 465
|
||||
spring.mail:
|
||||
# 根据需要更换
|
||||
host: smtp.126.com
|
||||
port: 465
|
||||
username: 你的邮箱
|
||||
password: 你的邮箱授权码
|
||||
default-encoding: utf-8
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
auth: true
|
||||
socketFactory:
|
||||
class: javax.net.ssl.SSLSocketFactory
|
||||
port: 465
|
||||
|
||||
--- ### 验证码配置
|
||||
captcha:
|
||||
@@ -109,19 +107,17 @@ captcha:
|
||||
# 模板路径
|
||||
templatePath: mail/captcha.ftl
|
||||
|
||||
--- ### 安全配置
|
||||
security:
|
||||
# 排除路径配置
|
||||
excludes:
|
||||
# 静态资源
|
||||
- /*.html
|
||||
- /**/*.html
|
||||
- /**/*.css
|
||||
- /**/*.js
|
||||
- /webSocket/**
|
||||
# 本地存储资源
|
||||
- /avatar/**
|
||||
- /file/**
|
||||
--- ### 安全配置-排除路径配置
|
||||
security.excludes:
|
||||
# 静态资源
|
||||
- /*.html
|
||||
- /**/*.html
|
||||
- /**/*.css
|
||||
- /**/*.js
|
||||
- /webSocket/**
|
||||
# 本地存储资源
|
||||
- /avatar/**
|
||||
- /file/**
|
||||
|
||||
--- ### 非对称加密配置(例如:密码加密传输,前端公钥加密,后端私钥解密;在线生成 RSA 密钥对:http://web.chacuo.net/netrsakeypair)
|
||||
rsa:
|
||||
@@ -132,16 +128,19 @@ rsa:
|
||||
springdoc:
|
||||
swagger-ui:
|
||||
enabled: false
|
||||
## 接口文档增强配置
|
||||
knife4j:
|
||||
# 开启生产环境屏蔽
|
||||
production: true
|
||||
|
||||
--- ### 文件上传配置
|
||||
spring:
|
||||
servlet:
|
||||
multipart:
|
||||
enabled: true
|
||||
# 单文件上传大小限制
|
||||
max-file-size: 10MB
|
||||
# 单次总上传文件大小限制
|
||||
max-request-size: 20MB
|
||||
spring.servlet:
|
||||
multipart:
|
||||
enabled: true
|
||||
# 单文件上传大小限制
|
||||
max-file-size: 10MB
|
||||
# 单次总上传文件大小限制
|
||||
max-request-size: 20MB
|
||||
|
||||
--- ### 本地存储配置
|
||||
local-storage:
|
||||
|
@@ -5,7 +5,7 @@ continew-admin:
|
||||
# 应用名称
|
||||
appName: continew-admin
|
||||
# 版本
|
||||
version: 1.0.0
|
||||
version: 1.0.1
|
||||
# 描述
|
||||
description: ContiNew Admin 中后台管理框架/脚手架,Continue New Admin,持续以最新流行技术栈构建,拥抱变化,迭代优化。
|
||||
# URL
|
||||
@@ -69,7 +69,7 @@ knife4j:
|
||||
# 是否自定义 footer(默认 false 非自定义)
|
||||
enable-footer-custom: true
|
||||
# 自定义 footer 内容,支持 Markdown 语法
|
||||
footer-custom-content: 'Copyright © 2022-present [Charles7c](${continew-admin.author.url}) ⋅ [ContiNew Admin](https://github.com/Charles7c/continew-admin) v${continew-admin.version}'
|
||||
footer-custom-content: 'Copyright © 2022-present [${continew-admin.author.name}](${continew-admin.author.url}) ⋅ [${continew-admin.name}](${continew-admin.url}) v${continew-admin.version}'
|
||||
|
||||
--- ### Sa-Token 配置
|
||||
sa-token:
|
||||
@@ -187,6 +187,12 @@ spring:
|
||||
# 允许反序列化不存在的属性
|
||||
FAIL_ON_UNKNOWN_PROPERTIES: false
|
||||
|
||||
--- ### 健康检查配置
|
||||
management.health:
|
||||
mail:
|
||||
# 关闭邮箱健康检查(邮箱配置错误或邮箱服务器不可用时,健康检查会报错)
|
||||
enabled: false
|
||||
|
||||
--- ### 线程池配置
|
||||
thread-pool:
|
||||
# 是否启用线程池
|
||||
|
@@ -3,11 +3,11 @@
|
||||
-- changeset Charles7c:1
|
||||
CREATE TABLE IF NOT EXISTS `sys_menu` (
|
||||
`id` bigint(20) UNSIGNED AUTO_INCREMENT COMMENT 'ID',
|
||||
`title` varchar(255) NOT NULL COMMENT '菜单标题',
|
||||
`title` varchar(50) NOT NULL COMMENT '菜单标题',
|
||||
`parent_id` bigint(20) UNSIGNED DEFAULT 0 COMMENT '上级菜单ID',
|
||||
`type` tinyint(1) UNSIGNED DEFAULT 1 COMMENT '菜单类型(1:目录,2:菜单,3:按钮)',
|
||||
`path` varchar(512) DEFAULT NULL COMMENT '路由地址',
|
||||
`name` varchar(255) DEFAULT NULL COMMENT '组件名称',
|
||||
`name` varchar(50) DEFAULT NULL COMMENT '组件名称',
|
||||
`component` varchar(255) DEFAULT NULL COMMENT '组件路径',
|
||||
`icon` varchar(255) DEFAULT NULL COMMENT '菜单图标',
|
||||
`is_external` bit(1) DEFAULT b'0' COMMENT '是否外链',
|
||||
@@ -28,7 +28,7 @@ CREATE TABLE IF NOT EXISTS `sys_menu` (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_dept` (
|
||||
`id` bigint(20) UNSIGNED AUTO_INCREMENT COMMENT 'ID',
|
||||
`name` varchar(255) NOT NULL COMMENT '部门名称',
|
||||
`name` varchar(50) NOT NULL COMMENT '部门名称',
|
||||
`parent_id` bigint(20) UNSIGNED DEFAULT 0 COMMENT '上级部门ID',
|
||||
`ancestors` varchar(512) DEFAULT '' COMMENT '祖级列表',
|
||||
`description` varchar(512) DEFAULT NULL COMMENT '描述',
|
||||
@@ -47,8 +47,8 @@ CREATE TABLE IF NOT EXISTS `sys_dept` (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_role` (
|
||||
`id` bigint(20) UNSIGNED AUTO_INCREMENT COMMENT 'ID',
|
||||
`name` varchar(255) NOT NULL COMMENT '角色名称',
|
||||
`code` varchar(255) NOT NULL COMMENT '角色编码',
|
||||
`name` varchar(50) NOT NULL COMMENT '角色名称',
|
||||
`code` varchar(50) NOT NULL COMMENT '角色编码',
|
||||
`data_scope` tinyint(1) DEFAULT 4 COMMENT '数据权限(1:全部数据权限,2:本部门及以下数据权限,3:本部门数据权限,4:仅本人数据权限,5:自定义数据权限)',
|
||||
`description` varchar(512) DEFAULT NULL COMMENT '描述',
|
||||
`sort` int(11) UNSIGNED DEFAULT 999 COMMENT '角色排序',
|
||||
@@ -77,12 +77,12 @@ CREATE TABLE IF NOT EXISTS `sys_role_dept` (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_user` (
|
||||
`id` bigint(20) UNSIGNED AUTO_INCREMENT COMMENT 'ID',
|
||||
`username` varchar(255) NOT NULL COMMENT '用户名',
|
||||
`nickname` varchar(255) DEFAULT NULL COMMENT '昵称',
|
||||
`username` varchar(50) NOT NULL COMMENT '用户名',
|
||||
`nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
|
||||
`password` varchar(255) DEFAULT NULL COMMENT '密码',
|
||||
`gender` tinyint(1) UNSIGNED DEFAULT 0 COMMENT '性别(0:未知,1:男,2:女)',
|
||||
`email` varchar(255) DEFAULT NULL COMMENT '邮箱',
|
||||
`phone` varchar(255) DEFAULT NULL COMMENT '手机号码',
|
||||
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
|
||||
`phone` varchar(50) DEFAULT NULL COMMENT '手机号码',
|
||||
`avatar` varchar(255) DEFAULT NULL COMMENT '头像地址',
|
||||
`description` varchar(512) DEFAULT NULL COMMENT '描述',
|
||||
`status` tinyint(1) UNSIGNED DEFAULT 1 COMMENT '状态(1:启用,2:禁用)',
|
||||
@@ -110,7 +110,7 @@ CREATE TABLE IF NOT EXISTS `sys_user_role` (
|
||||
CREATE TABLE IF NOT EXISTS `sys_log` (
|
||||
`id` bigint(20) UNSIGNED AUTO_INCREMENT COMMENT 'ID',
|
||||
`description` varchar(255) NOT NULL COMMENT '日志描述',
|
||||
`module` varchar(255) NOT NULL COMMENT '所属模块',
|
||||
`module` varchar(50) NOT NULL COMMENT '所属模块',
|
||||
`request_url` varchar(512) NOT NULL COMMENT '请求URL',
|
||||
`request_method` varchar(10) NOT NULL COMMENT '请求方式',
|
||||
`request_headers` text DEFAULT NULL COMMENT '请求头',
|
||||
@@ -120,9 +120,9 @@ CREATE TABLE IF NOT EXISTS `sys_log` (
|
||||
`response_body` mediumtext DEFAULT NULL COMMENT '响应体',
|
||||
`elapsed_time` bigint(20) UNSIGNED NOT NULL COMMENT '请求耗时(ms)',
|
||||
`status` tinyint(1) UNSIGNED DEFAULT 1 COMMENT '操作状态(1:成功,2:失败)',
|
||||
`client_ip` varchar(255) DEFAULT NULL COMMENT '客户端IP',
|
||||
`location` varchar(512) DEFAULT NULL COMMENT 'IP归属地',
|
||||
`browser` varchar(255) DEFAULT NULL COMMENT '浏览器',
|
||||
`client_ip` varchar(100) DEFAULT NULL COMMENT '客户端IP',
|
||||
`location` varchar(255) DEFAULT NULL COMMENT 'IP归属地',
|
||||
`browser` varchar(100) DEFAULT NULL COMMENT '浏览器',
|
||||
`error_msg` text DEFAULT NULL COMMENT '错误信息',
|
||||
`exception_detail` mediumtext DEFAULT NULL COMMENT '异常详情',
|
||||
`create_user` bigint(20) UNSIGNED DEFAULT NULL COMMENT '创建人',
|
||||
|
2
pom.xml
2
pom.xml
@@ -59,7 +59,7 @@ limitations under the License.
|
||||
<hutool.version>5.8.11</hutool.version>
|
||||
|
||||
<!-- ### 基础环境相关 ### -->
|
||||
<revision>1.0.0</revision>
|
||||
<revision>1.0.1</revision>
|
||||
<java.version>1.8</java.version>
|
||||
<spotless.version>2.28.0</spotless.version>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
|
Reference in New Issue
Block a user