feat: 系统资源表

This commit is contained in:
2025-03-11 14:26:38 +08:00
parent e7bd916cae
commit ffb4c7691b
24 changed files with 761 additions and 532 deletions

View File

@@ -1,9 +1,9 @@
import request from '@/utils/request';
export function grantPermission(id: string, menuIds: string[]) {
return request.patch('/permission/' + id, { menuIds });
return request.put(`/permission/roles/${id}/menus`, { menuIds });
}
export function getRoleMenuIds(id: string) {
return request.get('/permission/' + id + '/menu');
return request.get(`/permission/roles/${id}/menus`);
}

View File

@@ -0,0 +1,21 @@
import request from '@/utils/request';
export function createResource(data) {
return request.post('/resource', data);
}
export function getResource() {
return request.get('/resource');
}
export function getTreeResource() {
return request.get('/resource/tree');
}
export function updateResource(data) {
return request.patch('/resource/' + data.id, data);
}
export function deleteResource(id: string) {
return request.delete('/resource/' + id);
}

View File

@@ -0,0 +1,60 @@
import React from 'react';
import {
TableColumnProps,
Button,
Popconfirm,
Space,
} from '@arco-design/web-react';
export const enum TableOptions {
EDIT = 'edit',
DELETE = 'delete',
}
export function getColumns(callback: (type: TableOptions, record: any) => Promise<void>): TableColumnProps[] {
return [
{
title: '权限id',
dataIndex: 'id',
align: 'center',
},
{
title: '权限名称',
dataIndex: 'name',
align: 'center',
},
{
title: '权限字符',
dataIndex: 'permission',
align: 'center',
},
{
title: '排序',
dataIndex: 'sort',
align: 'center',
},
{
title: '操作',
dataIndex: 'operations',
align: 'center',
width: '300px',
render: (_, record) => (
<Space>
<Button size='small' onClick={() => callback(TableOptions.EDIT, record)} type="text"
status="default"></Button>
<Popconfirm
focusLock
title="删除数据"
content="确认删除该条数据吗?"
onOk={() => callback(TableOptions.DELETE, record)}
>
<Button size='small' type="text" status="danger"></Button>
</Popconfirm>
</Space>
)
}
]
}

View File

@@ -0,0 +1,101 @@
import React, {useEffect, useState} from 'react';
import {Modal, Form, Input, Message, Select, InputNumber} from '@arco-design/web-react';
import {createResource, getResource, updateResource} from '@/api/resource';
interface Props {
record: { [key: string]: any } | null,
visible: boolean,
handleConfirm: () => void,
handleCancel: () => void
}
export default function FormComponent(props: Props) {
const {visible, record, handleConfirm, handleCancel} = props;
const [confirmLoading, setConfirmLoading] = useState(false);
const onOk = async () => {
try {
setConfirmLoading(true);
const values = await form.validate();
if (record?.id) {
await updateResource(values);
} else {
await createResource(values);
}
Message.success(`${record?.id ? '更新' : '新增'}成功!`);
handleConfirm();
} catch (e) {
console.log(e);
} finally {
setConfirmLoading(false);
}
};
const [form] = Form.useForm();
const [menuList, setMenuList] = useState([]);
const fetchMenuList = async () => {
const {data} = await getResource();
const mergeMenu = [
{
id: null,
name: '无父级'
},
...data
];
setMenuList(mergeMenu);
};
useEffect(() => {
record && form.setFieldsValue({
...record
});
}, [record]);
useEffect(() => {
if (visible) {
fetchMenuList();
} else {
form.resetFields();
}
}, [visible]);
return (
<div>
<Modal
title={<span>{`${record?.id ? '更新' : '新增'}数据`}</span>}
visible={visible}
onOk={onOk}
onCancel={handleCancel}
confirmLoading={confirmLoading}
>
<Form form={form}>
<Form.Item hidden field="id">
<Input/>
</Form.Item>
<Form.Item label="父级" field="parentId">
<Select
placeholder="请选择父级"
allowClear
>
{menuList.map((option) => (
<Select.Option disabled={option.id === record?.id} key={option.id} value={option.id}>
{option.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label="权限名称" field="name" rules={[{required: true}]}>
<Input placeholder="请输入权限名称"/>
</Form.Item>
<Form.Item label="权限字符" field="permission" rules={[{required: true}]}>
<Input placeholder="请输入权限字符"/>
</Form.Item>
<Form.Item label="排序" field="sort">
<InputNumber placeholder="请输入排序"/>
</Form.Item>
</Form>
</Modal>
</div>
);
}

View File

@@ -0,0 +1,100 @@
import React, {useState} from 'react';
import {
Card,
Table,
Typography,
TableColumnProps,
Button,
Space,
Message
} from '@arco-design/web-react';
import {useQuery} from 'react-query';
import Form from './form';
import {deleteResource, getTreeResource} from '@/api/resource';
import {getColumns, TableOptions} from './constants';
import {useHistory} from "react-router";
const {Title} = Typography;
function ConfigManage() {
const reactQueryKey = useHistory().location.pathname;
const [visible, setVisible] = useState(false);
const [record, setRecord] = useState(null);
const onDelete = async ({id}) => {
await deleteResource(id);
Message.success('删除成功!');
await refetch();
};
const tableCallback = async (type: TableOptions, record: any) => {
switch (type) {
case TableOptions.EDIT:
showModal(record)
break;
case TableOptions.DELETE:
await onDelete(record);
break
}
}
const columns: TableColumnProps[] = getColumns(tableCallback)
const fetchData = async () => {
const {data} = await getTreeResource();
return data
};
const {
data,
isLoading,
isFetching,
refetch
} = useQuery(
[reactQueryKey], fetchData);
const showModal = (payload?) => {
payload && setRecord(payload);
setVisible(true);
};
const handleConfirm = async () => {
setRecord(null);
setVisible(false);
await refetch();
};
const handleCancel = () => {
setRecord(null);
setVisible(false);
};
return (
<>
<Card>
<Form
handleConfirm={handleConfirm}
handleCancel={handleCancel}
visible={visible}
record={record}
/>
<Title heading={6}></Title>
<Space style={{marginBottom: 12}}>
<Button type="primary" onClick={() => showModal()}></Button>
</Space>
<Table
rowKey="id"
loading={isLoading || isFetching}
columns={columns}
pagination={false}
data={data}
/>
</Card>
</>
);
}
export default ConfigManage;

View File

@@ -50,6 +50,10 @@ class Request {
return this.instance.patch(url, data);
}
put(url: string, data?: object) {
return this.instance.put(url, data);
}
delete(url: string) {
return this.instance.delete(url);
}