fix user group permission scope
This commit is contained in:
@@ -216,13 +216,6 @@ public class AuthInterceptor implements HandlerInterceptor {
|
|||||||
requestContext.setLangMap(userDto.getLangMap());
|
requestContext.setLangMap(userDto.getLangMap());
|
||||||
requestContext.setLang(userDto.getLang());
|
requestContext.setLang(userDto.getLang());
|
||||||
log.debug("JWT token OK, userId: {} ", userDto.getId());
|
log.debug("JWT token OK, userId: {} ", userDto.getId());
|
||||||
if (originalRequestUri.startsWith("/api/system/")) {
|
|
||||||
//判断是不是管理员
|
|
||||||
if (userDto.getRole() != User.Role.Admin) {
|
|
||||||
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.permissionDenied);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String header = request.getHeader("X-Client-Type");
|
String header = request.getHeader("X-Client-Type");
|
||||||
if (header != null || token.startsWith("ticket")) {
|
if (header != null || token.startsWith("ticket")) {
|
||||||
authService.renewToken(token);
|
authService.renewToken(token);
|
||||||
@@ -358,4 +351,4 @@ public class AuthInterceptor implements HandlerInterceptor {
|
|||||||
throw BizException.of(HttpStatusEnum.UNAUTHORIZED, ErrorCodeEnum.UNAUTHORIZED,
|
throw BizException.of(HttpStatusEnum.UNAUTHORIZED, ErrorCodeEnum.UNAUTHORIZED,
|
||||||
BizExceptionCodeEnum.systemUnauthorizedOrSessionExpired);
|
BizExceptionCodeEnum.systemUnauthorizedOrSessionExpired);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class UserQueryDto implements Serializable {
|
public class UserQueryDto implements Serializable {
|
||||||
@@ -29,4 +30,7 @@ public class UserQueryDto implements Serializable {
|
|||||||
|
|
||||||
@Schema(description = "角色")
|
@Schema(description = "角色")
|
||||||
private User.Role role;
|
private User.Role role;
|
||||||
|
|
||||||
|
@Schema(description = "用户ID范围,后端数据权限过滤使用")
|
||||||
|
private List<Long> userIds;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.xspaceagi.system.application.dto.permission;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ public class SysGroupAddDto implements Serializable {
|
|||||||
private Integer authEnabled;
|
private Integer authEnabled;
|
||||||
|
|
||||||
@Schema(description = "授权到期时间,空表示不过期")
|
@Schema(description = "授权到期时间,空表示不过期")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||||
private Date expireTime;
|
private Date expireTime;
|
||||||
|
|
||||||
@Schema(description = "来源,1:系统内置 2:用户自定义", hidden = true)
|
@Schema(description = "来源,1:系统内置 2:用户自定义", hidden = true)
|
||||||
@@ -38,4 +40,4 @@ public class SysGroupAddDto implements Serializable {
|
|||||||
|
|
||||||
@Schema(description = "排序")
|
@Schema(description = "排序")
|
||||||
private Integer sortIndex;
|
private Integer sortIndex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.xspaceagi.system.application.dto.permission;
|
package com.xspaceagi.system.application.dto.permission;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ public class SysGroupUpdateDto implements Serializable {
|
|||||||
private Integer authEnabled;
|
private Integer authEnabled;
|
||||||
|
|
||||||
@Schema(description = "授权到期时间,空表示不过期")
|
@Schema(description = "授权到期时间,空表示不过期")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||||
private Date expireTime;
|
private Date expireTime;
|
||||||
|
|
||||||
@Schema(description = "来源,1:系统内置 2:用户自定义", hidden = true)
|
@Schema(description = "来源,1:系统内置 2:用户自定义", hidden = true)
|
||||||
@@ -41,4 +43,4 @@ public class SysGroupUpdateDto implements Serializable {
|
|||||||
|
|
||||||
@Schema(description = "排序")
|
@Schema(description = "排序")
|
||||||
private Integer sortIndex;
|
private Integer sortIndex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -247,6 +247,12 @@ public class UserApplicationServiceImpl implements UserApplicationService {
|
|||||||
UserQueryDto userQueryDto = pageQueryVo.getQueryFilter();
|
UserQueryDto userQueryDto = pageQueryVo.getQueryFilter();
|
||||||
if (userQueryDto != null) {
|
if (userQueryDto != null) {
|
||||||
BeanUtils.copyProperties(userQueryDto, user);
|
BeanUtils.copyProperties(userQueryDto, user);
|
||||||
|
if (userQueryDto.getUserIds() != null) {
|
||||||
|
if (userQueryDto.getUserIds().isEmpty()) {
|
||||||
|
return new Page<>(pageQueryVo.getPageNo(), pageQueryVo.getPageSize(), 0);
|
||||||
|
}
|
||||||
|
queryWrapper.in(User::getId, userQueryDto.getUserIds());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ("".equals(user.getUserName())) {
|
if ("".equals(user.getUserName())) {
|
||||||
user.setUserName(null);
|
user.setUserName(null);
|
||||||
|
|||||||
@@ -5,15 +5,20 @@ import com.xspaceagi.system.application.dto.SendNotifyMessageDto;
|
|||||||
import com.xspaceagi.system.application.dto.UserDto;
|
import com.xspaceagi.system.application.dto.UserDto;
|
||||||
import com.xspaceagi.system.application.dto.UserQueryDto;
|
import com.xspaceagi.system.application.dto.UserQueryDto;
|
||||||
import com.xspaceagi.system.application.service.NotifyMessageApplicationService;
|
import com.xspaceagi.system.application.service.NotifyMessageApplicationService;
|
||||||
|
import com.xspaceagi.system.application.service.SysGroupApplicationService;
|
||||||
import com.xspaceagi.system.application.service.UserApplicationService;
|
import com.xspaceagi.system.application.service.UserApplicationService;
|
||||||
|
import com.xspaceagi.system.infra.dao.entity.SysGroup;
|
||||||
import com.xspaceagi.system.infra.dao.entity.NotifyMessage;
|
import com.xspaceagi.system.infra.dao.entity.NotifyMessage;
|
||||||
import com.xspaceagi.system.infra.dao.entity.User;
|
import com.xspaceagi.system.infra.dao.entity.User;
|
||||||
import com.xspaceagi.system.infra.dao.mapper.UserMapper;
|
import com.xspaceagi.system.infra.dao.mapper.UserMapper;
|
||||||
|
import com.xspaceagi.system.infra.dao.service.UserService;
|
||||||
import com.xspaceagi.system.spec.annotation.RequireResource;
|
import com.xspaceagi.system.spec.annotation.RequireResource;
|
||||||
import com.xspaceagi.system.spec.common.RequestContext;
|
import com.xspaceagi.system.spec.common.RequestContext;
|
||||||
|
import com.xspaceagi.system.spec.common.UserContext;
|
||||||
import com.xspaceagi.system.spec.dto.PageQueryVo;
|
import com.xspaceagi.system.spec.dto.PageQueryVo;
|
||||||
import com.xspaceagi.system.spec.dto.ReqResult;
|
import com.xspaceagi.system.spec.dto.ReqResult;
|
||||||
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
|
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
|
||||||
|
import com.xspaceagi.system.spec.enums.GroupEnum;
|
||||||
import com.xspaceagi.system.spec.exception.BizException;
|
import com.xspaceagi.system.spec.exception.BizException;
|
||||||
import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
|
import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
|
||||||
import com.xspaceagi.system.web.dto.UserAddDto;
|
import com.xspaceagi.system.web.dto.UserAddDto;
|
||||||
@@ -28,6 +33,7 @@ import org.springframework.beans.BeanUtils;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.xspaceagi.system.spec.enums.ResourceEnum.*;
|
import static com.xspaceagi.system.spec.enums.ResourceEnum.*;
|
||||||
@@ -40,17 +46,32 @@ public class UserManageController {
|
|||||||
@Resource
|
@Resource
|
||||||
private UserApplicationService userApplicationService;
|
private UserApplicationService userApplicationService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SysGroupApplicationService sysGroupApplicationService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private NotifyMessageApplicationService notifyMessageApplicationService;
|
private NotifyMessageApplicationService notifyMessageApplicationService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private UserMapper userMapper;
|
private UserMapper userMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
@RequireResource(USER_MANAGE_QUERY)
|
@RequireResource(USER_MANAGE_QUERY)
|
||||||
@Operation(summary = "查询用户列表")
|
@Operation(summary = "查询用户列表")
|
||||||
@RequestMapping(path = "/list", method = RequestMethod.POST)
|
@RequestMapping(path = "/list", method = RequestMethod.POST)
|
||||||
public ReqResult<IPage<UserDto>> listQuery(@RequestBody PageQueryVo<UserQueryDto> pageQueryVo) {
|
public ReqResult<IPage<UserDto>> listQuery(@RequestBody PageQueryVo<UserQueryDto> pageQueryVo) {
|
||||||
checkAdmin();
|
if (pageQueryVo == null) {
|
||||||
|
pageQueryVo = new PageQueryVo<>();
|
||||||
|
}
|
||||||
|
if (pageQueryVo.getPageNo() == null) {
|
||||||
|
pageQueryVo.setPageNo(1L);
|
||||||
|
}
|
||||||
|
if (pageQueryVo.getPageSize() == null) {
|
||||||
|
pageQueryVo.setPageSize(10L);
|
||||||
|
}
|
||||||
|
applyUserManageScope(pageQueryVo);
|
||||||
return ReqResult.success(userApplicationService.listQuery(pageQueryVo));
|
return ReqResult.success(userApplicationService.listQuery(pageQueryVo));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,8 +79,6 @@ public class UserManageController {
|
|||||||
@Operation(summary = "获取用户统计")
|
@Operation(summary = "获取用户统计")
|
||||||
@RequestMapping(path = "/stats", method = RequestMethod.GET)
|
@RequestMapping(path = "/stats", method = RequestMethod.GET)
|
||||||
public ReqResult<UserStatsDto> getUserStats() {
|
public ReqResult<UserStatsDto> getUserStats() {
|
||||||
checkAdmin();
|
|
||||||
|
|
||||||
Long totalUserCount = userMapper.countTotalUsers();
|
Long totalUserCount = userMapper.countTotalUsers();
|
||||||
Long todayNewUserCount = userMapper.countTodayNewUsers();
|
Long todayNewUserCount = userMapper.countTodayNewUsers();
|
||||||
|
|
||||||
@@ -90,13 +109,17 @@ public class UserManageController {
|
|||||||
@Operation(summary = "添加用户")
|
@Operation(summary = "添加用户")
|
||||||
@RequestMapping(path = "/add", method = RequestMethod.POST)
|
@RequestMapping(path = "/add", method = RequestMethod.POST)
|
||||||
public ReqResult<Void> addUser(@RequestBody UserAddDto userAddDto) {
|
public ReqResult<Void> addUser(@RequestBody UserAddDto userAddDto) {
|
||||||
checkAdmin();
|
|
||||||
UserDto userDto = new UserDto();
|
UserDto userDto = new UserDto();
|
||||||
BeanUtils.copyProperties(userAddDto, userDto);
|
BeanUtils.copyProperties(userAddDto, userDto);
|
||||||
if (StringUtils.isNotBlank(userAddDto.getPassword())) {
|
if (StringUtils.isNotBlank(userAddDto.getPassword())) {
|
||||||
userDto.setResetPass(1);
|
userDto.setResetPass(1);
|
||||||
}
|
}
|
||||||
|
userDto.setRole(User.Role.User);
|
||||||
userApplicationService.add(userDto);
|
userApplicationService.add(userDto);
|
||||||
|
List<Long> groupIds = currentUserGroupIds().stream().toList();
|
||||||
|
if (CollectionUtils.isNotEmpty(groupIds)) {
|
||||||
|
groupIds.forEach(groupId -> sysGroupApplicationService.groupAddUser(groupId, userDto.getId(), getCurrentUserContext()));
|
||||||
|
}
|
||||||
return ReqResult.success();
|
return ReqResult.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +127,7 @@ public class UserManageController {
|
|||||||
@Operation(summary = "更新用户信息")
|
@Operation(summary = "更新用户信息")
|
||||||
@RequestMapping(path = "/updateById/{id}", method = RequestMethod.POST)
|
@RequestMapping(path = "/updateById/{id}", method = RequestMethod.POST)
|
||||||
public ReqResult<Void> updateUserById(@PathVariable Long id, @RequestBody UserUpdateDto userUpdateDto) {
|
public ReqResult<Void> updateUserById(@PathVariable Long id, @RequestBody UserUpdateDto userUpdateDto) {
|
||||||
checkAdmin();
|
checkCurrentTenantUser(id);
|
||||||
UserDto userUpdate = new UserDto();
|
UserDto userUpdate = new UserDto();
|
||||||
userUpdate.setId(id);
|
userUpdate.setId(id);
|
||||||
userUpdate.setAvatar(userUpdateDto.getAvatar());
|
userUpdate.setAvatar(userUpdateDto.getAvatar());
|
||||||
@@ -122,7 +145,7 @@ public class UserManageController {
|
|||||||
@Operation(summary = "禁用用户")
|
@Operation(summary = "禁用用户")
|
||||||
@RequestMapping(path = "/disable/{id}", method = RequestMethod.POST)
|
@RequestMapping(path = "/disable/{id}", method = RequestMethod.POST)
|
||||||
public ReqResult<Void> disableUserById(@PathVariable Long id) {
|
public ReqResult<Void> disableUserById(@PathVariable Long id) {
|
||||||
checkAdmin();
|
checkCurrentTenantUser(id);
|
||||||
UserDto userUpdate = new UserDto();
|
UserDto userUpdate = new UserDto();
|
||||||
userUpdate.setId(id);
|
userUpdate.setId(id);
|
||||||
userUpdate.setStatus(User.Status.Disabled);
|
userUpdate.setStatus(User.Status.Disabled);
|
||||||
@@ -134,7 +157,7 @@ public class UserManageController {
|
|||||||
@Operation(summary = "启用用户")
|
@Operation(summary = "启用用户")
|
||||||
@RequestMapping(path = "/enable/{id}", method = RequestMethod.POST)
|
@RequestMapping(path = "/enable/{id}", method = RequestMethod.POST)
|
||||||
public ReqResult<Void> enableUserById(@PathVariable Long id) {
|
public ReqResult<Void> enableUserById(@PathVariable Long id) {
|
||||||
checkAdmin();
|
checkCurrentTenantUser(id);
|
||||||
UserDto userUpdate = new UserDto();
|
UserDto userUpdate = new UserDto();
|
||||||
userUpdate.setId(id);
|
userUpdate.setId(id);
|
||||||
userUpdate.setStatus(User.Status.Enabled);
|
userUpdate.setStatus(User.Status.Enabled);
|
||||||
@@ -146,7 +169,7 @@ public class UserManageController {
|
|||||||
@Operation(summary = "删除用户")
|
@Operation(summary = "删除用户")
|
||||||
@RequestMapping(path = "/delete/{id}", method = RequestMethod.POST)
|
@RequestMapping(path = "/delete/{id}", method = RequestMethod.POST)
|
||||||
public ReqResult<Void> deleteUserById(@PathVariable Long id) {
|
public ReqResult<Void> deleteUserById(@PathVariable Long id) {
|
||||||
checkAdmin();
|
checkCurrentTenantUser(id);
|
||||||
userApplicationService.logicDelete(id);
|
userApplicationService.logicDelete(id);
|
||||||
return ReqResult.success();
|
return ReqResult.success();
|
||||||
}
|
}
|
||||||
@@ -165,12 +188,53 @@ public class UserManageController {
|
|||||||
return ReqResult.success();
|
return ReqResult.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void checkCurrentTenantUser(Long userId) {
|
||||||
* 当前简单的在商家范围支持普通用户和管理员两种角色;后续再迭代完整的权限角色
|
if (userId == null || userService.getById(userId) == null || !visibleUserIds().contains(userId)) {
|
||||||
*/
|
|
||||||
private void checkAdmin() {
|
|
||||||
if (((UserDto) RequestContext.get().getUser()).getRole() != User.Role.Admin) {
|
|
||||||
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.permissionDenied);
|
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.permissionDenied);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private void applyUserManageScope(PageQueryVo<UserQueryDto> pageQueryVo) {
|
||||||
|
if (pageQueryVo.getQueryFilter() == null) {
|
||||||
|
pageQueryVo.setQueryFilter(new UserQueryDto());
|
||||||
|
}
|
||||||
|
pageQueryVo.getQueryFilter().setUserIds(visibleUserIds().stream().toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<Long> visibleUserIds() {
|
||||||
|
return currentUserGroupIds().stream()
|
||||||
|
.flatMap(groupId -> sysGroupApplicationService.getUserListByGroupId(groupId).stream())
|
||||||
|
.map(User::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<Long> currentUserGroupIds() {
|
||||||
|
return sysGroupApplicationService.getGroupListByUserId(getCurrentUserDto().getId()).stream()
|
||||||
|
.filter(group -> !GroupEnum.DEFAULT_GROUP.getCode().equals(group.getCode()))
|
||||||
|
.map(SysGroup::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserDto getCurrentUserDto() {
|
||||||
|
UserDto userDto = (UserDto) RequestContext.get().getUser();
|
||||||
|
if (userDto == null) {
|
||||||
|
throw BizException.of(ErrorCodeEnum.UNAUTHORIZED, BizExceptionCodeEnum.systemUserNotLoggedInWeb);
|
||||||
|
}
|
||||||
|
return userDto;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserContext getCurrentUserContext() {
|
||||||
|
UserDto userDto = getCurrentUserDto();
|
||||||
|
return UserContext.builder()
|
||||||
|
.userId(userDto.getId())
|
||||||
|
.userName(userDto.getUserName())
|
||||||
|
.nickName(userDto.getNickName())
|
||||||
|
.avatar(userDto.getAvatar())
|
||||||
|
.email(userDto.getEmail())
|
||||||
|
.phone(userDto.getPhone())
|
||||||
|
.status(userDto.getStatus() == User.Status.Enabled ? 1 : -1)
|
||||||
|
.tenantId(userDto.getTenantId())
|
||||||
|
.roleType(userDto.getRole() == User.Role.Admin ? 1 : 2)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -344,8 +344,8 @@ public class SysGroupController extends BaseController {
|
|||||||
return ReqResult.error("参数不能为空");
|
return ReqResult.error("参数不能为空");
|
||||||
}
|
}
|
||||||
checkGroupManageScope(dto.getGroupId());
|
checkGroupManageScope(dto.getGroupId());
|
||||||
// 用户组不允许绑定 生态市场/系统管理
|
// 用户组不允许绑定生态市场和系统管理整棵子树。
|
||||||
checkForbiddenMenuCodes(dto.getMenuTree());
|
checkForbiddenMenuCodes(dto.getMenuTree(), false);
|
||||||
|
|
||||||
GroupBindMenuModel model = GroupBindMenuModelConverter.convertToModel(dto);
|
GroupBindMenuModel model = GroupBindMenuModelConverter.convertToModel(dto);
|
||||||
sysGroupApplicationService.bindMenu(model, getUser());
|
sysGroupApplicationService.bindMenu(model, getUser());
|
||||||
@@ -370,29 +370,31 @@ public class SysGroupController extends BaseController {
|
|||||||
return ReqResult.success(menuDtoList);
|
return ReqResult.success(menuDtoList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkForbiddenMenuCodes(List<MenuNodeDto> nodes) {
|
private void checkForbiddenMenuCodes(List<MenuNodeDto> nodes, boolean underForbiddenMenu) {
|
||||||
if (CollectionUtils.isEmpty(nodes)) {
|
if (CollectionUtils.isEmpty(nodes)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (MenuNodeDto node : nodes) {
|
for (MenuNodeDto node : nodes) {
|
||||||
|
boolean currentForbidden = underForbiddenMenu;
|
||||||
if (StringUtils.isNotBlank(node.getCode())) {
|
if (StringUtils.isNotBlank(node.getCode())) {
|
||||||
if (node.getCode().equals(MenuEnum.ECO_MARKET.getCode()) && node.getMenuBindType() > 0) {
|
if (node.getCode().equals(MenuEnum.ECO_MARKET.getCode()) && node.getMenuBindType() > 0) {
|
||||||
throw BizException.of(ErrorCodeEnum.INVALID_PARAM, BizExceptionCodeEnum.systemGroupCannotBindForbiddenMenu,
|
throw BizException.of(ErrorCodeEnum.INVALID_PARAM, BizExceptionCodeEnum.systemGroupCannotBindForbiddenMenu,
|
||||||
MenuEnum.ECO_MARKET.getName());
|
MenuEnum.ECO_MARKET.getName());
|
||||||
}
|
}
|
||||||
if (node.getCode().equals(MenuEnum.SYSTEM_MANAGE.getCode()) && node.getMenuBindType() > 0) {
|
currentForbidden = currentForbidden || node.getCode().equals(MenuEnum.SYSTEM_MANAGE.getCode());
|
||||||
|
if (currentForbidden && node.getMenuBindType() > 0) {
|
||||||
throw BizException.of(ErrorCodeEnum.INVALID_PARAM, BizExceptionCodeEnum.systemGroupCannotBindForbiddenMenu,
|
throw BizException.of(ErrorCodeEnum.INVALID_PARAM, BizExceptionCodeEnum.systemGroupCannotBindForbiddenMenu,
|
||||||
MenuEnum.SYSTEM_MANAGE.getName());
|
MenuEnum.SYSTEM_MANAGE.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (CollectionUtils.isNotEmpty(node.getChildren())) {
|
if (CollectionUtils.isNotEmpty(node.getChildren())) {
|
||||||
checkForbiddenMenuCodes(node.getChildren());
|
checkForbiddenMenuCodes(node.getChildren(), currentForbidden);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<SysGroup> filterGroupManageScope(List<SysGroup> groupList) {
|
private List<SysGroup> filterGroupManageScope(List<SysGroup> groupList) {
|
||||||
if (isPlatformAdmin() || CollectionUtils.isEmpty(groupList)) {
|
if (CollectionUtils.isEmpty(groupList)) {
|
||||||
return groupList;
|
return groupList;
|
||||||
}
|
}
|
||||||
Set<Long> allowedGroupIds = currentUserGroupIds();
|
Set<Long> allowedGroupIds = currentUserGroupIds();
|
||||||
@@ -402,24 +404,18 @@ public class SysGroupController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkGroupManageScope(Long groupId) {
|
private void checkGroupManageScope(Long groupId) {
|
||||||
if (isPlatformAdmin()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (groupId == null || !currentUserGroupIds().contains(groupId)) {
|
if (groupId == null || !currentUserGroupIds().contains(groupId)) {
|
||||||
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.permissionDenied);
|
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.permissionDenied);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Long> currentUserGroupIds() {
|
private Set<Long> currentUserGroupIds() {
|
||||||
return sysGroupApplicationService.getEffectiveGroupListByUserId(getCurrentUserDto().getId()).stream()
|
return sysGroupApplicationService.getGroupListByUserId(getCurrentUserDto().getId()).stream()
|
||||||
|
.filter(group -> !GroupEnum.DEFAULT_GROUP.getCode().equals(group.getCode()))
|
||||||
.map(SysGroup::getId)
|
.map(SysGroup::getId)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPlatformAdmin() {
|
|
||||||
return getCurrentUserDto().getRole() == User.Role.Admin;
|
|
||||||
}
|
|
||||||
|
|
||||||
private UserDto getCurrentUserDto() {
|
private UserDto getCurrentUserDto() {
|
||||||
UserDto userDto = (UserDto) RequestContext.get().getUser();
|
UserDto userDto = (UserDto) RequestContext.get().getUser();
|
||||||
if (userDto == null) {
|
if (userDto == null) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.xspaceagi.system.application.dto.permission.*;
|
|||||||
import com.xspaceagi.system.application.service.SysDataPermissionApplicationService;
|
import com.xspaceagi.system.application.service.SysDataPermissionApplicationService;
|
||||||
import com.xspaceagi.system.application.service.SysGroupApplicationService;
|
import com.xspaceagi.system.application.service.SysGroupApplicationService;
|
||||||
import com.xspaceagi.system.application.service.SysRoleApplicationService;
|
import com.xspaceagi.system.application.service.SysRoleApplicationService;
|
||||||
|
import com.xspaceagi.system.application.service.UserApplicationService;
|
||||||
import com.xspaceagi.system.application.service.impl.SysUserPermissionCacheServiceImpl;
|
import com.xspaceagi.system.application.service.impl.SysUserPermissionCacheServiceImpl;
|
||||||
import com.xspaceagi.system.infra.dao.entity.SysGroup;
|
import com.xspaceagi.system.infra.dao.entity.SysGroup;
|
||||||
import com.xspaceagi.system.infra.dao.entity.SysRole;
|
import com.xspaceagi.system.infra.dao.entity.SysRole;
|
||||||
@@ -15,6 +16,7 @@ import com.xspaceagi.system.spec.annotation.SaasAdmin;
|
|||||||
import com.xspaceagi.system.spec.common.RequestContext;
|
import com.xspaceagi.system.spec.common.RequestContext;
|
||||||
import com.xspaceagi.system.spec.dto.ReqResult;
|
import com.xspaceagi.system.spec.dto.ReqResult;
|
||||||
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
|
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
|
||||||
|
import com.xspaceagi.system.spec.enums.GroupEnum;
|
||||||
import com.xspaceagi.system.spec.exception.BizException;
|
import com.xspaceagi.system.spec.exception.BizException;
|
||||||
import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
|
import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
|
||||||
import com.xspaceagi.system.spec.utils.I18nUtil;
|
import com.xspaceagi.system.spec.utils.I18nUtil;
|
||||||
@@ -28,6 +30,7 @@ import org.springframework.beans.BeanUtils;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -47,6 +50,8 @@ public class SysUserController extends BaseController {
|
|||||||
private SysDataPermissionApplicationService sysDataPermissionApplicationService;
|
private SysDataPermissionApplicationService sysDataPermissionApplicationService;
|
||||||
@Resource
|
@Resource
|
||||||
private SysUserPermissionCacheServiceImpl sysUserPermissionCacheService;
|
private SysUserPermissionCacheServiceImpl sysUserPermissionCacheService;
|
||||||
|
@Resource
|
||||||
|
private UserApplicationService userApplicationService;
|
||||||
|
|
||||||
@RequireResource(USER_MANAGE_BIND_ROLE)
|
@RequireResource(USER_MANAGE_BIND_ROLE)
|
||||||
@Operation(summary = "查询用户绑定的角色列表")
|
@Operation(summary = "查询用户绑定的角色列表")
|
||||||
@@ -56,6 +61,7 @@ public class SysUserController extends BaseController {
|
|||||||
return ReqResult.error("参数不能为空");
|
return ReqResult.error("参数不能为空");
|
||||||
}
|
}
|
||||||
checkUserManageScope(userId);
|
checkUserManageScope(userId);
|
||||||
|
checkRoleAuthorizationTarget(userId);
|
||||||
|
|
||||||
List<SysRole> roleList = sysRoleApplicationService.getRoleListByUserId(userId);
|
List<SysRole> roleList = sysRoleApplicationService.getRoleListByUserId(userId);
|
||||||
if (CollectionUtils.isEmpty(roleList)) {
|
if (CollectionUtils.isEmpty(roleList)) {
|
||||||
@@ -79,6 +85,7 @@ public class SysUserController extends BaseController {
|
|||||||
return ReqResult.error("参数不能为空");
|
return ReqResult.error("参数不能为空");
|
||||||
}
|
}
|
||||||
checkUserManageScope(dto.getUserId());
|
checkUserManageScope(dto.getUserId());
|
||||||
|
checkRoleAuthorizationTarget(dto.getUserId());
|
||||||
checkRoleBindScope(dto.getRoleIds());
|
checkRoleBindScope(dto.getRoleIds());
|
||||||
sysRoleApplicationService.userBindRole(dto.getUserId(), dto.getRoleIds(), getUser());
|
sysRoleApplicationService.userBindRole(dto.getUserId(), dto.getRoleIds(), getUser());
|
||||||
return ReqResult.success();
|
return ReqResult.success();
|
||||||
@@ -146,11 +153,13 @@ public class SysUserController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkUserManageScope(Long userId) {
|
private void checkUserManageScope(Long userId) {
|
||||||
if (isPlatformAdmin()) {
|
UserDto targetUser = userApplicationService.queryById(userId);
|
||||||
return;
|
if (targetUser == null || !Objects.equals(targetUser.getTenantId(), RequestContext.get().getTenantId())) {
|
||||||
|
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.permissionDenied);
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<Long> allowedGroupIds = currentUserGroupIds();
|
Set<Long> allowedGroupIds = currentUserGroupIds();
|
||||||
boolean inScope = sysGroupApplicationService.getEffectiveGroupListByUserId(userId).stream()
|
boolean inScope = sysGroupApplicationService.getGroupListByUserId(userId).stream()
|
||||||
.map(SysGroup::getId)
|
.map(SysGroup::getId)
|
||||||
.anyMatch(allowedGroupIds::contains);
|
.anyMatch(allowedGroupIds::contains);
|
||||||
if (!inScope) {
|
if (!inScope) {
|
||||||
@@ -159,7 +168,7 @@ public class SysUserController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkGroupBindScope(List<Long> groupIds) {
|
private void checkGroupBindScope(List<Long> groupIds) {
|
||||||
if (isPlatformAdmin() || CollectionUtils.isEmpty(groupIds)) {
|
if (CollectionUtils.isEmpty(groupIds)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Set<Long> allowedGroupIds = currentUserGroupIds();
|
Set<Long> allowedGroupIds = currentUserGroupIds();
|
||||||
@@ -170,7 +179,7 @@ public class SysUserController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkRoleBindScope(List<Long> roleIds) {
|
private void checkRoleBindScope(List<Long> roleIds) {
|
||||||
if (isPlatformAdmin() || CollectionUtils.isEmpty(roleIds)) {
|
if (CollectionUtils.isEmpty(roleIds)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Set<Long> allowedRoleIds = sysRoleApplicationService.getRoleListByUserId(getCurrentUserDto().getId()).stream()
|
Set<Long> allowedRoleIds = sysRoleApplicationService.getRoleListByUserId(getCurrentUserDto().getId()).stream()
|
||||||
@@ -182,14 +191,18 @@ public class SysUserController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Long> currentUserGroupIds() {
|
private void checkRoleAuthorizationTarget(Long userId) {
|
||||||
return sysGroupApplicationService.getEffectiveGroupListByUserId(getCurrentUserDto().getId()).stream()
|
UserDto targetUser = userApplicationService.queryById(userId);
|
||||||
.map(SysGroup::getId)
|
if (targetUser == null || targetUser.getRole() != User.Role.Admin) {
|
||||||
.collect(Collectors.toSet());
|
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.permissionDenied);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPlatformAdmin() {
|
private Set<Long> currentUserGroupIds() {
|
||||||
return getCurrentUserDto().getRole() == User.Role.Admin;
|
return sysGroupApplicationService.getGroupListByUserId(getCurrentUserDto().getId()).stream()
|
||||||
|
.filter(group -> !GroupEnum.DEFAULT_GROUP.getCode().equals(group.getCode()))
|
||||||
|
.map(SysGroup::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserDto getCurrentUserDto() {
|
private UserDto getCurrentUserDto() {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const CustomFormModal: React.FC<PropsWithChildren<CustomFormModalProps>> = ({
|
|||||||
onCancel,
|
onCancel,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
okDisabled,
|
okDisabled,
|
||||||
|
skipFormValidation,
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
@@ -45,6 +46,7 @@ const CustomFormModal: React.FC<PropsWithChildren<CustomFormModalProps>> = ({
|
|||||||
form={form}
|
form={form}
|
||||||
okText={okText}
|
okText={okText}
|
||||||
disabled={okDisabled}
|
disabled={okDisabled}
|
||||||
|
skipFormValidation={skipFormValidation}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,18 +14,24 @@ const SubmitButton: React.FC<SubmitButtonProps> = ({
|
|||||||
okText,
|
okText,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
disabled,
|
disabled,
|
||||||
|
skipFormValidation = false,
|
||||||
}) => {
|
}) => {
|
||||||
const [submittable, setSubmittable] = useState<boolean>(false);
|
const [submittable, setSubmittable] = useState<boolean>(skipFormValidation);
|
||||||
|
|
||||||
// Watch all values
|
// Watch all values
|
||||||
const values = Form.useWatch([], { form, preserve: true });
|
const values = Form.useWatch([], { form, preserve: true });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (skipFormValidation) {
|
||||||
|
setSubmittable(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
form
|
form
|
||||||
.validateFields({ validateOnly: true })
|
.validateFields({ validateOnly: true })
|
||||||
.then(() => setSubmittable(true))
|
.then(() => setSubmittable(true))
|
||||||
.catch(() => setSubmittable(false));
|
.catch(() => setSubmittable(false));
|
||||||
}, [form, values]);
|
}, [form, values, skipFormValidation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ const UserAuthModal: React.FC<UserAuthModalProps> = ({
|
|||||||
onCancel,
|
onCancel,
|
||||||
}) => {
|
}) => {
|
||||||
const { hasPermission } = useModel('menuModel');
|
const { hasPermission } = useModel('menuModel');
|
||||||
|
const canBindRole = hasPermission('user_manage_bind_role');
|
||||||
|
const canBindGroup = hasPermission('user_manage_bind_group');
|
||||||
// 如果是普通用户,默认显示用户组 tab;否则显示角色 tab
|
// 如果是普通用户,默认显示用户组 tab;否则显示角色 tab
|
||||||
const [activeTab, setActiveTab] = useState<TabKey>('role');
|
const [activeTab, setActiveTab] = useState<TabKey>('role');
|
||||||
// 创建一个空的 Form 实例,用于 CustomFormModal(不使用表单功能)
|
// 创建一个空的 Form 实例,用于 CustomFormModal(不使用表单功能)
|
||||||
@@ -114,9 +116,13 @@ const UserAuthModal: React.FC<UserAuthModalProps> = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
setActiveTab(role === UserRoleEnum.User ? 'group' : 'role');
|
setActiveTab(
|
||||||
|
role === UserRoleEnum.User || !hasPermission('user_manage_bind_role')
|
||||||
|
? 'group'
|
||||||
|
: 'role',
|
||||||
|
);
|
||||||
// 如果不是普通用户,才查询角色列表
|
// 如果不是普通用户,才查询角色列表
|
||||||
if (role !== UserRoleEnum.User) {
|
if (role !== UserRoleEnum.User && canBindRole) {
|
||||||
// 查询角色列表
|
// 查询角色列表
|
||||||
runGetRoleList();
|
runGetRoleList();
|
||||||
// 查询用户绑定的角色列表
|
// 查询用户绑定的角色列表
|
||||||
@@ -136,7 +142,7 @@ const UserAuthModal: React.FC<UserAuthModalProps> = ({
|
|||||||
setGroupLoading(false);
|
setGroupLoading(false);
|
||||||
setRoleLoading(false);
|
setRoleLoading(false);
|
||||||
}
|
}
|
||||||
}, [open, targetId, role]);
|
}, [open, targetId, role, canBindRole, hasPermission]);
|
||||||
|
|
||||||
// 处理角色选择变化
|
// 处理角色选择变化
|
||||||
const handleRoleChange = (checkedValues: number[]) => {
|
const handleRoleChange = (checkedValues: number[]) => {
|
||||||
@@ -151,7 +157,7 @@ const UserAuthModal: React.FC<UserAuthModalProps> = ({
|
|||||||
// 提交数据
|
// 提交数据
|
||||||
const handlerSubmit = async () => {
|
const handlerSubmit = async () => {
|
||||||
// 如果不是普通用户,需要同时提交角色和用户组
|
// 如果不是普通用户,需要同时提交角色和用户组
|
||||||
if (role !== UserRoleEnum.User) {
|
if (role !== UserRoleEnum.User && canBindRole) {
|
||||||
setRoleLoading(true);
|
setRoleLoading(true);
|
||||||
await runBindRole({
|
await runBindRole({
|
||||||
userId: targetId,
|
userId: targetId,
|
||||||
@@ -161,12 +167,14 @@ const UserAuthModal: React.FC<UserAuthModalProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 用户组总是需要提交
|
// 用户组总是需要提交
|
||||||
setGroupLoading(true);
|
if (canBindGroup) {
|
||||||
await runBindGroup({
|
setGroupLoading(true);
|
||||||
userId: targetId,
|
await runBindGroup({
|
||||||
groupIds: selectedGroupIds,
|
userId: targetId,
|
||||||
});
|
groupIds: selectedGroupIds,
|
||||||
setGroupLoading(false);
|
});
|
||||||
|
setGroupLoading(false);
|
||||||
|
}
|
||||||
onCancel();
|
onCancel();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -298,6 +306,7 @@ const UserAuthModal: React.FC<UserAuthModalProps> = ({
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
onConfirm={handlerSubmit}
|
onConfirm={handlerSubmit}
|
||||||
|
skipFormValidation
|
||||||
okDisabled={
|
okDisabled={
|
||||||
activeTab === 'role'
|
activeTab === 'role'
|
||||||
? !hasPermission('user_manage_bind_role')
|
? !hasPermission('user_manage_bind_role')
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ const UserFormModal: React.FC<UserFormModalProps> = ({
|
|||||||
form.setFieldsValue(record);
|
form.setFieldsValue(record);
|
||||||
} else {
|
} else {
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
|
form.setFieldsValue({ role: UserRoleEnum.User });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [open, isEdit, record, form]);
|
}, [open, isEdit, record, form]);
|
||||||
@@ -50,6 +51,7 @@ const UserFormModal: React.FC<UserFormModalProps> = ({
|
|||||||
} else {
|
} else {
|
||||||
await apiAddSystemUser({
|
await apiAddSystemUser({
|
||||||
...values,
|
...values,
|
||||||
|
role: UserRoleEnum.User,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
onSuccess(isEdit);
|
onSuccess(isEdit);
|
||||||
@@ -195,12 +197,14 @@ const UserFormModal: React.FC<UserFormModalProps> = ({
|
|||||||
<Form.Item
|
<Form.Item
|
||||||
label={dict('PC.Pages.UserManage.UserFormModal.userType')}
|
label={dict('PC.Pages.UserManage.UserFormModal.userType')}
|
||||||
name="role"
|
name="role"
|
||||||
initialValue={UserRoleEnum.Admin}
|
initialValue={UserRoleEnum.User}
|
||||||
>
|
>
|
||||||
<Radio.Group>
|
<Radio.Group>
|
||||||
<Radio value={UserRoleEnum.Admin}>
|
{isEdit && (
|
||||||
{dict('PC.Pages.UserManage.UserFormModal.admin')}
|
<Radio value={UserRoleEnum.Admin}>
|
||||||
</Radio>
|
{dict('PC.Pages.UserManage.UserFormModal.admin')}
|
||||||
|
</Radio>
|
||||||
|
)}
|
||||||
<Radio value={UserRoleEnum.User}>
|
<Radio value={UserRoleEnum.User}>
|
||||||
{dict('PC.Pages.UserManage.UserFormModal.normalUser')}
|
{dict('PC.Pages.UserManage.UserFormModal.normalUser')}
|
||||||
</Radio>
|
</Radio>
|
||||||
|
|||||||
@@ -161,8 +161,10 @@ const UserManage: React.FC = () => {
|
|||||||
key: 'auth',
|
key: 'auth',
|
||||||
label: dict('PC.Pages.UserManage.Index.auth'),
|
label: dict('PC.Pages.UserManage.Index.auth'),
|
||||||
disabled:
|
disabled:
|
||||||
!hasPermissionByMenuCode('user_manage', 'user_manage_bind_role') &&
|
record.role === UserRoleEnum.Admin
|
||||||
!hasPermissionByMenuCode('user_manage', 'user_manage_bind_group'),
|
? !hasPermissionByMenuCode('user_manage', 'user_manage_bind_role') &&
|
||||||
|
!hasPermissionByMenuCode('user_manage', 'user_manage_bind_group')
|
||||||
|
: !hasPermissionByMenuCode('user_manage', 'user_manage_bind_group'),
|
||||||
onClick: handleAuth,
|
onClick: handleAuth,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -188,6 +188,8 @@ export interface CustomFormModalProps {
|
|||||||
onConfirm: () => void;
|
onConfirm: () => void;
|
||||||
// 确定按钮是否禁用
|
// 确定按钮是否禁用
|
||||||
okDisabled?: boolean;
|
okDisabled?: boolean;
|
||||||
|
// 非表单场景跳过 Form 校验,仅由 okDisabled 控制按钮状态
|
||||||
|
skipFormValidation?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Form.Item 验证rule
|
// Form.Item 验证rule
|
||||||
@@ -456,6 +458,8 @@ export interface SubmitButtonProps {
|
|||||||
onConfirm: () => void;
|
onConfirm: () => void;
|
||||||
// 确定按钮是否禁用
|
// 确定按钮是否禁用
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
// 非表单场景跳过 Form 校验,仅由 disabled 控制按钮状态
|
||||||
|
skipFormValidation?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传文件信息
|
// 上传文件信息
|
||||||
|
|||||||
Reference in New Issue
Block a user