feat(client): improve the collection manager module
This commit is contained in:
parent
57c9524f34
commit
37be46aacb
@ -5,6 +5,7 @@ import {
|
||||
AntdSchemaComponentProvider,
|
||||
APIClientProvider,
|
||||
AuthLayout,
|
||||
ChinaRegionProvider,
|
||||
CollectionManagerShortcut,
|
||||
compose,
|
||||
DesignableSwitch,
|
||||
@ -53,6 +54,7 @@ const providers = [
|
||||
RemoteCollectionManagerProvider,
|
||||
[SchemaInitializerProvider, { initializers: { MenuItemInitializers } }],
|
||||
AntdSchemaComponentProvider,
|
||||
ChinaRegionProvider,
|
||||
[DocumentTitleProvider, { addonAfter: 'NocoBase' }],
|
||||
[
|
||||
RouteSwitchProvider,
|
||||
|
76
packages/client/src/china-region/index.tsx
Normal file
76
packages/client/src/china-region/index.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
import { ArrayField } from '@formily/core';
|
||||
import { useField } from '@formily/react';
|
||||
import React from 'react';
|
||||
import { SchemaComponentOptions } from '..';
|
||||
import { useAPIClient, useRequest } from '../api-client';
|
||||
|
||||
const useChinaRegionDataSource = (options) => {
|
||||
const field = useField<ArrayField>();
|
||||
const maxLevel = field.componentProps.maxLevel;
|
||||
return useRequest(
|
||||
{
|
||||
resource: 'chinaRegions',
|
||||
action: 'list',
|
||||
params: {
|
||||
sort: 'code',
|
||||
filter: {
|
||||
level: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
...options,
|
||||
onSuccess(data) {
|
||||
options?.onSuccess({
|
||||
data:
|
||||
data?.data?.map((item) => {
|
||||
if (maxLevel !== 1) {
|
||||
item.isLeaf = false;
|
||||
}
|
||||
return item;
|
||||
}) || [],
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const useChinaRegionLoadData = () => {
|
||||
const api = useAPIClient();
|
||||
const field = useField<ArrayField>();
|
||||
const maxLevel = field.componentProps.maxLevel;
|
||||
return (selectedOptions) => {
|
||||
const targetOption = selectedOptions[selectedOptions.length - 1];
|
||||
if (targetOption?.children?.length > 0) {
|
||||
return;
|
||||
}
|
||||
targetOption.loading = true;
|
||||
api
|
||||
.resource('chinaRegions')
|
||||
.list({
|
||||
sort: 'code',
|
||||
filter: {
|
||||
parentCode: targetOption.code,
|
||||
},
|
||||
})
|
||||
.then(({ data }) => {
|
||||
targetOption.loading = false;
|
||||
targetOption.children =
|
||||
data?.data?.map((item) => {
|
||||
if (maxLevel > item.level) {
|
||||
item.isLeaf = false;
|
||||
}
|
||||
return item;
|
||||
}) || [];
|
||||
field.dataSource = [...field.dataSource];
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const ChinaRegionProvider = (props) => {
|
||||
return (
|
||||
<SchemaComponentOptions scope={{ useChinaRegionDataSource, useChinaRegionLoadData }}>
|
||||
{props.children}
|
||||
</SchemaComponentOptions>
|
||||
);
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
import { Field } from '@formily/core';
|
||||
import { connect, useField, useFieldSchema } from '@formily/react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useComponent } from '..';
|
||||
import { useCompile, useComponent } from '..';
|
||||
import { CollectionFieldProvider } from './CollectionFieldProvider';
|
||||
import { useCollectionField } from './hooks';
|
||||
|
||||
@ -11,6 +11,7 @@ const InternalField: React.FC = (props) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { uiSchema } = useCollectionField();
|
||||
const component = useComponent(uiSchema?.['x-component']);
|
||||
const compile = useCompile();
|
||||
const setFieldProps = (key, value) => {
|
||||
field[key] = typeof field[key] === 'undefined' ? value : field[key];
|
||||
};
|
||||
@ -24,6 +25,7 @@ const InternalField: React.FC = (props) => {
|
||||
if (!uiSchema) {
|
||||
return;
|
||||
}
|
||||
console.log('uiSchema', uiSchema);
|
||||
setFieldProps('title', uiSchema.title);
|
||||
setFieldProps('description', uiSchema.description);
|
||||
setFieldProps('initialValue', uiSchema.default);
|
||||
|
@ -32,7 +32,7 @@ export const RemoteCollectionManagerProvider = (props: any) => {
|
||||
resource: 'collections',
|
||||
action: 'list',
|
||||
params: {
|
||||
pageSize: 999,
|
||||
paginate: false,
|
||||
appends: ['fields', 'fields.uiSchema'],
|
||||
},
|
||||
};
|
||||
|
@ -1,18 +1,29 @@
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { ArrayTable } from '@formily/antd';
|
||||
import { ISchema } from '@formily/react';
|
||||
import { ISchema, useForm } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import { Button, Dropdown, Menu } from 'antd';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import React, { useState } from 'react';
|
||||
import { useRequest } from '../../api-client';
|
||||
import { ActionContext, SchemaComponent, useCompile } from '../../schema-component';
|
||||
import { useCreateAction } from '../action-hooks';
|
||||
import { useCollectionManager } from '../hooks';
|
||||
import { IField } from '../interfaces/types';
|
||||
import { ArrayTable } from './ArrayTable';
|
||||
import { options } from './interfaces';
|
||||
|
||||
const getSchema = (schema: IField): ISchema => {
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
const properties = cloneDeep(schema.properties) as any;
|
||||
const initialValue = {
|
||||
...cloneDeep(schema.default),
|
||||
interface: schema.name,
|
||||
name: `f_${uid()}`,
|
||||
};
|
||||
initialValue.uiSchema.title = schema.title;
|
||||
console.log('initialValue', initialValue);
|
||||
return {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@ -21,16 +32,20 @@ const getSchema = (schema: IField): ISchema => {
|
||||
'x-component': 'Action.Drawer',
|
||||
'x-decorator': 'Form',
|
||||
'x-decorator-props': {
|
||||
initialValue: {
|
||||
interface: schema.name,
|
||||
...schema.default,
|
||||
name: `f_${uid()}`,
|
||||
useValues(options) {
|
||||
return useRequest(
|
||||
() =>
|
||||
Promise.resolve({
|
||||
data: initialValue,
|
||||
}),
|
||||
options,
|
||||
);
|
||||
},
|
||||
},
|
||||
title: '{{ t("Add field") }}',
|
||||
properties: {
|
||||
// @ts-ignore
|
||||
...schema.properties,
|
||||
...properties,
|
||||
footer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Drawer.Footer',
|
||||
@ -47,7 +62,7 @@ const getSchema = (schema: IField): ISchema => {
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
useAction: '{{ cm.useCreateActionAndRefreshCM }}',
|
||||
useAction: '{{ useCreateCollectionField }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -58,6 +73,30 @@ const getSchema = (schema: IField): ISchema => {
|
||||
};
|
||||
};
|
||||
|
||||
const useCreateCollectionField = () => {
|
||||
const form = useForm();
|
||||
const { run } = useCreateAction();
|
||||
const { refreshCM } = useCollectionManager();
|
||||
return {
|
||||
async run() {
|
||||
await form.submit();
|
||||
const options = form?.values?.uiSchema?.enum?.slice() || [];
|
||||
form.setValuesIn(
|
||||
'uiSchema.enum',
|
||||
options.map((option) => {
|
||||
return {
|
||||
value: uid(),
|
||||
...option,
|
||||
};
|
||||
}),
|
||||
);
|
||||
console.log('form.values', form.values);
|
||||
await run();
|
||||
await refreshCM();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const AddFieldAction = () => {
|
||||
const { getInterface } = useCollectionManager();
|
||||
const [visible, setVisible] = useState(false);
|
||||
@ -78,7 +117,6 @@ export const AddFieldAction = () => {
|
||||
return (
|
||||
<Menu.SubMenu title={compile(option.label)}>
|
||||
{option.children.map((child) => {
|
||||
console.log(child);
|
||||
return <Menu.Item key={child.name}>{compile(child.title)}</Menu.Item>;
|
||||
})}
|
||||
</Menu.SubMenu>
|
||||
@ -91,7 +129,7 @@ export const AddFieldAction = () => {
|
||||
添加字段
|
||||
</Button>
|
||||
</Dropdown>
|
||||
<SchemaComponent schema={schema} components={{ ArrayTable }} />
|
||||
<SchemaComponent schema={schema} components={{ ArrayTable }} scope={{ useCreateCollectionField }} />
|
||||
</ActionContext.Provider>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,25 @@
|
||||
import { ArrayBase, ArrayTable as FormilyArrayTable } from '@formily/antd';
|
||||
import { connect } from '@formily/react';
|
||||
import React, { Fragment } from 'react';
|
||||
|
||||
export const ArrayTable: any = connect((props) => {
|
||||
const { onChange } = props;
|
||||
return (
|
||||
<FormilyArrayTable
|
||||
{...props}
|
||||
onChange={(value) => {
|
||||
console.log('onChange', value);
|
||||
onChange(value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
ArrayTable.displayName = 'ArrayTable'
|
||||
|
||||
ArrayTable.Column = () => {
|
||||
return <Fragment />
|
||||
}
|
||||
|
||||
ArrayBase.mixin(ArrayTable)
|
||||
|
@ -23,6 +23,8 @@ const useCollectionValues = (options) => {
|
||||
Promise.resolve({
|
||||
data: {
|
||||
name: `t_${uid()}`,
|
||||
createdBy: true,
|
||||
updatedBy: true,
|
||||
},
|
||||
}),
|
||||
{
|
||||
@ -38,7 +40,7 @@ const useCollectionValues = (options) => {
|
||||
}, [visible]);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
export const ConfigurationTable = () => {
|
||||
const { collections = [] } = useCollectionManager();
|
||||
@ -50,7 +52,7 @@ export const ConfigurationTable = () => {
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<SchemaComponent schema={collectionSchema} scope={{ useCollectionValues, useAsyncDataSource, loadCollections }}/>
|
||||
<SchemaComponent schema={collectionSchema} scope={{ useCollectionValues, useAsyncDataSource, loadCollections }} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,18 +1,22 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
import { ISchema, useForm } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAPIClient } from '../../api-client';
|
||||
import { useAPIClient, useRequest } from '../../api-client';
|
||||
import { useRecord } from '../../record-provider';
|
||||
import { ActionContext, SchemaComponent } from '../../schema-component';
|
||||
import { useUpdateAction } from '../action-hooks';
|
||||
import { useCollectionManager } from '../hooks';
|
||||
import { IField } from '../interfaces/types';
|
||||
import { ArrayTable } from './ArrayTable';
|
||||
|
||||
const getSchema = (schema: IField): ISchema => {
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
const properties = cloneDeep(schema.properties) as any;
|
||||
properties.name['x-disabled'] = true;
|
||||
return {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@ -21,12 +25,20 @@ const getSchema = (schema: IField): ISchema => {
|
||||
'x-component': 'Action.Drawer',
|
||||
'x-decorator': 'Form',
|
||||
'x-decorator-props': {
|
||||
initialValue: cloneDeep(schema.default),
|
||||
useValues(options) {
|
||||
return useRequest(
|
||||
() =>
|
||||
Promise.resolve({
|
||||
data: cloneDeep(schema.default),
|
||||
}),
|
||||
options,
|
||||
);
|
||||
},
|
||||
},
|
||||
title: '{{ t("Edit field") }}',
|
||||
properties: {
|
||||
// @ts-ignore
|
||||
...schema.properties,
|
||||
...properties,
|
||||
footer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Drawer.Footer',
|
||||
@ -43,7 +55,7 @@ const getSchema = (schema: IField): ISchema => {
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
useAction: '{{ cm.useUpdateActionAndRefreshCM }}',
|
||||
useAction: '{{ useUpdateCollectionField }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -54,6 +66,29 @@ const getSchema = (schema: IField): ISchema => {
|
||||
};
|
||||
};
|
||||
|
||||
const useUpdateCollectionField = () => {
|
||||
const form = useForm();
|
||||
const { run } = useUpdateAction();
|
||||
const { refreshCM } = useCollectionManager();
|
||||
return {
|
||||
async run() {
|
||||
await form.submit();
|
||||
const options = form?.values?.uiSchema?.enum?.slice() || [];
|
||||
form.setValuesIn(
|
||||
'uiSchema.enum',
|
||||
options.map((option) => {
|
||||
return {
|
||||
value: uid(),
|
||||
...option,
|
||||
};
|
||||
}),
|
||||
);
|
||||
await run();
|
||||
await refreshCM();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const EditFieldAction = (props) => {
|
||||
const record = useRecord();
|
||||
const { getInterface } = useCollectionManager();
|
||||
@ -79,7 +114,7 @@ export const EditFieldAction = (props) => {
|
||||
>
|
||||
{t('Edit')}
|
||||
</a>
|
||||
<SchemaComponent schema={schema} />
|
||||
<SchemaComponent schema={schema} components={{ ArrayTable }} scope={{ useUpdateCollectionField }} />
|
||||
</ActionContext.Provider>
|
||||
);
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useContext } from 'react';
|
||||
import { useAPIClient } from '../../api-client';
|
||||
import { useRecord } from '../../record-provider';
|
||||
import { useCompile } from '../../schema-component';
|
||||
import { CollectionFieldContext } from '../context';
|
||||
import { useCollection } from './useCollection';
|
||||
|
||||
@ -8,6 +9,7 @@ export const useCollectionField = () => {
|
||||
const collection = useCollection();
|
||||
const record = useRecord();
|
||||
const api = useAPIClient();
|
||||
const compile = useCompile();
|
||||
const ctx = useContext(CollectionFieldContext);
|
||||
if (!ctx) {
|
||||
return {} as any;
|
||||
@ -16,6 +18,7 @@ export const useCollectionField = () => {
|
||||
const resource = api?.resource(resourceName, record[ctx.sourceKey]);
|
||||
return {
|
||||
...ctx,
|
||||
uiSchema: compile(ctx.uiSchema),
|
||||
resource,
|
||||
};
|
||||
};
|
||||
|
@ -16,8 +16,9 @@ export const attachment: IField = {
|
||||
type: 'array',
|
||||
// title,
|
||||
'x-component': 'Upload.Attachment',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-designable-bar': 'Upload.DesignableBar',
|
||||
'x-component-props': {
|
||||
action: 'attachments:upload',
|
||||
},
|
||||
},
|
||||
},
|
||||
initialize: (values: any) => {
|
||||
|
@ -20,6 +20,8 @@ export const chinaRegion: IField = {
|
||||
// title,
|
||||
'x-component': 'Cascader',
|
||||
'x-component-props': {
|
||||
useDataSource: '{{ useChinaRegionDataSource }}',
|
||||
useLoadData: '{{ useChinaRegionLoadData }}',
|
||||
changeOnSelectLast: false,
|
||||
labelInValue: true,
|
||||
maxLevel: 3,
|
||||
|
@ -15,12 +15,12 @@ export const linkTo: IField = {
|
||||
uiSchema: {
|
||||
type: 'array',
|
||||
// title,
|
||||
'x-component': 'Select.Drawer',
|
||||
'x-component': 'RecordPicker',
|
||||
'x-component-props': {},
|
||||
},
|
||||
},
|
||||
initialize: (values: any) => {
|
||||
if (values.dataType === 'belongsToMany') {
|
||||
if (values.type === 'belongsToMany') {
|
||||
if (!values.through) {
|
||||
values.through = `t_${uid()}`;
|
||||
}
|
||||
@ -34,11 +34,7 @@ export const linkTo: IField = {
|
||||
values.sourceKey = 'id';
|
||||
}
|
||||
if (!values.targetKey) {
|
||||
if (values.target === 'roles') {
|
||||
values.targetKey = 'name';
|
||||
} else {
|
||||
values.targetKey = 'id';
|
||||
}
|
||||
values.targetKey = 'id';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -15,7 +15,7 @@ export const updatedBy: IField = {
|
||||
uiSchema: {
|
||||
type: 'object',
|
||||
title: '{{t("Last updated by")}}',
|
||||
'x-component': 'Select.Drawer',
|
||||
'x-component': 'RecordPicker',
|
||||
'x-component-props': {
|
||||
fieldNames: {
|
||||
value: 'id',
|
||||
|
@ -7,6 +7,7 @@ export * from './api-client';
|
||||
export * from './application';
|
||||
export * from './async-data-provider';
|
||||
export * from './board';
|
||||
export * from './china-region';
|
||||
export * from './collection-manager';
|
||||
export * from './document-title';
|
||||
export * from './i18n';
|
||||
|
@ -131,7 +131,7 @@
|
||||
"Created at": "创建日期",
|
||||
"Last updated at": "最后修改日期",
|
||||
"Created by": "创建人",
|
||||
"Last updated by": "最后更新日期",
|
||||
"Last updated by": "最后修改人",
|
||||
"Add field": "添加字段",
|
||||
"Field display name": "字段名称",
|
||||
"Field type": "字段类型",
|
||||
|
@ -5,10 +5,18 @@ import { toArr } from '@formily/shared';
|
||||
import { Cascader as AntdCascader } from 'antd';
|
||||
import { isBoolean, omit } from 'lodash';
|
||||
import React from 'react';
|
||||
import { useRequest } from '../../../api-client';
|
||||
import { defaultFieldNames } from './defaultFieldNames';
|
||||
import { ReadPretty } from './ReadPretty';
|
||||
|
||||
const useDefLoadData = (props: any) => props.loadData;
|
||||
const useDefDataSource = (options) => {
|
||||
const field = useField<ArrayField>();
|
||||
return useRequest(() => Promise.resolve({ data: field.dataSource || [] }), options);
|
||||
};
|
||||
|
||||
const useDefLoadData = (props: any) => {
|
||||
return props?.loadData;
|
||||
};
|
||||
|
||||
export const Cascader = connect(
|
||||
(props: any) => {
|
||||
@ -18,6 +26,7 @@ export const Cascader = connect(
|
||||
onChange,
|
||||
labelInValue,
|
||||
fieldNames = defaultFieldNames,
|
||||
useDataSource = useDefDataSource,
|
||||
useLoadData = useDefLoadData,
|
||||
changeOnSelectLast,
|
||||
changeOnSelect,
|
||||
@ -25,6 +34,11 @@ export const Cascader = connect(
|
||||
...others
|
||||
} = props;
|
||||
const loadData = useLoadData(props);
|
||||
const { loading } = useDataSource({
|
||||
onSuccess(data) {
|
||||
field.dataSource = data?.data || [];
|
||||
},
|
||||
});
|
||||
// 兼容值为 object[] 的情况
|
||||
const toValue = () => {
|
||||
return toArr(value).map((item) => {
|
||||
@ -54,17 +68,11 @@ export const Cascader = connect(
|
||||
return <span key={option[fieldNames.value]}>{option[fieldNames.label]} / </span>;
|
||||
});
|
||||
};
|
||||
// if (loadData) {
|
||||
// Object.assign(others, {
|
||||
// loadData: (selectedOptions: any[]) => {
|
||||
// // 将 field 传给 loadData
|
||||
// loadData(selectedOptions, field);
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
return (
|
||||
<AntdCascader
|
||||
loading={loading}
|
||||
{...others}
|
||||
options={field.dataSource}
|
||||
loadData={loadData}
|
||||
changeOnSelect={isBoolean(changeOnSelectLast) ? !changeOnSelectLast : changeOnSelect}
|
||||
value={toValue()}
|
||||
|
Loading…
Reference in New Issue
Block a user