mirror of
https://gitee.com/nocobase/nocobase.git
synced 2024-12-04 13:18:55 +08:00
feat: add acl components
This commit is contained in:
parent
15950ece05
commit
fd130901be
@ -1,8 +1,10 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import { LockOutlined } from '@ant-design/icons';
|
import { LockOutlined } from '@ant-design/icons';
|
||||||
import { SchemaComponent, useActionVisible, VisibleContext } from '../schema-component';
|
|
||||||
import { ISchema, useForm } from '@formily/react';
|
import { ISchema, useForm } from '@formily/react';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
import React, { useState } from 'react';
|
||||||
import { PluginManager } from '../plugin-manager';
|
import { PluginManager } from '../plugin-manager';
|
||||||
|
import { SchemaComponent, useActionVisible, VisibleContext } from '../schema-component';
|
||||||
|
import { RoleTable } from './RolePermissionManager';
|
||||||
|
|
||||||
const useCloseAction = () => {
|
const useCloseAction = () => {
|
||||||
const { setVisible } = useActionVisible();
|
const { setVisible } = useActionVisible();
|
||||||
@ -20,28 +22,28 @@ const useCloseAction = () => {
|
|||||||
const schema: ISchema = {
|
const schema: ISchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
drawer1: {
|
[uid()]: {
|
||||||
'x-component': 'Action.Drawer',
|
'x-component': 'Action.Drawer',
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: 'Drawer Title',
|
title: 'Drawer Title',
|
||||||
properties: {
|
properties: {
|
||||||
hello1: {
|
hello1: {
|
||||||
'x-content': 'Hello',
|
|
||||||
title: 'T1',
|
|
||||||
},
|
|
||||||
footer1: {
|
|
||||||
'x-component': 'Action.Drawer.Footer',
|
|
||||||
type: 'void',
|
type: 'void',
|
||||||
properties: {
|
'x-component': 'RoleTable',
|
||||||
close1: {
|
|
||||||
title: 'Close',
|
|
||||||
'x-component': 'Action',
|
|
||||||
'x-component-props': {
|
|
||||||
useAction: '{{ useCloseAction }}',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
// footer1: {
|
||||||
|
// 'x-component': 'Action.Drawer.Footer',
|
||||||
|
// type: 'void',
|
||||||
|
// properties: {
|
||||||
|
// close1: {
|
||||||
|
// title: 'Close',
|
||||||
|
// 'x-component': 'Action',
|
||||||
|
// 'x-component-props': {
|
||||||
|
// useAction: '{{ useCloseAction }}',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -58,7 +60,7 @@ export const ACLShortcut = () => {
|
|||||||
setVisible(true);
|
setVisible(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<SchemaComponent scope={{ useCloseAction }} schema={schema} />
|
<SchemaComponent components={{ RoleTable }} scope={{ useCloseAction }} schema={schema} />
|
||||||
</VisibleContext.Provider>
|
</VisibleContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1 +1,233 @@
|
|||||||
|
import { Button, Checkbox, Divider, Drawer, Space, Table, Tabs, Tag, Typography } from 'antd';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { ScopeRecordPicker } from './ScopeRecordPicker';
|
||||||
|
|
||||||
export function RoleManager() {}
|
export function RoleManager() {}
|
||||||
|
|
||||||
|
export const RoleTable = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Space style={{ justifyContent: 'flex-end', width: '100%', marginBottom: 16 }}>
|
||||||
|
<Button key="destroy">删除</Button>
|
||||||
|
<Button type={'primary'} key="create">
|
||||||
|
添加
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
<Table
|
||||||
|
rowSelection={{
|
||||||
|
type: 'checkbox',
|
||||||
|
}}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: '角色名称',
|
||||||
|
dataIndex: 'title',
|
||||||
|
key: 'title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '角色描述',
|
||||||
|
dataIndex: 'description',
|
||||||
|
key: 'description',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '默认角色',
|
||||||
|
dataIndex: 'default',
|
||||||
|
key: 'default',
|
||||||
|
render: (value) => value && '是',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'actions',
|
||||||
|
key: 'actions',
|
||||||
|
render: () => (
|
||||||
|
<Space split={<Divider type="vertical" />}>
|
||||||
|
<ConfigurePermissions />
|
||||||
|
<Typography.Link>Edit role</Typography.Link>
|
||||||
|
<Typography.Link>Delete</Typography.Link>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
dataSource={[
|
||||||
|
{
|
||||||
|
name: 'root',
|
||||||
|
title: '管理员',
|
||||||
|
description: '描述',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'member',
|
||||||
|
title: '普通成员',
|
||||||
|
description: '描述',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ConfigurePermissions = () => {
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Typography.Link onClick={() => setVisible(true)}>Configure</Typography.Link>
|
||||||
|
<Drawer width={800} title={'权限配置'} visible={visible} destroyOnClose onClose={() => setVisible(false)}>
|
||||||
|
<RolePermissions />
|
||||||
|
</Drawer>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RolePermissions = () => {
|
||||||
|
return (
|
||||||
|
<Tabs defaultActiveKey={'actions'}>
|
||||||
|
<Tabs.TabPane key={'global'} tab={'系统全局权限'}></Tabs.TabPane>
|
||||||
|
<Tabs.TabPane key={'actions'} tab={'数据操作权限'}>
|
||||||
|
<Table
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: '数据表名称',
|
||||||
|
dataIndex: 'title',
|
||||||
|
key: 'title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '数据表标识',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '权限策略',
|
||||||
|
dataIndex: 'usingConfigure',
|
||||||
|
key: 'usingConfigure',
|
||||||
|
render: (value) => (value ? <Tag>单独配置</Tag> : <Tag>通用配置</Tag>),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'actions',
|
||||||
|
key: 'actions',
|
||||||
|
render: () => <ResourceActionsConfigure />,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
dataSource={[
|
||||||
|
{
|
||||||
|
title: '用户',
|
||||||
|
name: 'users',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane key={'accessible'} tab={'菜单访问权限'}>
|
||||||
|
<Table
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: '菜单页面',
|
||||||
|
dataIndex: 'title',
|
||||||
|
key: 'title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: (
|
||||||
|
<>
|
||||||
|
<Checkbox /> 允许访问
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
dataIndex: 'accessible',
|
||||||
|
key: 'accessible',
|
||||||
|
render: () => <Checkbox />,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
dataSource={[
|
||||||
|
{
|
||||||
|
title: '页面1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '页面2',
|
||||||
|
accessible: true,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
</Tabs>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ResourceActionsConfigure = () => {
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Typography.Link onClick={() => setVisible(true)}>Configure</Typography.Link>
|
||||||
|
<Drawer
|
||||||
|
width={800}
|
||||||
|
title={'权限配置'}
|
||||||
|
visible={visible}
|
||||||
|
destroyOnClose
|
||||||
|
onClose={() => setVisible(false)}
|
||||||
|
footer={
|
||||||
|
<Space style={{ justifyContent: 'flex-end', width: '100%' }}>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type={'primary'}
|
||||||
|
onClick={() => {
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ResourceActionsForm />
|
||||||
|
</Drawer>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ResourceActionsForm = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Table
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'title',
|
||||||
|
key: 'title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '类型',
|
||||||
|
dataIndex: 'type',
|
||||||
|
key: 'type',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: (
|
||||||
|
<>
|
||||||
|
<Checkbox /> 允许操作
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
dataIndex: 'enable',
|
||||||
|
key: 'enable',
|
||||||
|
render: () => <Checkbox />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '可操作的数据范围',
|
||||||
|
dataIndex: 'scope',
|
||||||
|
key: 'scope',
|
||||||
|
render: () => <ScopeRecordPicker />
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
dataSource={[
|
||||||
|
{
|
||||||
|
title: '添加',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '导入',
|
||||||
|
enable: true,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
77
packages/client/src/acl/ScopeRecordPicker.tsx
Normal file
77
packages/client/src/acl/ScopeRecordPicker.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { createForm } from '@formily/core';
|
||||||
|
import { FormContext, ISchema } from '@formily/react';
|
||||||
|
import { Button, Space } from 'antd';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { SchemaComponent } from '../';
|
||||||
|
import { AntdSchemaComponentProvider } from '../schema-component';
|
||||||
|
|
||||||
|
const schema: ISchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
scope: {
|
||||||
|
type: 'array',
|
||||||
|
default: [
|
||||||
|
{ id: 1, name: 'name1' },
|
||||||
|
{ id: 2, name: 'name2' },
|
||||||
|
],
|
||||||
|
'x-component': 'RecordPicker',
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
'x-component': 'ScopeActions',
|
||||||
|
},
|
||||||
|
rowSelection: {
|
||||||
|
'x-component': 'RowSelection',
|
||||||
|
'x-component-props': {
|
||||||
|
rowKey: 'id',
|
||||||
|
objectValue: true,
|
||||||
|
rowSelection: {
|
||||||
|
type: 'radio',
|
||||||
|
},
|
||||||
|
dataSource: [
|
||||||
|
{ id: 1, title: '全部数据' },
|
||||||
|
{ id: 2, title: '用户自己创建的数据' },
|
||||||
|
{ id: 3, title: '待审核的文章' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
column1: {
|
||||||
|
type: 'void',
|
||||||
|
title: '数据范围',
|
||||||
|
'x-component': 'RowSelection.Column',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const ScopeActions = () => {
|
||||||
|
return (
|
||||||
|
<Space style={{ justifyContent: 'flex-end', width: '100%', marginBottom: 16 }}>
|
||||||
|
<Button key="destroy">删除</Button>
|
||||||
|
<Button type={'primary'} key="create">
|
||||||
|
添加
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ScopeRecordPicker = () => {
|
||||||
|
const form = useMemo(() => createForm({}), []);
|
||||||
|
return (
|
||||||
|
<FormContext.Provider value={form}>
|
||||||
|
<AntdSchemaComponentProvider>
|
||||||
|
<SchemaComponent components={{ ScopeActions }} schema={schema} />
|
||||||
|
</AntdSchemaComponentProvider>
|
||||||
|
</FormContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
6
packages/client/src/acl/demos/demo1.tsx
Normal file
6
packages/client/src/acl/demos/demo1.tsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { RoleTable } from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return <RoleTable />;
|
||||||
|
};
|
6
packages/client/src/acl/demos/demo2.tsx
Normal file
6
packages/client/src/acl/demos/demo2.tsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { RolePermissions } from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return <RolePermissions />;
|
||||||
|
};
|
6
packages/client/src/acl/demos/demo3.tsx
Normal file
6
packages/client/src/acl/demos/demo3.tsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { ResourceActionsForm } from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return <ResourceActionsForm />;
|
||||||
|
};
|
@ -10,3 +10,7 @@ group:
|
|||||||
# ACL <Badge>待定</Badge>
|
# ACL <Badge>待定</Badge>
|
||||||
|
|
||||||
访问控制列表,plugin-acl 的前端模块
|
访问控制列表,plugin-acl 的前端模块
|
||||||
|
|
||||||
|
<code src="./demos/demo1.tsx" />
|
||||||
|
<code src="./demos/demo2.tsx" />
|
||||||
|
<code src="./demos/demo3.tsx" />
|
||||||
|
Loading…
Reference in New Issue
Block a user