feat:角色 role 的数据权限 80%

pull/62/head
YunaiV 2025-03-31 16:53:51 +08:00
parent 108d13b896
commit 36838c910b
5 changed files with 105 additions and 76 deletions

View File

@ -0,0 +1,57 @@
import { requestClient } from '#/api/request';
export namespace SystemPermissionApi {
/** 分配用户角色请求 */
export interface AssignUserRoleReqVO {
userId: number;
roleIds: number[];
}
/** 分配角色菜单请求 */
export interface AssignRoleMenuReqVO {
roleId: number;
menuIds: number[];
}
/** 分配角色数据权限请求 */
export interface AssignRoleDataScopeReqVO {
roleId: number;
dataScope: number;
dataScopeDeptIds: number[];
}
}
/** 查询角色拥有的菜单权限 */
export async function getRoleMenuList(roleId: number) {
return requestClient.get(
`/system/permission/list-role-menus?roleId=${roleId}`,
);
}
/** 赋予角色菜单权限 */
export async function assignRoleMenu(
data: SystemPermissionApi.AssignRoleMenuReqVO,
) {
return requestClient.post('/system/permission/assign-role-menu', data);
}
/** 赋予角色数据权限 */
export async function assignRoleDataScope(
data: SystemPermissionApi.AssignRoleDataScopeReqVO,
) {
return requestClient.post('/system/permission/assign-role-data-scope', data);
}
/** 查询用户拥有的角色数组 */
export async function getUserRoleList(userId: number) {
return requestClient.get(
`/system/permission/list-user-roles?userId=${userId}`,
);
}
/** 赋予用户角色 */
export async function assignUserRole(
data: SystemPermissionApi.AssignUserRoleReqVO,
) {
return requestClient.post('/system/permission/assign-user-role', data);
}

View File

@ -11,7 +11,7 @@ import { DICT_TYPE, getDictOptions } from '#/utils/dict';
import { CommonStatusEnum } from '#/utils/constants'; import { CommonStatusEnum } from '#/utils/constants';
import { handleTree } from '#/utils/tree'; import { handleTree } from '#/utils/tree';
/** 获取编辑表单的字段配置 */ /** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] { export function useFormSchema(): VbenFormSchema[] {
return [ return [
{ {
@ -123,7 +123,7 @@ export function useFormSchema(): VbenFormSchema[] {
]; ];
} }
/** 获取表格列配置 */ /** 列表的字段 */
const userList = await getSimpleUserList(); const userList = await getSimpleUserList();
export function useGridColumns( export function useGridColumns(
onActionClick?: OnActionClickFn<SystemDeptApi.SystemDept>, onActionClick?: OnActionClickFn<SystemDeptApi.SystemDept>,

View File

@ -5,6 +5,7 @@ import type { SystemPostApi } from '#/api/system/post';
import { DICT_TYPE, getDictOptions } from '#/utils/dict'; import { DICT_TYPE, getDictOptions } from '#/utils/dict';
import { CommonStatusEnum } from '#/utils/constants'; import { CommonStatusEnum } from '#/utils/constants';
/** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] { export function useFormSchema(): VbenFormSchema[] {
return [ return [
{ {
@ -59,6 +60,7 @@ export function useFormSchema(): VbenFormSchema[] {
]; ];
} }
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] { export function useGridFormSchema(): VbenFormSchema[] {
return [ return [
{ {
@ -83,6 +85,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
]; ];
} }
/** 列表的字段 */
export function useGridColumns<T = SystemPostApi.SystemPost>( export function useGridColumns<T = SystemPostApi.SystemPost>(
onActionClick: OnActionClickFn<T>, onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] { ): VxeTableGridOptions['columns'] {

View File

@ -1,7 +1,8 @@
import {type VbenFormSchema, z} from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table'; import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemRoleApi } from '#/api/system/role'; import type { SystemRoleApi } from '#/api/system/role';
import { z } from '#/adapter/form';
import { DICT_TYPE, getDictOptions } from '#/utils/dict'; import { DICT_TYPE, getDictOptions } from '#/utils/dict';
import { CommonStatusEnum, SystemDataScopeEnum } from '#/utils/constants'; import { CommonStatusEnum, SystemDataScopeEnum } from '#/utils/constants';
@ -27,7 +28,7 @@ export function useFormSchema(): VbenFormSchema[] {
component: 'Input', component: 'Input',
fieldName: 'code', fieldName: 'code',
label: '角色标识', label: '角色标识',
rules:'required', rules: 'required',
}, },
{ {
component: 'InputNumber', component: 'InputNumber',
@ -56,7 +57,7 @@ export function useFormSchema(): VbenFormSchema[] {
component: 'Textarea', component: 'Textarea',
fieldName: 'remark', fieldName: 'remark',
label: '角色备注', label: '角色备注',
} },
]; ];
} }
@ -78,7 +79,7 @@ export function useAssignDataPermissionFormSchema(): VbenFormSchema[] {
label: '角色名称', label: '角色名称',
componentProps: { componentProps: {
disabled: true, disabled: true,
} },
}, },
{ {
component: 'Input', component: 'Input',
@ -86,7 +87,7 @@ export function useAssignDataPermissionFormSchema(): VbenFormSchema[] {
label: '角色标识', label: '角色标识',
componentProps: { componentProps: {
disabled: true, disabled: true,
} },
}, },
{ {
component: 'Select', component: 'Select',
@ -95,22 +96,22 @@ export function useAssignDataPermissionFormSchema(): VbenFormSchema[] {
componentProps: { componentProps: {
class: 'w-full', class: 'w-full',
options: getDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE, 'number'), options: getDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE, 'number'),
} },
}, },
{ {
component: 'Input', component: 'Input',
fieldName: 'dataScopeDeptIds', fieldName: 'dataScopeDeptIds',
label: '部门范围', label: '部门范围',
// dependencies: { dependencies: {
// triggerFields: ['dataScope'], triggerFields: ['dataScope'],
// show: (values) => { show: (values) => {
// return values.dataScope === SystemDataScopeEnum.DEPT_CUSTOM; return values.dataScope === SystemDataScopeEnum.DEPT_CUSTOM;
// } },
// }, },
formItemClass: 'items-start', // TODO @芋艿: formItemClass: 'items-start',
modelPropName: 'modelValue', // TODO @芋艿: modelPropName: 'modelValue', // TODO @芋艿:这个是不是可以去掉哈
} },
] ];
} }
/** 列表的搜索表单 */ /** 列表的搜索表单 */
@ -224,4 +225,3 @@ export function useGridColumns<T = SystemRoleApi.SystemRole>(
} }
// TODO @芋艿:角色分配 // TODO @芋艿:角色分配
// TODO @芋艿:数据权限

