Commit 85287e1f7f0dc402135e8bae317583e158469377

Authored by xingyu
1 parent 72db9341

feat: 完善用户权限

yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignUserRoleReqVO.java
@@ -12,8 +12,8 @@ import java.util.Set; @@ -12,8 +12,8 @@ import java.util.Set;
12 @Data 12 @Data
13 public class PermissionAssignUserRoleReqVO { 13 public class PermissionAssignUserRoleReqVO {
14 14
15 - @ApiModelProperty(value = "角色编号", required = true, example = "1")  
16 - @NotNull(message = "角色编号不能为空") 15 + @ApiModelProperty(value = "用户编号", required = true, example = "1")
  16 + @NotNull(message = "用户编号不能为空")
17 private Long userId; 17 private Long userId;
18 18
19 @ApiModelProperty(value = "角色编号列表", example = "1,3,5") 19 @ApiModelProperty(value = "角色编号列表", example = "1,3,5")
yudao-ui-admin-vue3/src/api/system/permission/index.ts 0 → 100644
  1 +import { useAxios } from '@/hooks/web/useAxios'
  2 +import type {
  3 + PermissionAssignRoleDataScopeReqVO,
  4 + PermissionAssignRoleMenuReqVO,
  5 + PermissionAssignUserRoleReqVO
  6 +} from './types'
  7 +
  8 +const request = useAxios()
  9 +
  10 +// 查询角色拥有的菜单权限
  11 +export const listRoleMenusApi = async (roleId: number) => {
  12 + return await request.get({ url: '/system/permission/list-role-resources?roleId=' + roleId })
  13 +}
  14 +
  15 +// 赋予角色菜单权限
  16 +export const assignRoleMenuApi = async (data: PermissionAssignRoleMenuReqVO) => {
  17 + return await request.post({ url: '/system/permission/assign-role-menu', data })
  18 +}
  19 +
  20 +// 赋予角色数据权限
  21 +export const assignRoleDataScopeApi = async (data: PermissionAssignRoleDataScopeReqVO) => {
  22 + return await request.post({ url: '/system/permission/assign-role-data-scope', data })
  23 +}
  24 +
  25 +// 查询用户拥有的角色数组
  26 +export const listUserRolesApi = async (userId: number) => {
  27 + return await request.get({ url: '/system/permission/list-user-roles?userId=' + userId })
  28 +}
  29 +
  30 +// 赋予用户角色
  31 +export const aassignUserRoleApi = async (data: PermissionAssignUserRoleReqVO) => {
  32 + return await request.post({ url: '/system/permission/assign-user-role', data })
  33 +}
yudao-ui-admin-vue3/src/api/system/permission/types.ts 0 → 100644
  1 +export type PermissionAssignUserRoleReqVO = {
  2 + userId: number
  3 + roleIds: number[]
  4 +}
  5 +
  6 +export type PermissionAssignRoleMenuReqVO = {
  7 + roleId: number
  8 + menuIds: number[]
  9 +}
  10 +
  11 +export type PermissionAssignRoleDataScopeReqVO = {
  12 + roleId: number
  13 + dataScope: number
  14 + dataScopeDeptIds: number[]
  15 +}
yudao-ui-admin-vue3/src/api/system/role/index.ts
@@ -4,31 +4,36 @@ import type { RoleVO } from './types' @@ -4,31 +4,36 @@ import type { RoleVO } from './types'
4 const request = useAxios() 4 const request = useAxios()
5 5
6 // 查询角色列表 6 // 查询角色列表
7 -export const getRolePageApi = (params) => {  
8 - return request.get({ url: '/system/role/page', params }) 7 +export const getRolePageApi = async (params) => {
  8 + return await request.get({ url: '/system/role/page', params })
  9 +}
  10 +
  11 +// 查询角色(精简)列表
  12 +export const listSimpleRolesApi = async () => {
  13 + return await request.get({ url: '/system/role/list-all-simple' })
9 } 14 }
10 15
11 // 查询角色详情 16 // 查询角色详情
12 -export const getRoleApi = (id: number) => {  
13 - return request.get({ url: '/system/role/get?id=' + id }) 17 +export const getRoleApi = async (id: number) => {
  18 + return await request.get({ url: '/system/role/get?id=' + id })
14 } 19 }
15 20
16 // 新增角色 21 // 新增角色
17 -export const createRoleApi = (data: RoleVO) => {  
18 - return request.post({ url: '/system/role/create', data }) 22 +export const createRoleApi = async (data: RoleVO) => {
  23 + return await request.post({ url: '/system/role/create', data })
19 } 24 }
20 25
21 // 修改角色 26 // 修改角色
22 -export const updateRoleApi = (data: RoleVO) => {  
23 - return request.put({ url: '/system/role/update', data }) 27 +export const updateRoleApi = async (data: RoleVO) => {
  28 + return await request.put({ url: '/system/role/update', data })
24 } 29 }
25 30
26 // 修改角色状态 31 // 修改角色状态
27 -export const updateRoleStatusApi = (data: RoleVO) => {  
28 - return request.put({ url: '/system/role/update-status', data }) 32 +export const updateRoleStatusApi = async (data: RoleVO) => {
  33 + return await request.put({ url: '/system/role/update-status', data })
29 } 34 }
30 35
31 // 删除角色 36 // 删除角色
32 -export const deleteRoleApi = (id: number) => {  
33 - return request.delete({ url: '/system/role/delete?id=' + id }) 37 +export const deleteRoleApi = async (id: number) => {
  38 + return await request.delete({ url: '/system/role/delete?id=' + id })
34 } 39 }
yudao-ui-admin-vue3/src/components/CountTo/src/CountTo.vue
@@ -157,7 +157,7 @@ const count = (timestamp: number) => { @@ -157,7 +157,7 @@ const count = (timestamp: number) => {
157 } else { 157 } else {
158 state.printVal = state.printVal > endVal ? endVal : state.printVal 158 state.printVal = state.printVal > endVal ? endVal : state.printVal
159 } 159 }
160 - state.displayValue = formatNumber(state.printVal) 160 + state.displayValue = formatNumber(state.printVal!)
161 if (progress < (state.localDuration as number)) { 161 if (progress < (state.localDuration as number)) {
162 state.rAF = requestAnimationFrame(count) 162 state.rAF = requestAnimationFrame(count)
163 } else { 163 } else {
yudao-ui-admin-vue3/src/views/system/role/index.vue
1 <script setup lang="ts"> 1 <script setup lang="ts">
2 import { onMounted, reactive, ref, unref } from 'vue' 2 import { onMounted, reactive, ref, unref } from 'vue'
3 import dayjs from 'dayjs' 3 import dayjs from 'dayjs'
4 -import { DICT_TYPE, getDictOptions } from '@/utils/dict'  
5 -import { useTable } from '@/hooks/web/useTable'  
6 -import { useI18n } from '@/hooks/web/useI18n'  
7 -import { FormExpose } from '@/components/Form'  
8 -import type { RoleVO } from '@/api/system/role/types'  
9 -import { rules, allSchemas } from './role.data'  
10 -import * as RoleApi from '@/api/system/role'  
11 -import Dialog from '@/components/Dialog/src/Dialog.vue'  
12 import { 4 import {
13 ElForm, 5 ElForm,
14 ElFormItem, 6 ElFormItem,
@@ -20,6 +12,18 @@ import { @@ -20,6 +12,18 @@ import {
20 ElCard, 12 ElCard,
21 ElSwitch 13 ElSwitch
22 } from 'element-plus' 14 } from 'element-plus'
  15 +import { DICT_TYPE, getDictOptions } from '@/utils/dict'
  16 +import { useTable } from '@/hooks/web/useTable'
  17 +import { useI18n } from '@/hooks/web/useI18n'
  18 +import { FormExpose } from '@/components/Form'
  19 +import { rules, allSchemas } from './role.data'
  20 +import type { RoleVO } from '@/api/system/role/types'
  21 +import type {
  22 + PermissionAssignRoleDataScopeReqVO,
  23 + PermissionAssignRoleMenuReqVO
  24 +} from '@/api/system/permission/types'
  25 +import * as RoleApi from '@/api/system/role'
  26 +import * as PermissionApi from '@/api/system/permission'
23 import { listSimpleMenusApi } from '@/api/system/menu' 27 import { listSimpleMenusApi } from '@/api/system/menu'
24 import { listSimpleDeptApi } from '@/api/system/dept' 28 import { listSimpleDeptApi } from '@/api/system/dept'
25 import { handleTree } from '@/utils/tree' 29 import { handleTree } from '@/utils/tree'
@@ -95,6 +99,7 @@ const handleDetail = async (row: RoleVO) =&gt; { @@ -95,6 +99,7 @@ const handleDetail = async (row: RoleVO) =&gt; {
95 99
96 // ========== 数据权限 ========== 100 // ========== 数据权限 ==========
97 const dataScopeForm = reactive({ 101 const dataScopeForm = reactive({
  102 + id: 0,
98 name: '', 103 name: '',
99 code: '', 104 code: '',
100 dataScope: 0, 105 dataScope: 0,
@@ -111,33 +116,62 @@ const dialogScopeVisible = ref(false) @@ -111,33 +116,62 @@ const dialogScopeVisible = ref(false)
111 const dialogScopeTitle = ref('数据权限') 116 const dialogScopeTitle = ref('数据权限')
112 const actionScopeType = ref('') 117 const actionScopeType = ref('')
113 const dataScopeDictDatas = ref() 118 const dataScopeDictDatas = ref()
  119 +const defaultCheckedKeys = ref()
114 // 选项 120 // 选项
115 const checkStrictly = ref(true) 121 const checkStrictly = ref(true)
116 const treeNodeAll = ref(false) 122 const treeNodeAll = ref(false)
  123 +// 全选/全不选
  124 +const handleCheckedTreeNodeAll = () => {
  125 + treeRef.value!.setCheckedNodes(treeNodeAll.value ? treeOptions.value : [])
  126 +}
117 // 权限操作 127 // 权限操作
118 const handleScope = async (type: string, row: RoleVO) => { 128 const handleScope = async (type: string, row: RoleVO) => {
119 - checkStrictly.value = true  
120 - treeNodeAll.value = false  
121 - dataScopeForm.dataScope = 0 129 + dataScopeForm.id = row.id
122 dataScopeForm.name = row.name 130 dataScopeForm.name = row.name
123 dataScopeForm.code = row.code 131 dataScopeForm.code = row.code
124 if (type === 'menu') { 132 if (type === 'menu') {
125 const menuRes = await listSimpleMenusApi() 133 const menuRes = await listSimpleMenusApi()
126 treeOptions.value = handleTree(menuRes) 134 treeOptions.value = handleTree(menuRes)
127 - } else if (type === 'dept') { 135 + const role = await PermissionApi.listRoleMenusApi(row.id)
  136 + console.info(role)
  137 + if (role) {
  138 + // treeRef.value!.setCheckedKeys(role as unknown as Array<number>)
  139 + defaultCheckedKeys.value = role
  140 + }
  141 + } else if (type === 'data') {
128 const deptRes = await listSimpleDeptApi() 142 const deptRes = await listSimpleDeptApi()
129 treeOptions.value = handleTree(deptRes) 143 treeOptions.value = handleTree(deptRes)
  144 + const role = await RoleApi.getRoleApi(row.id)
  145 + console.info(role)
  146 + dataScopeForm.dataScope = role.dataScope
  147 + if (role.dataScopeDeptIds) {
  148 + // treeRef.value!.setCheckedKeys(role.dataScopeDeptIds as unknown as Array<number>, false)
  149 + defaultCheckedKeys.value = role.dataScopeDeptIds
  150 + }
130 } 151 }
131 actionScopeType.value = type 152 actionScopeType.value = type
132 dialogScopeVisible.value = true 153 dialogScopeVisible.value = true
133 } 154 }
134 -// 全选/全不选  
135 -const handleCheckedTreeNodeAll = () => {  
136 - treeRef.value?.setCheckedNodes(treeNodeAll.value ? treeOptions.value : [])  
137 -}  
138 -// TODO:保存  
139 -const submitScope = () => {  
140 - console.info() 155 +// 保存权限
  156 +const submitScope = async () => {
  157 + const keys = treeRef.value!.getCheckedKeys(false) as unknown as Array<number>
  158 + console.info(keys)
  159 + if ('data' === actionScopeType.value) {
  160 + const data = ref<PermissionAssignRoleDataScopeReqVO>({
  161 + roleId: dataScopeForm.id,
  162 + dataScope: dataScopeForm.dataScope,
  163 + dataScopeDeptIds: dataScopeForm.dataScope !== SystemDataScopeEnum.DEPT_CUSTOM ? [] : keys
  164 + })
  165 + await PermissionApi.assignRoleDataScopeApi(data.value)
  166 + } else if ('menu' === actionScopeType.value) {
  167 + const data = ref<PermissionAssignRoleMenuReqVO>({
  168 + roleId: dataScopeForm.id,
  169 + menuIds: keys
  170 + })
  171 + await PermissionApi.assignRoleMenuApi(data.value)
  172 + }
  173 + ElMessage.success(t('common.updateSuccess'))
  174 + dialogScopeVisible.value = false
141 } 175 }
142 const init = () => { 176 const init = () => {
143 dataScopeDictDatas.value = getDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE) 177 dataScopeDictDatas.value = getDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE)
@@ -308,7 +342,7 @@ onMounted(() =&gt; { @@ -308,7 +342,7 @@ onMounted(() =&gt; {
308 ref="treeRef" 342 ref="treeRef"
309 node-key="id" 343 node-key="id"
310 show-checkbox 344 show-checkbox
311 - default-expand-all 345 + :default-checked-keys="defaultCheckedKeys"
312 :check-strictly="!checkStrictly" 346 :check-strictly="!checkStrictly"
313 :props="defaultProps" 347 :props="defaultProps"
314 :data="treeOptions" 348 :data="treeOptions"
yudao-ui-admin-vue3/src/views/system/user/index.vue
1 <script setup lang="ts"> 1 <script setup lang="ts">
2 -import { onMounted, ref, unref, watch } from 'vue' 2 +import { onMounted, reactive, ref, unref, watch } from 'vue'
3 import dayjs from 'dayjs' 3 import dayjs from 'dayjs'
4 import { 4 import {
  5 + ElTag,
5 ElInput, 6 ElInput,
6 ElCard, 7 ElCard,
7 ElTree, 8 ElTree,
@@ -23,14 +24,19 @@ import { useTable } from &#39;@/hooks/web/useTable&#39; @@ -23,14 +24,19 @@ import { useTable } from &#39;@/hooks/web/useTable&#39;
23 import { FormExpose } from '@/components/Form' 24 import { FormExpose } from '@/components/Form'
24 import type { UserVO } from '@/api/system/user/types' 25 import type { UserVO } from '@/api/system/user/types'
25 import type { PostVO } from '@/api/system/post/types' 26 import type { PostVO } from '@/api/system/post/types'
  27 +import type { PermissionAssignUserRoleReqVO } from '@/api/system/permission/types'
26 import { listSimpleDeptApi } from '@/api/system/dept' 28 import { listSimpleDeptApi } from '@/api/system/dept'
27 import { listSimplePostsApi } from '@/api/system/post' 29 import { listSimplePostsApi } from '@/api/system/post'
  30 +import { listSimpleRolesApi } from '@/api/system/role'
  31 +import { aassignUserRoleApi, listUserRolesApi } from '@/api/system/permission'
28 import { rules, allSchemas } from './user.data' 32 import { rules, allSchemas } from './user.data'
29 import * as UserApi from '@/api/system/user' 33 import * as UserApi from '@/api/system/user'
30 import download from '@/utils/download' 34 import download from '@/utils/download'
  35 +import { useRouter } from 'vue-router'
31 import { CommonStatusEnum } from '@/utils/constants' 36 import { CommonStatusEnum } from '@/utils/constants'
32 import { getAccessToken, getTenantId } from '@/utils/auth' 37 import { getAccessToken, getTenantId } from '@/utils/auth'
33 import { useMessage } from '@/hooks/web/useMessage' 38 import { useMessage } from '@/hooks/web/useMessage'
  39 +
34 const message = useMessage() 40 const message = useMessage()
35 interface Tree { 41 interface Tree {
36 id: number 42 id: number
@@ -74,6 +80,10 @@ const handleDeptNodeClick = (data: { [key: string]: any }) =&gt; { @@ -74,6 +80,10 @@ const handleDeptNodeClick = (data: { [key: string]: any }) =&gt; {
74 tableTitle.value = data.name 80 tableTitle.value = data.name
75 methods.getList() 81 methods.getList()
76 } 82 }
  83 +const { push } = useRouter()
  84 +const handleDeptEdit = () => {
  85 + push('/system/dept')
  86 +}
77 watch(filterText, (val) => { 87 watch(filterText, (val) => {
78 treeRef.value!.filter(val) 88 treeRef.value!.filter(val)
79 }) 89 })
@@ -164,7 +174,37 @@ const handleResetPwd = (row: UserVO) =&gt; { @@ -164,7 +174,37 @@ const handleResetPwd = (row: UserVO) =&gt; {
164 }) 174 })
165 }) 175 })
166 } 176 }
167 - 177 +// 分配角色
  178 +const roleDialogVisible = ref(false)
  179 +const roleOptions = ref()
  180 +const userRole = reactive({
  181 + id: 0,
  182 + username: '',
  183 + nickname: '',
  184 + roleIds: []
  185 +})
  186 +const handleRole = async (row: UserVO) => {
  187 + userRole.id = row.id
  188 + userRole.username = row.username
  189 + userRole.nickname = row.nickname
  190 + // 获得角色拥有的权限集合
  191 + const roles = await listUserRolesApi(row.id)
  192 + userRole.roleIds = roles
  193 + // 获取角色列表
  194 + const roleOpt = await listSimpleRolesApi()
  195 + roleOptions.value = roleOpt
  196 + roleDialogVisible.value = true
  197 +}
  198 +// 提交
  199 +const submitRole = async () => {
  200 + const data = ref<PermissionAssignUserRoleReqVO>({
  201 + userId: userRole.id,
  202 + roleIds: userRole.roleIds
  203 + })
  204 + await aassignUserRoleApi(data.value)
  205 + message.success(t('common.updateSuccess'))
  206 + roleDialogVisible.value = false
  207 +}
168 // ========== 详情相关 ========== 208 // ========== 详情相关 ==========
169 const detailRef = ref() 209 const detailRef = ref()
170 210
@@ -251,6 +291,9 @@ getList() @@ -251,6 +291,9 @@ getList()
251 <template #header> 291 <template #header>
252 <div class="card-header"> 292 <div class="card-header">
253 <span>部门列表</span> 293 <span>部门列表</span>
  294 + <el-button link class="button" type="primary" @click="handleDeptEdit">
  295 + 修改部门
  296 + </el-button>
254 </div> 297 </div>
255 </template> 298 </template>
256 <el-input v-model="filterText" placeholder="搜索部门" /> 299 <el-input v-model="filterText" placeholder="搜索部门" />
@@ -354,6 +397,14 @@ getList() @@ -354,6 +397,14 @@ getList()
354 <el-button 397 <el-button
355 link 398 link
356 type="primary" 399 type="primary"
  400 + v-hasPermi="['system:permission:assign-user-role']"
  401 + @click="handleRole(row)"
  402 + >
  403 + <Icon icon="ep:key" class="mr-1px" /> 分配角色
  404 + </el-button>
  405 + <el-button
  406 + link
  407 + type="primary"
357 v-hasPermi="['system:user:delete']" 408 v-hasPermi="['system:user:delete']"
358 @click="delList(row.id, false)" 409 @click="delList(row.id, false)"
359 > 410 >
@@ -398,10 +449,14 @@ getList() @@ -398,10 +449,14 @@ getList()
398 :data="detailRef" 449 :data="detailRef"
399 > 450 >
400 <template #deptId="{ row }"> 451 <template #deptId="{ row }">
401 - <span>{{ row.dept.name }}</span> 452 + <span>{{ row.dept?.name }}</span>
402 </template> 453 </template>
403 <template #postIds="{ row }"> 454 <template #postIds="{ row }">
404 - <span>{{ row.dept.name }}</span> 455 + <el-tag v-for="(post, index) in row.postIds" :key="index" index="">
  456 + <template v-for="postObj in postOptions">
  457 + {{ post === postObj.id ? postObj.name : '' }}
  458 + </template>
  459 + </el-tag>
405 </template> 460 </template>
406 <template #sex="{ row }"> 461 <template #sex="{ row }">
407 <DictTag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" /> 462 <DictTag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
@@ -426,6 +481,34 @@ getList() @@ -426,6 +481,34 @@ getList()
426 <el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button> 481 <el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
427 </template> 482 </template>
428 </Dialog> 483 </Dialog>
  484 + <!-- 分配用户角色 -->
  485 + <Dialog v-model="roleDialogVisible" title="分配角色">
  486 + <el-form :model="userRole" label-width="80px">
  487 + <el-form-item label="用户名称">
  488 + <el-input v-model="userRole.username" :disabled="true" />
  489 + </el-form-item>
  490 + <el-form-item label="用户昵称">
  491 + <el-input v-model="userRole.nickname" :disabled="true" />
  492 + </el-form-item>
  493 + <el-form-item label="角色">
  494 + <el-select v-model="userRole.roleIds" multiple>
  495 + <el-option
  496 + v-for="item in roleOptions"
  497 + :key="parseInt(item.id)"
  498 + :label="item.name"
  499 + :value="parseInt(item.id)"
  500 + />
  501 + </el-select>
  502 + </el-form-item>
  503 + </el-form>
  504 + <!-- 操作按钮 -->
  505 + <template #footer>
  506 + <el-button type="primary" :loading="loading" @click="submitRole">
  507 + {{ t('action.save') }}
  508 + </el-button>
  509 + <el-button @click="roleDialogVisible = false">{{ t('dialog.close') }}</el-button>
  510 + </template>
  511 + </Dialog>
429 <!-- 导入 --> 512 <!-- 导入 -->
430 <Dialog 513 <Dialog
431 v-model="importDialogVisible" 514 v-model="importDialogVisible"
yudao-ui-admin-vue3/src/views/system/user/user.data.ts
@@ -152,7 +152,7 @@ const crudSchemas = reactive&lt;CrudSchema[]&gt;([ @@ -152,7 +152,7 @@ const crudSchemas = reactive&lt;CrudSchema[]&gt;([
152 }, 152 },
153 { 153 {
154 field: 'action', 154 field: 'action',
155 - width: '340px', 155 + width: '400px',
156 label: t('table.action'), 156 label: t('table.action'),
157 form: { 157 form: {
158 show: false 158 show: false