feat: menu permissions
This commit is contained in:
parent
66549dc4bf
commit
23db248743
@ -0,0 +1,121 @@
|
||||
import React from 'react';
|
||||
import { observer } from '@formily/react';
|
||||
import { Table, Checkbox, Spin } from 'antd';
|
||||
import { useRequest } from 'ahooks';
|
||||
import { request } from '../../../schemas';
|
||||
import { Resource } from '@nocobase/client/src/resource';
|
||||
import { useContext } from 'react';
|
||||
import { RoleContext } from '.';
|
||||
import { useState } from 'react';
|
||||
|
||||
const getKeys = (items: any[]) => {
|
||||
const keys = [];
|
||||
for (const item of items) {
|
||||
keys.push(item.key);
|
||||
const children = getKeys(item.children || []);
|
||||
keys.push(...children);
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
|
||||
export const MenuPermissionTable = observer((props) => {
|
||||
const role = useContext(RoleContext);
|
||||
const [allUiSchemaKyes, setAllUiSchemaKyes] = useState([]);
|
||||
const [uiSchemaKyes, setUiSchemaKeys] = useState([]);
|
||||
const { data, loading } = useRequest(
|
||||
() => request('ui_schemas:getMenuItems'),
|
||||
{
|
||||
formatResult: (data) => data?.data,
|
||||
onSuccess(data) {
|
||||
setAllUiSchemaKyes(getKeys(data));
|
||||
},
|
||||
},
|
||||
);
|
||||
console.log('allUiSchemaKyes', allUiSchemaKyes);
|
||||
const resource = Resource.make({
|
||||
associatedName: 'roles',
|
||||
associatedKey: role.name,
|
||||
resourceName: 'ui_schemas',
|
||||
});
|
||||
useRequest(() => resource.list(), {
|
||||
formatResult: (data) => data?.data,
|
||||
onSuccess(data) {
|
||||
setUiSchemaKeys(getKeys(data));
|
||||
},
|
||||
});
|
||||
if (loading) {
|
||||
return <Spin />;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Table
|
||||
loading={loading}
|
||||
dataSource={data}
|
||||
pagination={false}
|
||||
expandable={{
|
||||
defaultExpandAllRows: true,
|
||||
}}
|
||||
columns={[
|
||||
{
|
||||
title: '菜单名称',
|
||||
dataIndex: 'title',
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<div>
|
||||
<Checkbox
|
||||
checked={
|
||||
allUiSchemaKyes.length > 0 &&
|
||||
allUiSchemaKyes.length === uiSchemaKyes.length
|
||||
}
|
||||
onChange={async (e) => {
|
||||
const resource = Resource.make({
|
||||
resourceName: 'roles',
|
||||
resourceKey: role.name,
|
||||
});
|
||||
if (e.target.checked) {
|
||||
await resource.save({
|
||||
ui_schemas: allUiSchemaKyes,
|
||||
});
|
||||
setUiSchemaKeys(allUiSchemaKyes);
|
||||
} else {
|
||||
await resource.save({
|
||||
ui_schemas: [],
|
||||
});
|
||||
setUiSchemaKeys([]);
|
||||
}
|
||||
}}
|
||||
/>{' '}
|
||||
允许访问
|
||||
</div>
|
||||
),
|
||||
dataIndex: 'key',
|
||||
render: (value) => {
|
||||
return (
|
||||
<Checkbox
|
||||
checked={uiSchemaKyes.includes(value)}
|
||||
onChange={async (e) => {
|
||||
setUiSchemaKeys((prevUiSchemaKeys) => {
|
||||
if (e.target.checked) {
|
||||
prevUiSchemaKeys.push(value);
|
||||
} else {
|
||||
const index = prevUiSchemaKeys.findIndex(
|
||||
(key) => key === value,
|
||||
);
|
||||
prevUiSchemaKeys.splice(index, 1);
|
||||
}
|
||||
return [...prevUiSchemaKeys];
|
||||
});
|
||||
await resource.toggle({
|
||||
resourceKey: value,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
@ -15,6 +15,7 @@ import { connect, ISchema, observer, useForm } from '@formily/react';
|
||||
import { DescriptionsContext } from '../../../schemas/form';
|
||||
import { createContext } from 'react';
|
||||
import { ActionPermissionField } from './ActionPermissionField';
|
||||
import { MenuPermissionTable } from './MenuPermissionTable';
|
||||
|
||||
export const RoleContext = createContext(null);
|
||||
|
||||
@ -246,96 +247,7 @@ const collectionSchema: ISchema = {
|
||||
|
||||
const menuSchema: ISchema = {
|
||||
type: 'array',
|
||||
// 'x-decorator': 'CardItem',
|
||||
'x-component': 'Table',
|
||||
default: [],
|
||||
'x-component-props': {
|
||||
rowKey: 'name',
|
||||
showIndex: true,
|
||||
refreshRequestOnChange: true,
|
||||
pagination: {
|
||||
pageSize: 10,
|
||||
},
|
||||
useResource: useCollectionsResource,
|
||||
collectionName: 'collections',
|
||||
},
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
title: '操作',
|
||||
'x-component': 'Table.Operation',
|
||||
'x-component-props': {
|
||||
className: 'nb-table-operation',
|
||||
},
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
icon: 'EllipsisOutlined',
|
||||
},
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Dropdown',
|
||||
'x-component-props': {},
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
title: '配置',
|
||||
'x-component': 'Menu.Action',
|
||||
'x-component-props': {
|
||||
style: {
|
||||
minWidth: 150,
|
||||
},
|
||||
},
|
||||
'x-action-type': 'view',
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
title: '权限配置',
|
||||
'x-component': 'Action.Drawer',
|
||||
'x-component-props': {
|
||||
bodyStyle: {
|
||||
background: '#f0f2f5',
|
||||
paddingTop: 0,
|
||||
},
|
||||
},
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
column1: {
|
||||
type: 'void',
|
||||
title: '权限名称',
|
||||
'x-component': 'Table.Column',
|
||||
properties: {
|
||||
title: {
|
||||
type: 'string',
|
||||
'x-component': 'Input',
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
},
|
||||
},
|
||||
column2: {
|
||||
type: 'void',
|
||||
title: '权限标识',
|
||||
'x-component': 'Table.Column',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
'x-component': 'Input',
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'x-component': 'MenuPermissionTable',
|
||||
};
|
||||
|
||||
const schema: ISchema = {
|
||||
@ -488,7 +400,7 @@ const schema: ISchema = {
|
||||
},
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
title: '菜单权限',
|
||||
title: '菜单访问权限',
|
||||
'x-component': 'Tabs.TabPane',
|
||||
'x-component-props': {},
|
||||
properties: {
|
||||
@ -579,7 +491,7 @@ const schema: ISchema = {
|
||||
export const Permissions = () => {
|
||||
return (
|
||||
<SchemaRenderer
|
||||
components={{ RoleProvider, ActionPermissionField }}
|
||||
components={{ RoleProvider, ActionPermissionField, MenuPermissionTable }}
|
||||
schema={schema}
|
||||
/>
|
||||
);
|
||||
|
@ -110,6 +110,15 @@ export class Resource {
|
||||
});
|
||||
}
|
||||
|
||||
toggle(options?: any) {
|
||||
const { associatedKey, associatedName, resourceName } = this.options;
|
||||
const { resourceKey } = options;
|
||||
let url = `${associatedName}/${associatedKey}/${resourceName}:toggle/${resourceKey}`;
|
||||
return request(url, {
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
||||
static make(options: null | string | Resource | ResourceOptions): Resource | null {
|
||||
if (typeof options === 'string') {
|
||||
return new Resource({ resourceName: options });
|
||||
|
@ -25,5 +25,11 @@ export default {
|
||||
target: 'users',
|
||||
through: 'roles_users'
|
||||
},
|
||||
{
|
||||
type: 'belongsToMany',
|
||||
name: 'ui_schemas',
|
||||
target: 'ui_schemas',
|
||||
through: 'roles_ui_schemas'
|
||||
},
|
||||
],
|
||||
} as TableOptions;
|
||||
|
@ -135,3 +135,14 @@ export const getTree = async (ctx: actions.Context, next: actions.Next) => {
|
||||
|
||||
await next();
|
||||
};
|
||||
|
||||
export const getMenuItems = async (ctx: actions.Context, next: actions.Next) => {
|
||||
const UISchema = ctx.db.getModel('ui_schemas');
|
||||
const schema = await UISchema.findOne({
|
||||
where: {
|
||||
'x-component': 'Menu',
|
||||
}
|
||||
});
|
||||
ctx.body = await schema.getHierarchy();
|
||||
await next();
|
||||
};
|
||||
|
@ -32,6 +32,10 @@ export default {
|
||||
type: 'string',
|
||||
name: 'type',
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'x-component',
|
||||
},
|
||||
{
|
||||
type: 'json',
|
||||
name: 'options',
|
||||
|
@ -86,4 +86,23 @@ export class UISchema extends Model {
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
async getHierarchy() {
|
||||
const data = [];
|
||||
const children: UISchema[] = await this.getChildren({
|
||||
where: {
|
||||
async: false,
|
||||
},
|
||||
order: [['sort', 'asc']],
|
||||
});
|
||||
for (const child of children) {
|
||||
const item: any = child.toJSON();
|
||||
const nested = await child.getHierarchy();
|
||||
if (nested.length) {
|
||||
item.children = nested;
|
||||
}
|
||||
data.push(item);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import path from 'path';
|
||||
import { Application } from '@nocobase/server';
|
||||
import { registerModels, Table } from '@nocobase/database';
|
||||
import * as models from './models';
|
||||
import { create, update, getTree } from './actions';
|
||||
import * as actions from './actions';
|
||||
|
||||
export default async function (this: Application, options = {}) {
|
||||
const database = this.database;
|
||||
@ -12,7 +12,7 @@ export default async function (this: Application, options = {}) {
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
|
||||
this.resourcer.registerActionHandler('ui_schemas:create', create);
|
||||
this.resourcer.registerActionHandler('ui_schemas:update', update);
|
||||
this.resourcer.registerActionHandler('ui_schemas:getTree', getTree);
|
||||
for (const [name, action] of Object.entries(actions)) {
|
||||
this.resourcer.registerActionHandler(`ui_schemas:${name}`, action);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user