feat: collection action permissions

This commit is contained in:
chenos 2021-08-11 00:09:54 +08:00
parent b046723371
commit c1dfc8b2f5
4 changed files with 828 additions and 9 deletions

View File

@ -0,0 +1,246 @@
import { SchemaRenderer } from '../../../';
import React, { useContext, useEffect } from 'react';
import { FormItem } from '@formily/antd';
import { action } from '@formily/reactive';
import { useCollectionsContext } from '../../../constate/Collections';
import { Button, Select, Table, Tag } from 'antd';
import { LockOutlined } from '@ant-design/icons';
import cls from 'classnames';
import { uid } from '@formily/shared';
import { Resource } from '../../../resource';
import { TableRowContext, useTable } from '../../../schemas/table';
import { useRequest } from 'ahooks';
import { VisibleContext } from '../../../context';
import { connect, ISchema, observer, useField } from '@formily/react';
import { DescriptionsContext } from '../../../schemas/form';
import { createContext } from 'react';
import { RoleContext } from '.';
import Checkbox from 'antd/lib/checkbox/Checkbox';
import { useState } from 'react';
const actionTypeMap = new Map(
Object.entries({
create: '新增',
get: '查看',
update: '编辑',
destroy: '删除',
}),
);
const useActionDataSource = () => {
const field = useField<Formily.Core.Models.ArrayField>();
const dataSource = [];
for (const [actionName, value] of actionTypeMap) {
const index = field?.value?.findIndex((item) => {
return item.actionName === actionName;
});
dataSource.push({
actionName,
enable: index > -1,
});
}
return dataSource;
};
const useFieldPermissions = () => {
const role = useContext(RoleContext);
const ctx = useContext(TableRowContext);
const field = useField<Formily.Core.Models.ArrayField>();
const [dataSource, setDataSource] = useState([]);
const findFieldKeys = (actionName): any[] => {
const item = field?.value?.find((item) => {
return item.actionName === actionName;
});
return (
item?.fieldPermissions?.map((permission) => permission.field_key) || []
);
};
const service = useRequest(
() =>
Resource.make({
associatedName: 'collections',
associatedKey: ctx.record.name,
resourceName: 'fields',
}).list({
filter: {
state: 1,
},
appends: ['uiSchema'],
}),
{
formatResult: (data) => data?.data,
onSuccess(data) {
setDataSource(
data.map((field) => {
return {
field,
};
}),
);
},
// refreshDeps: [ctx.record.name],
},
);
const keys = {
get: findFieldKeys('get'),
update: findFieldKeys('update'),
destroy: findFieldKeys('destroy'),
create: findFieldKeys('create'),
}
const renderCell = (actionName, value, record) => {
return (
<Checkbox
checked={keys[actionName].includes(record.field.key)}
onChange={(e) => {
let fieldIndex = field?.value?.findIndex((item) => {
return item.actionName === actionName;
});
console.log('fieldIndex', fieldIndex)
if (e.target.checked) {
if (fieldIndex === -1) {
field.push({
actionName,
collection_name: ctx.record.name,
role_name: role.name,
fieldPermissions: [{
field_key: record.field.key,
}],
});
} else {
const permissions = field.value[fieldIndex].fieldPermissions || [];
permissions.push({
field_key: record.field.key,
});
field.value[fieldIndex].fieldPermissions = permissions;
console.log('fieldIndex', permissions);
}
}
// fieldIndex = field.value.length - 1;
// const permissions: any[] =
// field.value[fieldIndex].fieldPermissions || [];
// const index = permissions?.findIndex(
// (permission) => permission.field_key === record?.field?.key,
// );
// if (e.target.checked) {
// permissions.push({
// field_key: record.field.key,
// });
// } else {
// permissions.splice(index, 1);
// }
// console.log({ permissions });
// field.value[fieldIndex].fieldPermissions = permissions;
}}
/>
);
};
const columns = [
{
title: '字段名称',
dataIndex: ['field', 'uiSchema', 'title'],
},
{
title: <div> <Checkbox /></div>,
dataIndex: 'create',
render: (value, record) => renderCell('create', value, record),
},
{
title: '查看',
dataIndex: 'get',
render: (value, record) => renderCell('get', value, record),
},
{
title: '编辑',
dataIndex: 'update',
render: (value, record) => renderCell('update', value, record),
},
];
return { columns, dataSource, service };
};
export const ActionPermissionField = observer((props) => {
const role = useContext(RoleContext);
const ctx = useContext(TableRowContext);
const field = useField<Formily.Core.Models.ArrayField>();
const actionDataSource = useActionDataSource();
const { columns, dataSource, service } = useFieldPermissions();
console.log('field?.value', field?.value);
return (
<div>
<Table
dataSource={actionDataSource}
pagination={false}
columns={[
{
title: '操作',
dataIndex: 'actionName',
render: (value) => <span>{actionTypeMap.get(value)}</span>,
},
{
title: '类型',
dataIndex: 'actionName',
render: (value) =>
value === 'create' ? (
<Tag></Tag>
) : (
<Tag></Tag>
),
},
{
title: '允许操作',
dataIndex: 'enable',
render: (value, record) => (
<Checkbox
checked={!!value}
onChange={(e) => {
const index = field?.value?.findIndex((item) => {
return item.actionName === record.actionName;
});
console.log(
'e.target.checked',
e.target.checked,
index,
field?.value,
);
if (e.target.checked) {
if (index === -1) {
field.push({
collection_name: ctx.record.name,
role_name: role.name,
actionName: record.actionName,
fieldPermissions: [],
});
}
} else {
if (index > -1) {
field.remove(index);
}
}
}}
/>
),
},
{
title: '可操作的数据范围',
dataIndex: ['scope', 'title'],
render: (value) => (
<Select style={{ minWidth: 150 }} size={'small'}></Select>
),
},
]}
/>
<br />
<br />
{/* ActionPermissionField <br />
{role.name} <br />
{ctx.record.name} */}
<Table
loading={service.loading}
columns={columns}
dataSource={dataSource}
pagination={false}
/>
</div>
);
});

