feat(acl): supports 'current role' variable and collections filtering (#3181)
* feat(acl): support current role variable * feat(acl): support to filter collections * fix: the save conditions button should not be shown * feat(formDataTemplates): supports to use variables for data scope * test: make testing more stable * fix: should only support filtering title and name * fix: fix parse * test: make testing more stable * fix: should not use async function
This commit is contained in:
parent
2f892f00d7
commit
aaee46e0c2
@ -265,6 +265,7 @@ export class ACL extends EventEmitter {
|
||||
state,
|
||||
},
|
||||
$user: async () => state.currentUser,
|
||||
$nRole: () => state.currentRole,
|
||||
},
|
||||
});
|
||||
json.filter = filter;
|
||||
|
@ -1,88 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useCompile } from '../../schema-component';
|
||||
import { useGetFilterOptions } from '../../schema-component/antd/filter';
|
||||
import { useValues } from '../../schema-component/antd/filter/useValues';
|
||||
import { Variable } from '../../schema-component/antd/variable';
|
||||
|
||||
interface GetOptionsParams {
|
||||
schema: any;
|
||||
operator: string;
|
||||
maxDepth: number;
|
||||
getFilterOptions: (collectionName: string) => any[];
|
||||
count?: number;
|
||||
}
|
||||
|
||||
const getOptions = (
|
||||
collectionName: string,
|
||||
{ schema, operator, maxDepth, getFilterOptions, count = 1 }: GetOptionsParams,
|
||||
) => {
|
||||
if (count > maxDepth) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const result = getFilterOptions(collectionName).map((option) => {
|
||||
if ((option.type !== 'belongsTo' && option.type !== 'hasOne') || !option.target) {
|
||||
return {
|
||||
key: option.name,
|
||||
value: option.name,
|
||||
label: option.title,
|
||||
// TODO: 现在是通过组件的名称来过滤能够被选择的选项,这样的坏处是不够精确,后续可以优化
|
||||
disabled: schema?.['x-component'] !== option.schema?.['x-component'],
|
||||
};
|
||||
}
|
||||
|
||||
const children =
|
||||
getOptions(option.target, {
|
||||
schema,
|
||||
operator,
|
||||
maxDepth,
|
||||
count: count + 1,
|
||||
getFilterOptions,
|
||||
}) || [];
|
||||
|
||||
return {
|
||||
key: option.name,
|
||||
value: option.name,
|
||||
label: option.title,
|
||||
children,
|
||||
disabled: children.every((child) => child.disabled),
|
||||
};
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const useUserVariable = ({ schema, operator }) => {
|
||||
const getFilterOptions = useGetFilterOptions();
|
||||
const options = getOptions('users', { schema, operator, maxDepth: 1, getFilterOptions }) || [];
|
||||
|
||||
return {
|
||||
label: `{{t("Current user")}}`,
|
||||
value: '$user',
|
||||
key: '$user',
|
||||
disabled: options.every((option) => option.disabled),
|
||||
children: options,
|
||||
};
|
||||
};
|
||||
|
||||
const useVariableOptions = () => {
|
||||
const { operator, schema } = useValues();
|
||||
const userVariable = useUserVariable({ schema, operator });
|
||||
|
||||
if (!operator || !schema) return [];
|
||||
|
||||
return [userVariable];
|
||||
};
|
||||
|
||||
export function FilterDynamicComponent(props) {
|
||||
const { value, onChange, renderSchemaComponent } = props;
|
||||
const options = useVariableOptions();
|
||||
const compile = useCompile();
|
||||
const scope = compile(options);
|
||||
|
||||
return (
|
||||
<Variable.Input value={value} onChange={onChange} scope={scope}>
|
||||
{renderSchemaComponent()}
|
||||
</Variable.Input>
|
||||
);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { Spin } from 'antd';
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import { useRequest } from '../../api-client';
|
||||
import { SchemaComponent } from '../../schema-component';
|
||||
import { SchemaComponent, SchemaComponentContext } from '../../schema-component';
|
||||
import { MenuItemsProvider } from '../Configuration/MenuItemsProvider';
|
||||
import { PermissionProvider, SettingCenterPermissionProvider } from '../Configuration/PermisionProvider';
|
||||
import { roleSchema } from './schemas/roles';
|
||||
@ -28,12 +28,14 @@ export const useAvailableActions = () => {
|
||||
export const RoleTable = () => {
|
||||
return (
|
||||
<div>
|
||||
<SchemaComponentContext.Provider value={{ designable: false }}>
|
||||
<AvailableActionsProver>
|
||||
<SchemaComponent
|
||||
schema={roleSchema}
|
||||
components={{ MenuItemsProvider, SettingCenterPermissionProvider, PermissionProvider }}
|
||||
/>
|
||||
</AvailableActionsProver>
|
||||
</SchemaComponentContext.Provider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import { useRoleResourceValues } from './useRoleResourceValues';
|
||||
import { useSaveRoleResourceAction } from './useSaveRoleResourceAction';
|
||||
|
||||
@ -90,6 +91,31 @@ export const roleCollectionsSchema: ISchema = {
|
||||
},
|
||||
},
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
style: {
|
||||
marginBottom: 16,
|
||||
},
|
||||
},
|
||||
properties: {
|
||||
filter: {
|
||||
type: 'void',
|
||||
title: '{{ t("Filter") }}',
|
||||
default: {
|
||||
$and: [{ title: { $includes: '' } }, { name: { $includes: '' } }],
|
||||
},
|
||||
'x-action': 'filter',
|
||||
'x-component': 'Filter.Action',
|
||||
'x-component-props': {
|
||||
icon: 'FilterOutlined',
|
||||
useProps: '{{ cm.useFilterActionProps }}',
|
||||
},
|
||||
'x-align': 'left',
|
||||
},
|
||||
},
|
||||
},
|
||||
table1: {
|
||||
type: 'void',
|
||||
'x-uid': 'input',
|
||||
|
@ -2,7 +2,7 @@ import { ISchema } from '@formily/react';
|
||||
import { useContext, useEffect } from 'react';
|
||||
import { useFormBlockContext } from '../../../block-provider';
|
||||
import { useFilterOptions } from '../../../schema-component';
|
||||
import { FilterDynamicComponent } from '../FilterDynamicComponent';
|
||||
import { VariableInput } from '../../../schema-settings';
|
||||
import { RoleResourceCollectionContext } from '../RolesResourcesActions';
|
||||
|
||||
export const rolesResourcesScopesCollection = {
|
||||
@ -140,11 +140,10 @@ export const scopesSchema: ISchema = {
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Filter',
|
||||
'x-component-props': {
|
||||
dynamicComponent: FilterDynamicComponent,
|
||||
dynamicComponent: VariableInput,
|
||||
useProps() {
|
||||
const ctx = useContext(RoleResourceCollectionContext);
|
||||
const options = useFilterOptions(ctx.name);
|
||||
console.log('ctx.name', ctx.name, options);
|
||||
return {
|
||||
options,
|
||||
};
|
||||
@ -269,7 +268,7 @@ export const scopesSchema: ISchema = {
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Filter',
|
||||
'x-component-props': {
|
||||
dynamicComponent: FilterDynamicComponent,
|
||||
dynamicComponent: VariableInput,
|
||||
useProps() {
|
||||
const ctx = useContext(RoleResourceCollectionContext);
|
||||
const options = useFilterOptions(ctx.name);
|
||||
|
@ -78,7 +78,7 @@ const AssociationResourceActionProvider = (props) => {
|
||||
);
|
||||
const resource = api.resource(request.resource, resourceOf);
|
||||
return (
|
||||
<ResourceContext.Provider value={{ type: 'association', resource, association }}>
|
||||
<ResourceContext.Provider value={{ type: 'association', resource, association, collection }}>
|
||||
<ResourceActionContext.Provider value={{ ...service, defaultRequest: request, dragSort }}>
|
||||
<CollectionProvider collection={collection}>{props.children}</CollectionProvider>
|
||||
</ResourceActionContext.Provider>
|
||||
|
@ -579,7 +579,8 @@ export const useFilterActionProps = () => {
|
||||
const options = useFilterFieldOptions(collection.fields);
|
||||
const service = useResourceActionContext();
|
||||
return useFilterFieldProps({
|
||||
options,
|
||||
// 目前仅需要支持筛选 title 和 name,其它字段可能会报错。详见:https://nocobase.height.app/T-2745
|
||||
options: options.filter((option) => ['title', 'name'].includes(option.name)),
|
||||
params: service.state?.params?.[0] || service.params,
|
||||
service,
|
||||
});
|
||||
|
@ -9,6 +9,7 @@ import { mergeFilter } from '../../block-provider';
|
||||
import { useCollectionManager } from '../../collection-manager';
|
||||
import { SchemaComponent, SchemaComponentContext, removeNullCondition } from '../../schema-component';
|
||||
import { ITemplate } from '../../schema-component/antd/form-v2/Templates';
|
||||
import { VariableInput } from '../VariableInput';
|
||||
import { AsDefaultTemplate } from './components/AsDefaultTemplate';
|
||||
import { ArrayCollapse } from './components/DataTemplateTitle';
|
||||
import { getSelectedIdFilter } from './components/Designer';
|
||||
@ -143,6 +144,9 @@ export const FormDataTemplates = observer(
|
||||
title: '{{ t("Assign data scope for the template") }}',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Filter',
|
||||
'x-component-props': {
|
||||
dynamicComponent: VariableInput,
|
||||
},
|
||||
'x-decorator-props': {
|
||||
style: {
|
||||
marginBottom: '0px',
|
||||
|
Loading…
Reference in New Issue
Block a user