This commit is contained in:
‘Liammcl’
2025-01-14 01:08:51 +08:00
parent 952edee24d
commit 2edb91cbea
3 changed files with 587 additions and 7 deletions

View File

@@ -77,6 +77,8 @@ export const useResources = (initialPagination, initialSorter, type) => {
const deleteResource = async (id) => {
try {
const data= await resourceService.deleteResource(id, type);
console.log(data,data.length,'data');
if(data?.length>0){
const newCurrent =
resources.length === 1 && currentPagination.current > 1

View File

@@ -1,7 +1,585 @@
import React from 'react'
import React, { useState, useEffect } from 'react';
import { Card, App, Table, Button, Modal, Form, Select, message, Input, Divider } from 'antd';
import { PlusOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons';
import { supabase } from '@/config/supabase';
const RoleHeader = ({ onAdd, onSearch }) => (
<div className="flex justify-between mb-4">
<Input.Search
placeholder="搜索权限"
onSearch={onSearch}
style={{ width: 300 }}
/>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={onAdd}
>
添加权限
</Button>
</div>
);
export default function PermissionManagement() {
const [loading, setLoading] = useState(false);
const [permissions, setPermissions] = useState([]);
const [roles, setRoles] = useState([]);
const [resources, setResources] = useState([]);
const [modalVisible, setModalVisible] = useState(false);
const [modalType, setModalType] = useState('add');
const [form] = Form.useForm();
const [pagination, setPagination] = useState({
current: 1,
pageSize: 10,
total: 0,
});
const [sorter, setSorter] = useState({
field: 'created_at',
order: 'descend',
});
const [roleModalVisible, setRoleModalVisible] = useState(false);
const [resourceModalVisible, setResourceModalVisible] = useState(false);
const [roleForm] = Form.useForm();
const [resourceForm] = Form.useForm();
// 获取所有权限数据(包括关联数据)
const fetchPermissions = async (params = {}) => {
try {
setLoading(true);
let query = supabase
.from('permissions')
.select(`
*,
roles:role_id(*),
resources:resource_id(*)
`, { count: 'exact' });
// 添加搜索条件
if (params.search) {
query = query.or(`
roles.name.ilike.%${params.search}%,
resources.resource_name.ilike.%${params.search}%,
resources.description.ilike.%${params.search}%
`);
}
// 添加排序
if (params.field && params.order) {
const ascending = params.order === 'ascend';
query = query.order(params.field, { ascending });
} else {
// 默认排序
query = query.order('created_at', { ascending: false });
}
// 添加分页
const from = (params.current - 1) * params.pageSize || 0;
const to = from + (params.pageSize || 10) - 1;
query = query.range(from, to);
const { data, count, error } = await query;
if (error) throw error;
setPermissions(data || []);
setPagination(prev => ({
...prev,
total: count || 0,
current: params.current || prev.current,
pageSize: params.pageSize || prev.pageSize,
}));
} catch (error) {
message.error('获取权限数据失败');
console.error(error);
} finally {
setLoading(false);
}
};
// 获取所有角色
const fetchRoles = async () => {
try {
const { data, error } = await supabase
.from('roles')
.select('*')
.order('name');
if (error) throw error;
setRoles(data || []);
} catch (error) {
message.error('获取角色数据失败');
console.error(error);
}
};
// 获取所有资源
const fetchResources = async () => {
try {
const { data, error } = await supabase
.from('permission_resources')
.select('*')
.order('resource_name');
if (error) throw error;
setResources(data || []);
} catch (error) {
message.error('获取资源数据失败');
console.error(error);
}
};
// 添加权限
const handleAdd = async (values) => {
try {
const { error } = await supabase
.from('permissions')
.insert([{
role_id: values.role_id,
resource_id: values.resource_id,
action: values.action
}]);
if (error) throw error;
message.success('添加成功');
fetchPermissions(pagination);
} catch (error) {
message.error('添加失败');
throw error;
}
};
// 更新权限
const handleUpdate = async (values) => {
try {
const { error } = await supabase
.from('permissions')
.update({
role_id: values.role_id,
resource_id: values.resource_id,
action: values.action
})
.eq('id', values.id);
if (error) throw error;
message.success('更新成功');
fetchPermissions(pagination);
} catch (error) {
message.error('更新失败');
throw error;
}
};
// 删除权限
const handleDelete = async (id) => {
try {
const { error } = await supabase
.from('permissions')
.delete()
.eq('id', id);
if (error) throw error;
message.success('删除成功');
fetchPermissions(pagination);
} catch (error) {
message.error('删除失败');
throw error;
}
};
// 添加新角色
const handleAddRole = async (values) => {
try {
const { data, error } = await supabase
.from('roles')
.insert([{
name: values.name,
description: values.description
}])
.select()
.single();
if (error) throw error;
message.success('添加角色成功');
setRoles([...roles, data]);
setRoleModalVisible(false);
roleForm.resetFields();
return data;
} catch (error) {
message.error('添加角色失败');
throw error;
}
};
// 添加新资源
const handleAddResource = async (values) => {
try {
const { data, error } = await supabase
.from('permission_resources')
.insert([{
resource_name: values.resource_name,
resource_type: values.resource_type,
attributes_type: values.attributes_type,
description: values.description
}])
.select()
.single();
if (error) throw error;
message.success('添加资源成功');
setResources([...resources, data]);
setResourceModalVisible(false);
resourceForm.resetFields();
return data;
} catch (error) {
message.error('添加资源失败');
throw error;
}
};
// 初始化加载
useEffect(() => {
fetchPermissions();
fetchRoles();
fetchResources();
}, []);
const handleTableChange = (newPagination, filters, newSorter) => {
const params = {
current: newPagination?.current,
pageSize: newPagination?.pageSize,
field: newSorter?.field,
order: newSorter?.order,
};
setPagination(prev => ({
...prev,
current: params.current,
pageSize: params.pageSize,
}));
setSorter({
field: params.field || sorter.field,
order: params.order || sorter.order,
});
fetchPermissions(params);
};
const columns = [
{
title: '角色',
dataIndex: ['roles', 'name'],
key: 'role_name',
sorter: true,
render: (name) => (
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-sm font-medium bg-blue-100 text-blue-800">
{name}
</span>
)
},
{
title: '资源',
dataIndex: ['resources', 'resource_name'],
key: 'resource_name',
sorter: true,
},
{
title: '资源类型',
dataIndex: ['resources', 'resource_type'],
key: 'resource_type',
render: (type) => (
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-sm font-medium
${type === 'table'
? 'bg-green-100 text-green-800'
: 'bg-purple-100 text-purple-800'}`}>
{type === 'table' ? '数据表' : '字段值'}
</span>
)
},
{
title: '属性类型',
dataIndex: ['resources', 'attributes_type'],
key: 'attributes_type',
render: (attributes_type) => (
<span>
{!attributes_type ? '无' : attributes_type}
</span>
)
},
{
title: '操作类型',
dataIndex: 'action',
key: 'action',
render: (action) => {
const actionMap = {
create: { text: '创建', color: 'bg-green-100 text-green-800' },
read: { text: '读取', color: 'bg-blue-100 text-blue-800' },
update: { text: '更新', color: 'bg-yellow-100 text-yellow-800' },
delete: { text: '删除', color: 'bg-red-100 text-red-800' }
};
const { text, color } = actionMap[action] || { text: action, color: 'bg-gray-100 text-gray-800' };
return (
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-sm font-medium ${color}`}>
{text}
</span>
);
}
},
{
title: '描述',
dataIndex: ['resources', 'description'],
key: 'description',
ellipsis: true,
},
{
title: '操作',
key: 'operation',
render: (_, record) => (
<div className="space-x-2">
<Button
type="text"
icon={<EditOutlined />}
onClick={() => {
setModalType('edit');
form.setFieldsValue(record);
setModalVisible(true);
}}
/>
<Button
type="text"
danger
icon={<DeleteOutlined />}
onClick={() => handleDelete(record.id)}
/>
</div>
),
},
];
// Modal 表单内容
const renderFormItems = () => (
<>
<Form.Item
name="role_id"
label="角色"
rules={[{ required: true, message: '请选择角色' }]}
>
<Select
placeholder="请选择角色"
dropdownRender={(menu) => (
<>
{menu}
<Divider style={{ margin: '8px 0' }} />
<Button
type="text"
icon={<PlusOutlined />}
onClick={() => setRoleModalVisible(true)}
style={{ paddingLeft: 8 }}
>
添加角色
</Button>
</>
)}
>
{roles.map(role => (
<Select.Option key={role.id} value={role.id}>
{role.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name="resource_id"
label="资源"
rules={[{ required: true, message: '请选择资源' }]}
>
<Select
placeholder="请选择资源"
dropdownRender={(menu) => (
<>
{menu}
<Divider style={{ margin: '8px 0' }} />
<Button
type="text"
icon={<PlusOutlined />}
onClick={() => setResourceModalVisible(true)}
style={{ paddingLeft: 8 }}
>
添加资源
</Button>
</>
)}
>
{resources.map(resource => (
<Select.Option key={resource.id} value={resource.id}>
{`${resource.resource_name} ${resource.attributes_type?`(${resource.attributes_type})`:''}`}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name="action"
label="操作类型"
rules={[{ required: true, message: '请选择操作类型' }]}
>
<Select placeholder="请选择操作类型">
<Select.Option value="create">创建</Select.Option>
<Select.Option value="read">读取</Select.Option>
<Select.Option value="update">更新</Select.Option>
<Select.Option value="delete">删除</Select.Option>
</Select>
</Form.Item>
</>
);
export default function index() {
return (
<div>index</div>
)
}
<App>
<Card title="权限管理" bordered={false}>
<RoleHeader
onAdd={() => {
setModalType('add');
setModalVisible(true);
}}
onSearch={(value) => {
fetchPermissions({
...pagination,
current: 1,
search: value
});
}}
/>
<Table
columns={columns}
dataSource={permissions}
rowKey="id"
loading={loading}
pagination={pagination}
onChange={handleTableChange}
/>
<Modal
title={`${modalType === 'add' ? '添加' : '编辑'}权限`}
open={modalVisible}
onCancel={() => {
setModalVisible(false);
form.resetFields();
}}
onOk={() => form.submit()}
confirmLoading={loading}
>
<Form
form={form}
layout="vertical"
onFinish={async (values) => {
try {
setLoading(true);
if (modalType === 'add') {
await handleAdd(values);
} else {
await handleUpdate(values);
}
setModalVisible(false);
form.resetFields();
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
}}
>
{renderFormItems()}
</Form>
</Modal>
{/* 添加角色 Modal */}
<Modal
title="添加角色"
open={roleModalVisible}
onCancel={() => {
setRoleModalVisible(false);
roleForm.resetFields();
}}
onOk={() => roleForm.submit()}
>
<Form
form={roleForm}
layout="vertical"
onFinish={handleAddRole}
>
<Form.Item
name="name"
label="角色名称"
rules={[{ required: true, message: '请输入角色名称' }]}
>
<Input placeholder="请输入角色名称" />
</Form.Item>
<Form.Item
name="description"
label="角色描述"
>
<Input.TextArea placeholder="请输入角色描述" />
</Form.Item>
</Form>
</Modal>
{/* 添加资源 Modal */}
<Modal
title="添加资源"
open={resourceModalVisible}
onCancel={() => {
setResourceModalVisible(false);
resourceForm.resetFields();
}}
onOk={() => resourceForm.submit()}
>
<Form
form={resourceForm}
layout="vertical"
onFinish={handleAddResource}
>
<Form.Item
name="resource_name"
label="资源名称"
rules={[{ required: true, message: '请输入资源名称' }]}
>
<Input placeholder="请输入资源名称" />
</Form.Item>
<Form.Item
name="resource_type"
label="资源类型"
rules={[{ required: true, message: '请选择资源类型' }]}
>
<Select placeholder="请选择资源类型">
<Select.Option value="table">数据表</Select.Option>
<Select.Option value="field_value">字段值</Select.Option>
</Select>
</Form.Item>
<Form.Item
name="attributes_type"
label="属性类型"
>
<Input placeholder="请输入属性类型" />
</Form.Item>
<Form.Item
name="description"
label="资源描述"
>
<Input.TextArea placeholder="请输入资源描述" />
</Form.Item>
</Form>
</Modal>
</Card>
</App>
);
}

View File

@@ -78,7 +78,7 @@ export const resourceService = {
async deleteResource(id,type) {
try {
const { error } = await supabase
const { data,error } = await supabase
.from('resources')
.delete()
.eq('id', id)
@@ -86,7 +86,7 @@ export const resourceService = {
.select()
if (error) throw error;
return true;
return data;
} catch (error) {
throw error;
}