feat: RBAC

This commit is contained in:
2025-03-12 16:34:54 +08:00
parent fb3686968d
commit 1c691e8626
9 changed files with 188 additions and 26 deletions

View File

@@ -1,17 +1,17 @@
import request from '@/utils/request';
export function assignMenus(id: string, menuIds: string[]) {
return request.put(`/permission/roles/${id}/menus`, { menuIds });
export function assignMenus(id: string, ids: string[]) {
return request.put(`/permission/roles/${id}/menus`, { ids });
}
export function getMenus(id: string) {
return request.get(`/permission/roles/${id}/menus`);
}
export function assignResources(id: string, resourceIds: string[]) {
return request.put(`/permission/roles/${id}/menus`, { resourceIds });
export function assignResources(id: string, ids: string[]) {
return request.put(`/permission/roles/${id}/resources`, { ids });
}
export function getResources(id: string) {
return request.get(`/permission/roles/${id}/menus`);
return request.get(`/permission/roles/${id}/resources`);
}

View File

@@ -3,9 +3,7 @@ import { Modal, Form, Input, Message, Tree } from '@arco-design/web-react';
import { getTreeMenu } from '@/api/menu';
import {
getMenus,
getResources,
assignMenus,
assignResources,
} from '@/api/permission';
interface Props {

View File

@@ -0,0 +1,126 @@
import React, { useEffect, useState } from 'react';
import { Modal, Form, Input, Message, Tree } from '@arco-design/web-react';
import { getTreeResource } from '@/api/resource';
import {
getResources,
assignResources,
} from '@/api/permission';
interface Props {
record: { [key: string]: any } | null;
visible: boolean;
handleConfirm: () => void;
handleCancel: () => void;
}
function GrantPermission(props: Props) {
const { visible, record, handleConfirm, handleCancel } = props;
const [confirmLoading, setConfirmLoading] = useState(false);
const [checkedKeys, setCheckedKeys] = useState([]);
const [treeData, setTreeData] = useState([]);
const onOk = async () => {
try {
setConfirmLoading(true);
const values = await form.validate();
await assignResources(values.id, checkedKeys);
Message.success(`操作成功!`);
handleConfirm();
} catch (e) {
console.log(e);
} finally {
setConfirmLoading(false);
}
};
const [form] = Form.useForm();
useEffect(() => {
record &&
form.setFieldsValue({
...record,
});
}, [record]);
const [handleFinished, setHandleFinished] = useState(false);
const handleMenuAndKeys = (menu, menuIds) => {
let newMenuIds = menuIds.map((menuId) => menuId);
function handleMenu(_menu) {
_menu.forEach((item) => {
// 组件需要的key必须为string
item.parentId = item.id;
if (item.children?.length > 0) {
if (item.children.some((child) => !newMenuIds.includes(child.id))) {
newMenuIds = newMenuIds.filter((id) => id !== item.id);
}
handleMenu(item.children);
}
});
}
handleMenu(menu);
setTreeData(menu);
setCheckedKeys(newMenuIds);
};
useEffect(() => {
if (treeData.length) {
setHandleFinished(true);
}
}, [treeData]);
useEffect(() => {
if (visible) {
const fetchTreeMenu = async () => {
const { data } = await getTreeResource();
const { data: menuIds } = await getResources(record.id);
handleMenuAndKeys(data, menuIds);
};
fetchTreeMenu();
} else {
form.resetFields();
setTreeData([]);
setCheckedKeys([]);
setHandleFinished(false);
}
}, [visible]);
return (
<div>
<Modal
title={<span></span>}
visible={visible}
onOk={onOk}
onCancel={handleCancel}
confirmLoading={confirmLoading}
>
<Form form={form}>
<Form.Item hidden field="id">
<Input />
</Form.Item>
<Form.Item label="菜单权限">
{handleFinished && (
<Tree
checkable
checkedKeys={checkedKeys}
autoExpandParent={true}
onCheck={(value) => {
setCheckedKeys(value);
}}
treeData={treeData}
fieldNames={{
key: 'id',
title: 'name',
}}
></Tree>
)}
</Form.Item>
</Form>
</Modal>
</div>
);
}
export default GrantPermission;

View File

@@ -14,10 +14,10 @@ import Form from './form';
import {IconPlus} from '@arco-design/web-react/icon';
import {getColumns, TableOptions} from './constants';
import {getRole,deleteRole} from "@/api/role";
import AssignMenusForm from "./grantPerm";
import AssignMenusForm from "./assignMenus";
import AssignResources from "./assignResources";
import SearchForm from "./searchForm";
const {Title} = Typography;
function UserManage() {
@@ -25,6 +25,7 @@ function UserManage() {
const [visible, setVisible] = useState(false);
const [visibleAssignMenus, setVisibleAssignMenus] = useState(false);
const [visibleAssignResources, setVisibleAssignResources] = useState(false);
const [record, setRecord] = useState(null);
const [pagination, setPagination] = useState<PaginationProps>({
sizeCanChange: true,
@@ -45,8 +46,9 @@ function UserManage() {
setVisibleAssignMenus(true);
};
const showAssignResourcesModal = () => {
const showAssignResourcesModal = (payload) => {
setRecord(payload);
setVisibleAssignResources(true);
}
const showModal = (payload?) => {
@@ -116,6 +118,7 @@ function UserManage() {
setRecord(null);
setVisible(false);
setVisibleAssignMenus(false);
setVisibleAssignResources(false);
await refetch();
};
@@ -123,6 +126,7 @@ function UserManage() {
setRecord(null);
setVisible(false);
setVisibleAssignMenus(false);
setVisibleAssignResources(false);
};
useEffect(() => {
@@ -148,6 +152,14 @@ function UserManage() {
visible={visibleAssignMenus}
record={record}
/>
<AssignResources
handleConfirm={handleConfirm}
handleCancel={handleCancel}
visible={visibleAssignResources}
record={record}
/>
<Title heading={6}></Title>
<SearchForm onSearch={handleSearch}/>