feat: improve acl module
This commit is contained in:
parent
5e51973b21
commit
9704f8a342
@ -1,16 +1,19 @@
|
|||||||
import { FormItem, FormLayout } from '@formily/antd';
|
import { FormItem, FormLayout } from '@formily/antd';
|
||||||
import { ArrayField } from '@formily/core';
|
import { ArrayField } from '@formily/core';
|
||||||
import { connect, useField, useForm } from '@formily/react';
|
import { connect, useField, useForm } from '@formily/react';
|
||||||
import { Checkbox, Select, Table, Tag } from 'antd';
|
import { Checkbox, Table, Tag } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAvailableActions } from '.';
|
import { useAvailableActions } from '.';
|
||||||
import { useCollectionManager, useCompile, useRecord } from '../..';
|
import { useCollectionManager, useCompile, useRecord } from '../..';
|
||||||
|
import { ScopeSelect } from './ScopeSelect';
|
||||||
|
|
||||||
const toActionMap = (arr: any[]) => {
|
const toActionMap = (arr: any[]) => {
|
||||||
const obj = {};
|
const obj = {};
|
||||||
arr?.forEach((action) => {
|
arr?.forEach?.((action) => {
|
||||||
obj[action.name] = action;
|
if (action.name) {
|
||||||
|
obj[action.name] = action;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
@ -52,11 +55,16 @@ export const RolesResourcesActions = connect((props) => {
|
|||||||
}
|
}
|
||||||
onChange(Object.values(actionMap));
|
onChange(Object.values(actionMap));
|
||||||
};
|
};
|
||||||
|
const setScope = (actionName, scope) => {
|
||||||
|
if (!actionMap[actionName]) {
|
||||||
|
toggleAction(actionName);
|
||||||
|
}
|
||||||
|
actionMap[actionName]['scope'] = scope;
|
||||||
|
};
|
||||||
const allChecked = {};
|
const allChecked = {};
|
||||||
for (const action of availableActionsWithFields) {
|
for (const action of availableActionsWithFields) {
|
||||||
allChecked[action.name] = collection?.fields?.length === actionMap?.[action.name]?.fields?.length;
|
allChecked[action.name] = collection?.fields?.length === actionMap?.[action.name]?.fields?.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<FormLayout layout={'vertical'}>
|
<FormLayout layout={'vertical'}>
|
||||||
@ -95,7 +103,15 @@ export const RolesResourcesActions = connect((props) => {
|
|||||||
{
|
{
|
||||||
dataIndex: 'scope',
|
dataIndex: 'scope',
|
||||||
title: '可操作的数据范围',
|
title: '可操作的数据范围',
|
||||||
render: () => <Select size={'small'} />,
|
render: (value, action) =>
|
||||||
|
!action.onNewRecord && (
|
||||||
|
<ScopeSelect
|
||||||
|
value={value}
|
||||||
|
onChange={(scope) => {
|
||||||
|
setScope(action.name, scope);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
dataSource={availableActions?.map((item) => {
|
dataSource={availableActions?.map((item) => {
|
||||||
|
46
packages/client/src/acl/Configuration/ScopeSelect.tsx
Normal file
46
packages/client/src/acl/Configuration/ScopeSelect.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { createForm } from '@formily/core';
|
||||||
|
import React, { createContext, useContext, useMemo, useState } from 'react';
|
||||||
|
import { FormProvider, SchemaComponent } from '../../schema-component';
|
||||||
|
import { scopesSchema } from './schemas/scopes';
|
||||||
|
|
||||||
|
const RolesResourcesScopesSelectedRowKeysContext = createContext(null);
|
||||||
|
|
||||||
|
const RolesResourcesScopesSelectedRowKeysProvider: React.FC = (props) => {
|
||||||
|
const [keys, setKeys] = useState([]);
|
||||||
|
return (
|
||||||
|
<RolesResourcesScopesSelectedRowKeysContext.Provider value={[keys, setKeys]}>
|
||||||
|
{props.children}
|
||||||
|
</RolesResourcesScopesSelectedRowKeysContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useRolesResourcesScopesSelectedRowKeys = () => {
|
||||||
|
return useContext(RolesResourcesScopesSelectedRowKeysContext);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ScopeSelect = (props) => {
|
||||||
|
const form = useMemo(
|
||||||
|
() =>
|
||||||
|
createForm({
|
||||||
|
values: {
|
||||||
|
scope: props.value,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
console.log('props.value', props.value, form.values);
|
||||||
|
return (
|
||||||
|
<FormProvider form={form}>
|
||||||
|
<SchemaComponent
|
||||||
|
components={{ RolesResourcesScopesSelectedRowKeysProvider }}
|
||||||
|
scope={{
|
||||||
|
onChange(value) {
|
||||||
|
props?.onChange?.(value);
|
||||||
|
console.log('onChange', value);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
schema={scopesSchema}
|
||||||
|
/>
|
||||||
|
</FormProvider>
|
||||||
|
);
|
||||||
|
};
|
202
packages/client/src/acl/Configuration/schemas/scopes.ts
Normal file
202
packages/client/src/acl/Configuration/schemas/scopes.ts
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
import { ISchema, useForm } from '@formily/react';
|
||||||
|
import { useActionContext } from '../../../';
|
||||||
|
import { useAPIClient, useRequest } from '../../../api-client';
|
||||||
|
import { useRecord } from '../../../record-provider';
|
||||||
|
|
||||||
|
const collection = {
|
||||||
|
name: 'rolesResourcesScopes',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'title',
|
||||||
|
interface: 'input',
|
||||||
|
uiSchema: {
|
||||||
|
title: '名称',
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
required: true,
|
||||||
|
} as ISchema,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const scopesSchema: ISchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
scope: {
|
||||||
|
'x-component': 'RecordPicker',
|
||||||
|
'x-component-props': {
|
||||||
|
size: 'small',
|
||||||
|
fieldNames: {
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
},
|
||||||
|
onChange: '{{ onChange }}',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
options: {
|
||||||
|
'x-decorator': 'RolesResourcesScopesSelectedRowKeysProvider',
|
||||||
|
'x-component': 'RecordPicker.Options',
|
||||||
|
type: 'void',
|
||||||
|
title: '可操作的数据范围',
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
marginBottom: 16,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
// delete: {
|
||||||
|
// type: 'void',
|
||||||
|
// title: '删除',
|
||||||
|
// 'x-component': 'Action',
|
||||||
|
// },
|
||||||
|
create: {
|
||||||
|
type: 'void',
|
||||||
|
title: '添加数据范围',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'primary',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
drawer: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Action.Drawer',
|
||||||
|
'x-decorator': 'Form',
|
||||||
|
title: '添加数据范围',
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
title: '数据范围名称',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
},
|
||||||
|
// scope: {
|
||||||
|
// 'x-component': 'Input',
|
||||||
|
// 'x-decorator': 'FormItem',
|
||||||
|
// },
|
||||||
|
footer: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Action.Drawer.Footer',
|
||||||
|
properties: {
|
||||||
|
action1: {
|
||||||
|
title: 'Cancel',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
useAction: '{{ cm.useCancelAction }}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
action2: {
|
||||||
|
title: 'Submit',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'primary',
|
||||||
|
useAction() {
|
||||||
|
const api = useAPIClient();
|
||||||
|
const ctx = useActionContext();
|
||||||
|
const form = useForm();
|
||||||
|
const record = useRecord();
|
||||||
|
return {
|
||||||
|
async run() {
|
||||||
|
await api.resource('rolesResourcesScopes').create({
|
||||||
|
values: {
|
||||||
|
...form.values,
|
||||||
|
resourceName: record.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
ctx.setVisible(false);
|
||||||
|
api.service('rolesResourcesScopesList')?.refresh?.();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
type: 'array',
|
||||||
|
'x-component': 'Table.RowSelection',
|
||||||
|
'x-component-props': {
|
||||||
|
rowKey: 'id',
|
||||||
|
objectValue: true,
|
||||||
|
rowSelection: {
|
||||||
|
type: 'radio',
|
||||||
|
},
|
||||||
|
// useSelectedRowKeys() {
|
||||||
|
// const [selectedRowKeys, setSelectedRowKeys] = useRolesResourcesScopesSelectedRowKeys();
|
||||||
|
// return [selectedRowKeys, setSelectedRowKeys];
|
||||||
|
// },
|
||||||
|
useDataSource(options) {
|
||||||
|
const record = useRecord();
|
||||||
|
console.log('useRecord', record);
|
||||||
|
return useRequest(
|
||||||
|
{
|
||||||
|
resource: 'rolesResourcesScopes',
|
||||||
|
action: 'list',
|
||||||
|
params: {
|
||||||
|
sort: 'id',
|
||||||
|
filter: JSON.stringify({
|
||||||
|
$or: [
|
||||||
|
{
|
||||||
|
'resourceName.$eq': record.name,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'resourceName.$eq': '*',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...options,
|
||||||
|
uid: 'rolesResourcesScopesList',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// dataSource: [
|
||||||
|
// { id: 1, name: 'Name1' },
|
||||||
|
// { id: 2, name: 'Name2' },
|
||||||
|
// { id: 3, name: 'Name3' },
|
||||||
|
// ],
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
column1: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Name',
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
column2: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Actions',
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
// properties: {
|
||||||
|
// delete: {
|
||||||
|
// type: 'void',
|
||||||
|
// title: '删除',
|
||||||
|
// 'x-component': 'Action.Link',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
@ -6,82 +6,97 @@ import {
|
|||||||
FormContext,
|
FormContext,
|
||||||
mapProps,
|
mapProps,
|
||||||
mapReadPretty,
|
mapReadPretty,
|
||||||
|
observer,
|
||||||
RecursionField,
|
RecursionField,
|
||||||
Schema,
|
|
||||||
useField,
|
useField,
|
||||||
useFieldSchema
|
useFieldSchema,
|
||||||
|
useForm
|
||||||
} from '@formily/react';
|
} from '@formily/react';
|
||||||
import { toArr } from '@formily/shared';
|
import { toArr } from '@formily/shared';
|
||||||
import { Button, Drawer, Select, Space, Tag } from 'antd';
|
import { Button, Drawer, Select, Space, Tag } from 'antd';
|
||||||
import React, { createContext, useContext, useMemo, useState } from 'react';
|
import React, { createContext, useContext, useMemo, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAttach } from '../../hooks/useAttach';
|
import { useAttach } from '../../hooks/useAttach';
|
||||||
import { ActionContext } from '../action';
|
import { ActionContext, useActionContext } from '../action';
|
||||||
|
|
||||||
const InputRecordPicker: React.FC = (props) => {
|
const InputRecordPicker: React.FC<any> = (props) => {
|
||||||
|
const { onChange } = props;
|
||||||
|
const fieldNames = { label: 'label', value: 'value', ...props.fieldNames };
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const field = useField<Field>();
|
const field = useField<Field>();
|
||||||
const s = fieldSchema.reduceProperties((buf, s) => {
|
const s = fieldSchema.reduceProperties((buf, s) => {
|
||||||
if (s['x-component'] === 'RowSelection') {
|
if (s['x-component'] === 'RecordPicker.Options') {
|
||||||
return s;
|
return s.reduceProperties((buf, s) => {
|
||||||
|
if (s['x-component'] === 'Table.RowSelection') {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}, null);
|
||||||
}
|
}
|
||||||
return buf;
|
return buf;
|
||||||
}, new Schema({}));
|
}, null);
|
||||||
|
const [value, setValue] = useState(field.value);
|
||||||
const form = useMemo(
|
const form = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createForm({
|
createForm({
|
||||||
initialValues: {
|
initialValues: s?.name
|
||||||
[s.name]: field.value,
|
? {
|
||||||
},
|
[s.name]: field.value,
|
||||||
|
}
|
||||||
|
: {},
|
||||||
effects() {
|
effects() {
|
||||||
onFormSubmit((form) => {
|
onFormSubmit((form) => {
|
||||||
field.value = form.values[s.name];
|
setValue(form.values[s.name]);
|
||||||
console.log('field.value', form.values[s.name]);
|
onChange?.(form.values[s.name]);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
const toValue = (value) => {
|
||||||
|
if (!value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.map((item) => {
|
||||||
|
return {
|
||||||
|
label: item[fieldNames.label] || item[fieldNames.value],
|
||||||
|
value: item[fieldNames.value],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
label: value[fieldNames.label] || value[fieldNames.value],
|
||||||
|
value: value[fieldNames.value],
|
||||||
|
};
|
||||||
|
};
|
||||||
const f = useAttach(form.createVoidField({ ...field.props, basePath: '' }));
|
const f = useAttach(form.createVoidField({ ...field.props, basePath: '' }));
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Select
|
<Select
|
||||||
|
size={props.size}
|
||||||
|
mode={props.mode}
|
||||||
|
fieldNames={fieldNames}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
}}
|
}}
|
||||||
|
labelInValue={true}
|
||||||
|
value={toValue(value)}
|
||||||
open={false}
|
open={false}
|
||||||
></Select>
|
></Select>
|
||||||
<FormContext.Provider value={form}>
|
<FormContext.Provider value={form}>
|
||||||
<FieldContext.Provider value={f}>
|
<FieldContext.Provider value={f}>
|
||||||
<Drawer
|
<ActionContext.Provider value={{ visible, setVisible }}>
|
||||||
width={'50%'}
|
|
||||||
placement={'right'}
|
|
||||||
destroyOnClose
|
|
||||||
visible={visible}
|
|
||||||
onClose={() => setVisible(false)}
|
|
||||||
footer={
|
|
||||||
<Space style={{ justifyContent: 'flex-end', width: '100%' }}>
|
|
||||||
<Button
|
|
||||||
type={'primary'}
|
|
||||||
onClick={async () => {
|
|
||||||
await form.submit();
|
|
||||||
setVisible(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<RecursionField
|
<RecursionField
|
||||||
onlyRenderProperties
|
onlyRenderProperties
|
||||||
basePath={f.address}
|
basePath={f.address}
|
||||||
schema={fieldSchema}
|
schema={fieldSchema}
|
||||||
// filterProperties={(s) => {
|
filterProperties={(s) => {
|
||||||
// return s['x-component'] === 'RowSelection';
|
return s['x-component'] === 'RecordPicker.Options';
|
||||||
// }}
|
}}
|
||||||
/>
|
/>
|
||||||
</Drawer>
|
</ActionContext.Provider>
|
||||||
</FieldContext.Provider>
|
</FieldContext.Provider>
|
||||||
</FormContext.Provider>
|
</FormContext.Provider>
|
||||||
</div>
|
</div>
|
||||||
@ -125,6 +140,44 @@ export const RecordPicker: any = connect(
|
|||||||
mapReadPretty(ReadPrettyRecordPicker),
|
mapReadPretty(ReadPrettyRecordPicker),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
RecordPicker.Options = observer((props) => {
|
||||||
|
const field = useField();
|
||||||
|
const form = useForm();
|
||||||
|
const { visible, setVisible } = useActionContext();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<Drawer
|
||||||
|
width={'50%'}
|
||||||
|
placement={'right'}
|
||||||
|
title={field.title}
|
||||||
|
{...props}
|
||||||
|
destroyOnClose
|
||||||
|
visible={visible}
|
||||||
|
onClose={() => setVisible(false)}
|
||||||
|
footer={
|
||||||
|
<Space style={{ justifyContent: 'flex-end', width: '100%' }}>
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type={'primary'}
|
||||||
|
onClick={async () => {
|
||||||
|
await form.submit();
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Submit')}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
RecordPicker.SelectedItem = () => {
|
RecordPicker.SelectedItem = () => {
|
||||||
const ctx = useContext(RowContext);
|
const ctx = useContext(RowContext);
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
import { FormItem } from '@formily/antd';
|
import { FormItem } from '@formily/antd';
|
||||||
import { ISchema } from '@formily/react';
|
import { ISchema } from '@formily/react';
|
||||||
import { Action, Input, RecordPicker, RowSelection, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
import { Action, Input, RecordPicker, SchemaComponent, SchemaComponentProvider, Table } from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const schema: ISchema = {
|
const schema: ISchema = {
|
||||||
@ -18,6 +18,13 @@ const schema: ISchema = {
|
|||||||
],
|
],
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'RecordPicker',
|
'x-component': 'RecordPicker',
|
||||||
|
'x-component-props': {
|
||||||
|
mode: 'tags',
|
||||||
|
fieldNames: {
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
'x-reactions': {
|
'x-reactions': {
|
||||||
target: 'read',
|
target: 'read',
|
||||||
fulfill: {
|
fulfill: {
|
||||||
@ -27,30 +34,39 @@ const schema: ISchema = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
rowSelection: {
|
options: {
|
||||||
'x-component': 'RowSelection',
|
'x-component': 'RecordPicker.Options',
|
||||||
'x-component-props': {
|
type: 'void',
|
||||||
rowKey: 'id',
|
title: 'Drawer Title',
|
||||||
objectValue: true,
|
|
||||||
rowSelection: {
|
|
||||||
type: 'checkbox',
|
|
||||||
},
|
|
||||||
dataSource: [
|
|
||||||
{ id: 1, name: 'Name1' },
|
|
||||||
{ id: 2, name: 'Name2' },
|
|
||||||
{ id: 3, name: 'Name3' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
properties: {
|
properties: {
|
||||||
column1: {
|
input: {
|
||||||
type: 'void',
|
type: 'array',
|
||||||
title: 'Name',
|
title: `编辑模式`,
|
||||||
'x-component': 'RowSelection.Column',
|
'x-component': 'Table.RowSelection',
|
||||||
|
'x-component-props': {
|
||||||
|
rowKey: 'id',
|
||||||
|
objectValue: true,
|
||||||
|
rowSelection: {
|
||||||
|
type: 'checkbox',
|
||||||
|
},
|
||||||
|
dataSource: [
|
||||||
|
{ id: 1, name: 'Name1' },
|
||||||
|
{ id: 2, name: 'Name2' },
|
||||||
|
{ id: 3, name: 'Name3' },
|
||||||
|
],
|
||||||
|
},
|
||||||
properties: {
|
properties: {
|
||||||
name: {
|
column1: {
|
||||||
type: 'string',
|
type: 'void',
|
||||||
'x-component': 'Input',
|
title: 'Name',
|
||||||
'x-read-pretty': true,
|
'x-component': 'Table.Column',
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -102,7 +118,7 @@ const schema: ISchema = {
|
|||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
<SchemaComponentProvider components={{ Input, RecordPicker, RowSelection, FormItem, Action }}>
|
<SchemaComponentProvider components={{ Table, Input, RecordPicker, FormItem, Action }}>
|
||||||
<SchemaComponent schema={schema} />
|
<SchemaComponent schema={schema} />
|
||||||
</SchemaComponentProvider>
|
</SchemaComponentProvider>
|
||||||
);
|
);
|
||||||
|
24
packages/client/src/schema-component/core/FormProvider.tsx
Normal file
24
packages/client/src/schema-component/core/FormProvider.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
FormProvider as FormilyFormProvider,
|
||||||
|
IProviderProps,
|
||||||
|
SchemaExpressionScopeContext,
|
||||||
|
SchemaOptionsContext
|
||||||
|
} from '@formily/react';
|
||||||
|
import React, { useContext } from 'react';
|
||||||
|
import { SchemaComponentOptions } from './SchemaComponentOptions';
|
||||||
|
|
||||||
|
export const FormProvider: React.FC<IProviderProps> = (props) => {
|
||||||
|
const { children, ...others } = props;
|
||||||
|
let options = useContext(SchemaOptionsContext);
|
||||||
|
const expressionScope = useContext(SchemaExpressionScopeContext);
|
||||||
|
const scope = { ...options?.scope, ...expressionScope };
|
||||||
|
const components = { ...options?.components };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormilyFormProvider {...others}>
|
||||||
|
<SchemaComponentOptions components={components} scope={scope}>
|
||||||
|
{children}
|
||||||
|
</SchemaComponentOptions>
|
||||||
|
</FormilyFormProvider>
|
||||||
|
);
|
||||||
|
};
|
@ -1,5 +1,7 @@
|
|||||||
export * from './DesignableSwitch';
|
export * from './DesignableSwitch';
|
||||||
|
export * from './FormProvider';
|
||||||
export * from './RemoteSchemaComponent';
|
export * from './RemoteSchemaComponent';
|
||||||
export * from './SchemaComponent';
|
export * from './SchemaComponent';
|
||||||
export * from './SchemaComponentOptions';
|
export * from './SchemaComponentOptions';
|
||||||
export * from './SchemaComponentProvider';
|
export * from './SchemaComponentProvider';
|
||||||
|
|
||||||
|
@ -238,7 +238,10 @@ export class PluginACL extends Plugin {
|
|||||||
await this.writeRolesToACL();
|
await this.writeRolesToACL();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.app.on('afterInstallUsersPlugin', async () => {
|
this.app.on('beforeInstallPlugin', async (plugin) => {
|
||||||
|
if (plugin.constructor.name !== 'UsersPlugin') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const repository = this.app.db.getRepository('roles');
|
const repository = this.app.db.getRepository('roles');
|
||||||
await repository.createMany({
|
await repository.createMany({
|
||||||
records: [
|
records: [
|
||||||
|
@ -5,22 +5,6 @@ import * as actions from './actions/users';
|
|||||||
import * as middlewares from './middlewares';
|
import * as middlewares from './middlewares';
|
||||||
|
|
||||||
export default class UsersPlugin extends Plugin {
|
export default class UsersPlugin extends Plugin {
|
||||||
async install() {
|
|
||||||
const {
|
|
||||||
adminNickname = 'Super Admin',
|
|
||||||
adminEmail = 'admin@nocobase.com',
|
|
||||||
adminPassword = 'admin123',
|
|
||||||
} = this.options;
|
|
||||||
|
|
||||||
const User = this.db.getCollection('users');
|
|
||||||
await User.repository.create({
|
|
||||||
values: {
|
|
||||||
nickname: adminNickname,
|
|
||||||
email: adminEmail,
|
|
||||||
password: adminPassword,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async beforeLoad() {
|
async beforeLoad() {
|
||||||
this.db.on('users.afterCreateWithAssociations', async (model, options) => {
|
this.db.on('users.afterCreateWithAssociations', async (model, options) => {
|
||||||
@ -83,4 +67,23 @@ export default class UsersPlugin extends Plugin {
|
|||||||
directory: resolve(__dirname, 'collections'),
|
directory: resolve(__dirname, 'collections'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async install() {
|
||||||
|
const {
|
||||||
|
adminNickname = 'Super Admin',
|
||||||
|
adminEmail = 'admin@nocobase.com',
|
||||||
|
adminPassword = 'admin123',
|
||||||
|
} = this.options;
|
||||||
|
|
||||||
|
const User = this.db.getCollection('users');
|
||||||
|
await User.repository.create({
|
||||||
|
values: {
|
||||||
|
nickname: adminNickname,
|
||||||
|
email: adminEmail,
|
||||||
|
password: adminPassword,
|
||||||
|
roles: ['admin'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
import { CleanOptions, SyncOptions } from '@nocobase/database';
|
||||||
import Application from './application';
|
import Application from './application';
|
||||||
import { Plugin } from './plugin';
|
import { Plugin } from './plugin';
|
||||||
import { CleanOptions, SyncOptions } from '@nocobase/database';
|
|
||||||
|
|
||||||
interface PluginManagerOptions {
|
interface PluginManagerOptions {
|
||||||
app: Application;
|
app: Application;
|
||||||
|
Loading…
Reference in New Issue
Block a user