View File

@ -1,24 +1,27 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { SystemRoleApi } from '#/api/system/role'; import type { SystemRoleApi } from '#/api/system/role';
import type { SystemDeptApi } from '#/api/system/dept';
import { ref } from 'vue'; import { VbenTree } from '@vben/common-ui';
import { useVbenModal } from '@vben/common-ui'; import { useVbenModal } from '@vben/common-ui';
import { Button, message } from 'ant-design-vue'; import { Button, message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form'; import { ref } from 'vue';
import { createRole, updateRole, getRole } from '#/api/system/role';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { useVbenForm } from '#/adapter/form';
import { getRole } from '#/api/system/role';
import { assignRoleDataScope} from '#/api/system/permission';
import { getDeptList } from '#/api/system/dept';
import { handleTree } from '#/utils/tree';
import { useAssignDataPermissionFormSchema } from '../data'; import { useAssignDataPermissionFormSchema } from '../data';
import { SystemDataScopeEnum } from '#/utils/constants';
const emit = defineEmits(['success']); const emit = defineEmits(['success']);
const formData = ref<SystemRoleApi.SystemRole>(); const formData = ref<SystemRoleApi.SystemRole>();
// TODO @ const deptTree = ref<SystemDeptApi.SystemDept[]>([]); //
import { getDeptList, type SystemDeptApi } from '#/api/system/dept'; const deptLoading = ref(false); //
import { handleTree } from '#/utils/tree';
import type { Recordable } from '@vben-core/typings';
const [Form, formApi] = useVbenForm({ const [Form, formApi] = useVbenForm({
layout: 'horizontal', layout: 'horizontal',
@ -38,11 +41,13 @@ const [Modal, modalApi] = useVbenModal({
return; return;
} }
modalApi.lock(); modalApi.lock();
const data = (await formApi.getValues()) as SystemRoleApi.SystemRole; const data = await formApi.getValues();
try { try {
await (formData.value?.id await assignRoleDataScope({
? updateRole(data) roleId: data.id,
: createRole(data)); dataScope: data.dataScope,
dataScopeDeptIds: data.dataScope === SystemDataScopeEnum.DEPT_CUSTOM ? data.dataScopeDeptIds : undefined,
});
await modalApi.close(); await modalApi.close();
emit('success'); emit('success');
message.success({ message.success({
@ -65,54 +70,36 @@ const [Modal, modalApi] = useVbenModal({
try { try {
formData.value = await getRole(data.id as number); formData.value = await getRole(data.id as number);
await formApi.setValues(formData.value); await formApi.setValues(formData.value);
await loadPermissions(); //
await loadDeptTree();
} finally { } finally {
modalApi.lock(false); modalApi.lock(false);
} }
}, },
}); });
// TODO /** 加载部门树 */
const permissions = ref<SystemDeptApi.SystemDept[]>([]); async function loadDeptTree() {
const loadingPermissions = ref(false); deptLoading.value = true;
// TODO
async function loadPermissions() {
loadingPermissions.value = true;
try { try {
debugger
const res = await getDeptList(); const res = await getDeptList();
permissions.value = handleTree(res); deptTree.value = handleTree(res);
} finally { } finally {
loadingPermissions.value = false; deptLoading.value = false;
} }
} }
// TODO
function getNodeClass(node: Recordable<any>) {
const classes: string[] = [];
if (node.value?.type === 'button') {
classes.push('inline-flex');
if (node.index % 3 >= 1) {
classes.push('!pl-0');
}
}
return classes.join(' ');
}
</script> </script>
<template> <template>
<Modal title="数据权限"> <Modal title="数据权限">
<Form class="mx-4"> <Form class="mx-4">
<template #dataScopeDeptIds="slotProps"> <template #dataScopeDeptIds="slotProps">
<Spin :spinning="loadingPermissions" wrapper-class-name="w-full"> <Spin :spinning="deptLoading" class="w-full">
<VbenTree <VbenTree
:tree-data="permissions" :tree-data="deptTree"
multiple multiple
bordered bordered
:default-expanded-level="2" :default-expanded-level="2"
:get-node-class="getNodeClass"
v-bind="slotProps" v-bind="slotProps"
value-field="id" value-field="id"
label-field="name" label-field="name"
@ -129,21 +116,3 @@ function getNodeClass(node: Recordable<any>) {
</template> </template>
</Modal> </Modal>
</template> </template>
<!-- TODO @芋艿测试 -->
<style lang="css" scoped>
:deep(.ant-tree-title) {
.tree-actions {
display: none;
margin-left: 20px;
}
}
:deep(.ant-tree-title:hover) {
.tree-actions {
display: flex;
flex: auto;
justify-content: flex-end;
margin-left: 20px;
}
}
</style>