feat: improve sub table
This commit is contained in:
parent
f5b2600640
commit
6d0978f2c2
@ -1,8 +1,8 @@
|
|||||||
import React, { createContext } from 'react';
|
|
||||||
import { I18nextProvider, useTranslation } from 'react-i18next';
|
|
||||||
import { ConfigProvider, Spin } from 'antd';
|
import { ConfigProvider, Spin } from 'antd';
|
||||||
import enUS from 'antd/lib/locale/en_US';
|
import enUS from 'antd/lib/locale/en_US';
|
||||||
import zhCN from 'antd/lib/locale/zh_CN';
|
import zhCN from 'antd/lib/locale/zh_CN';
|
||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useRequest } from '../api-client';
|
import { useRequest } from '../api-client';
|
||||||
|
|
||||||
export function AntdConfigProvider(props) {
|
export function AntdConfigProvider(props) {
|
||||||
@ -19,7 +19,7 @@ export function AntdConfigProvider(props) {
|
|||||||
i18n.changeLanguage(data?.data?.lang);
|
i18n.changeLanguage(data?.data?.lang);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
manual: !remoteLocale,
|
manual: true, // !remoteLocale,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (loading) {
|
if (loading) {
|
||||||
|
@ -2,7 +2,7 @@ import { Result } from 'ahooks/lib/useRequest/src/types';
|
|||||||
import React, { createContext, useContext } from 'react';
|
import React, { createContext, useContext } from 'react';
|
||||||
import { useRequest } from '../api-client';
|
import { useRequest } from '../api-client';
|
||||||
|
|
||||||
export const AsyncDataContext = createContext<Result<any, any>>(null);
|
export const AsyncDataContext = createContext<Result<any, any> & { state?: any; setState?: any }>(null);
|
||||||
|
|
||||||
export interface AsyncDataProviderProps {
|
export interface AsyncDataProviderProps {
|
||||||
value?: any;
|
value?: any;
|
||||||
|
@ -0,0 +1,138 @@
|
|||||||
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
|
import { ArrayTable } from '@formily/antd';
|
||||||
|
import { ISchema } 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, useActionContext, useCompile, useFormBlockContext } from '../../schema-component';
|
||||||
|
import { useCollectionManager } from '../hooks';
|
||||||
|
import { IField } from '../interfaces/types';
|
||||||
|
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: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Action.Drawer',
|
||||||
|
'x-decorator': 'Form',
|
||||||
|
'x-decorator-props': {
|
||||||
|
useValues(options) {
|
||||||
|
return useRequest(
|
||||||
|
() =>
|
||||||
|
Promise.resolve({
|
||||||
|
data: initialValue,
|
||||||
|
}),
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
title: '{{ t("Add field") }}',
|
||||||
|
properties: {
|
||||||
|
// @ts-ignore
|
||||||
|
...properties,
|
||||||
|
footer: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Action.Drawer.Footer',
|
||||||
|
properties: {
|
||||||
|
action1: {
|
||||||
|
title: '{{ t("Cancel") }}',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
useAction: '{{ cm.useCancelAction }}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
action2: {
|
||||||
|
title: '{{ t("Submit") }}',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'primary',
|
||||||
|
useAction: '{{ useCreateSubField }}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const useCreateSubField = () => {
|
||||||
|
// const form = useForm();
|
||||||
|
const { form, parent } = useFormBlockContext();
|
||||||
|
const ctx = useActionContext();
|
||||||
|
return {
|
||||||
|
async run() {
|
||||||
|
await form.submit();
|
||||||
|
const children = parent.form.values?.children?.slice?.();
|
||||||
|
children.push(cloneDeep(form.values));
|
||||||
|
console.log('form.values', form.values, children);
|
||||||
|
parent.form.setValuesIn('children', children);
|
||||||
|
ctx.setVisible(false);
|
||||||
|
// const options = form?.values?.uiSchema?.enum?.slice() || [];
|
||||||
|
// form.setValuesIn(
|
||||||
|
// 'uiSchema.enum',
|
||||||
|
// options.map((option) => {
|
||||||
|
// return {
|
||||||
|
// value: uid(),
|
||||||
|
// ...option,
|
||||||
|
// };
|
||||||
|
// }),
|
||||||
|
// );
|
||||||
|
// await run();
|
||||||
|
// await refreshCM();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AddSubFieldAction = () => {
|
||||||
|
const { getInterface } = useCollectionManager();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [schema, setSchema] = useState({});
|
||||||
|
const compile = useCompile();
|
||||||
|
return (
|
||||||
|
<ActionContext.Provider value={{ visible, setVisible }}>
|
||||||
|
<Dropdown
|
||||||
|
overlay={
|
||||||
|
<Menu
|
||||||
|
onClick={(info) => {
|
||||||
|
const schema = getSchema(getInterface(info.key));
|
||||||
|
setSchema(schema);
|
||||||
|
setVisible(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{options.map((option) => {
|
||||||
|
return (
|
||||||
|
<Menu.SubMenu title={compile(option.label)}>
|
||||||
|
{option.children.map((child) => {
|
||||||
|
return <Menu.Item key={child.name}>{compile(child.title)}</Menu.Item>;
|
||||||
|
})}
|
||||||
|
</Menu.SubMenu>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button icon={<PlusOutlined />} type={'primary'}>
|
||||||
|
添加字段
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
|
<SchemaComponent schema={schema} components={{ ArrayTable }} scope={{ useCreateSubField }} />
|
||||||
|
</ActionContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
@ -1,9 +1,13 @@
|
|||||||
|
import { useForm } from '@formily/react';
|
||||||
import { action } from '@formily/reactive';
|
import { action } from '@formily/reactive';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useRequest } from '../../api-client';
|
import { useRequest } from '../../api-client';
|
||||||
|
import { useRecord } from '../../record-provider';
|
||||||
import { SchemaComponent, useActionContext } from '../../schema-component';
|
import { SchemaComponent, useActionContext } from '../../schema-component';
|
||||||
import { useCollectionManager } from '../hooks/useCollectionManager';
|
import { useCollectionManager } from '../hooks/useCollectionManager';
|
||||||
|
import { AddSubFieldAction } from './AddSubFieldAction';
|
||||||
|
import { EditSubFieldAction } from './EditSubFieldAction';
|
||||||
import { collectionSchema } from './schemas/collections';
|
import { collectionSchema } from './schemas/collections';
|
||||||
|
|
||||||
const useAsyncDataSource = (service: any) => (field: any) => {
|
const useAsyncDataSource = (service: any) => (field: any) => {
|
||||||
@ -42,6 +46,33 @@ const useCollectionValues = (options) => {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const useSelectedRowKeys = () => {
|
||||||
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||||
|
return [selectedRowKeys, setSelectedRowKeys];
|
||||||
|
};
|
||||||
|
|
||||||
|
const useDestroySubField = () => {
|
||||||
|
const record = useRecord();
|
||||||
|
const form = useForm();
|
||||||
|
return {
|
||||||
|
async run() {
|
||||||
|
const children = form.values?.children?.slice?.();
|
||||||
|
form.setValuesIn(
|
||||||
|
'children',
|
||||||
|
children.filter((child) => {
|
||||||
|
return child.name !== record.name;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const useBulkDestroySubField = () => {
|
||||||
|
return {
|
||||||
|
async run() {},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const ConfigurationTable = () => {
|
export const ConfigurationTable = () => {
|
||||||
const { collections = [] } = useCollectionManager();
|
const { collections = [] } = useCollectionManager();
|
||||||
const loadCollections = async (field: any) => {
|
const loadCollections = async (field: any) => {
|
||||||
@ -52,7 +83,21 @@ export const ConfigurationTable = () => {
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SchemaComponent schema={collectionSchema} scope={{ useCollectionValues, useAsyncDataSource, loadCollections }} />
|
<SchemaComponent
|
||||||
|
schema={collectionSchema}
|
||||||
|
components={{
|
||||||
|
AddSubFieldAction,
|
||||||
|
EditSubFieldAction,
|
||||||
|
}}
|
||||||
|
scope={{
|
||||||
|
useDestroySubField,
|
||||||
|
useBulkDestroySubField,
|
||||||
|
useSelectedRowKeys,
|
||||||
|
useCollectionValues,
|
||||||
|
useAsyncDataSource,
|
||||||
|
loadCollections,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -102,7 +102,7 @@ export const EditFieldAction = (props) => {
|
|||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const { data } = await api.resource('collections.fields', record.collectionName).get({
|
const { data } = await api.resource('collections.fields', record.collectionName).get({
|
||||||
filterByTk: record.name,
|
filterByTk: record.name,
|
||||||
appends: ['uiSchema'],
|
appends: record.interface === 'subTable' ? ['uiSchema', 'children'] : ['uiSchema'],
|
||||||
});
|
});
|
||||||
const schema = getSchema({
|
const schema = getSchema({
|
||||||
...getInterface(record.interface),
|
...getInterface(record.interface),
|
||||||
|
@ -0,0 +1,119 @@
|
|||||||
|
import { ArrayTable } from '@formily/antd';
|
||||||
|
import { ISchema } 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, useRequest } from '../../api-client';
|
||||||
|
import { useRecord } from '../../record-provider';
|
||||||
|
import { ActionContext, SchemaComponent, useActionContext, useFormBlockContext } from '../../schema-component';
|
||||||
|
import { useCollectionManager } from '../hooks';
|
||||||
|
import { IField } from '../interfaces/types';
|
||||||
|
|
||||||
|
const getSchema = (schema: IField): ISchema => {
|
||||||
|
if (!schema) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const properties = cloneDeep(schema.properties) as any;
|
||||||
|
properties.name['x-disabled'] = true;
|
||||||
|
return {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Action.Drawer',
|
||||||
|
'x-decorator': 'Form',
|
||||||
|
'x-decorator-props': {
|
||||||
|
useValues(options) {
|
||||||
|
return useRequest(
|
||||||
|
() =>
|
||||||
|
Promise.resolve({
|
||||||
|
data: cloneDeep(schema.default),
|
||||||
|
}),
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
title: '{{ t("Edit field") }}',
|
||||||
|
properties: {
|
||||||
|
// @ts-ignore
|
||||||
|
...properties,
|
||||||
|
footer: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Action.Drawer.Footer',
|
||||||
|
properties: {
|
||||||
|
action1: {
|
||||||
|
title: '{{ t("Cancel") }}',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
useAction: '{{ cm.useCancelAction }}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
action2: {
|
||||||
|
title: '{{ t("Submit") }}',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'primary',
|
||||||
|
useAction: '{{ useUpdateSubField }}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const useUpdateSubField = () => {
|
||||||
|
const { form, parent } = useFormBlockContext();
|
||||||
|
const ctx = useActionContext();
|
||||||
|
return {
|
||||||
|
async run() {
|
||||||
|
await form.submit();
|
||||||
|
const children = parent.form.values?.children?.slice?.();
|
||||||
|
parent.form.setValuesIn(
|
||||||
|
'children',
|
||||||
|
children.map((child) => {
|
||||||
|
if (child.name === form.values.name) {
|
||||||
|
return cloneDeep(form.values);
|
||||||
|
}
|
||||||
|
return child;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
ctx.setVisible(false);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EditSubFieldAction = (props) => {
|
||||||
|
const record = useRecord();
|
||||||
|
const { getInterface } = useCollectionManager();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [schema, setSchema] = useState({});
|
||||||
|
const api = useAPIClient();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<ActionContext.Provider value={{ visible, setVisible }}>
|
||||||
|
<a
|
||||||
|
onClick={async () => {
|
||||||
|
// const { data } = await api.resource('fields.children', record.key).get({
|
||||||
|
// filterByTk: record.name,
|
||||||
|
// appends: ['uiSchema'],
|
||||||
|
// });
|
||||||
|
const schema = getSchema({
|
||||||
|
...getInterface(record.interface),
|
||||||
|
default: {
|
||||||
|
...record,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setSchema(schema);
|
||||||
|
setVisible(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Edit')}
|
||||||
|
</a>
|
||||||
|
<SchemaComponent schema={schema} components={{ ArrayTable }} scope={{ useUpdateSubField }} />
|
||||||
|
</ActionContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
@ -31,6 +31,132 @@ export const subTable: IField = {
|
|||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
...defaultProps,
|
...defaultProps,
|
||||||
|
subtable: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'div',
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
marginBottom: 16,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
delete: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Delete") }}',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
useAction: '{{ useBulkDestroySubField }}',
|
||||||
|
confirm: {
|
||||||
|
title: "{{t('Delete record')}}",
|
||||||
|
content: "{{t('Are you sure you want to delete it?')}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Add new") }}',
|
||||||
|
'x-component': 'AddSubFieldAction',
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'primary',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
children: {
|
||||||
|
type: 'array',
|
||||||
|
title: '{{t("Fields")}}',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Table.Array',
|
||||||
|
'x-component-props': {
|
||||||
|
pagination: false,
|
||||||
|
rowKey: 'name',
|
||||||
|
rowSelection: {
|
||||||
|
type: 'checkbox',
|
||||||
|
},
|
||||||
|
useSelectedRowKeys: '{{ useSelectedRowKeys }}',
|
||||||
|
// scroll: { x: '100%' },
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
column1: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Field display name") }}',
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
properties: {
|
||||||
|
'uiSchema.title': {
|
||||||
|
type: 'number',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
column2: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Field name") }}',
|
||||||
|
'x-decorator': 'Table.Column.Decorator',
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
column3: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Field interface") }}',
|
||||||
|
'x-decorator': 'Table.Column.Decorator',
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
properties: {
|
||||||
|
interface: {
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
column4: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Actions") }}',
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Space',
|
||||||
|
'x-component-props': {
|
||||||
|
split: '|',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
update: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Edit") }}',
|
||||||
|
'x-component': 'EditSubFieldAction',
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'primary',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Delete") }}',
|
||||||
|
'x-component': 'Action.Link',
|
||||||
|
'x-component-props': {
|
||||||
|
useAction: '{{ useDestroySubField }}',
|
||||||
|
confirm: {
|
||||||
|
title: "{{t('Delete record')}}",
|
||||||
|
content: "{{t('Are you sure you want to delete it?')}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
// children: {
|
// children: {
|
||||||
// type: 'array',
|
// type: 'array',
|
||||||
// title: '{{t("Sub-table fields")}}',
|
// title: '{{t("Sub-table fields")}}',
|
||||||
|
@ -104,6 +104,7 @@
|
|||||||
"Edit": "编辑",
|
"Edit": "编辑",
|
||||||
"Edit collection": "编辑数据表",
|
"Edit collection": "编辑数据表",
|
||||||
"Configure fields": "配置字段",
|
"Configure fields": "配置字段",
|
||||||
|
"Configure columns": "配置字段",
|
||||||
"Edit field": "编辑字段",
|
"Edit field": "编辑字段",
|
||||||
"Configure fields of {{title}}": "「{{title}}」的字段配置",
|
"Configure fields of {{title}}": "「{{title}}」的字段配置",
|
||||||
"Basic": "基本类型",
|
"Basic": "基本类型",
|
||||||
|
@ -3,7 +3,7 @@ import { createForm } from '@formily/core';
|
|||||||
import { FieldContext, FormContext, observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
import { FieldContext, FormContext, observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
||||||
import { Options, Result } from 'ahooks/lib/useRequest/src/types';
|
import { Options, Result } from 'ahooks/lib/useRequest/src/types';
|
||||||
import { Spin } from 'antd';
|
import { Spin } from 'antd';
|
||||||
import React, { useMemo } from 'react';
|
import React, { createContext, useContext, useMemo } from 'react';
|
||||||
import { useAttach, useComponent } from '../..';
|
import { useAttach, useComponent } from '../..';
|
||||||
import { useRequest } from '../../../api-client';
|
import { useRequest } from '../../../api-client';
|
||||||
import { useCollection } from '../../../collection-manager';
|
import { useCollection } from '../../../collection-manager';
|
||||||
@ -75,9 +75,17 @@ const useDef = (opts: any = {}, props: FormProps = {}) => {
|
|||||||
return useRequest(useRequestProps(props), opts);
|
return useRequest(useRequestProps(props), opts);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const FormBlockContext = createContext<any>(null);
|
||||||
|
|
||||||
|
export const useFormBlockContext = () => {
|
||||||
|
const ctx = useContext(FormBlockContext);
|
||||||
|
return ctx;
|
||||||
|
};
|
||||||
|
|
||||||
export const Form: React.FC<FormProps> & { Designer?: any } = observer((props) => {
|
export const Form: React.FC<FormProps> & { Designer?: any } = observer((props) => {
|
||||||
const { request, effects, initialValue, useValues = useDef, ...others } = props;
|
const { request, effects, initialValue, useValues = useDef, ...others } = props;
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
|
const field = useField();
|
||||||
const form = useMemo(() => createForm({ effects }), []);
|
const form = useMemo(() => createForm({ effects }), []);
|
||||||
const result = useValues(
|
const result = useValues(
|
||||||
{
|
{
|
||||||
@ -89,14 +97,17 @@ export const Form: React.FC<FormProps> & { Designer?: any } = observer((props) =
|
|||||||
},
|
},
|
||||||
props,
|
props,
|
||||||
);
|
);
|
||||||
|
const parent = useContext(FormBlockContext);
|
||||||
return (
|
return (
|
||||||
<Spin spinning={result?.loading || false}>
|
<FormBlockContext.Provider value={{ parent, form, result, field, fieldSchema }}>
|
||||||
{fieldSchema['x-decorator'] === 'Form' ? (
|
<Spin spinning={result?.loading || false}>
|
||||||
<FormDecorator form={form} {...others} />
|
{fieldSchema['x-decorator'] === 'Form' ? (
|
||||||
) : (
|
<FormDecorator form={form} {...others} />
|
||||||
<FormComponent form={form} {...others} />
|
) : (
|
||||||
)}
|
<FormComponent form={form} {...others} />
|
||||||
</Spin>
|
)}
|
||||||
|
</Spin>
|
||||||
|
</FormBlockContext.Provider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { ArrayField } from '@formily/core';
|
import { ArrayField, Field } from '@formily/core';
|
||||||
import { observer, RecursionField, Schema, useField, useFieldSchema } from '@formily/react';
|
import { observer, RecursionField, Schema, useField, useFieldSchema } from '@formily/react';
|
||||||
import { Table, TableColumnProps } from 'antd';
|
import { Table, TableColumnProps } from 'antd';
|
||||||
import cls from 'classnames';
|
import cls from 'classnames';
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { DndContext } from '../..';
|
import { DndContext } from '../..';
|
||||||
import { RecordProvider, useSchemaInitializer } from '../../../';
|
import { RecordProvider, useRequest, useSchemaInitializer } from '../../../';
|
||||||
|
|
||||||
const isColumnComponent = (schema: Schema) => {
|
const isColumnComponent = (schema: Schema) => {
|
||||||
return schema['x-component']?.endsWith('.Column') > -1;
|
return schema['x-component']?.endsWith('.Column') > -1;
|
||||||
@ -83,13 +83,45 @@ export const components = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const useDef = () => {
|
||||||
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||||
|
return [selectedRowKeys, setSelectedRowKeys];
|
||||||
|
};
|
||||||
|
|
||||||
|
const useDefDataSource = (options, props) => {
|
||||||
|
const field = useField<Field>();
|
||||||
|
return useRequest(() => {
|
||||||
|
return Promise.resolve({
|
||||||
|
data: field.value,
|
||||||
|
});
|
||||||
|
}, options);
|
||||||
|
};
|
||||||
|
|
||||||
export const TableArray: React.FC<any> = observer((props) => {
|
export const TableArray: React.FC<any> = observer((props) => {
|
||||||
const field = useField<ArrayField>();
|
const field = useField<ArrayField>();
|
||||||
const columns = useTableColumns();
|
const columns = useTableColumns();
|
||||||
const { onChange, ...others } = props;
|
const { useSelectedRowKeys = useDef, useDataSource = useDefDataSource, onChange, ...others } = props;
|
||||||
|
const [selectedRowKeys, setSelectedRowKeys] = useSelectedRowKeys();
|
||||||
|
useDataSource({
|
||||||
|
onSuccess(data) {
|
||||||
|
field.value = data?.data || [];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const restProps = {
|
||||||
|
rowSelection: props.rowSelection
|
||||||
|
? {
|
||||||
|
type: 'checkbox',
|
||||||
|
selectedRowKeys,
|
||||||
|
onChange(selectedRowKeys: any[]) {
|
||||||
|
setSelectedRowKeys(selectedRowKeys);
|
||||||
|
},
|
||||||
|
...props.rowSelection,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Table {...others} components={components} columns={columns} dataSource={field.value?.slice()} />
|
<Table {...others} {...restProps} components={components} columns={columns} dataSource={field.value?.slice()} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { useField, useFieldSchema } from '@formily/react';
|
import { useField, useFieldSchema } from '@formily/react';
|
||||||
import React, { useLayoutEffect } from 'react';
|
import React, { useLayoutEffect } from 'react';
|
||||||
import { designerCss, SortableItem, useCollection, useDesignable, useDesigner } from '../../../';
|
import { SortableItem, useCollection, useCompile, useDesignable, useDesigner } from '../../../';
|
||||||
|
import { designerCss } from './Table.Column.ActionBar';
|
||||||
|
|
||||||
export const useColumnSchema = () => {
|
export const useColumnSchema = () => {
|
||||||
const { getField } = useCollection();
|
const { getField } = useCollection();
|
||||||
|
const compile = useCompile();
|
||||||
const columnSchema = useFieldSchema();
|
const columnSchema = useFieldSchema();
|
||||||
const fieldSchema = columnSchema.reduceProperties((buf, s) => {
|
const fieldSchema = columnSchema.reduceProperties((buf, s) => {
|
||||||
if (s['x-component'] === 'CollectionField') {
|
if (s['x-component'] === 'CollectionField') {
|
||||||
@ -15,14 +17,15 @@ export const useColumnSchema = () => {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const collectionField = getField(fieldSchema.name);
|
const collectionField = getField(fieldSchema.name);
|
||||||
return { columnSchema, fieldSchema, collectionField };
|
return { columnSchema, fieldSchema, uiSchema: compile(collectionField?.uiSchema) };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TableColumnDecorator = (props) => {
|
export const TableColumnDecorator = (props) => {
|
||||||
const Designer = useDesigner();
|
const Designer = useDesigner();
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const { fieldSchema, collectionField } = useColumnSchema();
|
const { fieldSchema, uiSchema } = useColumnSchema();
|
||||||
const { refresh } = useDesignable();
|
const { refresh } = useDesignable();
|
||||||
|
const compile = useCompile();
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (field.title) {
|
if (field.title) {
|
||||||
return;
|
return;
|
||||||
@ -30,16 +33,16 @@ export const TableColumnDecorator = (props) => {
|
|||||||
if (!fieldSchema) {
|
if (!fieldSchema) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (collectionField?.uiSchema?.title) {
|
if (uiSchema?.title) {
|
||||||
field.title = collectionField?.uiSchema?.title;
|
field.title = uiSchema?.title;
|
||||||
}
|
}
|
||||||
}, [collectionField?.uiSchema?.title]);
|
}, [uiSchema?.title]);
|
||||||
console.log('field.title', collectionField?.uiSchema?.title, field.title);
|
console.log('field.title', uiSchema?.title, field.title);
|
||||||
return (
|
return (
|
||||||
<SortableItem className={designerCss}>
|
<SortableItem className={designerCss}>
|
||||||
<Designer />
|
<Designer />
|
||||||
{/* <RecursionField name={columnSchema.name} schema={columnSchema}/> */}
|
{/* <RecursionField name={columnSchema.name} schema={columnSchema}/> */}
|
||||||
{field.title || collectionField?.uiSchema?.title}
|
{field.title || compile(uiSchema?.title)}
|
||||||
{/* <div
|
{/* <div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
field.title = uid();
|
field.title = uid();
|
||||||
|
@ -4,12 +4,13 @@ import { Options, Result } from 'ahooks/lib/useRequest/src/types';
|
|||||||
import { TablePaginationConfig, TableProps } from 'antd';
|
import { TablePaginationConfig, TableProps } from 'antd';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { AsyncDataProvider, useRequest } from '../../../';
|
import { AsyncDataProvider, useAsyncData, useRequest } from '../../../';
|
||||||
import { useAttach } from '../../hooks';
|
import { useAttach } from '../../hooks';
|
||||||
import { TableArray } from './Table.Array';
|
import { TableArray } from './Table.Array';
|
||||||
|
|
||||||
type TableVoidProps = TableProps<any> & {
|
type TableVoidProps = TableProps<any> & {
|
||||||
request?: any;
|
request?: any;
|
||||||
|
useSelectedRowKeys?: any;
|
||||||
useDataSource?: (
|
useDataSource?: (
|
||||||
options?: Options<any, any> & { uid?: string },
|
options?: Options<any, any> & { uid?: string },
|
||||||
props?: any,
|
props?: any,
|
||||||
@ -69,8 +70,13 @@ const useDef = (options, props) => {
|
|||||||
return useRequest(useRequestProps(props), options);
|
return useRequest(useRequestProps(props), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const useDefSelectedRowKeys = () => {
|
||||||
|
const result = useAsyncData();
|
||||||
|
return [result?.state?.selectedRowKeys, (selectedRowKeys) => result?.setState?.({ selectedRowKeys })];
|
||||||
|
};
|
||||||
|
|
||||||
export const TableVoid: React.FC<TableVoidProps> = observer((props) => {
|
export const TableVoid: React.FC<TableVoidProps> = observer((props) => {
|
||||||
const { useDataSource = useDef } = props;
|
const { useDataSource = useDef, useSelectedRowKeys = useDefSelectedRowKeys } = props;
|
||||||
const field = useField<Field>();
|
const field = useField<Field>();
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const form = useMemo(() => createForm(), []);
|
const form = useMemo(() => createForm(), []);
|
||||||
@ -96,23 +102,16 @@ export const TableVoid: React.FC<TableVoidProps> = observer((props) => {
|
|||||||
},
|
},
|
||||||
props,
|
props,
|
||||||
);
|
);
|
||||||
const others = {
|
|
||||||
rowSelection: props.rowSelection
|
|
||||||
? {
|
|
||||||
type: 'checkbox',
|
|
||||||
...props.rowSelection,
|
|
||||||
selectedRowKeys: result?.state?.selectedRowKeys || [],
|
|
||||||
onChange(selectedRowKeys: any[]) {
|
|
||||||
result?.setState?.({ selectedRowKeys });
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<AsyncDataProvider value={result}>
|
<AsyncDataProvider value={result}>
|
||||||
<FormContext.Provider value={form}>
|
<FormContext.Provider value={form}>
|
||||||
<FieldContext.Provider value={f}>
|
<FieldContext.Provider value={f}>
|
||||||
<TableArray {...props} {...others} loading={result?.loading} pagination={usePaginationProps(props, result)} />
|
<TableArray
|
||||||
|
{...props}
|
||||||
|
useSelectedRowKeys={useSelectedRowKeys}
|
||||||
|
loading={result?.loading}
|
||||||
|
pagination={usePaginationProps(props, result)}
|
||||||
|
/>
|
||||||
</FieldContext.Provider>
|
</FieldContext.Provider>
|
||||||
</FormContext.Provider>
|
</FormContext.Provider>
|
||||||
</AsyncDataProvider>
|
</AsyncDataProvider>
|
||||||
|
181
packages/client/src/schema-component/antd/table/demos/demo4.tsx
Normal file
181
packages/client/src/schema-component/antd/table/demos/demo4.tsx
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
import { ISchema, observer } from '@formily/react';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
import {
|
||||||
|
Action,
|
||||||
|
Input,
|
||||||
|
SchemaComponent,
|
||||||
|
SchemaComponentProvider,
|
||||||
|
Table,
|
||||||
|
useRecord,
|
||||||
|
useRequest
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import React, { createContext, useContext, useState } from 'react';
|
||||||
|
|
||||||
|
const DataSourceContext = createContext(null);
|
||||||
|
|
||||||
|
const schema: ISchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
context: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Context',
|
||||||
|
properties: {
|
||||||
|
action1: {
|
||||||
|
title: '提交',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
useAction() {
|
||||||
|
const ctx = useContext(DataSourceContext);
|
||||||
|
return {
|
||||||
|
async run() {
|
||||||
|
console.log(ctx.dataSource);
|
||||||
|
const dataSource = ctx.dataSource || [];
|
||||||
|
dataSource.push({
|
||||||
|
id: uid(),
|
||||||
|
name: uid(),
|
||||||
|
});
|
||||||
|
ctx.setDataSource([...dataSource]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
action2: {
|
||||||
|
title: '删除',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
useAction() {
|
||||||
|
const ctx = useContext(DataSourceContext);
|
||||||
|
const { selectedRowKeys, setSelectedRowKeys } = ctx;
|
||||||
|
return {
|
||||||
|
async run() {
|
||||||
|
const dataSource = ctx.dataSource || [];
|
||||||
|
ctx.setDataSource(
|
||||||
|
dataSource.filter((item) => {
|
||||||
|
return !selectedRowKeys.includes(item.id);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
setSelectedRowKeys([]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
type: 'array',
|
||||||
|
title: `编辑模式`,
|
||||||
|
'x-component': 'Table.Array',
|
||||||
|
'x-component-props': {
|
||||||
|
rowKey: 'id',
|
||||||
|
rowSelection: {
|
||||||
|
type: 'checkbox',
|
||||||
|
},
|
||||||
|
useSelectedRowKeys() {
|
||||||
|
const ctx = useContext(DataSourceContext);
|
||||||
|
return [ctx.selectedRowKeys, ctx.setSelectedRowKeys];
|
||||||
|
},
|
||||||
|
useDataSource(options) {
|
||||||
|
const ctx = useContext(DataSourceContext);
|
||||||
|
return useRequest(
|
||||||
|
() => {
|
||||||
|
console.log('ctx.dataSource', ctx.dataSource);
|
||||||
|
return Promise.resolve({
|
||||||
|
data: ctx.dataSource,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...options,
|
||||||
|
refreshDeps: [JSON.stringify(ctx.dataSource)],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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: {
|
||||||
|
action: {
|
||||||
|
title: '删除',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
useAction() {
|
||||||
|
const record = useRecord();
|
||||||
|
const ctx = useContext(DataSourceContext);
|
||||||
|
return {
|
||||||
|
async run() {
|
||||||
|
const dataSource = ctx.dataSource || [];
|
||||||
|
ctx.setDataSource(
|
||||||
|
dataSource.filter((item) => {
|
||||||
|
return record.id !== item.id;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const Context = observer((props) => {
|
||||||
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||||
|
const [dataSource, setDataSource] = useState([]);
|
||||||
|
const service = useRequest(
|
||||||
|
() => {
|
||||||
|
return Promise.resolve({
|
||||||
|
data: [
|
||||||
|
{ id: 1, name: 'Name1' },
|
||||||
|
{ id: 2, name: 'Name2' },
|
||||||
|
{ id: 3, name: 'Name3' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess(data) {
|
||||||
|
setDataSource(data.data);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
console.log('dataSource1', dataSource);
|
||||||
|
return (
|
||||||
|
<DataSourceContext.Provider
|
||||||
|
value={{
|
||||||
|
service,
|
||||||
|
dataSource,
|
||||||
|
setDataSource,
|
||||||
|
selectedRowKeys,
|
||||||
|
setSelectedRowKeys,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</DataSourceContext.Provider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider components={{ Action, Context, Table, Input }}>
|
||||||
|
<SchemaComponent schema={schema} />
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
};
|
@ -25,3 +25,5 @@ group:
|
|||||||
## Table.RowSelection
|
## Table.RowSelection
|
||||||
|
|
||||||
<code src="./demos/demo3.tsx" />
|
<code src="./demos/demo3.tsx" />
|
||||||
|
|
||||||
|
<code src="./demos/demo4.tsx" />
|
||||||
|
@ -12,8 +12,8 @@ export const TableColumnInitializers = (props: any) => {
|
|||||||
wrap={(s) => {
|
wrap={(s) => {
|
||||||
return {
|
return {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-decorator': 'TableColumnDecorator',
|
'x-decorator': 'Table.Column.Decorator',
|
||||||
'x-designer': 'TableColumnDeigner',
|
'x-designer': 'Table.Column.Deigner',
|
||||||
'x-component': 'Table.Column',
|
'x-component': 'Table.Column',
|
||||||
properties: {
|
properties: {
|
||||||
[s.name]: {
|
[s.name]: {
|
||||||
|
Loading…
Reference in New Issue
Block a user