From c1dfc8b2f578c4a746dfed3f18cdbd5b22e0e293 Mon Sep 17 00:00:00 2001 From: chenos Date: Wed, 11 Aug 2021 00:09:54 +0800 Subject: [PATCH] feat: collection action permissions --- .../Permissions/ActionPermissionField.tsx | 246 ++++++++ .../admin-layout/Permissions/index.tsx | 571 ++++++++++++++++++ .../src/components/admin-layout/datatable.tsx | 2 +- .../src/components/admin-layout/index.tsx | 18 +- 4 files changed, 828 insertions(+), 9 deletions(-) create mode 100644 packages/client/src/components/admin-layout/Permissions/ActionPermissionField.tsx create mode 100644 packages/client/src/components/admin-layout/Permissions/index.tsx diff --git a/packages/client/src/components/admin-layout/Permissions/ActionPermissionField.tsx b/packages/client/src/components/admin-layout/Permissions/ActionPermissionField.tsx new file mode 100644 index 000000000..14b1f110c --- /dev/null +++ b/packages/client/src/components/admin-layout/Permissions/ActionPermissionField.tsx @@ -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(); + 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(); + 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 ( + { + 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:
新增
, + 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(); + const actionDataSource = useActionDataSource(); + const { columns, dataSource, service } = useFieldPermissions(); + console.log('field?.value', field?.value); + return ( +
+ {actionTypeMap.get(value)}, + }, + { + title: '类型', + dataIndex: 'actionName', + render: (value) => + value === 'create' ? ( + 对新数据操作 + ) : ( + 对已有数据操作 + ), + }, + { + title: '允许操作', + dataIndex: 'enable', + render: (value, record) => ( + { + 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) => ( + + ), + }, + ]} + /> +
+
+ {/* ActionPermissionField
+ {role.name}
+ {ctx.record.name} */} +
+ + ); +}); diff --git a/packages/client/src/components/admin-layout/Permissions/index.tsx b/packages/client/src/components/admin-layout/Permissions/index.tsx new file mode 100644 index 000000000..42aec1181 --- /dev/null +++ b/packages/client/src/components/admin-layout/Permissions/index.tsx @@ -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 ( + + {props.children} + + ); +} + +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 ( + + ); +}; + +export default Permissions; diff --git a/packages/client/src/components/admin-layout/datatable.tsx b/packages/client/src/components/admin-layout/datatable.tsx index 939a55ba5..f77ed69a1 100644 --- a/packages/client/src/components/admin-layout/datatable.tsx +++ b/packages/client/src/components/admin-layout/datatable.tsx @@ -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) => { diff --git a/packages/client/src/components/admin-layout/index.tsx b/packages/client/src/components/admin-layout/index.tsx index 580baf163..373062171 100644 --- a/packages/client/src/components/admin-layout/index.tsx +++ b/packages/client/src/components/admin-layout/index.tsx @@ -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) { }} /> + @@ -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 ;