diff --git a/backend/src/main/java/io/dataease/base/domain/SysMenu.java b/backend/src/main/java/io/dataease/base/domain/SysMenu.java index b103528cb9..a9df1d56ce 100644 --- a/backend/src/main/java/io/dataease/base/domain/SysMenu.java +++ b/backend/src/main/java/io/dataease/base/domain/SysMenu.java @@ -1,6 +1,8 @@ package io.dataease.base.domain; import java.io.Serializable; +import java.util.Objects; + import lombok.Data; @Data @@ -42,4 +44,30 @@ public class SysMenu implements Serializable { private Long updateTime; private static final long serialVersionUID = 1L; + + + /** + * 由于该类型作为HashSet key所以必须重写以下方法 + * @param o + * @return + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SysMenu menu = (SysMenu) o; + return Objects.equals(menuId, menu.menuId); + } + + @Override + public int hashCode() { + return Objects.hash(menuId); + } + + + } \ No newline at end of file diff --git a/backend/src/main/java/io/dataease/base/mapper/ext/ExtSysRoleMapper.java b/backend/src/main/java/io/dataease/base/mapper/ext/ExtSysRoleMapper.java index 7af7d39ae6..964c258016 100644 --- a/backend/src/main/java/io/dataease/base/mapper/ext/ExtSysRoleMapper.java +++ b/backend/src/main/java/io/dataease/base/mapper/ext/ExtSysRoleMapper.java @@ -5,10 +5,15 @@ import io.dataease.controller.sys.response.RoleNodeResponse; import org.apache.ibatis.annotations.Param; import java.util.List; +import java.util.Map; public interface ExtSysRoleMapper { List query(@Param("request")RoleGridRequest request); + + int deleteRoleMenu(@Param("roleId") Long roleId); + + int batchInsertRoleMenu(@Param("maps") List> maps); } diff --git a/backend/src/main/java/io/dataease/base/mapper/ext/ExtSysRoleMapper.xml b/backend/src/main/java/io/dataease/base/mapper/ext/ExtSysRoleMapper.xml index f2887ccd61..05f4f55c32 100644 --- a/backend/src/main/java/io/dataease/base/mapper/ext/ExtSysRoleMapper.xml +++ b/backend/src/main/java/io/dataease/base/mapper/ext/ExtSysRoleMapper.xml @@ -23,4 +23,16 @@ + + delete from sys_roles_menus where role_id = #{roleId} + + + + insert into sys_roles_menus (role_id, menu_id) values + + (#{map.roleId},#{map.menuId}) + + + + diff --git a/backend/src/main/java/io/dataease/controller/sys/SysMenuController.java b/backend/src/main/java/io/dataease/controller/sys/SysMenuController.java index f454aa5c0f..19e7026fdf 100644 --- a/backend/src/main/java/io/dataease/controller/sys/SysMenuController.java +++ b/backend/src/main/java/io/dataease/controller/sys/SysMenuController.java @@ -14,6 +14,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; @RestController @@ -29,15 +30,11 @@ public class SysMenuController { @PostMapping("/childNodes/{pid}") public List childNodes(@PathVariable("pid") Long pid){ List nodes = menuService.nodesByPid(pid); - List nodeResponses = nodes.stream().map(node -> { - MenuNodeResponse menuNodeResponse = BeanUtils.copyBean(new MenuNodeResponse(), node); - menuNodeResponse.setHasChildren(node.getSubCount() > 0); - menuNodeResponse.setTop(node.getPid() == menuService.MENU_ROOT_PID); - return menuNodeResponse; - }).collect(Collectors.toList()); - return nodeResponses; + return menuService.convert(nodes); } + + @ApiOperation("新增菜单") @PostMapping("/create") public void create(@RequestBody MenuCreateRequest request){ @@ -49,6 +46,7 @@ public class SysMenuController { public void delete(@RequestBody MenuDeleteRequest request){ menuService.delete(request); } + @ApiOperation("更新菜单") @PostMapping("/update") public void update(@RequestBody MenuCreateRequest menu){ @@ -56,4 +54,14 @@ public class SysMenuController { } + + @PostMapping("/childMenus/{pid}") + public Set childMenus(@PathVariable Long pid){ + List childs = menuService.childs(pid); + Set sets = childs.stream().map(MenuNodeResponse::getMenuId).collect(Collectors.toSet()); + sets.add(pid); + return sets; + } + + } diff --git a/backend/src/main/java/io/dataease/controller/sys/SysRoleController.java b/backend/src/main/java/io/dataease/controller/sys/SysRoleController.java index 782983eaf8..c39351d10b 100644 --- a/backend/src/main/java/io/dataease/controller/sys/SysRoleController.java +++ b/backend/src/main/java/io/dataease/controller/sys/SysRoleController.java @@ -7,6 +7,7 @@ import io.dataease.base.domain.SysRole; import io.dataease.commons.utils.PageUtils; import io.dataease.commons.utils.Pager; import io.dataease.controller.sys.request.RoleGridRequest; +import io.dataease.controller.sys.request.RoleMenusRequest; import io.dataease.controller.sys.response.RoleNodeResponse; import io.dataease.service.sys.SysRoleService; import io.swagger.annotations.Api; @@ -34,8 +35,8 @@ public class SysRoleController { @ApiOperation("删除角色") - @PostMapping("/delete") - public void delete(Long roleId){ + @PostMapping("/delete/{roleId}") + public void delete(@PathVariable("roleId") Long roleId){ sysRoleService.delete(roleId); } @@ -52,4 +53,10 @@ public class SysRoleController { Page page = PageHelper.startPage(goPage, pageSize, true); return PageUtils.setPageInfo(page, sysRoleService.query(request)); } + + + @PostMapping("/saveRolesMenus") + public void saveRolesMenus(@RequestBody RoleMenusRequest request){ + sysRoleService.batchSaveRolesMenus(request); + } } diff --git a/backend/src/main/java/io/dataease/controller/sys/request/RoleMenusRequest.java b/backend/src/main/java/io/dataease/controller/sys/request/RoleMenusRequest.java new file mode 100644 index 0000000000..72cb3a3478 --- /dev/null +++ b/backend/src/main/java/io/dataease/controller/sys/request/RoleMenusRequest.java @@ -0,0 +1,13 @@ +package io.dataease.controller.sys.request; + +import lombok.Data; + +import java.util.List; + +@Data +public class RoleMenusRequest { + + private Long roleId; + + private List menuIds; +} diff --git a/backend/src/main/java/io/dataease/service/sys/MenuService.java b/backend/src/main/java/io/dataease/service/sys/MenuService.java index 9b04ee6d0e..083b33c5e3 100644 --- a/backend/src/main/java/io/dataease/service/sys/MenuService.java +++ b/backend/src/main/java/io/dataease/service/sys/MenuService.java @@ -7,11 +7,16 @@ import io.dataease.base.mapper.ext.ExtMenuMapper; import io.dataease.commons.utils.BeanUtils; import io.dataease.controller.sys.request.MenuCreateRequest; import io.dataease.controller.sys.request.MenuDeleteRequest; +import io.dataease.controller.sys.response.MenuNodeResponse; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; @Service public class MenuService { @@ -108,5 +113,32 @@ public class MenuService { return sysMenuMapper.updateByPrimaryKeySelective(sysMenu); } + public List childs(Long pid){ + Set childs = getChilds(nodesByPid(pid), new HashSet()); + List menus = childs.stream().collect(Collectors.toList()); + List responses = convert(menus); + return responses; + } + + private Set getChilds(List lists, Set sets){ + lists.forEach(menu -> { + sets.add(menu); + List kidMenus = nodesByPid(menu.getMenuId()); + if (CollectionUtils.isNotEmpty(kidMenus)){ + getChilds(kidMenus, sets); + } + }); + return sets; + } + + public List convert(List menus){ + return menus.stream().map(node -> { + MenuNodeResponse menuNodeResponse = BeanUtils.copyBean(new MenuNodeResponse(), node); + menuNodeResponse.setHasChildren(node.getSubCount() > 0); + menuNodeResponse.setTop(node.getPid() == MENU_ROOT_PID); + return menuNodeResponse; + }).collect(Collectors.toList()); + } + } diff --git a/backend/src/main/java/io/dataease/service/sys/SysRoleService.java b/backend/src/main/java/io/dataease/service/sys/SysRoleService.java index 5bbfbcb9d3..494e429409 100644 --- a/backend/src/main/java/io/dataease/service/sys/SysRoleService.java +++ b/backend/src/main/java/io/dataease/service/sys/SysRoleService.java @@ -5,11 +5,16 @@ import io.dataease.base.domain.SysRole; import io.dataease.base.mapper.SysRoleMapper; import io.dataease.base.mapper.ext.ExtSysRoleMapper; import io.dataease.controller.sys.request.RoleGridRequest; +import io.dataease.controller.sys.request.RoleMenusRequest; import io.dataease.controller.sys.response.RoleNodeResponse; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; @Service public class SysRoleService { @@ -45,4 +50,19 @@ public class SysRoleService { return result; } + + + @Transactional + public int batchSaveRolesMenus(RoleMenusRequest request){ + extSysRoleMapper.deleteRoleMenu(request.getRoleId()); + List> maps = request.getMenuIds().stream().map(menuId -> { + Map map = new HashMap<>(); + map.put("roleId", request.getRoleId()); + map.put("menuId", menuId); + return map; + }).collect(Collectors.toList()); + return extSysRoleMapper.batchInsertRoleMenu(maps); + } + + } diff --git a/frontend/src/business/components/settings/sys/role.vue b/frontend/src/business/components/settings/sys/role.vue index 6150a9bc2d..141f2e493b 100644 --- a/frontend/src/business/components/settings/sys/role.vue +++ b/frontend/src/business/components/settings/sys/role.vue @@ -4,12 +4,12 @@ - + - + @@ -35,9 +35,8 @@ :load="getMenuDatas" :props="defaultProps" check-strictly - accordion show-checkbox - node-key="menuId" + node-key="id" @check="menuChange" /> @@ -45,6 +44,26 @@ + + + + + + + + + + + + + + + @@ -55,7 +74,10 @@ import MsTableHeader from "../../common/components/MsTableHeader"; import MsTableOperator from "../../common/components/MsTableOperator"; import MsDialogFooter from "../../common/components/MsDialogFooter"; import MsTableOperatorButton from "../../common/components/MsTableOperatorButton"; - +import { + listenGoBack, + removeGoBackListener +} from "@/common/js/utils"; export default { name: 'role', components: { @@ -70,9 +92,12 @@ export default { return { result: {}, queryPath: '/api/role/roleGrid', - deletePath: '/user/special/delete/', - createPath: '/user/special/add', - updatePath: '/user/special/update', + deletePath: '/api/role/delete/', + createPath: '/api/role/create', + updatePath: '/api/role/update', + queryMenusPath: '/api/menu/childNodes/', + childMenusPath: '/api/menu/childMenus/', + saveRoleMenusPath: '/api/role/saveRolesMenus', currentPage: 1, pageSize: 10, total: 0, @@ -80,37 +105,147 @@ export default { tableData: [], menus: [], menuIds: [], - defaultProps: {}, - activeName: 'second' + defaultProps: { children: 'children', label: 'label' ,isLeaf: 'isLeaf'}, + activeName: 'first', + dialogVisible: false, + formType: 'add', + form: {}, + rule: { + name: [ + { required: true, message: '请输入名称', trigger: 'blur' } + ] + }, + currentRow: null }; }, activated() { this.search(); }, + watch: { + currentRow: 'currentRowChange' + }, methods: { handleClick(tab, event) { console.log(tab, event); }, - create(){}, + create(){ + this.form = {} + this.formType = "add"; + this.dialogVisible = true + listenGoBack(this.closeFunc) + }, search(){ this.result = this.$post(this.queryPath+ "/" + this.currentPage + "/" + this.pageSize, this.condition, response => { - let data = response.data; - this.total = data.itemCount; - this.tableData = data.listObject; + let data = response.data + this.total = data.itemCount + this.tableData = data.listObject }) }, edit(row){ - + this.formType = 'modify' + this.dialogVisible = true + this.form = Object.assign({}, row) + listenGoBack(this.closeFunc) }, - getMenuDatas(node, resolve){ + saveRole(roleForm){ + this.$refs[roleForm].validate(valid => { + if (valid) { + const url = this.formType=='add' ? this.createPath : this.updatePath + this.result = this.$post(url, this.form, () => { + this.$success(this.$t('commons.save_success')) + this.search(); + this.dialogVisible = false + }); + } else { + return false; + } + }) + }, + + closeFunc() { + this.dialogVisible = false + removeGoBackListener(this.closeFunc); + }, + + getMenuDatas(node, resolve){ + this.$post(this.queryMenusPath+(node.data.id ? node.data.id : 0), null, (res) => { + const datas = res.data + const nodes = datas.map(data => this.formatNode(data)) + resolve && resolve(nodes) + }) + }, + formatNode(node) { + const result = { + id: node.menuId, + label: node.title, + isLeaf: !node.hasChildren, + children: node.children + } + return result }, menuChange(menu){ + this.$post(this.childMenusPath + menu.id, null, res => { + const childIds = res.data + if (this.menuIds.indexOf(menu.id) !== -1) { + for (let i = 0; i < childIds.length; i++) { + const index = this.menuIds.indexOf(childIds[i]) + if (index !== -1) { + this.menuIds.splice(index, 1) + } + } + } else { + for (let i = 0; i < childIds.length; i++) { + const index = this.menuIds.indexOf(childIds[i]) + if (index === -1) { + this.menuIds.push(childIds[i]) + } + } + } + console.log(this.menuIds) + this.$refs.menu.setCheckedKeys(this.menuIds) + this.saveMenus() + }) + }, + saveMenus(){ + if (!this.currentRow) { + return + } + const param = {roleId: this.currentRow.roleId, menuIds: this.menuIds} + this.$post(this.saveRoleMenusPath, param, res => { + this.search() + }) + }, + rowClick(row,column, event){ + this.currentRow = row + }, + currentRowChange(newVal, oldVal){ + if (newVal == oldVal) { + return + } + if (!newVal) { + this.menuIds = [] + return + } + this.menuIds = newVal.menuIds; + this.$refs.menu.setCheckedKeys(this.menuIds) }, handleDelete(row){ + this.$confirm('确认删除角色['+row.name+']?', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + const url = this.deletePath+row.roleId + this.$post(url, null, () => { + this.$success(this.$t('commons.modify_success')) + this.search() + }); + }).catch(() => { + }) } } } diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index 2e2bfd0ced..35a9de1f18 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -385,6 +385,8 @@ export default { test_user: '测试人员', test_viewer: '只读用户', add: '添加角色', + delete: '删除角色', + modify: '修改角色', }, report: { api_test_report: '接口测试报告',