View File

@ -0,0 +1,571 @@
import { SchemaRenderer } from '../../../';
import React, { useContext, useEffect } from 'react';
import { FormItem } from '@formily/antd';
import { action } from '@formily/reactive';
import { useCollectionsContext } from '../../../constate/Collections';
import { Button } from 'antd';
import { LockOutlined } from '@ant-design/icons';
import cls from 'classnames';
import { uid } from '@formily/shared';
import { Resource } from '../../../resource';
import { TableRowContext, useTable } from '../../../schemas/table';
import { useRequest } from 'ahooks';
import { VisibleContext } from '../../../context';
import { connect, ISchema, observer } from '@formily/react';
import { DescriptionsContext } from '../../../schemas/form';
import { createContext } from 'react';
import { ActionPermissionField } from './ActionPermissionField';
export const RoleContext = createContext(null);
function RoleProvider(props) {
const ctx = useContext(TableRowContext);
return (
<RoleContext.Provider value={ctx.record}>
{props.children}
</RoleContext.Provider>
);
}
const useResource = () => {
const resource = Resource.make('roles');
return {
resource,
};
};
const useCollectionsResource = () => {
const descriptionsContext = useContext(DescriptionsContext);
console.log('descriptionsContext.service', descriptionsContext.service);
const resource = Resource.make('collections');
return {
resource,
};
};
class ActionPermissionResource extends Resource {
save(options?: any) {
console.log('ActionPermissionResource.save');
return Promise.resolve({});
}
}
const useActionPermissionSubmit = () => {
return {
async run() {
console.log('useActionPermissionSubmit');
},
};
};
const useActionPermissionResource = ({ onSuccess }) => {
const role = useContext(RoleContext);
console.log('RoleContext', role);
// const { props } = useTable();
const ctx = useContext(TableRowContext);
const resource = ActionPermissionResource.make({
resourceName: 'action_permissions',
});
const service = useRequest(
(params?: any) => {
return resource.list({
...params,
filter: {
role_name: role.name,
collection_name: ctx.record.name,
},
appends: ['fieldPermissions'],
});
},
{
formatResult: (result) => result?.data,
onSuccess(data) {
onSuccess({
permissions: data,
});
},
manual: true,
},
);
const [visible] = useContext(VisibleContext);
useEffect(() => {
if (visible) {
service.run({});
}
}, [visible]);
return { resource, service, initialValues: service.data, ...service };
};
const useDetailsResource = ({ onSuccess }) => {
const { props } = useTable();
const ctx = useContext(TableRowContext);
const resource = Resource.make({
resourceName: 'roles',
resourceKey: ctx.record[props.rowKey],
});
const service = useRequest(
(params?: any) => {
return resource.get({ ...params });
},
{
formatResult: (result) => result?.data,
onSuccess,
manual: true,
},
);
const [visible] = useContext(VisibleContext);
useEffect(() => {
if (visible) {
service.run({});
}
}, [visible]);
return { resource, service, initialValues: service.data, ...service };
};
const collectionSchema: 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-decorator': 'Form',
'x-decorator-props': {
useResource: useActionPermissionResource,
},
'x-component-props': {
useOkAction: useActionPermissionSubmit,
},
properties: {
permissions: {
type: 'array',
'x-component': 'ActionPermissionField',
},
},
},
},
},
},
},
},
},
},
},
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,
},
},
},
},
};
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,
},
},
},
},
};
const schema: ISchema = {
type: 'void',
name: 'action',
'x-component': 'Action',
'x-component-props': {
className: 'nb-database-config',
icon: 'LockOutlined',
type: 'primary',
},
properties: {
modal1: {
type: 'void',
title: '权限组',
'x-component': 'Action.Drawer',
properties: {
table: {
type: 'array',
// 'x-decorator': 'CardItem',
'x-component': 'Table',
default: [],
'x-component-props': {
rowKey: 'name',
dragSort: true,
showIndex: true,
refreshRequestOnChange: true,
pagination: {
pageSize: 10,
},
useResource,
collectionName: 'roles',
},
properties: {
[uid()]: {
type: 'void',
'x-component': 'Table.ActionBar',
properties: {
[uid()]: {
type: 'void',
name: 'action1',
title: '删除',
'x-align': 'right',
'x-decorator': 'AddNew.Displayed',
'x-decorator-props': {
displayName: 'destroy',
},
'x-component': 'Action',
'x-designable-bar': 'Table.Action.DesignableBar',
'x-component-props': {
useAction: '{{ Table.useTableDestroyAction }}',
},
},
[uid()]: {
type: 'void',
name: 'action1',
title: '新增',
'x-align': 'right',
'x-decorator': 'AddNew.Displayed',
'x-decorator-props': {
displayName: 'create',
},
'x-component': 'Action',
'x-component-props': {
type: 'primary',
},
properties: {
modal: {
type: 'void',
title: '新增数据',
'x-decorator': 'Form',
'x-component': 'Action.Drawer',
'x-component-props': {
useOkAction: '{{ Table.useTableCreateAction }}',
},
properties: {
title: {
type: 'string',
title: '权限名称',
'x-component': 'Input',
'x-decorator': 'FormilyFormItem',
},
name: {
type: 'string',
title: '权限标识',
'x-component': 'Input',
'x-decorator': 'FormilyFormItem',
},
},
},
},
},
},
},
[uid()]: {
type: 'void',
title: '操作',
'x-component': 'Table.Operation',
'x-component-props': {
className: 'nb-table-operation',
},
'x-designable-bar': 'Table.Operation.DesignableBar',
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-decorator': 'RoleProvider',
'x-component': 'Action.Drawer',
'x-component-props': {
bodyStyle: {
background: '#f0f2f5',
paddingTop: 0,
},
},
properties: {
[uid()]: {
type: 'void',
'x-component': 'Tabs',
properties: {
[uid()]: {
type: 'void',
title: '数据表操作权限',
'x-component': 'Tabs.TabPane',
'x-component-props': {},
properties: {
collectionSchema,
},
},
[uid()]: {
type: 'void',
title: '菜单权限',
'x-component': 'Tabs.TabPane',
'x-component-props': {},
properties: {
menuSchema,
},
},
},
},
},
},
},
},
[uid()]: {
type: 'void',
title: '编辑',
'x-component': 'Menu.Action',
'x-action-type': 'update',
properties: {
[uid()]: {
type: 'void',
title: '编辑数据',
'x-decorator': 'Form',
'x-decorator-props': {
useResource: useDetailsResource,
},
'x-component': 'Action.Drawer',
'x-component-props': {
useOkAction: '{{ Table.useTableUpdateAction }}',
},
properties: {
title: {
type: 'string',
title: '权限名称',
'x-component': 'Input',
'x-decorator': 'FormilyFormItem',
},
},
},
},
},
[uid()]: {
type: 'void',
title: '删除',
'x-component': 'Menu.Action',
'x-designable-bar': 'Table.Action.DesignableBar',
'x-action-type': 'destroy',
'x-component-props': {
useAction: '{{ Table.useTableDestroyAction }}',
},
},
},
},
},
},
},
},
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,
},
},
},
},
},
},
},
},
};
export const Permissions = () => {
return (
<SchemaRenderer
components={{ RoleProvider, ActionPermissionField }}
schema={schema}
/>
);
};
export default Permissions;

