mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-10-31 22:57:17 +08:00 
			
		
		
		
	新增:新增系统管理/用户管理(列表、查看详情、新增、修改、删除、导出)
This commit is contained in:
		| @@ -26,7 +26,6 @@ import org.springframework.data.domain.Sort; | |||||||
| import org.springframework.transaction.annotation.Transactional; | import org.springframework.transaction.annotation.Transactional; | ||||||
|  |  | ||||||
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | ||||||
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; |  | ||||||
| import com.baomidou.mybatisplus.core.metadata.IPage; | import com.baomidou.mybatisplus.core.metadata.IPage; | ||||||
| import com.baomidou.mybatisplus.core.metadata.TableInfo; | import com.baomidou.mybatisplus.core.metadata.TableInfo; | ||||||
| import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; | import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; | ||||||
|   | |||||||
| @@ -32,4 +32,9 @@ public class Constants { | |||||||
|      * 超级管理员角色编码 |      * 超级管理员角色编码 | ||||||
|      */ |      */ | ||||||
|     public static final String ADMIN_ROLE_CODE = "admin"; |     public static final String ADMIN_ROLE_CODE = "admin"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 默认密码 | ||||||
|  |      */ | ||||||
|  |     public static final String DEFAULT_PASSWORD = "123456"; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -16,8 +16,7 @@ | |||||||
|  |  | ||||||
| package top.charles7c.cnadmin.monitor.mapper; | package top.charles7c.cnadmin.monitor.mapper; | ||||||
|  |  | ||||||
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; | import top.charles7c.cnadmin.common.base.BaseMapper; | ||||||
|  |  | ||||||
| import top.charles7c.cnadmin.monitor.model.entity.LogDO; | import top.charles7c.cnadmin.monitor.model.entity.LogDO; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|   | |||||||
| @@ -35,6 +35,7 @@ import cn.hutool.core.util.StrUtil; | |||||||
|  |  | ||||||
| import top.charles7c.cnadmin.common.model.query.PageQuery; | import top.charles7c.cnadmin.common.model.query.PageQuery; | ||||||
| import top.charles7c.cnadmin.common.model.vo.PageDataVO; | import top.charles7c.cnadmin.common.model.vo.PageDataVO; | ||||||
|  | import top.charles7c.cnadmin.common.service.CommonUserService; | ||||||
| import top.charles7c.cnadmin.common.util.ExceptionUtils; | import top.charles7c.cnadmin.common.util.ExceptionUtils; | ||||||
| import top.charles7c.cnadmin.common.util.ReflectUtils; | import top.charles7c.cnadmin.common.util.ReflectUtils; | ||||||
| import top.charles7c.cnadmin.common.util.helper.QueryHelper; | import top.charles7c.cnadmin.common.util.helper.QueryHelper; | ||||||
| @@ -46,7 +47,6 @@ import top.charles7c.cnadmin.monitor.model.query.OperationLogQuery; | |||||||
| import top.charles7c.cnadmin.monitor.model.query.SystemLogQuery; | import top.charles7c.cnadmin.monitor.model.query.SystemLogQuery; | ||||||
| import top.charles7c.cnadmin.monitor.model.vo.*; | import top.charles7c.cnadmin.monitor.model.vo.*; | ||||||
| import top.charles7c.cnadmin.monitor.service.LogService; | import top.charles7c.cnadmin.monitor.service.LogService; | ||||||
| import top.charles7c.cnadmin.system.service.UserService; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 系统日志业务实现类 |  * 系统日志业务实现类 | ||||||
| @@ -60,7 +60,7 @@ import top.charles7c.cnadmin.system.service.UserService; | |||||||
| public class LogServiceImpl implements LogService { | public class LogServiceImpl implements LogService { | ||||||
|  |  | ||||||
|     private final LogMapper logMapper; |     private final LogMapper logMapper; | ||||||
|     private final UserService userService; |     private final CommonUserService commonUserService; | ||||||
|  |  | ||||||
|     @Async |     @Async | ||||||
|     @EventListener |     @EventListener | ||||||
| @@ -84,7 +84,7 @@ public class LogServiceImpl implements LogService { | |||||||
|  |  | ||||||
|         // 填充数据(如果是查询个人操作日志,只查询一次用户信息即可) |         // 填充数据(如果是查询个人操作日志,只查询一次用户信息即可) | ||||||
|         if (query.getUid() != null) { |         if (query.getUid() != null) { | ||||||
|             String nickname = ExceptionUtils.exToNull(() -> userService.getById(query.getUid()).getNickname()); |             String nickname = ExceptionUtils.exToNull(() -> commonUserService.getNicknameById(query.getUid())); | ||||||
|             pageDataVO.getList().forEach(o -> o.setCreateUserString(nickname)); |             pageDataVO.getList().forEach(o -> o.setCreateUserString(nickname)); | ||||||
|         } else { |         } else { | ||||||
|             pageDataVO.getList().forEach(this::fill); |             pageDataVO.getList().forEach(this::fill); | ||||||
| @@ -153,6 +153,6 @@ public class LogServiceImpl implements LogService { | |||||||
|         if (createUser == null) { |         if (createUser == null) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         logVO.setCreateUserString(ExceptionUtils.exToNull(() -> userService.getById(createUser)).getNickname()); |         logVO.setCreateUserString(ExceptionUtils.exToNull(() -> commonUserService.getNicknameById(createUser))); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -16,8 +16,7 @@ | |||||||
|  |  | ||||||
| package top.charles7c.cnadmin.system.mapper; | package top.charles7c.cnadmin.system.mapper; | ||||||
|  |  | ||||||
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; | import top.charles7c.cnadmin.common.base.BaseMapper; | ||||||
|  |  | ||||||
| import top.charles7c.cnadmin.system.model.entity.DeptDO; | import top.charles7c.cnadmin.system.model.entity.DeptDO; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|   | |||||||
| @@ -16,8 +16,7 @@ | |||||||
|  |  | ||||||
| package top.charles7c.cnadmin.system.mapper; | package top.charles7c.cnadmin.system.mapper; | ||||||
|  |  | ||||||
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; | import top.charles7c.cnadmin.common.base.BaseMapper; | ||||||
|  |  | ||||||
| import top.charles7c.cnadmin.system.model.entity.MenuDO; | import top.charles7c.cnadmin.system.model.entity.MenuDO; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|   | |||||||
| @@ -16,8 +16,7 @@ | |||||||
|  |  | ||||||
| package top.charles7c.cnadmin.system.mapper; | package top.charles7c.cnadmin.system.mapper; | ||||||
|  |  | ||||||
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; | import top.charles7c.cnadmin.common.base.BaseMapper; | ||||||
|  |  | ||||||
| import top.charles7c.cnadmin.system.model.entity.RoleDO; | import top.charles7c.cnadmin.system.model.entity.RoleDO; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|   | |||||||
| @@ -16,8 +16,7 @@ | |||||||
|  |  | ||||||
| package top.charles7c.cnadmin.system.mapper; | package top.charles7c.cnadmin.system.mapper; | ||||||
|  |  | ||||||
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; | import top.charles7c.cnadmin.common.base.BaseMapper; | ||||||
|  |  | ||||||
| import top.charles7c.cnadmin.system.model.entity.UserDO; | import top.charles7c.cnadmin.system.model.entity.UserDO; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|   | |||||||
| @@ -16,8 +16,7 @@ | |||||||
|  |  | ||||||
| package top.charles7c.cnadmin.system.mapper; | package top.charles7c.cnadmin.system.mapper; | ||||||
|  |  | ||||||
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; | import top.charles7c.cnadmin.common.base.BaseMapper; | ||||||
|  |  | ||||||
| import top.charles7c.cnadmin.system.model.entity.UserRoleDO; | import top.charles7c.cnadmin.system.model.entity.UserRoleDO; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|   | |||||||
| @@ -65,16 +65,16 @@ public class UserDO extends BaseDO { | |||||||
|      */ |      */ | ||||||
|     private GenderEnum gender; |     private GenderEnum gender; | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 手机号码 |  | ||||||
|      */ |  | ||||||
|     private String phone; |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 邮箱 |      * 邮箱 | ||||||
|      */ |      */ | ||||||
|     private String email; |     private String email; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 手机号码 | ||||||
|  |      */ | ||||||
|  |     private String phone; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 头像地址 |      * 头像地址 | ||||||
|      */ |      */ | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ package top.charles7c.cnadmin.system.model.entity; | |||||||
| import java.io.Serializable; | import java.io.Serializable; | ||||||
|  |  | ||||||
| import lombok.Data; | import lombok.Data; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  |  | ||||||
| import com.baomidou.mybatisplus.annotation.TableName; | import com.baomidou.mybatisplus.annotation.TableName; | ||||||
|  |  | ||||||
| @@ -29,6 +30,7 @@ import com.baomidou.mybatisplus.annotation.TableName; | |||||||
|  * @since 2023/2/13 23:13 |  * @since 2023/2/13 23:13 | ||||||
|  */ |  */ | ||||||
| @Data | @Data | ||||||
|  | @NoArgsConstructor | ||||||
| @TableName("sys_user_role") | @TableName("sys_user_role") | ||||||
| public class UserRoleDO implements Serializable { | public class UserRoleDO implements Serializable { | ||||||
|  |  | ||||||
| @@ -43,4 +45,9 @@ public class UserRoleDO implements Serializable { | |||||||
|      * 角色 ID |      * 角色 ID | ||||||
|      */ |      */ | ||||||
|     private Long roleId; |     private Long roleId; | ||||||
|  |  | ||||||
|  |     public UserRoleDO(Long userId, Long roleId) { | ||||||
|  |         this.userId = userId; | ||||||
|  |         this.roleId = roleId; | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,73 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.charles7c.cnadmin.system.model.query; | ||||||
|  |  | ||||||
|  | import java.io.Serializable; | ||||||
|  | import java.util.Date; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  |  | ||||||
|  | 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.annotation.Query; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 用户查询条件 | ||||||
|  |  * | ||||||
|  |  * @author Charles7c | ||||||
|  |  * @since 2023/2/20 21:01 | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | @ParameterObject | ||||||
|  | @Schema(description = "用户查询条件") | ||||||
|  | public class UserQuery implements Serializable { | ||||||
|  |  | ||||||
|  |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 用户名 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "用户名") | ||||||
|  |     @Query(blurry = "username,nickname,email,phone") | ||||||
|  |     private String username; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 状态(1启用 2禁用) | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "状态(1启用 2禁用)") | ||||||
|  |     @Query | ||||||
|  |     private Integer status; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 创建时间 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "创建时间") | ||||||
|  |     @Query(type = Query.Type.BETWEEN) | ||||||
|  |     @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") | ||||||
|  |     private List<Date> createTime; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 部门 ID | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "部门 ID") | ||||||
|  |     @Query | ||||||
|  |     private Long deptId; | ||||||
|  | } | ||||||
| @@ -0,0 +1,117 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.charles7c.cnadmin.system.model.request; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | import javax.validation.constraints.NotBlank; | ||||||
|  | import javax.validation.constraints.NotNull; | ||||||
|  | import javax.validation.constraints.Null; | ||||||
|  | import javax.validation.constraints.Pattern; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  |  | ||||||
|  | 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.enums.DisEnableStatusEnum; | ||||||
|  | import top.charles7c.cnadmin.common.enums.GenderEnum; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 创建或修改用户信息 | ||||||
|  |  * | ||||||
|  |  * @author Charles7c | ||||||
|  |  * @since 2023/2/20 21:03 | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | @Schema(description = "创建或修改用户信息") | ||||||
|  | public class UserRequest extends BaseRequest { | ||||||
|  |  | ||||||
|  |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 用户 ID | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "角色 ID") | ||||||
|  |     @Null(message = "新增时,ID 必须为空", groups = Create.class) | ||||||
|  |     @NotNull(message = "修改时,ID 不能为空", groups = Update.class) | ||||||
|  |     private Long userId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 用户名 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "用户名") | ||||||
|  |     @NotBlank(message = "用户名不能为空") | ||||||
|  |     private String username; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 昵称 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "昵称") | ||||||
|  |     @Length(max = 32, message = "昵称长度不能超过 {max} 个字符") | ||||||
|  |     private String nickname; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 性别(0未知 1男 2女) | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "性别(0未知 1男 2女)", type = "Integer", allowableValues = {"0", "1", "2"}) | ||||||
|  |     @NotNull(message = "性别非法") | ||||||
|  |     private GenderEnum gender; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 邮箱 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "邮箱") | ||||||
|  |     @Pattern(regexp = RegexPool.EMAIL, message = "邮箱格式错误") | ||||||
|  |     private String email; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 手机号码 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "手机号码") | ||||||
|  |     @Pattern(regexp = RegexPool.MOBILE, message = "手机号码格式错误") | ||||||
|  |     private String phone; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 描述 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "描述") | ||||||
|  |     @Length(max = 200, message = "描述长度不能超过 {max} 个字符") | ||||||
|  |     private String description; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 状态(1启用 2禁用) | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "状态(1启用 2禁用)", type = "Integer", allowableValues = {"1", "2"}) | ||||||
|  |     private DisEnableStatusEnum status; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 部门 ID | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "部门 ID") | ||||||
|  |     private Long deptId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 角色 ID 列表 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "角色 ID 列表") | ||||||
|  |     private List<Long> roleIds; | ||||||
|  | } | ||||||
| @@ -0,0 +1,134 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.charles7c.cnadmin.system.model.vo; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  |  | ||||||
|  | import io.swagger.v3.oas.annotations.media.Schema; | ||||||
|  |  | ||||||
|  | import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | ||||||
|  | import com.alibaba.excel.annotation.ExcelProperty; | ||||||
|  |  | ||||||
|  | import top.charles7c.cnadmin.common.base.BaseDetailVO; | ||||||
|  | import top.charles7c.cnadmin.common.config.easyexcel.ExcelBaseEnumConverter; | ||||||
|  | import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum; | ||||||
|  | import top.charles7c.cnadmin.common.enums.GenderEnum; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 用户详情信息 | ||||||
|  |  * | ||||||
|  |  * @author Charles7c | ||||||
|  |  * @since 2023/2/20 21:11 | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | @ExcelIgnoreUnannotated | ||||||
|  | @Schema(description = "用户详情信息") | ||||||
|  | public class UserDetailVO extends BaseDetailVO { | ||||||
|  |  | ||||||
|  |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 用户 ID | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "用户 ID") | ||||||
|  |     @ExcelProperty(value = "用户ID") | ||||||
|  |     private Long userId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 用户名 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "用户名") | ||||||
|  |     @ExcelProperty(value = "用户名") | ||||||
|  |     private String username; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 昵称 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "昵称") | ||||||
|  |     @ExcelProperty(value = "昵称") | ||||||
|  |     private String nickname; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 性别(0未知 1男 2女) | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "性别(0未知 1男 2女)") | ||||||
|  |     @ExcelProperty(value = "性别", converter = ExcelBaseEnumConverter.class) | ||||||
|  |     private GenderEnum gender; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 邮箱 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "邮箱") | ||||||
|  |     @ExcelProperty(value = "邮箱") | ||||||
|  |     private String email; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 手机号码 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "手机号码") | ||||||
|  |     @ExcelProperty(value = "手机号码") | ||||||
|  |     private String phone; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 头像地址 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "头像地址") | ||||||
|  |     @ExcelProperty(value = "头像地址") | ||||||
|  |     private String avatar; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 状态(1启用 2禁用) | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "状态(1启用 2禁用)") | ||||||
|  |     @ExcelProperty(value = "状态", converter = ExcelBaseEnumConverter.class) | ||||||
|  |     private DisEnableStatusEnum status; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 描述 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "描述") | ||||||
|  |     @ExcelProperty(value = "描述") | ||||||
|  |     private String description; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 角色 ID 列表 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "角色 ID 列表") | ||||||
|  |     private List<Long> roleIds; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 所属角色 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "所属角色") | ||||||
|  |     @ExcelProperty(value = "所属角色") | ||||||
|  |     private String roleNames; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 部门 ID | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "部门 ID") | ||||||
|  |     private Long deptId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 所属部门 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "所属部门") | ||||||
|  |     @ExcelProperty(value = "所属部门") | ||||||
|  |     private String deptName; | ||||||
|  | } | ||||||
| @@ -0,0 +1,119 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.charles7c.cnadmin.system.model.vo; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  | import lombok.experimental.Accessors; | ||||||
|  |  | ||||||
|  | import io.swagger.v3.oas.annotations.media.Schema; | ||||||
|  |  | ||||||
|  | import com.fasterxml.jackson.annotation.JsonInclude; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.util.DesensitizedUtil; | ||||||
|  |  | ||||||
|  | import top.charles7c.cnadmin.common.base.BaseVO; | ||||||
|  | import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum; | ||||||
|  | import top.charles7c.cnadmin.common.enums.GenderEnum; | ||||||
|  | import top.charles7c.cnadmin.common.util.helper.LoginHelper; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 用户信息 | ||||||
|  |  * | ||||||
|  |  * @author Charles7c | ||||||
|  |  * @since 2023/2/20 21:08 | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | @Accessors(chain = true) | ||||||
|  | @Schema(description = "用户信息") | ||||||
|  | public class UserVO extends BaseVO { | ||||||
|  |  | ||||||
|  |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 用户 ID | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "用户 ID") | ||||||
|  |     private Long userId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 用户名 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "用户名") | ||||||
|  |     private String username; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 昵称 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "昵称") | ||||||
|  |     private String nickname; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 性别(0未知 1男 2女) | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "性别(0未知 1男 2女)") | ||||||
|  |     private GenderEnum gender; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 邮箱 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "邮箱") | ||||||
|  |     private String email; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 手机号码 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "手机号码") | ||||||
|  |     private String phone; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 头像地址 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "头像地址") | ||||||
|  |     private String avatar; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 状态(1启用 2禁用) | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "状态(1启用 2禁用)") | ||||||
|  |     private DisEnableStatusEnum status; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 描述 | ||||||
|  |      */ | ||||||
|  |     @Schema(description = "描述") | ||||||
|  |     private String description; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 是否禁用修改 | ||||||
|  |      */ | ||||||
|  |     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||||
|  |     private Boolean disabled; | ||||||
|  |  | ||||||
|  |     public Boolean getDisabled() { | ||||||
|  |         if (userId.equals(LoginHelper.getUserId())) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |         return disabled; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public String getPhone() { | ||||||
|  |         if (phone == null) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         return DesensitizedUtil.mobilePhone(phone); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -16,6 +16,10 @@ | |||||||
|  |  | ||||||
| package top.charles7c.cnadmin.system.service; | package top.charles7c.cnadmin.system.service; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.lang.tree.Tree; | ||||||
|  |  | ||||||
| import top.charles7c.cnadmin.common.base.BaseService; | import top.charles7c.cnadmin.common.base.BaseService; | ||||||
| import top.charles7c.cnadmin.system.model.query.RoleQuery; | import top.charles7c.cnadmin.system.model.query.RoleQuery; | ||||||
| import top.charles7c.cnadmin.system.model.request.RoleRequest; | import top.charles7c.cnadmin.system.model.request.RoleRequest; | ||||||
| @@ -28,4 +32,23 @@ import top.charles7c.cnadmin.system.model.vo.RoleVO; | |||||||
|  * @author Charles7c |  * @author Charles7c | ||||||
|  * @since 2023/2/8 23:15 |  * @since 2023/2/8 23:15 | ||||||
|  */ |  */ | ||||||
| public interface RoleService extends BaseService<RoleVO, RoleDetailVO, RoleQuery, RoleRequest> {} | public interface RoleService extends BaseService<RoleVO, RoleDetailVO, RoleQuery, RoleRequest> { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 构建树 | ||||||
|  |      * | ||||||
|  |      * @param list | ||||||
|  |      *            原始列表数据 | ||||||
|  |      * @return 树列表 | ||||||
|  |      */ | ||||||
|  |     List<Tree<Long>> buildTree(List<RoleVO> list); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 根据角色 ID 列表查询 | ||||||
|  |      * | ||||||
|  |      * @param roleIds | ||||||
|  |      *            角色 ID 列表 | ||||||
|  |      * @return 角色名称列表 | ||||||
|  |      */ | ||||||
|  |     List<String> listRoleNamesByRoleIds(List<Long> roleIds); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -0,0 +1,56 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.charles7c.cnadmin.system.service; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 用户和角色业务接口 | ||||||
|  |  * | ||||||
|  |  * @author Charles7c | ||||||
|  |  * @since 2023/2/20 21:30 | ||||||
|  |  */ | ||||||
|  | public interface UserRoleService { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 保存 | ||||||
|  |      * | ||||||
|  |      * @param roleIds | ||||||
|  |      *            角色 ID 列表 | ||||||
|  |      * @param userId | ||||||
|  |      *            用户 ID | ||||||
|  |      */ | ||||||
|  |     void save(List<Long> roleIds, Long userId); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 根据角色 ID 列表查询 | ||||||
|  |      * | ||||||
|  |      * @param roleIds | ||||||
|  |      *            角色 ID 列表 | ||||||
|  |      * @return 总记录数 | ||||||
|  |      */ | ||||||
|  |     Long countByRoleIds(List<Long> roleIds); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 根据用户 ID 查询 | ||||||
|  |      * | ||||||
|  |      * @param userId | ||||||
|  |      *            用户 ID | ||||||
|  |      * @return 角色 ID 列表 | ||||||
|  |      */ | ||||||
|  |     List<Long> listRoleIdsByUserId(Long userId); | ||||||
|  | } | ||||||
| @@ -20,7 +20,12 @@ import java.util.List; | |||||||
|  |  | ||||||
| import org.springframework.web.multipart.MultipartFile; | 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.entity.UserDO; | ||||||
|  | import top.charles7c.cnadmin.system.model.query.UserQuery; | ||||||
|  | import top.charles7c.cnadmin.system.model.request.UserRequest; | ||||||
|  | import top.charles7c.cnadmin.system.model.vo.UserDetailVO; | ||||||
|  | import top.charles7c.cnadmin.system.model.vo.UserVO; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 用户业务接口 |  * 用户业务接口 | ||||||
| @@ -28,7 +33,7 @@ import top.charles7c.cnadmin.system.model.entity.UserDO; | |||||||
|  * @author Charles7c |  * @author Charles7c | ||||||
|  * @since 2022/12/21 21:48 |  * @since 2022/12/21 21:48 | ||||||
|  */ |  */ | ||||||
| public interface UserService { | public interface UserService extends BaseService<UserVO, UserDetailVO, UserQuery, UserRequest> { | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 根据用户名查询 |      * 根据用户名查询 | ||||||
| @@ -50,14 +55,6 @@ public interface UserService { | |||||||
|      */ |      */ | ||||||
|     String uploadAvatar(MultipartFile avatar, Long userId); |     String uploadAvatar(MultipartFile avatar, Long userId); | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 修改信息 |  | ||||||
|      * |  | ||||||
|      * @param user |  | ||||||
|      *            用户信息 |  | ||||||
|      */ |  | ||||||
|     void update(UserDO user); |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 修改密码 |      * 修改密码 | ||||||
|      * |      * | ||||||
| @@ -82,15 +79,6 @@ public interface UserService { | |||||||
|      */ |      */ | ||||||
|     void updateEmail(String newEmail, String currentPassword, Long userId); |     void updateEmail(String newEmail, String currentPassword, Long userId); | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 根据 ID 查询 |  | ||||||
|      * |  | ||||||
|      * @param userId |  | ||||||
|      *            用户 ID |  | ||||||
|      * @return 用户信息 |  | ||||||
|      */ |  | ||||||
|     UserDO getById(Long userId); |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 根据部门 ID 列表查询 |      * 根据部门 ID 列表查询 | ||||||
|      * |      * | ||||||
|   | |||||||
| @@ -21,6 +21,8 @@ import java.util.List; | |||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
|  | import javax.annotation.Resource; | ||||||
|  |  | ||||||
| import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||||
|  |  | ||||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||||
| @@ -54,7 +56,8 @@ import top.charles7c.cnadmin.system.service.UserService; | |||||||
| public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptVO, DeptDetailVO, DeptQuery, DeptRequest> | public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptVO, DeptDetailVO, DeptQuery, DeptRequest> | ||||||
|     implements DeptService { |     implements DeptService { | ||||||
|  |  | ||||||
|     private final UserService userService; |     @Resource | ||||||
|  |     private UserService userService; | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @Transactional(rollbackFor = Exception.class) |     @Transactional(rollbackFor = Exception.class) | ||||||
|   | |||||||
| @@ -16,17 +16,24 @@ | |||||||
|  |  | ||||||
| package top.charles7c.cnadmin.system.service.impl; | package top.charles7c.cnadmin.system.service.impl; | ||||||
|  |  | ||||||
|  | import java.util.Collections; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
|  | import javax.annotation.Resource; | ||||||
|  |  | ||||||
| import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||||
|  |  | ||||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||||
| import org.springframework.transaction.annotation.Transactional; | import org.springframework.transaction.annotation.Transactional; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.collection.CollUtil; | ||||||
|  | import cn.hutool.core.lang.tree.Tree; | ||||||
|  |  | ||||||
| import top.charles7c.cnadmin.common.base.BaseServiceImpl; | import top.charles7c.cnadmin.common.base.BaseServiceImpl; | ||||||
| import top.charles7c.cnadmin.common.consts.Constants; | import top.charles7c.cnadmin.common.consts.Constants; | ||||||
| import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum; | import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum; | ||||||
|  | import top.charles7c.cnadmin.common.util.TreeUtils; | ||||||
| import top.charles7c.cnadmin.common.util.validate.CheckUtils; | import top.charles7c.cnadmin.common.util.validate.CheckUtils; | ||||||
| import top.charles7c.cnadmin.system.mapper.RoleMapper; | import top.charles7c.cnadmin.system.mapper.RoleMapper; | ||||||
| import top.charles7c.cnadmin.system.model.entity.RoleDO; | import top.charles7c.cnadmin.system.model.entity.RoleDO; | ||||||
| @@ -51,7 +58,8 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleVO, | |||||||
|     private final RoleMenuService roleMenuService; |     private final RoleMenuService roleMenuService; | ||||||
|     private final RoleDeptService roleDeptService; |     private final RoleDeptService roleDeptService; | ||||||
|     private final MenuService menuService; |     private final MenuService menuService; | ||||||
|     private final UserService userService; |     @Resource | ||||||
|  |     private UserService userService; | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @Transactional(rollbackFor = Exception.class) |     @Transactional(rollbackFor = Exception.class) | ||||||
| @@ -122,4 +130,22 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleVO, | |||||||
|             detailVO.setDeptIds(roleDeptService.listDeptIdByRoleId(roleId)); |             detailVO.setDeptIds(roleDeptService.listDeptIdByRoleId(roleId)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public List<Tree<Long>> buildTree(List<RoleVO> list) { | ||||||
|  |         return TreeUtils.build(list, (r, tree) -> { | ||||||
|  |             tree.setId(r.getRoleId()); | ||||||
|  |             tree.setName(r.getRoleName()); | ||||||
|  |             tree.setWeight(r.getRoleSort()); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public List<String> listRoleNamesByRoleIds(List<Long> roleIds) { | ||||||
|  |         List<RoleDO> roleList = super.lambdaQuery().select(RoleDO::getRoleName).in(RoleDO::getRoleId, roleIds).list(); | ||||||
|  |         if (CollUtil.isEmpty(roleList)) { | ||||||
|  |             return Collections.emptyList(); | ||||||
|  |         } | ||||||
|  |         return roleList.stream().map(RoleDO::getRoleName).collect(Collectors.toList()); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,74 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.charles7c.cnadmin.system.service.impl; | ||||||
|  |  | ||||||
|  | import java.util.Collections; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
|  | import lombok.RequiredArgsConstructor; | ||||||
|  |  | ||||||
|  | import org.springframework.stereotype.Service; | ||||||
|  |  | ||||||
|  | import com.baomidou.mybatisplus.core.toolkit.Wrappers; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.collection.CollUtil; | ||||||
|  |  | ||||||
|  | import top.charles7c.cnadmin.system.mapper.UserRoleMapper; | ||||||
|  | import top.charles7c.cnadmin.system.model.entity.UserRoleDO; | ||||||
|  | import top.charles7c.cnadmin.system.service.UserRoleService; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 用户和角色业务实现类 | ||||||
|  |  * | ||||||
|  |  * @author Charles7c | ||||||
|  |  * @since 2023/2/20 21:30 | ||||||
|  |  */ | ||||||
|  | @Service | ||||||
|  | @RequiredArgsConstructor | ||||||
|  | public class UserRoleServiceImpl implements UserRoleService { | ||||||
|  |  | ||||||
|  |     private final UserRoleMapper userRoleMapper; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void save(List<Long> roleIds, Long userId) { | ||||||
|  |         if (CollUtil.isEmpty(roleIds)) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         // 删除原有关联 | ||||||
|  |         userRoleMapper.delete(Wrappers.<UserRoleDO>lambdaQuery().eq(UserRoleDO::getUserId, userId)); | ||||||
|  |         // 保存最新关联 | ||||||
|  |         List<UserRoleDO> userRoleList = | ||||||
|  |             roleIds.stream().map(roleId -> new UserRoleDO(userId, roleId)).collect(Collectors.toList()); | ||||||
|  |         userRoleMapper.insertBatch(userRoleList); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Long countByRoleIds(List<Long> roleIds) { | ||||||
|  |         return userRoleMapper.selectCount(Wrappers.<UserRoleDO>lambdaQuery().in(UserRoleDO::getRoleId, roleIds)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public List<Long> listRoleIdsByUserId(Long userId) { | ||||||
|  |         List<UserRoleDO> userRoleList = userRoleMapper.selectList( | ||||||
|  |             Wrappers.<UserRoleDO>lambdaQuery().select(UserRoleDO::getRoleId).eq(UserRoleDO::getUserId, userId)); | ||||||
|  |         if (CollUtil.isEmpty(userRoleList)) { | ||||||
|  |             return Collections.emptyList(); | ||||||
|  |         } | ||||||
|  |         return userRoleList.stream().map(UserRoleDO::getRoleId).collect(Collectors.toList()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -20,32 +20,39 @@ import java.io.File; | |||||||
| import java.time.LocalDateTime; | import java.time.LocalDateTime; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
|  | import javax.annotation.Resource; | ||||||
|  |  | ||||||
| import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||||
|  |  | ||||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||||
| import org.springframework.transaction.annotation.Transactional; | import org.springframework.transaction.annotation.Transactional; | ||||||
| import org.springframework.web.multipart.MultipartFile; | import org.springframework.web.multipart.MultipartFile; | ||||||
|  |  | ||||||
| import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |  | ||||||
| import com.baomidou.mybatisplus.core.toolkit.Wrappers; |  | ||||||
|  |  | ||||||
| import cn.hutool.core.bean.BeanUtil; |  | ||||||
| import cn.hutool.core.io.FileUtil; | import cn.hutool.core.io.FileUtil; | ||||||
| import cn.hutool.core.io.file.FileNameUtil; | import cn.hutool.core.io.file.FileNameUtil; | ||||||
| import cn.hutool.core.util.StrUtil; | import cn.hutool.core.util.StrUtil; | ||||||
|  |  | ||||||
|  | import top.charles7c.cnadmin.common.base.BaseServiceImpl; | ||||||
| import top.charles7c.cnadmin.common.config.properties.LocalStorageProperties; | import top.charles7c.cnadmin.common.config.properties.LocalStorageProperties; | ||||||
|  | import top.charles7c.cnadmin.common.consts.Constants; | ||||||
| import top.charles7c.cnadmin.common.consts.FileConstants; | import top.charles7c.cnadmin.common.consts.FileConstants; | ||||||
|  | import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum; | ||||||
| import top.charles7c.cnadmin.common.model.dto.LoginUser; | import top.charles7c.cnadmin.common.model.dto.LoginUser; | ||||||
| import top.charles7c.cnadmin.common.service.CommonUserService; | import top.charles7c.cnadmin.common.service.CommonUserService; | ||||||
|  | import top.charles7c.cnadmin.common.util.ExceptionUtils; | ||||||
| import top.charles7c.cnadmin.common.util.FileUtils; | import top.charles7c.cnadmin.common.util.FileUtils; | ||||||
| import top.charles7c.cnadmin.common.util.SecureUtils; | import top.charles7c.cnadmin.common.util.SecureUtils; | ||||||
| import top.charles7c.cnadmin.common.util.helper.LoginHelper; | import top.charles7c.cnadmin.common.util.helper.LoginHelper; | ||||||
| import top.charles7c.cnadmin.common.util.validate.CheckUtils; | import top.charles7c.cnadmin.common.util.validate.CheckUtils; | ||||||
| import top.charles7c.cnadmin.system.mapper.UserMapper; | import top.charles7c.cnadmin.system.mapper.UserMapper; | ||||||
| import top.charles7c.cnadmin.system.mapper.UserRoleMapper; |  | ||||||
| import top.charles7c.cnadmin.system.model.entity.UserDO; | import top.charles7c.cnadmin.system.model.entity.UserDO; | ||||||
| import top.charles7c.cnadmin.system.model.entity.UserRoleDO; | import top.charles7c.cnadmin.system.model.query.UserQuery; | ||||||
|  | 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.DeptService; | ||||||
|  | import top.charles7c.cnadmin.system.service.RoleService; | ||||||
|  | import top.charles7c.cnadmin.system.service.UserRoleService; | ||||||
| import top.charles7c.cnadmin.system.service.UserService; | import top.charles7c.cnadmin.system.service.UserService; | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -56,15 +63,76 @@ import top.charles7c.cnadmin.system.service.UserService; | |||||||
|  */ |  */ | ||||||
| @Service | @Service | ||||||
| @RequiredArgsConstructor | @RequiredArgsConstructor | ||||||
| public class UserServiceImpl implements UserService, CommonUserService { | public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserVO, UserDetailVO, UserQuery, UserRequest> | ||||||
|  |     implements UserService, CommonUserService { | ||||||
|  |  | ||||||
|     private final UserMapper userMapper; |     private final UserRoleService userRoleService; | ||||||
|     private final UserRoleMapper userRoleMapper; |  | ||||||
|     private final LocalStorageProperties localStorageProperties; |     private final LocalStorageProperties localStorageProperties; | ||||||
|  |     @Resource | ||||||
|  |     private RoleService roleService; | ||||||
|  |     @Resource | ||||||
|  |     private DeptService deptService; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     @Transactional(rollbackFor = Exception.class) | ||||||
|  |     public Long add(UserRequest request) { | ||||||
|  |         String username = request.getUsername(); | ||||||
|  |         boolean isExist = this.checkNameExists(username, request.getUserId()); | ||||||
|  |         CheckUtils.throwIf(() -> isExist, String.format("新增失败,'%s'已存在", username)); | ||||||
|  |  | ||||||
|  |         // 新增用户 | ||||||
|  |         request.setStatus(DisEnableStatusEnum.ENABLE); | ||||||
|  |         Long userId = super.add(request); | ||||||
|  |         super.lambdaUpdate() | ||||||
|  |             .set(UserDO::getPassword, SecureUtils.md5Salt(Constants.DEFAULT_PASSWORD, userId.toString())) | ||||||
|  |             .set(UserDO::getPwdResetTime, LocalDateTime.now()).eq(UserDO::getUserId, userId).update(); | ||||||
|  |         // 保存用户和角色关联 | ||||||
|  |         userRoleService.save(request.getRoleIds(), userId); | ||||||
|  |         return userId; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     @Transactional(rollbackFor = Exception.class) | ||||||
|  |     public void update(UserRequest request) { | ||||||
|  |         String username = request.getUsername(); | ||||||
|  |         boolean isExist = this.checkNameExists(username, request.getUserId()); | ||||||
|  |         CheckUtils.throwIf(() -> isExist, String.format("修改失败,'%s'已存在", username)); | ||||||
|  |  | ||||||
|  |         // 更新用户 | ||||||
|  |         super.update(request); | ||||||
|  |         Long userId = request.getUserId(); | ||||||
|  |         // 保存用户和角色关联 | ||||||
|  |         userRoleService.save(request.getRoleIds(), userId); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 检查名称是否存在 | ||||||
|  |      * | ||||||
|  |      * @param name | ||||||
|  |      *            名称 | ||||||
|  |      * @param id | ||||||
|  |      *            ID | ||||||
|  |      * @return 是否存在 | ||||||
|  |      */ | ||||||
|  |     private boolean checkNameExists(String name, Long id) { | ||||||
|  |         return super.lambdaQuery().eq(UserDO::getUsername, name).ne(id != null, UserDO::getUserId, id).exists(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void fillDetail(Object detailObj) { | ||||||
|  |         super.fillDetail(detailObj); | ||||||
|  |         if (detailObj instanceof UserDetailVO) { | ||||||
|  |             UserDetailVO detailVO = (UserDetailVO)detailObj; | ||||||
|  |             detailVO.setDeptName(ExceptionUtils.exToNull(() -> deptService.get(detailVO.getDeptId()).getDeptName())); | ||||||
|  |             List<Long> roleIds = userRoleService.listRoleIdsByUserId(detailVO.getUserId()); | ||||||
|  |             detailVO.setRoleIds(roleIds); | ||||||
|  |             detailVO.setRoleNames(String.join(",", roleService.listRoleNamesByRoleIds(roleIds))); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public UserDO getByUsername(String username) { |     public UserDO getByUsername(String username) { | ||||||
|         return userMapper.selectOne(Wrappers.<UserDO>lambdaQuery().eq(UserDO::getUsername, username)); |         return super.lambdaQuery().eq(UserDO::getUsername, username).one(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -86,8 +154,7 @@ public class UserServiceImpl implements UserService, CommonUserService { | |||||||
|  |  | ||||||
|         // 更新用户头像 |         // 更新用户头像 | ||||||
|         String newAvatar = newAvatarFile.getName(); |         String newAvatar = newAvatarFile.getName(); | ||||||
|         userMapper.update(null, |         super.lambdaUpdate().set(UserDO::getAvatar, newAvatar).eq(UserDO::getUserId, userId).update(); | ||||||
|             new LambdaUpdateWrapper<UserDO>().set(UserDO::getAvatar, newAvatar).eq(UserDO::getUserId, userId)); |  | ||||||
|  |  | ||||||
|         // 删除原头像 |         // 删除原头像 | ||||||
|         LoginUser loginUser = LoginHelper.getLoginUser(); |         LoginUser loginUser = LoginHelper.getLoginUser(); | ||||||
| @@ -102,18 +169,6 @@ public class UserServiceImpl implements UserService, CommonUserService { | |||||||
|         return newAvatar; |         return newAvatar; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     @Transactional(rollbackFor = Exception.class) |  | ||||||
|     public void update(UserDO user) { |  | ||||||
|         userMapper.updateById(user); |  | ||||||
|  |  | ||||||
|         // 更新登录用户信息 |  | ||||||
|         UserDO userDO = this.getById(user.getUserId()); |  | ||||||
|         LoginUser loginUser = LoginHelper.getLoginUser(); |  | ||||||
|         BeanUtil.copyProperties(userDO, loginUser); |  | ||||||
|         LoginHelper.updateLoginUser(loginUser); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @Transactional(rollbackFor = Exception.class) |     @Transactional(rollbackFor = Exception.class) | ||||||
|     public void updatePassword(String oldPassword, String newPassword, Long userId) { |     public void updatePassword(String oldPassword, String newPassword, Long userId) { | ||||||
| @@ -123,10 +178,8 @@ public class UserServiceImpl implements UserService, CommonUserService { | |||||||
|  |  | ||||||
|         // 更新密码和密码重置时间 |         // 更新密码和密码重置时间 | ||||||
|         LocalDateTime now = LocalDateTime.now(); |         LocalDateTime now = LocalDateTime.now(); | ||||||
|         userMapper.update(null, |         super.lambdaUpdate().set(UserDO::getPassword, SecureUtils.md5Salt(newPassword, userId.toString())) | ||||||
|             new LambdaUpdateWrapper<UserDO>() |             .set(UserDO::getPwdResetTime, now).eq(UserDO::getUserId, userId).update(); | ||||||
|                 .set(UserDO::getPassword, SecureUtils.md5Salt(newPassword, userId.toString())) |  | ||||||
|                 .set(UserDO::getPwdResetTime, now).eq(UserDO::getUserId, userId)); |  | ||||||
|  |  | ||||||
|         // 更新登录用户信息 |         // 更新登录用户信息 | ||||||
|         LoginUser loginUser = LoginHelper.getLoginUser(); |         LoginUser loginUser = LoginHelper.getLoginUser(); | ||||||
| @@ -140,13 +193,12 @@ public class UserServiceImpl implements UserService, CommonUserService { | |||||||
|         UserDO userDO = this.getById(userId); |         UserDO userDO = this.getById(userId); | ||||||
|         CheckUtils.throwIfNotEqual(SecureUtils.md5Salt(currentPassword, userId.toString()), userDO.getPassword(), |         CheckUtils.throwIfNotEqual(SecureUtils.md5Salt(currentPassword, userId.toString()), userDO.getPassword(), | ||||||
|             "当前密码错误"); |             "当前密码错误"); | ||||||
|         Long count = userMapper.selectCount(Wrappers.<UserDO>lambdaQuery().eq(UserDO::getEmail, newEmail)); |         Long count = super.lambdaQuery().eq(UserDO::getEmail, newEmail).count(); | ||||||
|         CheckUtils.throwIf(() -> count > 0, "邮箱已绑定其他账号,请更换其他邮箱"); |         CheckUtils.throwIf(() -> count > 0, "邮箱已绑定其他账号,请更换其他邮箱"); | ||||||
|         CheckUtils.throwIfEqual(newEmail, userDO.getEmail(), "新邮箱不能与当前邮箱相同"); |         CheckUtils.throwIfEqual(newEmail, userDO.getEmail(), "新邮箱不能与当前邮箱相同"); | ||||||
|  |  | ||||||
|         // 更新邮箱 |         // 更新邮箱 | ||||||
|         userMapper.update(null, |         super.lambdaUpdate().set(UserDO::getEmail, newEmail).eq(UserDO::getUserId, userId).update(); | ||||||
|             new LambdaUpdateWrapper<UserDO>().set(UserDO::getEmail, newEmail).eq(UserDO::getUserId, userId)); |  | ||||||
|  |  | ||||||
|         // 更新登录用户信息 |         // 更新登录用户信息 | ||||||
|         LoginUser loginUser = LoginHelper.getLoginUser(); |         LoginUser loginUser = LoginHelper.getLoginUser(); | ||||||
| @@ -154,25 +206,18 @@ public class UserServiceImpl implements UserService, CommonUserService { | |||||||
|         LoginHelper.updateLoginUser(loginUser); |         LoginHelper.updateLoginUser(loginUser); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public UserDO getById(Long userId) { |  | ||||||
|         UserDO userDO = userMapper.selectById(userId); |  | ||||||
|         CheckUtils.throwIfNull(userDO, String.format("ID为 [%s] 的用户已不存在", userId)); |  | ||||||
|         return userDO; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public Long countByDeptIds(List<Long> deptIds) { |     public Long countByDeptIds(List<Long> deptIds) { | ||||||
|         return userMapper.selectCount(Wrappers.<UserDO>lambdaQuery().in(UserDO::getDeptId, deptIds)); |         return super.lambdaQuery().in(UserDO::getDeptId, deptIds).count(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public Long countByRoleIds(List<Long> roleIds) { |     public Long countByRoleIds(List<Long> roleIds) { | ||||||
|         return userRoleMapper.selectCount(Wrappers.<UserRoleDO>lambdaQuery().in(UserRoleDO::getRoleId, roleIds)); |         return userRoleService.countByRoleIds(roleIds); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public String getNicknameById(Long userId) { |     public String getNicknameById(Long userId) { | ||||||
|         return this.getById(userId).getNickname(); |         return super.getById(userId).getNickname(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								continew-admin-ui/components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								continew-admin-ui/components.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -75,6 +75,7 @@ declare module '@vue/runtime-core' { | |||||||
|     ATag: typeof import('@arco-design/web-vue')['Tag'] |     ATag: typeof import('@arco-design/web-vue')['Tag'] | ||||||
|     ATextarea: typeof import('@arco-design/web-vue')['Textarea'] |     ATextarea: typeof import('@arco-design/web-vue')['Textarea'] | ||||||
|     ATooltip: typeof import('@arco-design/web-vue')['Tooltip'] |     ATooltip: typeof import('@arco-design/web-vue')['Tooltip'] | ||||||
|  |     ATree: typeof import('@arco-design/web-vue')['Tree'] | ||||||
|     ATreeSelect: typeof import('@arco-design/web-vue')['TreeSelect'] |     ATreeSelect: typeof import('@arco-design/web-vue')['TreeSelect'] | ||||||
|     ATypographyParagraph: typeof import('@arco-design/web-vue')['TypographyParagraph'] |     ATypographyParagraph: typeof import('@arco-design/web-vue')['TypographyParagraph'] | ||||||
|     ATypographyText: typeof import('@arco-design/web-vue')['TypographyText'] |     ATypographyText: typeof import('@arco-design/web-vue')['TypographyText'] | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ import axios from 'axios'; | |||||||
| import qs from 'query-string'; | import qs from 'query-string'; | ||||||
| import { DeptParam } from '@/api/system/dept'; | import { DeptParam } from '@/api/system/dept'; | ||||||
| import { MenuParam } from '@/api/system/menu'; | import { MenuParam } from '@/api/system/menu'; | ||||||
|  | import { RoleParam } from '@/api/system/role'; | ||||||
| import { TreeNodeData } from '@arco-design/web-vue'; | import { TreeNodeData } from '@arco-design/web-vue'; | ||||||
|  |  | ||||||
| export function listDeptTree(params: DeptParam) { | export function listDeptTree(params: DeptParam) { | ||||||
| @@ -21,3 +22,12 @@ export function listMenuTree(params: MenuParam) { | |||||||
|     }, |     }, | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export function listRoleTree(params: RoleParam) { | ||||||
|  |   return axios.get<TreeNodeData[]>('/common/tree/role', { | ||||||
|  |     params, | ||||||
|  |     paramsSerializer: (obj) => { | ||||||
|  |       return qs.stringify(obj); | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -36,7 +36,7 @@ export function getDept(id: number) { | |||||||
|   return axios.get<DeptRecord>(`${BASE_URL}/${id}`); |   return axios.get<DeptRecord>(`${BASE_URL}/${id}`); | ||||||
| } | } | ||||||
|  |  | ||||||
| export function createDept(req: DeptRecord) { | export function addDept(req: DeptRecord) { | ||||||
|   return axios.post(BASE_URL, req); |   return axios.post(BASE_URL, req); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -44,7 +44,7 @@ export function getMenu(id: number) { | |||||||
|   return axios.get<MenuRecord>(`${BASE_URL}/${id}`); |   return axios.get<MenuRecord>(`${BASE_URL}/${id}`); | ||||||
| } | } | ||||||
|  |  | ||||||
| export function createMenu(req: MenuRecord) { | export function addMenu(req: MenuRecord) { | ||||||
|   return axios.post(BASE_URL, req); |   return axios.post(BASE_URL, req); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,9 +23,9 @@ export interface RoleRecord { | |||||||
| export interface RoleParam { | export interface RoleParam { | ||||||
|   roleName?: string; |   roleName?: string; | ||||||
|   status?: number; |   status?: number; | ||||||
|   page: number; |   page?: number; | ||||||
|   size: number; |   size?: number; | ||||||
|   sort: Array<string>; |   sort?: Array<string>; | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface RoleListRes { | export interface RoleListRes { | ||||||
| @@ -46,7 +46,7 @@ export function getRole(id: number) { | |||||||
|   return axios.get<RoleRecord>(`${BASE_URL}/${id}`); |   return axios.get<RoleRecord>(`${BASE_URL}/${id}`); | ||||||
| } | } | ||||||
|  |  | ||||||
| export function createRole(req: RoleRecord) { | export function addRole(req: RoleRecord) { | ||||||
|   return axios.post(BASE_URL, req); |   return axios.post(BASE_URL, req); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										63
									
								
								continew-admin-ui/src/api/system/user.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								continew-admin-ui/src/api/system/user.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | |||||||
|  | import axios from 'axios'; | ||||||
|  | import qs from 'query-string'; | ||||||
|  |  | ||||||
|  | const BASE_URL = '/system/user'; | ||||||
|  |  | ||||||
|  | export interface UserRecord { | ||||||
|  |   userId?: number; | ||||||
|  |   username: string; | ||||||
|  |   nickname: string; | ||||||
|  |   gender: number; | ||||||
|  |   email?: string; | ||||||
|  |   phone?: string; | ||||||
|  |   description?: string; | ||||||
|  |   roleIds?: Array<number>; | ||||||
|  |   deptId?: number; | ||||||
|  |   status?: number; | ||||||
|  |   createUserString?: string; | ||||||
|  |   createTime?: string; | ||||||
|  |   updateUserString?: string; | ||||||
|  |   updateTime?: string; | ||||||
|  |   deptName?: string; | ||||||
|  |   roleNames?: Array<string>; | ||||||
|  |   disabled?: boolean; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export interface UserParam { | ||||||
|  |   username?: string; | ||||||
|  |   status?: number; | ||||||
|  |   createTime?: Array<string>; | ||||||
|  |   page?: number; | ||||||
|  |   size?: number; | ||||||
|  |   sort?: Array<string>; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export interface UserListRes { | ||||||
|  |   list: UserRecord[]; | ||||||
|  |   total: number; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function listUser(params: UserParam) { | ||||||
|  |   return axios.get<UserListRes>(`${BASE_URL}`, { | ||||||
|  |     params, | ||||||
|  |     paramsSerializer: (obj) => { | ||||||
|  |       return qs.stringify(obj); | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function getUser(id: number) { | ||||||
|  |   return axios.get<UserRecord>(`${BASE_URL}/${id}`); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function addUser(req: UserRecord) { | ||||||
|  |   return axios.post(BASE_URL, req); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function updateUser(req: UserRecord) { | ||||||
|  |   return axios.put(BASE_URL, req); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function deleteUser(ids: number | Array<number>) { | ||||||
|  |   return axios.delete(`${BASE_URL}/${ids}`); | ||||||
|  | } | ||||||
| @@ -147,7 +147,7 @@ | |||||||
|             :size="32" |             :size="32" | ||||||
|             :style="{ marginRight: '8px', cursor: 'pointer' }" |             :style="{ marginRight: '8px', cursor: 'pointer' }" | ||||||
|           > |           > | ||||||
|             <img alt="avatar" :src="getAvatar(loginStore)" /> |             <img alt="avatar" :src="getAvatar(loginStore.avatar, loginStore.gender)" /> | ||||||
|           </a-avatar> |           </a-avatar> | ||||||
|           <template #content> |           <template #content> | ||||||
|             <a-doption> |             <a-doption> | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| import localeWorkplace from '@/views/dashboard/workplace/locale/en-US'; | import localeWorkplace from '@/views/dashboard/workplace/locale/en-US'; | ||||||
|  |  | ||||||
|  | import localeUser from '@/views/system/user/locale/en-US'; | ||||||
| import localeRole from '@/views/system/role/locale/en-US'; | import localeRole from '@/views/system/role/locale/en-US'; | ||||||
| import localeMenu from '@/views/system/menu/locale/en-US'; | import localeMenu from '@/views/system/menu/locale/en-US'; | ||||||
| import localeDept from '@/views/system/dept/locale/en-US'; | import localeDept from '@/views/system/dept/locale/en-US'; | ||||||
| @@ -50,6 +51,7 @@ export default { | |||||||
|  |  | ||||||
|   ...localeWorkplace, |   ...localeWorkplace, | ||||||
|  |  | ||||||
|  |   ...localeUser, | ||||||
|   ...localeRole, |   ...localeRole, | ||||||
|   ...localeMenu, |   ...localeMenu, | ||||||
|   ...localeDept, |   ...localeDept, | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| import localeWorkplace from '@/views/dashboard/workplace/locale/zh-CN'; | import localeWorkplace from '@/views/dashboard/workplace/locale/zh-CN'; | ||||||
|  |  | ||||||
|  | import localeUser from '@/views/system/user/locale/zh-CN'; | ||||||
| import localeRole from '@/views/system/role/locale/zh-CN'; | import localeRole from '@/views/system/role/locale/zh-CN'; | ||||||
| import localeMenu from '@/views/system/menu/locale/zh-CN'; | import localeMenu from '@/views/system/menu/locale/zh-CN'; | ||||||
| import localeDept from '@/views/system/dept/locale/zh-CN'; | import localeDept from '@/views/system/dept/locale/zh-CN'; | ||||||
| @@ -50,6 +51,7 @@ export default { | |||||||
|  |  | ||||||
|   ...localeWorkplace, |   ...localeWorkplace, | ||||||
|  |  | ||||||
|  |   ...localeUser, | ||||||
|   ...localeRole, |   ...localeRole, | ||||||
|   ...localeMenu, |   ...localeMenu, | ||||||
|   ...localeDept, |   ...localeDept, | ||||||
|   | |||||||
| @@ -12,6 +12,16 @@ const System: AppRouteRecordRaw = { | |||||||
|     order: 1, |     order: 1, | ||||||
|   }, |   }, | ||||||
|   children: [ |   children: [ | ||||||
|  |     { | ||||||
|  |       path: '/system/user', | ||||||
|  |       name: 'User', | ||||||
|  |       component: () => import('@/views/system/user/index.vue'), | ||||||
|  |       meta: { | ||||||
|  |         locale: 'menu.system.user.list', | ||||||
|  |         requiresAuth: true, | ||||||
|  |         roles: ['*'], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|     { |     { | ||||||
|       path: '/system/role', |       path: '/system/role', | ||||||
|       name: 'Role', |       name: 'Role', | ||||||
|   | |||||||
| @@ -1,20 +1,17 @@ | |||||||
| import { UserState } from '@/store/modules/login/types'; |  | ||||||
| import Unknown from '../assets/images/avatar/unknown.png'; | import Unknown from '../assets/images/avatar/unknown.png'; | ||||||
| import Male from '../assets/images/avatar/male.png'; | import Male from '../assets/images/avatar/male.png'; | ||||||
| import Female from '../assets/images/avatar/female.png'; | import Female from '../assets/images/avatar/female.png'; | ||||||
|  |  | ||||||
| export default function getAvatar(loginStore: UserState) { | export default function getAvatar(avatar: string | undefined, gender: number | undefined) { | ||||||
|   const userAvatar = loginStore.avatar; |   if (avatar) { | ||||||
|   if (userAvatar) { |  | ||||||
|     const baseUrl = import.meta.env.VITE_API_BASE_URL; |     const baseUrl = import.meta.env.VITE_API_BASE_URL; | ||||||
|     return `${baseUrl}/avatar/${userAvatar}`; |     return `${baseUrl}/avatar/${avatar}`; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const userGender = loginStore.gender; |   if (gender === 1) { | ||||||
|   if (userGender === 1) { |  | ||||||
|     return Male; |     return Male; | ||||||
|   } |   } | ||||||
|   if (userGender === 2) { |   if (gender === 2) { | ||||||
|     return Female; |     return Female; | ||||||
|   } |   } | ||||||
|   return Unknown; |   return Unknown; | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ | |||||||
|         <div v-if="userInfo"> |         <div v-if="userInfo"> | ||||||
|           <a-space :size="12"> |           <a-space :size="12"> | ||||||
|             <a-avatar :size="24"> |             <a-avatar :size="24"> | ||||||
|               <img :src="getAvatar(userInfo)" /> |               <img :src="getAvatar(userInfo.avatar, userInfo.gender)" /> | ||||||
|             </a-avatar> |             </a-avatar> | ||||||
|             <a-typography-text> |             <a-typography-text> | ||||||
|               {{ userInfo.nickname }} {{ $t('monitor.studioPreview.studio') }} |               {{ userInfo.nickname }} {{ $t('monitor.studioPreview.studio') }} | ||||||
|   | |||||||
| @@ -89,7 +89,7 @@ | |||||||
|       <a-drawer |       <a-drawer | ||||||
|         title="日志详情" |         title="日志详情" | ||||||
|         :visible="visible" |         :visible="visible" | ||||||
|         :width="570" |         :width="580" | ||||||
|         :footer="false" |         :footer="false" | ||||||
|         unmount-on-close |         unmount-on-close | ||||||
|         render-to-body |         render-to-body | ||||||
| @@ -233,7 +233,7 @@ | |||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
|   import { getCurrentInstance, ref, toRefs, reactive, } from 'vue'; |   import { getCurrentInstance, ref, toRefs, reactive } from 'vue'; | ||||||
|   import { |   import { | ||||||
|     SystemLogParam, |     SystemLogParam, | ||||||
|     SystemLogRecord, |     SystemLogRecord, | ||||||
|   | |||||||
| @@ -210,7 +210,7 @@ | |||||||
|       <a-drawer |       <a-drawer | ||||||
|         title="部门详情" |         title="部门详情" | ||||||
|         :visible="detailVisible" |         :visible="detailVisible" | ||||||
|         :width="570" |         :width="580" | ||||||
|         :footer="false" |         :footer="false" | ||||||
|         unmount-on-close |         unmount-on-close | ||||||
|         render-to-body |         render-to-body | ||||||
| @@ -292,7 +292,7 @@ | |||||||
|     DeptParam, |     DeptParam, | ||||||
|     listDept, |     listDept, | ||||||
|     getDept, |     getDept, | ||||||
|     createDept, |     addDept, | ||||||
|     updateDept, |     updateDept, | ||||||
|     deleteDept, |     deleteDept, | ||||||
|   } from '@/api/system/dept'; |   } from '@/api/system/dept'; | ||||||
| @@ -431,7 +431,7 @@ | |||||||
|             proxy.$message.success(res.msg); |             proxy.$message.success(res.msg); | ||||||
|           }); |           }); | ||||||
|         } else { |         } else { | ||||||
|           createDept(form.value).then((res) => { |           addDept(form.value).then((res) => { | ||||||
|             handleCancel(); |             handleCancel(); | ||||||
|             getList(); |             getList(); | ||||||
|             proxy.$message.success(res.msg); |             proxy.$message.success(res.msg); | ||||||
|   | |||||||
| @@ -345,7 +345,7 @@ | |||||||
|     MenuParam, |     MenuParam, | ||||||
|     listMenu, |     listMenu, | ||||||
|     getMenu, |     getMenu, | ||||||
|     createMenu, |     addMenu, | ||||||
|     updateMenu, |     updateMenu, | ||||||
|     deleteMenu, |     deleteMenu, | ||||||
|   } from '@/api/system/menu'; |   } from '@/api/system/menu'; | ||||||
| @@ -482,7 +482,7 @@ | |||||||
|             proxy.$message.success(res.msg); |             proxy.$message.success(res.msg); | ||||||
|           }); |           }); | ||||||
|         } else { |         } else { | ||||||
|           createMenu(form.value).then((res) => { |           addMenu(form.value).then((res) => { | ||||||
|             handleCancel(); |             handleCancel(); | ||||||
|             getList(); |             getList(); | ||||||
|             proxy.$message.success(res.msg); |             proxy.$message.success(res.msg); | ||||||
|   | |||||||
| @@ -181,7 +181,7 @@ | |||||||
|       <a-drawer |       <a-drawer | ||||||
|         :title="title" |         :title="title" | ||||||
|         :visible="visible" |         :visible="visible" | ||||||
|         :width="570" |         :width="580" | ||||||
|         :mask-closable="false" |         :mask-closable="false" | ||||||
|         unmount-on-close |         unmount-on-close | ||||||
|         render-to-body |         render-to-body | ||||||
| @@ -275,7 +275,7 @@ | |||||||
|       <a-drawer |       <a-drawer | ||||||
|         title="角色详情" |         title="角色详情" | ||||||
|         :visible="detailVisible" |         :visible="detailVisible" | ||||||
|         :width="570" |         :width="580" | ||||||
|         :footer="false" |         :footer="false" | ||||||
|         unmount-on-close |         unmount-on-close | ||||||
|         render-to-body |         render-to-body | ||||||
| @@ -384,7 +384,7 @@ | |||||||
|     RoleParam, |     RoleParam, | ||||||
|     listRole, |     listRole, | ||||||
|     getRole, |     getRole, | ||||||
|     createRole, |     addRole, | ||||||
|     updateRole, |     updateRole, | ||||||
|     deleteRole, |     deleteRole, | ||||||
|   } from '@/api/system/role'; |   } from '@/api/system/role'; | ||||||
| @@ -621,7 +621,7 @@ | |||||||
|         } else { |         } else { | ||||||
|           form.value.menuIds = getMenuAllCheckedKeys(); |           form.value.menuIds = getMenuAllCheckedKeys(); | ||||||
|           form.value.deptIds = getDeptAllCheckedKeys(); |           form.value.deptIds = getDeptAllCheckedKeys(); | ||||||
|           createRole(form.value).then((res) => { |           addRole(form.value).then((res) => { | ||||||
|             handleCancel(); |             handleCancel(); | ||||||
|             getList(); |             getList(); | ||||||
|             proxy.$message.success(res.msg); |             proxy.$message.success(res.msg); | ||||||
|   | |||||||
| @@ -12,7 +12,11 @@ | |||||||
|         <template #upload-button> |         <template #upload-button> | ||||||
|           <a-avatar :size="100" class="info-avatar"> |           <a-avatar :size="100" class="info-avatar"> | ||||||
|             <template #trigger-icon><icon-camera /></template> |             <template #trigger-icon><icon-camera /></template> | ||||||
|             <img v-if="avatarList.length" :src="avatarList[0].url" :alt="$t('userCenter.panel.avatar')" /> |             <img | ||||||
|  |               v-if="avatarList.length" | ||||||
|  |               :src="avatarList[0].url" | ||||||
|  |               :alt="$t('userCenter.panel.avatar')" | ||||||
|  |             /> | ||||||
|           </a-avatar> |           </a-avatar> | ||||||
|         </template> |         </template> | ||||||
|       </a-upload> |       </a-upload> | ||||||
| @@ -32,22 +36,33 @@ | |||||||
|         align="right" |         align="right" | ||||||
|         layout="inline-horizontal" |         layout="inline-horizontal" | ||||||
|       > |       > | ||||||
|         <a-descriptions-item :label="$t('userCenter.panel.label.nickname')">{{ loginStore.nickname }}</a-descriptions-item> |         <a-descriptions-item :label="$t('userCenter.panel.label.nickname')">{{ | ||||||
|  |           loginStore.nickname | ||||||
|  |         }}</a-descriptions-item> | ||||||
|         <a-descriptions-item :label="$t('userCenter.panel.label.gender')"> |         <a-descriptions-item :label="$t('userCenter.panel.label.gender')"> | ||||||
|           <div v-if="loginStore.gender === 1"> |           <div v-if="loginStore.gender === 1"> | ||||||
|             {{ $t('userCenter.panel.male') }} |             {{ $t('userCenter.panel.male') }} | ||||||
|             <icon-man style="color: #19BBF1" /> |             <icon-man style="color: #19bbf1" /> | ||||||
|           </div> |           </div> | ||||||
|           <div v-else-if="loginStore.gender === 2"> |           <div v-else-if="loginStore.gender === 2"> | ||||||
|             {{ $t('userCenter.panel.female') }} |             {{ $t('userCenter.panel.female') }} | ||||||
|             <icon-woman style="color: #FA7FA9" /> |             <icon-woman style="color: #fa7fa9" /> | ||||||
|           </div> |           </div> | ||||||
|           <div v-else>{{ $t('userCenter.panel.unknown') }}</div> |           <div v-else>{{ $t('userCenter.panel.unknown') }}</div> | ||||||
|         </a-descriptions-item> |         </a-descriptions-item> | ||||||
|         <a-descriptions-item :label="$t('userCenter.panel.label.phone')">{{ loginStore.phone }}</a-descriptions-item> |         <a-descriptions-item :label="$t('userCenter.panel.label.phone')">{{ | ||||||
|         <a-descriptions-item :label="$t('userCenter.panel.label.email')">{{ loginStore.email }}</a-descriptions-item> |           loginStore.phone | ||||||
|         <a-descriptions-item :label="$t('userCenter.panel.label.deptName')">{{ loginStore.deptName }}</a-descriptions-item> |         }}</a-descriptions-item> | ||||||
|         <a-descriptions-item :label="$t('userCenter.panel.label.registrationDate')">{{ loginStore.registrationDate }}</a-descriptions-item> |         <a-descriptions-item :label="$t('userCenter.panel.label.email')">{{ | ||||||
|  |           loginStore.email | ||||||
|  |         }}</a-descriptions-item> | ||||||
|  |         <a-descriptions-item :label="$t('userCenter.panel.label.deptName')">{{ | ||||||
|  |           loginStore.deptName | ||||||
|  |         }}</a-descriptions-item> | ||||||
|  |         <a-descriptions-item | ||||||
|  |           :label="$t('userCenter.panel.label.registrationDate')" | ||||||
|  |           >{{ loginStore.registrationDate }}</a-descriptions-item | ||||||
|  |         > | ||||||
|       </a-descriptions> |       </a-descriptions> | ||||||
|     </a-space> |     </a-space> | ||||||
|   </a-card> |   </a-card> | ||||||
| @@ -66,7 +81,7 @@ | |||||||
|   const avatar = { |   const avatar = { | ||||||
|     uid: '-2', |     uid: '-2', | ||||||
|     name: 'avatar.png', |     name: 'avatar.png', | ||||||
|     url: getAvatar(loginStore), |     url: getAvatar(loginStore.avatar, loginStore.gender), | ||||||
|   }; |   }; | ||||||
|   const avatarList = ref<FileItem[]>([avatar]); |   const avatarList = ref<FileItem[]>([avatar]); | ||||||
|  |  | ||||||
| @@ -88,13 +103,15 @@ | |||||||
|       onProgress(20); |       onProgress(20); | ||||||
|       const formData = new FormData(); |       const formData = new FormData(); | ||||||
|       formData.append(name as string, fileItem.file as Blob); |       formData.append(name as string, fileItem.file as Blob); | ||||||
|       uploadAvatar(formData).then((res) => { |       uploadAvatar(formData) | ||||||
|         onSuccess(res); |         .then((res) => { | ||||||
|         loginStore.avatar = res.data.avatar; |           onSuccess(res); | ||||||
|         proxy.$message.success(res.msg); |           loginStore.avatar = res.data.avatar; | ||||||
|       }).catch((error) => { |           proxy.$message.success(res.msg); | ||||||
|         onError(error); |         }) | ||||||
|       }); |         .catch((error) => { | ||||||
|  |           onError(error); | ||||||
|  |         }); | ||||||
|     })(); |     })(); | ||||||
|     return { |     return { | ||||||
|       abort() { |       abort() { | ||||||
| @@ -109,7 +126,10 @@ | |||||||
|    * @param fileItemList 文件列表 |    * @param fileItemList 文件列表 | ||||||
|    * @param currentFile 当前文件 |    * @param currentFile 当前文件 | ||||||
|    */ |    */ | ||||||
|   const handleAvatarChange = (fileItemList: FileItem[], currentFile: FileItem) => { |   const handleAvatarChange = ( | ||||||
|  |     fileItemList: FileItem[], | ||||||
|  |     currentFile: FileItem | ||||||
|  |   ) => { | ||||||
|     avatarList.value = [currentFile]; |     avatarList.value = [currentFile]; | ||||||
|   }; |   }; | ||||||
| </script> | </script> | ||||||
|   | |||||||
							
								
								
									
										804
									
								
								continew-admin-ui/src/views/system/user/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										804
									
								
								continew-admin-ui/src/views/system/user/index.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,804 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="app-container"> | ||||||
|  |     <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: 12px"> | ||||||
|  |           <a-input-search | ||||||
|  |             v-model="deptName" | ||||||
|  |             placeholder="输入部门名称搜索" | ||||||
|  |             style="margin-bottom: 8px; max-width: 240px" | ||||||
|  |             allow-clear | ||||||
|  |           /> | ||||||
|  |           <a-tree | ||||||
|  |             ref="deptTreeRef" | ||||||
|  |             :data="deptTree" | ||||||
|  |             default-expand-all | ||||||
|  |             show-line | ||||||
|  |             @select="handleSelectNode" | ||||||
|  |           /> | ||||||
|  |         </a-col> | ||||||
|  |         <a-col :xs="15" :sm="18" :md="19" :lg="20" :xl="19"> | ||||||
|  |           <!-- 头部区域 --> | ||||||
|  |           <div class="header"> | ||||||
|  |             <!-- 搜索栏 --> | ||||||
|  |             <div v-if="showQuery" class="header-query"> | ||||||
|  |               <a-form ref="queryRef" :model="queryParams" layout="inline"> | ||||||
|  |                 <a-form-item field="username" hide-label> | ||||||
|  |                   <a-input | ||||||
|  |                     v-model="queryParams.username" | ||||||
|  |                     placeholder="输入用户名搜索" | ||||||
|  |                     allow-clear | ||||||
|  |                     style="width: 150px" | ||||||
|  |                     @press-enter="handleQuery" | ||||||
|  |                   /> | ||||||
|  |                 </a-form-item> | ||||||
|  |                 <a-form-item field="status" hide-label> | ||||||
|  |                   <a-select | ||||||
|  |                     v-model="queryParams.status" | ||||||
|  |                     :options="statusOptions" | ||||||
|  |                     placeholder="状态搜索" | ||||||
|  |                     allow-clear | ||||||
|  |                     style="width: 150px" | ||||||
|  |                   /> | ||||||
|  |                 </a-form-item> | ||||||
|  |                 <a-form-item field="createTime" hide-label> | ||||||
|  |                   <date-range-picker v-model="queryParams.createTime" /> | ||||||
|  |                 </a-form-item> | ||||||
|  |                 <a-form-item hide-label> | ||||||
|  |                   <a-space> | ||||||
|  |                     <a-button type="primary" @click="handleQuery"> | ||||||
|  |                       <template #icon><icon-search /></template>查询 | ||||||
|  |                     </a-button> | ||||||
|  |                     <a-button @click="resetQuery"> | ||||||
|  |                       <template #icon><icon-refresh /></template>重置 | ||||||
|  |                     </a-button> | ||||||
|  |                   </a-space> | ||||||
|  |                 </a-form-item> | ||||||
|  |               </a-form> | ||||||
|  |             </div> | ||||||
|  |             <!-- 操作栏 --> | ||||||
|  |             <div class="header-operation"> | ||||||
|  |               <a-row> | ||||||
|  |                 <a-col :span="12"> | ||||||
|  |                   <a-space> | ||||||
|  |                     <a-button type="primary" @click="toCreate"> | ||||||
|  |                       <template #icon><icon-plus /></template>新增 | ||||||
|  |                     </a-button> | ||||||
|  |                     <a-button | ||||||
|  |                       type="primary" | ||||||
|  |                       status="success" | ||||||
|  |                       :disabled="single" | ||||||
|  |                       :title="single ? '请选择一条要修改的数据' : ''" | ||||||
|  |                       @click="toUpdate(ids[0])" | ||||||
|  |                     > | ||||||
|  |                       <template #icon><icon-edit /></template>修改 | ||||||
|  |                     </a-button> | ||||||
|  |                     <a-button | ||||||
|  |                       type="primary" | ||||||
|  |                       status="danger" | ||||||
|  |                       :disabled="multiple" | ||||||
|  |                       :title="multiple ? '请选择要删除的数据' : ''" | ||||||
|  |                       @click="handleBatchDelete" | ||||||
|  |                     > | ||||||
|  |                       <template #icon><icon-delete /></template>删除 | ||||||
|  |                     </a-button> | ||||||
|  |                     <a-button | ||||||
|  |                       :loading="exportLoading" | ||||||
|  |                       type="primary" | ||||||
|  |                       status="warning" | ||||||
|  |                       @click="handleExport" | ||||||
|  |                     > | ||||||
|  |                       <template #icon><icon-download /></template>导出 | ||||||
|  |                     </a-button> | ||||||
|  |                   </a-space> | ||||||
|  |                 </a-col> | ||||||
|  |                 <a-col :span="12"> | ||||||
|  |                   <right-toolbar | ||||||
|  |                     v-model:show-query="showQuery" | ||||||
|  |                     @refresh="getList" | ||||||
|  |                   /> | ||||||
|  |                 </a-col> | ||||||
|  |               </a-row> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |  | ||||||
|  |           <!-- 列表区域 --> | ||||||
|  |           <a-table | ||||||
|  |             ref="tableRef" | ||||||
|  |             :data="userList" | ||||||
|  |             :row-selection="{ | ||||||
|  |               type: 'checkbox', | ||||||
|  |               showCheckedAll: true, | ||||||
|  |               onlyCurrent: false, | ||||||
|  |             }" | ||||||
|  |             :pagination="{ | ||||||
|  |               showTotal: true, | ||||||
|  |               showPageSize: true, | ||||||
|  |               total: total, | ||||||
|  |               current: queryParams.page, | ||||||
|  |             }" | ||||||
|  |             row-key="userId" | ||||||
|  |             :bordered="false" | ||||||
|  |             :stripe="true" | ||||||
|  |             :loading="loading" | ||||||
|  |             size="large" | ||||||
|  |             :scroll="{ | ||||||
|  |               x: '120%', | ||||||
|  |             }" | ||||||
|  |             @page-change="handlePageChange" | ||||||
|  |             @page-size-change="handlePageSizeChange" | ||||||
|  |             @selection-change="handleSelectionChange" | ||||||
|  |           > | ||||||
|  |             <template #columns> | ||||||
|  |               <a-table-column title="ID" data-index="userId" /> | ||||||
|  |               <a-table-column title="用户名"> | ||||||
|  |                 <template #cell="{ record }"> | ||||||
|  |                   <a-link @click="toDetail(record.userId)">{{ | ||||||
|  |                     record.username | ||||||
|  |                   }}</a-link> | ||||||
|  |                 </template> | ||||||
|  |               </a-table-column> | ||||||
|  |               <a-table-column title="昵称" data-index="nickname" :width="120" /> | ||||||
|  |               <a-table-column title="性别"> | ||||||
|  |                 <template #cell="{ record }"> | ||||||
|  |                   <span v-if="record.gender === 1">男</span> | ||||||
|  |                   <span v-else-if="record.gender === 2">女</span> | ||||||
|  |                   <span v-else>未知</span> | ||||||
|  |                 </template> | ||||||
|  |               </a-table-column> | ||||||
|  |               <a-table-column title="头像" align="center"> | ||||||
|  |                 <template #cell="{ record }"> | ||||||
|  |                   <a-avatar> | ||||||
|  |                     <img :src="getAvatar(record.avatar, record.gender)" alt="头像" /> | ||||||
|  |                   </a-avatar> | ||||||
|  |                 </template> | ||||||
|  |               </a-table-column> | ||||||
|  |               <a-table-column title="联系方式" :width="175"> | ||||||
|  |                 <template #cell="{ record }"> | ||||||
|  |                   {{ record.email }}<br v-if="record.email && record.phone"> | ||||||
|  |                   {{ record.phone }} | ||||||
|  |                 </template> | ||||||
|  |               </a-table-column> | ||||||
|  |               <a-table-column title="状态" align="center"> | ||||||
|  |                 <template #cell="{ record }"> | ||||||
|  |                   <a-switch | ||||||
|  |                     v-model="record.status" | ||||||
|  |                     :checked-value="1" | ||||||
|  |                     :unchecked-value="2" | ||||||
|  |                     :disabled="record.disabled" | ||||||
|  |                     @change="handleChangeStatus(record)" | ||||||
|  |                   /> | ||||||
|  |                 </template> | ||||||
|  |               </a-table-column> | ||||||
|  |               <a-table-column title="描述" data-index="description" ellipsis tooltip /> | ||||||
|  |               <a-table-column title="创建人/创建时间" :width="175"> | ||||||
|  |                 <template #cell="{ record }"> | ||||||
|  |                   {{ record.createUserString }}<br> | ||||||
|  |                   {{ record.createTime }} | ||||||
|  |                 </template> | ||||||
|  |               </a-table-column> | ||||||
|  |               <a-table-column | ||||||
|  |                 title="操作" | ||||||
|  |                 align="center" | ||||||
|  |                 fixed="right" | ||||||
|  |                 :width="120" | ||||||
|  |               > | ||||||
|  |                 <template #cell="{ record }"> | ||||||
|  |                   <a-button | ||||||
|  |                     v-permission="['admin']" | ||||||
|  |                     type="text" | ||||||
|  |                     size="small" | ||||||
|  |                     title="修改" | ||||||
|  |                     @click="toUpdate(record.userId)" | ||||||
|  |                   > | ||||||
|  |                     <template #icon><icon-edit /></template>修改 | ||||||
|  |                   </a-button> | ||||||
|  |                   <a-popconfirm | ||||||
|  |                     content="确定要删除当前选中的数据吗?" | ||||||
|  |                     type="warning" | ||||||
|  |                     @ok="handleDelete([record.userId])" | ||||||
|  |                   > | ||||||
|  |                     <a-button | ||||||
|  |                       v-permission="['admin']" | ||||||
|  |                       type="text" | ||||||
|  |                       size="small" | ||||||
|  |                       title="删除" | ||||||
|  |                       :disabled="record.disabled" | ||||||
|  |                     > | ||||||
|  |                       <template #icon><icon-delete /></template>删除 | ||||||
|  |                     </a-button> | ||||||
|  |                   </a-popconfirm> | ||||||
|  |                 </template> | ||||||
|  |               </a-table-column> | ||||||
|  |             </template> | ||||||
|  |           </a-table> | ||||||
|  |         </a-col> | ||||||
|  |       </a-row> | ||||||
|  |  | ||||||
|  |       <!-- 表单区域 --> | ||||||
|  |       <a-modal | ||||||
|  |         :title="title" | ||||||
|  |         :visible="visible" | ||||||
|  |         :width="580" | ||||||
|  |         :mask-closable="false" | ||||||
|  |         unmount-on-close | ||||||
|  |         render-to-body | ||||||
|  |         @ok="handleOk" | ||||||
|  |         @cancel="handleCancel" | ||||||
|  |       > | ||||||
|  |         <a-form | ||||||
|  |           ref="formRef" | ||||||
|  |           :model="form" | ||||||
|  |           :rules="rules" | ||||||
|  |           :label-col-style="{ width: '84px' }" | ||||||
|  |           size="large" | ||||||
|  |           layout="inline" | ||||||
|  |         > | ||||||
|  |           <a-form-item label="用户名" field="username"> | ||||||
|  |             <a-input | ||||||
|  |               v-model="form.username" | ||||||
|  |               placeholder="请输入用户名" | ||||||
|  |               style="width: 162px" | ||||||
|  |             /> | ||||||
|  |           </a-form-item> | ||||||
|  |           <a-form-item label="昵称" field="nickname"> | ||||||
|  |             <a-input | ||||||
|  |               v-model="form.nickname" | ||||||
|  |               placeholder="请输入昵称" | ||||||
|  |               style="width: 162px" | ||||||
|  |             /> | ||||||
|  |           </a-form-item> | ||||||
|  |           <a-form-item label="邮箱" field="email"> | ||||||
|  |             <a-input | ||||||
|  |               v-model="form.email" | ||||||
|  |               placeholder="请输入邮箱" | ||||||
|  |               style="width: 162px" | ||||||
|  |             /> | ||||||
|  |           </a-form-item> | ||||||
|  |           <a-form-item label="手机号码" field="phone"> | ||||||
|  |             <a-input | ||||||
|  |               v-model="form.phone" | ||||||
|  |               placeholder="请输入手机号码" | ||||||
|  |               style="width: 162px" | ||||||
|  |             /> | ||||||
|  |           </a-form-item> | ||||||
|  |           <a-form-item label="性别" field="gender"> | ||||||
|  |             <a-radio-group v-model="form.gender"> | ||||||
|  |               <a-radio :value="1">男</a-radio> | ||||||
|  |               <a-radio :value="2">女</a-radio> | ||||||
|  |               <a-radio :value="0" disabled>未知</a-radio> | ||||||
|  |             </a-radio-group> | ||||||
|  |           </a-form-item> | ||||||
|  |           <a-form-item label="所属角色" field="roleIds"> | ||||||
|  |             <a-select | ||||||
|  |               v-model="form.roleIds" | ||||||
|  |               :options="roleOptions" | ||||||
|  |               :field-names="{ | ||||||
|  |                 label: 'title', | ||||||
|  |                 value: 'key', | ||||||
|  |               }" | ||||||
|  |               placeholder="请选择所属角色" | ||||||
|  |               :loading="roleLoading" | ||||||
|  |               multiple | ||||||
|  |               allow-clear | ||||||
|  |               :allow-search="{ retainInputValue: true }" | ||||||
|  |               style="width: 416px" | ||||||
|  |             /> | ||||||
|  |           </a-form-item> | ||||||
|  |           <a-form-item label="所属部门" field="deptId"> | ||||||
|  |             <a-tree-select | ||||||
|  |               v-model="form.deptId" | ||||||
|  |               :data="deptOptions" | ||||||
|  |               placeholder="请选择所属部门" | ||||||
|  |               allow-clear | ||||||
|  |               allow-search | ||||||
|  |               :filter-tree-node="filterDeptOptions" | ||||||
|  |               style="width: 416px" | ||||||
|  |             /> | ||||||
|  |           </a-form-item> | ||||||
|  |           <a-form-item label="描述" field="description"> | ||||||
|  |             <a-textarea | ||||||
|  |               v-model="form.description" | ||||||
|  |               :max-length="200" | ||||||
|  |               placeholder="请输入描述" | ||||||
|  |               :auto-size="{ | ||||||
|  |                 minRows: 3, | ||||||
|  |               }" | ||||||
|  |               show-word-limit | ||||||
|  |               style="width: 416px" | ||||||
|  |             /> | ||||||
|  |           </a-form-item> | ||||||
|  |         </a-form> | ||||||
|  |       </a-modal> | ||||||
|  |  | ||||||
|  |       <!-- 详情区域 --> | ||||||
|  |       <a-drawer | ||||||
|  |         title="用户详情" | ||||||
|  |         :visible="detailVisible" | ||||||
|  |         :width="580" | ||||||
|  |         :footer="false" | ||||||
|  |         unmount-on-close | ||||||
|  |         render-to-body | ||||||
|  |         @cancel="handleDetailCancel" | ||||||
|  |       > | ||||||
|  |         <a-descriptions title="基础信息" :column="2" bordered size="large"> | ||||||
|  |           <a-descriptions-item label="用户名"> | ||||||
|  |             <a-skeleton v-if="detailLoading" :animation="true"> | ||||||
|  |               <a-skeleton-line :rows="1" /> | ||||||
|  |             </a-skeleton> | ||||||
|  |             <span v-else>{{ user.username }}</span> | ||||||
|  |           </a-descriptions-item> | ||||||
|  |           <a-descriptions-item label="昵称"> | ||||||
|  |             <a-skeleton v-if="detailLoading" :animation="true"> | ||||||
|  |               <a-skeleton-line :rows="1" /> | ||||||
|  |             </a-skeleton> | ||||||
|  |             <span v-else>{{ user.nickname }}</span> | ||||||
|  |           </a-descriptions-item> | ||||||
|  |           <a-descriptions-item label="性别"> | ||||||
|  |             <a-skeleton v-if="detailLoading" :animation="true"> | ||||||
|  |               <a-skeleton-line :rows="1" /> | ||||||
|  |             </a-skeleton> | ||||||
|  |             <span v-else> | ||||||
|  |               <span v-if="user.gender === 1">男</span> | ||||||
|  |               <span v-else>女</span> | ||||||
|  |             </span> | ||||||
|  |           </a-descriptions-item> | ||||||
|  |           <a-descriptions-item label="状态"> | ||||||
|  |             <a-skeleton v-if="detailLoading" :animation="true"> | ||||||
|  |               <a-skeleton-line :rows="1" /> | ||||||
|  |             </a-skeleton> | ||||||
|  |             <span v-else> | ||||||
|  |               <a-tag v-if="user.status === 1" color="green">启用</a-tag> | ||||||
|  |               <a-tag v-else color="red">禁用</a-tag> | ||||||
|  |             </span> | ||||||
|  |           </a-descriptions-item> | ||||||
|  |           <a-descriptions-item label="邮箱"> | ||||||
|  |             <a-skeleton v-if="detailLoading" :animation="true"> | ||||||
|  |               <a-skeleton-line :rows="1" /> | ||||||
|  |             </a-skeleton> | ||||||
|  |             <span v-else>{{ user.email || '无' }}</span> | ||||||
|  |           </a-descriptions-item> | ||||||
|  |           <a-descriptions-item label="手机号码"> | ||||||
|  |             <a-skeleton v-if="detailLoading" :animation="true"> | ||||||
|  |               <a-skeleton-line :rows="1" /> | ||||||
|  |             </a-skeleton> | ||||||
|  |             <span v-else>{{ user.phone || '无' }}</span> | ||||||
|  |           </a-descriptions-item> | ||||||
|  |           <a-descriptions-item label="所属角色"> | ||||||
|  |             <a-skeleton v-if="detailLoading" :animation="true"> | ||||||
|  |               <a-skeleton-line :rows="1" /> | ||||||
|  |             </a-skeleton> | ||||||
|  |             <span v-else>{{ user.roleNames }}</span> | ||||||
|  |           </a-descriptions-item> | ||||||
|  |           <a-descriptions-item label="所属部门"> | ||||||
|  |             <a-skeleton v-if="detailLoading" :animation="true"> | ||||||
|  |               <a-skeleton-line :rows="1" /> | ||||||
|  |             </a-skeleton> | ||||||
|  |             <span v-else>{{ user.deptName }}</span> | ||||||
|  |           </a-descriptions-item> | ||||||
|  |           <a-descriptions-item label="创建人"> | ||||||
|  |             <a-skeleton v-if="detailLoading" :animation="true"> | ||||||
|  |               <a-skeleton-line :rows="1" /> | ||||||
|  |             </a-skeleton> | ||||||
|  |             <span v-else>{{ user.createUserString }}</span> | ||||||
|  |           </a-descriptions-item> | ||||||
|  |           <a-descriptions-item label="创建时间"> | ||||||
|  |             <a-skeleton v-if="detailLoading" :animation="true"> | ||||||
|  |               <a-skeleton-line :rows="1" /> | ||||||
|  |             </a-skeleton> | ||||||
|  |             <span v-else>{{ user.createTime }}</span> | ||||||
|  |           </a-descriptions-item> | ||||||
|  |           <a-descriptions-item label="修改人"> | ||||||
|  |             <a-skeleton v-if="detailLoading" :animation="true"> | ||||||
|  |               <a-skeleton-line :rows="1" /> | ||||||
|  |             </a-skeleton> | ||||||
|  |             <span v-else>{{ user.updateUserString }}</span> | ||||||
|  |           </a-descriptions-item> | ||||||
|  |           <a-descriptions-item label="修改时间"> | ||||||
|  |             <a-skeleton v-if="detailLoading" :animation="true"> | ||||||
|  |               <a-skeleton-line :rows="1" /> | ||||||
|  |             </a-skeleton> | ||||||
|  |             <span v-else>{{ user.updateTime }}</span> | ||||||
|  |           </a-descriptions-item> | ||||||
|  |           <a-descriptions-item label="描述"> | ||||||
|  |             <a-skeleton v-if="detailLoading" :animation="true"> | ||||||
|  |               <a-skeleton-line :rows="1" /> | ||||||
|  |             </a-skeleton> | ||||||
|  |             <span v-else>{{ user.description }}</span> | ||||||
|  |           </a-descriptions-item> | ||||||
|  |         </a-descriptions> | ||||||
|  |       </a-drawer> | ||||||
|  |     </a-card> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script lang="ts" setup> | ||||||
|  |   import { getCurrentInstance, ref, toRefs, reactive, watch } from 'vue'; | ||||||
|  |   import { SelectOptionData, TreeNodeData } from '@arco-design/web-vue'; | ||||||
|  |   import { | ||||||
|  |     UserRecord, | ||||||
|  |     UserParam, | ||||||
|  |     listUser, | ||||||
|  |     getUser, | ||||||
|  |     addUser, | ||||||
|  |     updateUser, | ||||||
|  |     deleteUser, | ||||||
|  |   } from '@/api/system/user'; | ||||||
|  |   import { listRoleTree, listDeptTree } from '@/api/common'; | ||||||
|  |   import getAvatar from '@/utils/avatar'; | ||||||
|  |  | ||||||
|  |   const { proxy } = getCurrentInstance() as any; | ||||||
|  |  | ||||||
|  |   const userList = ref<UserRecord[]>([]); | ||||||
|  |   const user = ref<UserRecord>({ | ||||||
|  |     username: '', | ||||||
|  |     nickname: '', | ||||||
|  |     gender: 1, | ||||||
|  |     email: undefined, | ||||||
|  |     phone: undefined, | ||||||
|  |     status: 1, | ||||||
|  |     createUserString: '', | ||||||
|  |     createTime: '', | ||||||
|  |     updateUserString: '', | ||||||
|  |     updateTime: '', | ||||||
|  |     description: '', | ||||||
|  |     roleIds: undefined, | ||||||
|  |     deptId: undefined, | ||||||
|  |   }); | ||||||
|  |   const total = ref(0); | ||||||
|  |   const ids = ref<Array<number>>([]); | ||||||
|  |   const title = ref(''); | ||||||
|  |   const single = ref(true); | ||||||
|  |   const multiple = ref(true); | ||||||
|  |   const showQuery = ref(true); | ||||||
|  |   const loading = ref(false); | ||||||
|  |   const detailLoading = ref(false); | ||||||
|  |   const exportLoading = ref(false); | ||||||
|  |   const visible = ref(false); | ||||||
|  |   const detailVisible = ref(false); | ||||||
|  |   const statusOptions = ref<SelectOptionData[]>([ | ||||||
|  |     { label: '启用', value: 1 }, | ||||||
|  |     { label: '禁用', value: 2 }, | ||||||
|  |   ]); | ||||||
|  |   const roleLoading = ref(false); | ||||||
|  |   const deptLoading = ref(false); | ||||||
|  |   const roleOptions = ref<TreeNodeData[]>([]); | ||||||
|  |   const deptOptions = ref<TreeNodeData[]>([]); | ||||||
|  |   const deptTree = ref<TreeNodeData[]>([]); | ||||||
|  |   const deptName = ref(''); | ||||||
|  |  | ||||||
|  |   const data = reactive({ | ||||||
|  |     // 查询参数 | ||||||
|  |     queryParams: { | ||||||
|  |       username: undefined, | ||||||
|  |       status: undefined, | ||||||
|  |       createTime: undefined, | ||||||
|  |       deptId: undefined, | ||||||
|  |       page: 1, | ||||||
|  |       size: 10, | ||||||
|  |       sort: ['createTime,desc'], | ||||||
|  |     }, | ||||||
|  |     // 表单数据 | ||||||
|  |     form: {} as UserRecord, | ||||||
|  |     // 表单验证规则 | ||||||
|  |     rules: { | ||||||
|  |       username: [{ required: true, message: '请输入用户名' }], | ||||||
|  |       nickname: [{ required: true, message: '请输入昵称' }], | ||||||
|  |       roleIds: [{ required: true, message: '请选择所属角色' }], | ||||||
|  |       deptId: [{ required: true, message: '请选择所属部门' }], | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  |   const { queryParams, form, rules } = toRefs(data); | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 查询部门树 | ||||||
|  |    * | ||||||
|  |    * @param name 名称 | ||||||
|  |    */ | ||||||
|  |   const getDeptTree = (name: string) => { | ||||||
|  |     listDeptTree({ deptName: name }).then((res) => { | ||||||
|  |       deptTree.value = res.data; | ||||||
|  |       setTimeout(() => { | ||||||
|  |         proxy.$refs.deptTreeRef.expandAll(); | ||||||
|  |       }, 0); | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  |   getDeptTree(''); | ||||||
|  |   watch(deptName, (val) => { | ||||||
|  |     getDeptTree(val); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 查询列表 | ||||||
|  |    * | ||||||
|  |    * @param params 查询参数 | ||||||
|  |    */ | ||||||
|  |   const getList = (params: UserParam = { ...queryParams.value }) => { | ||||||
|  |     loading.value = true; | ||||||
|  |     listUser(params) | ||||||
|  |       .then((res) => { | ||||||
|  |         userList.value = res.data.list; | ||||||
|  |         total.value = res.data.total; | ||||||
|  |       }) | ||||||
|  |       .finally(() => { | ||||||
|  |         loading.value = false; | ||||||
|  |       }); | ||||||
|  |   }; | ||||||
|  |   getList(); | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 打开新增对话框 | ||||||
|  |    */ | ||||||
|  |   const toCreate = () => { | ||||||
|  |     reset(); | ||||||
|  |     getRoleOptions(); | ||||||
|  |     getDeptOptions(); | ||||||
|  |     title.value = '新增用户'; | ||||||
|  |     visible.value = true; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 打开修改对话框 | ||||||
|  |    * | ||||||
|  |    * @param id ID | ||||||
|  |    */ | ||||||
|  |   const toUpdate = (id: number) => { | ||||||
|  |     reset(); | ||||||
|  |     getRoleOptions(); | ||||||
|  |     getDeptOptions(); | ||||||
|  |     getUser(id).then((res) => { | ||||||
|  |       form.value = res.data; | ||||||
|  |       title.value = '修改用户'; | ||||||
|  |       visible.value = true; | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 查询角色列表 | ||||||
|  |    */ | ||||||
|  |   const getRoleOptions = () => { | ||||||
|  |     roleLoading.value = true; | ||||||
|  |     listRoleTree({}) | ||||||
|  |       .then((res) => { | ||||||
|  |         roleOptions.value = res.data; | ||||||
|  |       }) | ||||||
|  |       .finally(() => { | ||||||
|  |         roleLoading.value = false; | ||||||
|  |       }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 查询部门列表 | ||||||
|  |    */ | ||||||
|  |   const getDeptOptions = () => { | ||||||
|  |     deptLoading.value = true; | ||||||
|  |     listDeptTree({}) | ||||||
|  |       .then((res) => { | ||||||
|  |         deptOptions.value = res.data; | ||||||
|  |       }) | ||||||
|  |       .finally(() => { | ||||||
|  |         deptLoading.value = false; | ||||||
|  |       }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 重置表单 | ||||||
|  |    */ | ||||||
|  |   const reset = () => { | ||||||
|  |     form.value = { | ||||||
|  |       userId: undefined, | ||||||
|  |       username: '', | ||||||
|  |       nickname: '', | ||||||
|  |       gender: 1, | ||||||
|  |       email: undefined, | ||||||
|  |       phone: undefined, | ||||||
|  |       description: '', | ||||||
|  |       status: 1, | ||||||
|  |       roleIds: [], | ||||||
|  |       deptId: undefined, | ||||||
|  |     }; | ||||||
|  |     proxy.$refs.formRef?.resetFields(); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 取消 | ||||||
|  |    */ | ||||||
|  |   const handleCancel = () => { | ||||||
|  |     visible.value = false; | ||||||
|  |     proxy.$refs.formRef.resetFields(); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 确定 | ||||||
|  |    */ | ||||||
|  |   const handleOk = () => { | ||||||
|  |     proxy.$refs.formRef.validate((valid: any) => { | ||||||
|  |       if (!valid) { | ||||||
|  |         if (form.value.userId !== undefined) { | ||||||
|  |           updateUser(form.value).then((res) => { | ||||||
|  |             handleCancel(); | ||||||
|  |             getList(); | ||||||
|  |             proxy.$message.success(res.msg); | ||||||
|  |           }); | ||||||
|  |         } else { | ||||||
|  |           addUser(form.value).then((res) => { | ||||||
|  |             handleCancel(); | ||||||
|  |             getList(); | ||||||
|  |             proxy.$message.success(res.msg); | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 查看详情 | ||||||
|  |    * | ||||||
|  |    * @param id ID | ||||||
|  |    */ | ||||||
|  |   const toDetail = async (id: number) => { | ||||||
|  |     if (detailLoading.value) return; | ||||||
|  |     detailLoading.value = true; | ||||||
|  |     detailVisible.value = true; | ||||||
|  |     getUser(id) | ||||||
|  |       .then((res) => { | ||||||
|  |         user.value = res.data; | ||||||
|  |       }) | ||||||
|  |       .finally(() => { | ||||||
|  |         detailLoading.value = false; | ||||||
|  |       }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 关闭详情 | ||||||
|  |    */ | ||||||
|  |   const handleDetailCancel = () => { | ||||||
|  |     detailVisible.value = false; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 批量删除 | ||||||
|  |    */ | ||||||
|  |   const handleBatchDelete = () => { | ||||||
|  |     if (ids.value.length === 0) { | ||||||
|  |       proxy.$message.info('请选择要删除的数据'); | ||||||
|  |     } else { | ||||||
|  |       proxy.$modal.warning({ | ||||||
|  |         title: '警告', | ||||||
|  |         titleAlign: 'start', | ||||||
|  |         content: '确定要删除当前选中的数据吗?', | ||||||
|  |         hideCancel: false, | ||||||
|  |         onOk: () => { | ||||||
|  |           handleDelete(ids.value); | ||||||
|  |         }, | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 删除 | ||||||
|  |    * | ||||||
|  |    * @param ids ID 列表 | ||||||
|  |    */ | ||||||
|  |   const handleDelete = (ids: Array<number>) => { | ||||||
|  |     deleteUser(ids).then((res) => { | ||||||
|  |       proxy.$message.success(res.msg); | ||||||
|  |       getList(); | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 已选择的数据行发生改变时触发 | ||||||
|  |    * | ||||||
|  |    * @param rowKeys ID 列表 | ||||||
|  |    */ | ||||||
|  |   const handleSelectionChange = (rowKeys: Array<any>) => { | ||||||
|  |     ids.value = rowKeys; | ||||||
|  |     single.value = rowKeys.length !== 1; | ||||||
|  |     multiple.value = !rowKeys.length; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 导出 | ||||||
|  |    */ | ||||||
|  |   const handleExport = () => { | ||||||
|  |     if (exportLoading.value) return; | ||||||
|  |     exportLoading.value = true; | ||||||
|  |     proxy | ||||||
|  |       .download('/system/user/export', { ...queryParams.value }, '用户数据') | ||||||
|  |       .finally(() => { | ||||||
|  |         exportLoading.value = false; | ||||||
|  |       }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 修改状态 | ||||||
|  |    * | ||||||
|  |    * @param record 记录信息 | ||||||
|  |    */ | ||||||
|  |   const handleChangeStatus = (record: UserRecord) => { | ||||||
|  |     const tip = record.status === 1 ? '启用' : '禁用'; | ||||||
|  |     updateUser(record) | ||||||
|  |       .then(() => { | ||||||
|  |         proxy.$message.success(`${tip}成功`); | ||||||
|  |       }) | ||||||
|  |       .catch(() => { | ||||||
|  |         record.status = record.status === 1 ? 2 : 1; | ||||||
|  |       }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 过滤部门列表 | ||||||
|  |    * | ||||||
|  |    * @param searchValue 搜索值 | ||||||
|  |    * @param nodeData 节点值 | ||||||
|  |    */ | ||||||
|  |   const filterDeptOptions = (searchValue: string, nodeData: TreeNodeData) => { | ||||||
|  |     if (nodeData.title) { | ||||||
|  |       return ( | ||||||
|  |         nodeData.title.toLowerCase().indexOf(searchValue.toLowerCase()) > -1 | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 根据选中部门查询 | ||||||
|  |    * | ||||||
|  |    * @param keys 选中节点 key | ||||||
|  |    */ | ||||||
|  |   const handleSelectNode = (keys: Array<any>) => { | ||||||
|  |     if (queryParams.value.deptId === keys[0]) { | ||||||
|  |       queryParams.value.deptId = undefined; | ||||||
|  |       // 如已选中,再次点击则取消选中 | ||||||
|  |       proxy.$refs.deptTreeRef.selectNode(keys, false); | ||||||
|  |     } else { | ||||||
|  |       queryParams.value.deptId = keys.length === 1 ? keys[0] : undefined; | ||||||
|  |     } | ||||||
|  |     handleQuery(); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 查询 | ||||||
|  |    */ | ||||||
|  |   const handleQuery = () => { | ||||||
|  |     getList(); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 重置 | ||||||
|  |    */ | ||||||
|  |   const resetQuery = () => { | ||||||
|  |     proxy.$refs.queryRef.resetFields(); | ||||||
|  |     handleQuery(); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 切换页码 | ||||||
|  |    * | ||||||
|  |    * @param current 页码 | ||||||
|  |    */ | ||||||
|  |   const handlePageChange = (current: number) => { | ||||||
|  |     queryParams.value.page = current; | ||||||
|  |     getList(); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 切换每页条数 | ||||||
|  |    * | ||||||
|  |    * @param pageSize 每页条数 | ||||||
|  |    */ | ||||||
|  |   const handlePageSizeChange = (pageSize: number) => { | ||||||
|  |     queryParams.value.size = pageSize; | ||||||
|  |     getList(); | ||||||
|  |   }; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <script lang="ts"> | ||||||
|  |   export default { | ||||||
|  |     name: 'User', | ||||||
|  |   }; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style scoped lang="less"></style> | ||||||
							
								
								
									
										3
									
								
								continew-admin-ui/src/views/system/user/locale/en-US.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								continew-admin-ui/src/views/system/user/locale/en-US.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | export default { | ||||||
|  |   'menu.system.user.list': 'User management', | ||||||
|  | }; | ||||||
							
								
								
									
										3
									
								
								continew-admin-ui/src/views/system/user/locale/zh-CN.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								continew-admin-ui/src/views/system/user/locale/zh-CN.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | export default { | ||||||
|  |   'menu.system.user.list': '用户管理', | ||||||
|  | }; | ||||||
| @@ -35,10 +35,13 @@ import top.charles7c.cnadmin.common.model.vo.R; | |||||||
| import top.charles7c.cnadmin.monitor.annotation.Log; | import top.charles7c.cnadmin.monitor.annotation.Log; | ||||||
| import top.charles7c.cnadmin.system.model.query.DeptQuery; | import top.charles7c.cnadmin.system.model.query.DeptQuery; | ||||||
| import top.charles7c.cnadmin.system.model.query.MenuQuery; | import top.charles7c.cnadmin.system.model.query.MenuQuery; | ||||||
|  | import top.charles7c.cnadmin.system.model.query.RoleQuery; | ||||||
| import top.charles7c.cnadmin.system.model.vo.DeptVO; | import top.charles7c.cnadmin.system.model.vo.DeptVO; | ||||||
| import top.charles7c.cnadmin.system.model.vo.MenuVO; | import top.charles7c.cnadmin.system.model.vo.MenuVO; | ||||||
|  | import top.charles7c.cnadmin.system.model.vo.RoleVO; | ||||||
| import top.charles7c.cnadmin.system.service.DeptService; | import top.charles7c.cnadmin.system.service.DeptService; | ||||||
| import top.charles7c.cnadmin.system.service.MenuService; | import top.charles7c.cnadmin.system.service.MenuService; | ||||||
|  | import top.charles7c.cnadmin.system.service.RoleService; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 公共 API |  * 公共 API | ||||||
| @@ -54,6 +57,7 @@ public class CommonController { | |||||||
|  |  | ||||||
|     private final DeptService deptService; |     private final DeptService deptService; | ||||||
|     private final MenuService menuService; |     private final MenuService menuService; | ||||||
|  |     private final RoleService roleService; | ||||||
|  |  | ||||||
|     @Log(ignore = true) |     @Log(ignore = true) | ||||||
|     @Operation(summary = "查询部门树", description = "查询树结构的部门列表") |     @Operation(summary = "查询部门树", description = "查询树结构的部门列表") | ||||||
| @@ -72,4 +76,13 @@ public class CommonController { | |||||||
|         List<Tree<Long>> treeList = menuService.buildTree(list); |         List<Tree<Long>> treeList = menuService.buildTree(list); | ||||||
|         return R.ok(treeList); |         return R.ok(treeList); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Log(ignore = true) | ||||||
|  |     @Operation(summary = "查询角色树", description = "查询树结构的角色列表") | ||||||
|  |     @GetMapping("/tree/role") | ||||||
|  |     public R<List<Tree<Long>>> listRoleTree(@Validated RoleQuery query, @Validated SortQuery sortQuery) { | ||||||
|  |         List<RoleVO> list = roleService.list(query, sortQuery); | ||||||
|  |         List<Tree<Long>> treeList = roleService.buildTree(list); | ||||||
|  |         return R.ok(treeList); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -38,10 +38,10 @@ import top.charles7c.cnadmin.common.util.RedisUtils; | |||||||
| import top.charles7c.cnadmin.common.util.SecureUtils; | import top.charles7c.cnadmin.common.util.SecureUtils; | ||||||
| import top.charles7c.cnadmin.common.util.helper.LoginHelper; | import top.charles7c.cnadmin.common.util.helper.LoginHelper; | ||||||
| import top.charles7c.cnadmin.common.util.validate.ValidationUtils; | import top.charles7c.cnadmin.common.util.validate.ValidationUtils; | ||||||
| import top.charles7c.cnadmin.system.model.entity.UserDO; |  | ||||||
| import top.charles7c.cnadmin.system.model.request.UpdateBasicInfoRequest; | import top.charles7c.cnadmin.system.model.request.UpdateBasicInfoRequest; | ||||||
| import top.charles7c.cnadmin.system.model.request.UpdateEmailRequest; | import top.charles7c.cnadmin.system.model.request.UpdateEmailRequest; | ||||||
| import top.charles7c.cnadmin.system.model.request.UpdatePasswordRequest; | 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.model.vo.AvatarVO; | ||||||
| import top.charles7c.cnadmin.system.service.UserService; | import top.charles7c.cnadmin.system.service.UserService; | ||||||
|  |  | ||||||
| @@ -73,10 +73,10 @@ public class UserCenterController { | |||||||
|     @Operation(summary = "修改基础信息", description = "修改用户基础信息") |     @Operation(summary = "修改基础信息", description = "修改用户基础信息") | ||||||
|     @PatchMapping("/basic/info") |     @PatchMapping("/basic/info") | ||||||
|     public R updateBasicInfo(@Validated @RequestBody UpdateBasicInfoRequest updateBasicInfoRequest) { |     public R updateBasicInfo(@Validated @RequestBody UpdateBasicInfoRequest updateBasicInfoRequest) { | ||||||
|         UserDO userDO = new UserDO(); |         UserRequest userRequest = new UserRequest(); | ||||||
|         userDO.setUserId(LoginHelper.getUserId()); |         userRequest.setUserId(LoginHelper.getUserId()); | ||||||
|         BeanUtil.copyProperties(updateBasicInfoRequest, userDO); |         BeanUtil.copyProperties(updateBasicInfoRequest, userRequest); | ||||||
|         userService.update(userDO); |         userService.update(userRequest); | ||||||
|         return R.ok("修改成功"); |         return R.ok("修改成功"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,52 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.charles7c.cnadmin.webapi.controller.system; | ||||||
|  |  | ||||||
|  | import io.swagger.v3.oas.annotations.tags.Tag; | ||||||
|  |  | ||||||
|  | import org.springframework.validation.annotation.Validated; | ||||||
|  | import org.springframework.web.bind.annotation.RequestBody; | ||||||
|  | import org.springframework.web.bind.annotation.RestController; | ||||||
|  |  | ||||||
|  | import top.charles7c.cnadmin.common.annotation.CrudRequestMapping; | ||||||
|  | import top.charles7c.cnadmin.common.base.BaseController; | ||||||
|  | import top.charles7c.cnadmin.common.base.BaseRequest; | ||||||
|  | import top.charles7c.cnadmin.common.consts.Constants; | ||||||
|  | import top.charles7c.cnadmin.common.model.vo.R; | ||||||
|  | import top.charles7c.cnadmin.system.model.query.UserQuery; | ||||||
|  | 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.UserService; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 用户管理 API | ||||||
|  |  * | ||||||
|  |  * @author Charles7c | ||||||
|  |  * @since 2023/2/20 21:00 | ||||||
|  |  */ | ||||||
|  | @Tag(name = "用户管理 API") | ||||||
|  | @RestController | ||||||
|  | @CrudRequestMapping("/system/user") | ||||||
|  | public class UserController extends BaseController<UserService, UserVO, UserDetailVO, UserQuery, UserRequest> { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected R<Long> add(@Validated(BaseRequest.Create.class) @RequestBody UserRequest request) { | ||||||
|  |         Long id = baseService.add(request); | ||||||
|  |         return R.ok(String.format("新增成功,请牢记默认密码:%s", Constants.DEFAULT_PASSWORD), id); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -161,7 +161,15 @@ spring: | |||||||
|     # 配合 Maven Profile 选择不同配置文件进行启动,在 IntelliJ IDEA 右侧 Maven 工具窗口可以快速切换环境 |     # 配合 Maven Profile 选择不同配置文件进行启动,在 IntelliJ IDEA 右侧 Maven 工具窗口可以快速切换环境 | ||||||
|     active: @profiles.active@ |     active: @profiles.active@ | ||||||
|   main: |   main: | ||||||
|  |     # 允许定义重名的 bean 对象覆盖原有的 bean | ||||||
|     allow-bean-definition-overriding: true |     allow-bean-definition-overriding: true | ||||||
|  |     # 允许循环依赖 | ||||||
|  |     allow-circular-references: true | ||||||
|  |   ## MVC 配置 | ||||||
|  |   mvc: | ||||||
|  |     format: | ||||||
|  |       # 日期格式化(针对 java.util.Date) | ||||||
|  |       date-time: yyyy-MM-dd HH:mm:ss | ||||||
|   ## JSON 配置 |   ## JSON 配置 | ||||||
|   jackson: |   jackson: | ||||||
|     # 时区配置 |     # 时区配置 | ||||||
|   | |||||||
| @@ -42,29 +42,29 @@ INSERT IGNORE INTO `sys_role` VALUES (1, '超级管理员', 'admin', 1, '系统 | |||||||
| INSERT IGNORE INTO `sys_role` VALUES (2, '测试人员', 'test', 5, '系统初始角色', 2, 2, 1, NOW(), 1, NOW()); | INSERT IGNORE INTO `sys_role` VALUES (2, '测试人员', 'test', 5, '系统初始角色', 2, 2, 1, NOW(), 1, NOW()); | ||||||
|  |  | ||||||
| -- 初始化默认用户:admin/admin123;test/123456 | -- 初始化默认用户:admin/admin123;test/123456 | ||||||
| INSERT IGNORE INTO `sys_user` VALUES (1, 'admin', '超级管理员', '9802815bcc5baae7feb1ae0d0566baf2', 1, '18888888888', 'charles7c@126.com', NULL, '系统初始用户', 1, NOW(), 1, 1, NOW(), 1, NOW()); | INSERT IGNORE INTO `sys_user` VALUES (1, 'admin', '超级管理员', '9802815bcc5baae7feb1ae0d0566baf2', 1, 'charles7c@126.com', '18888888888', NULL, '系统初始用户', 1, NOW(), 1, 1, NOW(), 1, NOW()); | ||||||
| INSERT IGNORE INTO `sys_user` VALUES (2, 'test', '测试员', '8e114197e1b33783a00542ad67e80516', 0, NULL, NULL, NULL, '系统初始用户', 2, NOW(), 2, 1, NOW(), 1, NOW()); | INSERT IGNORE INTO `sys_user` VALUES (2, 'test', '测试员', '8e114197e1b33783a00542ad67e80516', 2, NULL, NULL, NULL, '系统初始用户', 2, NOW(), 2, 1, NOW(), 1, NOW()); | ||||||
|  |  | ||||||
| -- 初始化默认角色和菜单关联数据 | -- 初始化默认角色和菜单关联数据 | ||||||
| INSERT INTO `sys_role_menu` VALUES (2, 1000); | INSERT IGNORE INTO `sys_role_menu` VALUES (2, 1000); | ||||||
| INSERT INTO `sys_role_menu` VALUES (2, 1010); | INSERT IGNORE INTO `sys_role_menu` VALUES (2, 1010); | ||||||
| INSERT INTO `sys_role_menu` VALUES (2, 1011); | INSERT IGNORE INTO `sys_role_menu` VALUES (2, 1011); | ||||||
| INSERT INTO `sys_role_menu` VALUES (2, 1012); | INSERT IGNORE INTO `sys_role_menu` VALUES (2, 1012); | ||||||
| INSERT INTO `sys_role_menu` VALUES (2, 1013); | INSERT IGNORE INTO `sys_role_menu` VALUES (2, 1013); | ||||||
| INSERT INTO `sys_role_menu` VALUES (2, 1014); | INSERT IGNORE INTO `sys_role_menu` VALUES (2, 1014); | ||||||
| INSERT INTO `sys_role_menu` VALUES (2, 1030); | INSERT IGNORE INTO `sys_role_menu` VALUES (2, 1030); | ||||||
| INSERT INTO `sys_role_menu` VALUES (2, 1031); | INSERT IGNORE INTO `sys_role_menu` VALUES (2, 1031); | ||||||
| INSERT INTO `sys_role_menu` VALUES (2, 1032); | INSERT IGNORE INTO `sys_role_menu` VALUES (2, 1032); | ||||||
| INSERT INTO `sys_role_menu` VALUES (2, 1033); | INSERT IGNORE INTO `sys_role_menu` VALUES (2, 1033); | ||||||
| INSERT INTO `sys_role_menu` VALUES (2, 1034); | INSERT IGNORE INTO `sys_role_menu` VALUES (2, 1034); | ||||||
| INSERT INTO `sys_role_menu` VALUES (2, 1050); | INSERT IGNORE INTO `sys_role_menu` VALUES (2, 1050); | ||||||
| INSERT INTO `sys_role_menu` VALUES (2, 1051); | INSERT IGNORE INTO `sys_role_menu` VALUES (2, 1051); | ||||||
| INSERT INTO `sys_role_menu` VALUES (2, 1052); | INSERT IGNORE INTO `sys_role_menu` VALUES (2, 1052); | ||||||
| INSERT INTO `sys_role_menu` VALUES (2, 1053); | INSERT IGNORE INTO `sys_role_menu` VALUES (2, 1053); | ||||||
| INSERT INTO `sys_role_menu` VALUES (2, 1054); | INSERT IGNORE INTO `sys_role_menu` VALUES (2, 1054); | ||||||
|  |  | ||||||
| -- 初始化默认角色和部门关联数据 | -- 初始化默认角色和部门关联数据 | ||||||
| INSERT INTO `sys_role_dept` VALUES (2, 5); | INSERT IGNORE INTO `sys_role_dept` VALUES (2, 5); | ||||||
|  |  | ||||||
| -- 初始化默认用户和角色关联数据 | -- 初始化默认用户和角色关联数据 | ||||||
| INSERT IGNORE INTO `sys_user_role` VALUES (1, 1); | INSERT IGNORE INTO `sys_user_role` VALUES (1, 1); | ||||||
|   | |||||||
| @@ -78,8 +78,8 @@ CREATE TABLE IF NOT EXISTS `sys_user`  ( | |||||||
|     `nickname` varchar(255) DEFAULT NULL COMMENT '昵称', |     `nickname` varchar(255) DEFAULT NULL COMMENT '昵称', | ||||||
|     `password` varchar(255) DEFAULT NULL COMMENT '密码', |     `password` varchar(255) DEFAULT NULL COMMENT '密码', | ||||||
|     `gender` tinyint(1) unsigned DEFAULT 0 COMMENT '性别(0未知 1男 2女)', |     `gender` tinyint(1) unsigned DEFAULT 0 COMMENT '性别(0未知 1男 2女)', | ||||||
|     `phone` varchar(255) DEFAULT NULL COMMENT '手机号码', |  | ||||||
|     `email` varchar(255) DEFAULT NULL COMMENT '邮箱', |     `email` varchar(255) DEFAULT NULL COMMENT '邮箱', | ||||||
|  |     `phone` varchar(255) DEFAULT NULL COMMENT '手机号码', | ||||||
|     `avatar` varchar(255) DEFAULT NULL COMMENT '头像地址', |     `avatar` varchar(255) DEFAULT NULL COMMENT '头像地址', | ||||||
|     `description` varchar(512) DEFAULT NULL COMMENT '描述', |     `description` varchar(512) DEFAULT NULL COMMENT '描述', | ||||||
|     `status` tinyint(1) unsigned DEFAULT 1 COMMENT '状态(1启用 2禁用)', |     `status` tinyint(1) unsigned DEFAULT 1 COMMENT '状态(1启用 2禁用)', | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user