View File

@ -4,7 +4,7 @@ import { FormItem } from '@formily/antd';
import { action } from '@formily/reactive';
import { useCollectionsContext } from '../../constate/Collections';
const useAsyncDataSource = (service: any) => (field: any) => {
export const useAsyncDataSource = (service: any) => (field: any) => {
field.loading = true;
service(field).then(
action((data: any) => {

View File

@ -22,7 +22,7 @@ import { useRequest } from 'ahooks';
import './style.less';
import { ISchema, Schema } from '@formily/react';
import Database from './datatable';
import Database, { useAsyncDataSource } from './datatable';
import { HighlightOutlined } from '@ant-design/icons';
import cls from 'classnames';
import {
@ -34,6 +34,7 @@ import {
useCollectionsContext,
} from '../../constate';
import { uid } from '@formily/shared';
import { Permissions } from './Permissions';
function DesignableToggle() {
const { designable, setDesignable } = useDesignableSwitchContext();
@ -96,6 +97,7 @@ function LayoutWithMenu(props: LayoutWithMenuProps) {
}}
/>
<Database />
<Permissions />
<DesignableToggle />
</Layout.Header>
<Layout>
@ -115,13 +117,13 @@ function LayoutWithMenu(props: LayoutWithMenuProps) {
function Content({ activeKey }) {
const { designable } = useDesignableSwitchContext();
const { collections } = useCollectionsContext();
const {
data = {},
loading,
} = useRequest(`ui_schemas:getTree?filter[parentKey]=${activeKey}`, {
refreshDeps: [activeKey, collections, designable],
formatResult: (result) => result?.data,
});
const { data = {}, loading } = useRequest(
`ui_schemas:getTree?filter[parentKey]=${activeKey}`,
{
refreshDeps: [activeKey, collections, designable],
formatResult: (result) => result?.data,
},
);
if (loading) {
return <Spin />;