feat: improve form schema templates
This commit is contained in:
parent
e4fffc2245
commit
5308210991
@ -287,5 +287,6 @@ export default {
|
||||
"Template name": "模板名称",
|
||||
"Block type": "区块类型",
|
||||
"Action column": "操作列",
|
||||
"Records per page": "每页显示数量"
|
||||
"Records per page": "每页显示数量",
|
||||
"(Fields only)": "(仅字段)"
|
||||
}
|
||||
|
@ -75,7 +75,8 @@ export const FormDesigner = () => {
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<SchemaSettings.Template componentName={ctx?.action ? 'RecordForm' : 'CreateForm'} collectionName={name} />
|
||||
{/* <SchemaSettings.Template componentName={'FormItem'} collectionName={name} /> */}
|
||||
<SchemaSettings.FormItemTemplate componentName={'FormItem'} collectionName={name} />
|
||||
<SchemaSettings.Divider />
|
||||
<SchemaSettings.Remove
|
||||
removeParentsIfNoChildren
|
||||
@ -92,7 +93,12 @@ export const ReadPrettyFormDesigner = () => {
|
||||
const template = useSchemaTemplate();
|
||||
return (
|
||||
<GeneralSchemaDesigner template={template} title={title || name}>
|
||||
<SchemaSettings.Template componentName={'ReadPrettyForm'} collectionName={name} />
|
||||
{/* <SchemaSettings.Template componentName={'ReadPrettyForm'} collectionName={name} /> */}
|
||||
<SchemaSettings.FormItemTemplate
|
||||
insertAdjacentPosition={'beforeEnd'}
|
||||
componentName={'ReadPrettyFormItem'}
|
||||
collectionName={name}
|
||||
/>
|
||||
<SchemaSettings.Divider />
|
||||
<SchemaSettings.Remove
|
||||
removeParentsIfNoChildren
|
||||
|
@ -16,12 +16,22 @@ export interface RemoteSchemaComponentProps {
|
||||
render?: any;
|
||||
hidden?: any;
|
||||
onlyRenderProperties?: boolean;
|
||||
noForm?: boolean;
|
||||
}
|
||||
|
||||
const defaultTransform = (s: Schema) => s;
|
||||
|
||||
const RequestSchemaComponent: React.FC<RemoteSchemaComponentProps> = (props) => {
|
||||
const { onlyRenderProperties, hidden, scope, uid, components, onSuccess, schemaTransform = defaultTransform } = props;
|
||||
const {
|
||||
noForm,
|
||||
onlyRenderProperties,
|
||||
hidden,
|
||||
scope,
|
||||
uid,
|
||||
components,
|
||||
onSuccess,
|
||||
schemaTransform = defaultTransform,
|
||||
} = props;
|
||||
const { reset } = useSchemaComponentContext();
|
||||
const conf = {
|
||||
url: `/uiSchemas:${onlyRenderProperties ? 'getProperties' : 'getJsonSchema'}/${uid}`,
|
||||
@ -40,7 +50,9 @@ const RequestSchemaComponent: React.FC<RemoteSchemaComponentProps> = (props) =>
|
||||
if (hidden) {
|
||||
return <Spin />;
|
||||
}
|
||||
return (
|
||||
return noForm ? (
|
||||
<SchemaComponent memoized components={components} scope={scope} schema={schemaTransform(data?.data || {})} />
|
||||
) : (
|
||||
<FormProvider form={form}>
|
||||
<SchemaComponent memoized components={components} scope={scope} schema={schemaTransform(data?.data || {})} />
|
||||
</FormProvider>
|
||||
|
@ -60,7 +60,7 @@ export const ActionInitializer = (props) => {
|
||||
};
|
||||
|
||||
export const DataBlockInitializer = (props) => {
|
||||
const { onCreateBlockSchema, componentType, createBlockSchema, insert, ...others } = props;
|
||||
const { templateWrap, onCreateBlockSchema, componentType, createBlockSchema, insert, ...others } = props;
|
||||
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
@ -69,7 +69,7 @@ export const DataBlockInitializer = (props) => {
|
||||
onClick={async ({ item }) => {
|
||||
if (item.template) {
|
||||
const s = await getTemplateSchemaByMode(item);
|
||||
insert(s);
|
||||
templateWrap ? insert(templateWrap(s, { item })) : insert(s);
|
||||
} else {
|
||||
if (onCreateBlockSchema) {
|
||||
onCreateBlockSchema({ item });
|
||||
@ -105,7 +105,17 @@ export const FormBlockInitializer = (props) => {
|
||||
<DataBlockInitializer
|
||||
{...props}
|
||||
icon={<FormOutlined />}
|
||||
componentType={'CreateForm'}
|
||||
componentType={'FormItem'}
|
||||
templateWrap={(templateSchema, { item }) => {
|
||||
const s = createFormBlockSchema({
|
||||
template: templateSchema,
|
||||
collection: item.name,
|
||||
});
|
||||
if (item.template && item.mode === 'reference') {
|
||||
s['x-template-key'] = item.template.key;
|
||||
}
|
||||
return s;
|
||||
}}
|
||||
createBlockSchema={createFormBlockSchema}
|
||||
/>
|
||||
);
|
||||
@ -530,7 +540,20 @@ export const CreateFormBlockInitializer = (props) => {
|
||||
onClick={async ({ item }) => {
|
||||
if (item.template) {
|
||||
const s = await getTemplateSchemaByMode(item);
|
||||
if (item.template.componentName === 'FormItem') {
|
||||
const blockSchema = createFormBlockSchema({
|
||||
actionInitializers: 'CreateFormActionInitializers',
|
||||
association,
|
||||
collection: collection.name,
|
||||
template: s,
|
||||
});
|
||||
if (item.mode === 'reference') {
|
||||
blockSchema['x-template-key'] = item.template.key;
|
||||
}
|
||||
insert(blockSchema);
|
||||
} else {
|
||||
insert(s);
|
||||
}
|
||||
} else {
|
||||
insert(
|
||||
createFormBlockSchema({
|
||||
@ -541,7 +564,7 @@ export const CreateFormBlockInitializer = (props) => {
|
||||
);
|
||||
}
|
||||
}}
|
||||
items={useRecordCollectionDataSourceItems('CreateForm')}
|
||||
items={useRecordCollectionDataSourceItems('FormItem')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -558,7 +581,23 @@ export const RecordFormBlockInitializer = (props) => {
|
||||
onClick={async ({ item }) => {
|
||||
if (item.template) {
|
||||
const s = await getTemplateSchemaByMode(item);
|
||||
if (item.template.componentName === 'FormItem') {
|
||||
const blockSchema = createFormBlockSchema({
|
||||
association,
|
||||
collection: collection.name,
|
||||
action: 'get',
|
||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
||||
useParams: '{{ useParamsFromRecord }}',
|
||||
actionInitializers: 'UpdateFormActionInitializers',
|
||||
template: s,
|
||||
});
|
||||
if (item.mode === 'reference') {
|
||||
blockSchema['x-template-key'] = item.template.key;
|
||||
}
|
||||
insert(blockSchema);
|
||||
} else {
|
||||
insert(s);
|
||||
}
|
||||
} else {
|
||||
insert(
|
||||
createFormBlockSchema({
|
||||
@ -572,7 +611,7 @@ export const RecordFormBlockInitializer = (props) => {
|
||||
);
|
||||
}
|
||||
}}
|
||||
items={useRecordCollectionDataSourceItems('RecordForm')}
|
||||
items={useRecordCollectionDataSourceItems('FormItem')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -589,7 +628,22 @@ export const RecordReadPrettyFormBlockInitializer = (props) => {
|
||||
onClick={async ({ item }) => {
|
||||
if (item.template) {
|
||||
const s = await getTemplateSchemaByMode(item);
|
||||
if (item.template.componentName === 'ReadPrettyFormItem') {
|
||||
const blockSchema = createReadPrettyFormBlockSchema({
|
||||
association,
|
||||
collection: collection.name,
|
||||
action: 'get',
|
||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
||||
useParams: '{{ useParamsFromRecord }}',
|
||||
template: s,
|
||||
});
|
||||
if (item.mode === 'reference') {
|
||||
blockSchema['x-template-key'] = item.template.key;
|
||||
}
|
||||
insert(blockSchema);
|
||||
} else {
|
||||
insert(s);
|
||||
}
|
||||
} else {
|
||||
insert(
|
||||
createReadPrettyFormBlockSchema({
|
||||
@ -602,7 +656,7 @@ export const RecordReadPrettyFormBlockInitializer = (props) => {
|
||||
);
|
||||
}
|
||||
}}
|
||||
items={useRecordCollectionDataSourceItems('ReadPrettyForm')}
|
||||
items={useRecordCollectionDataSourceItems('ReadPrettyFormItem')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -149,6 +149,7 @@ export const useCurrentSchema = (action: string, key: string, find = findSchema,
|
||||
};
|
||||
|
||||
export const useRecordCollectionDataSourceItems = (componentName) => {
|
||||
const { t } = useTranslation();
|
||||
const collection = useCollection();
|
||||
const { getTemplatesByCollection } = useSchemaTemplateManager();
|
||||
const templates = getTemplatesByCollection(collection.name).filter((template) => {
|
||||
@ -173,12 +174,14 @@ export const useRecordCollectionDataSourceItems = (componentName) => {
|
||||
name: 'copy',
|
||||
title: '复制模板',
|
||||
children: templates.map((template) => {
|
||||
const templateName =
|
||||
template?.componentName === 'ReadPrettyFormItem' ? `${template?.name} ${t('(Fields only)')}` : template?.name;
|
||||
return {
|
||||
type: 'item',
|
||||
mode: 'copy',
|
||||
name: collection.name,
|
||||
template,
|
||||
title: template.name || '未命名',
|
||||
title: templateName || '未命名',
|
||||
};
|
||||
}),
|
||||
},
|
||||
@ -188,12 +191,14 @@ export const useRecordCollectionDataSourceItems = (componentName) => {
|
||||
name: 'ref',
|
||||
title: '引用模板',
|
||||
children: templates.map((template) => {
|
||||
const templateName =
|
||||
template?.componentName === 'ReadPrettyFormItem' ? `${template?.name} ${t('(Fields only)')}` : template?.name;
|
||||
return {
|
||||
type: 'item',
|
||||
mode: 'reference',
|
||||
name: collection.name,
|
||||
template,
|
||||
title: template.name || '未命名',
|
||||
title: templateName || '未命名',
|
||||
};
|
||||
}),
|
||||
},
|
||||
@ -242,12 +247,14 @@ export const useCollectionDataSourceItems = (componentName) => {
|
||||
name: 'copy',
|
||||
title: '复制模板',
|
||||
children: templates.map((template) => {
|
||||
const templateName =
|
||||
template?.componentName === 'FormItem' ? `${template?.name} ${t('(Fields only)')}` : template?.name;
|
||||
return {
|
||||
type: 'item',
|
||||
mode: 'copy',
|
||||
name: item.name,
|
||||
template,
|
||||
title: template.name || '未命名',
|
||||
title: templateName || '未命名',
|
||||
};
|
||||
}),
|
||||
},
|
||||
@ -257,12 +264,14 @@ export const useCollectionDataSourceItems = (componentName) => {
|
||||
name: 'ref',
|
||||
title: '引用模板',
|
||||
children: templates.map((template) => {
|
||||
const templateName =
|
||||
template?.componentName === 'FormItem' ? `${template?.name} ${t('(Fields only)')}` : template?.name;
|
||||
return {
|
||||
type: 'item',
|
||||
mode: 'reference',
|
||||
name: item.name,
|
||||
template,
|
||||
title: template.name || '未命名',
|
||||
title: templateName || '未命名',
|
||||
};
|
||||
}),
|
||||
},
|
||||
@ -281,6 +290,7 @@ export const createFormBlockSchema = (options) => {
|
||||
resource,
|
||||
association,
|
||||
action,
|
||||
template,
|
||||
...others
|
||||
} = options;
|
||||
const resourceName = resource || association || collection;
|
||||
@ -310,7 +320,7 @@ export const createFormBlockSchema = (options) => {
|
||||
useProps: '{{ useFormBlockProps }}',
|
||||
},
|
||||
properties: {
|
||||
grid: {
|
||||
grid: template || {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': formItemInitializers,
|
||||
@ -343,6 +353,7 @@ export const createReadPrettyFormBlockSchema = (options) => {
|
||||
collection,
|
||||
association,
|
||||
resource,
|
||||
template,
|
||||
...others
|
||||
} = options;
|
||||
const resourceName = resource || association || collection;
|
||||
@ -381,7 +392,7 @@ export const createReadPrettyFormBlockSchema = (options) => {
|
||||
},
|
||||
properties: {},
|
||||
},
|
||||
grid: {
|
||||
grid: template || {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': formItemInitializers,
|
||||
|
@ -4,6 +4,7 @@ import { useField, useFieldSchema } from '@formily/react';
|
||||
import { Space } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DragHandler, useCompile, useDesignable } from '../schema-component';
|
||||
import { SchemaSettings } from './SchemaSettings';
|
||||
|
||||
@ -33,6 +34,7 @@ export const GeneralSchemaDesigner = (props: any) => {
|
||||
const { title, template, draggable = true } = props;
|
||||
const { dn, designable } = useDesignable();
|
||||
const field = useField();
|
||||
const { t } = useTranslation();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const compile = useCompile();
|
||||
const schemaSettingsProps = {
|
||||
@ -43,13 +45,14 @@ export const GeneralSchemaDesigner = (props: any) => {
|
||||
if (!designable) {
|
||||
return null;
|
||||
}
|
||||
const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName) ? `${template?.name} ${t('(Fields only)')}` : template?.name;
|
||||
return (
|
||||
<div className={'general-schema-designer'}>
|
||||
{title && (
|
||||
<div className={classNames('general-schema-designer-title', titleCss)}>
|
||||
<Space size={2}>
|
||||
<span className={'title-tag'}>{compile(title)}</span>
|
||||
{template && <span className={'title-tag'}>引用模板: {template?.name || '未命名'}</span>}
|
||||
{template && <span className={'title-tag'}>引用模板: {templateName || '未命名'}</span>}
|
||||
</Space>
|
||||
</div>
|
||||
)}
|
||||
|
@ -67,9 +67,9 @@ interface SchemaSettingsProviderProps {
|
||||
|
||||
export const SchemaSettingsProvider: React.FC<SchemaSettingsProviderProps> = (props) => {
|
||||
const { children, fieldSchema, ...others } = props;
|
||||
const { getTemplateBySchemaId } = useSchemaTemplateManager();
|
||||
const { getTemplateBySchema } = useSchemaTemplateManager();
|
||||
const { name } = useCollection();
|
||||
const template = getTemplateBySchemaId(fieldSchema['x-uid']);
|
||||
const template = getTemplateBySchema(fieldSchema);
|
||||
return (
|
||||
<SchemaSettingsContext.Provider value={{ collectionName: name, template, fieldSchema, ...others }}>
|
||||
{children}
|
||||
@ -181,6 +181,147 @@ SchemaSettings.Template = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const findGridSchema = (fieldSchema) => {
|
||||
return fieldSchema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === 'FormV2') {
|
||||
const f = s.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === 'Grid') {
|
||||
return s;
|
||||
}
|
||||
return buf;
|
||||
}, null);
|
||||
if (f) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}, null);
|
||||
};
|
||||
|
||||
const findBlockTemplateSchema = (fieldSchema) => {
|
||||
return fieldSchema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === 'FormV2') {
|
||||
const f = s.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === 'BlockTemplate') {
|
||||
return s;
|
||||
}
|
||||
return buf;
|
||||
}, null);
|
||||
if (f) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}, null);
|
||||
};
|
||||
|
||||
SchemaSettings.FormItemTemplate = (props) => {
|
||||
const { insertAdjacentPosition = 'afterBegin', componentName, collectionName } = props;
|
||||
const { t } = useTranslation();
|
||||
const { dn, setVisible, template, fieldSchema } = useSchemaSettings();
|
||||
const api = useAPIClient();
|
||||
const { saveAsTemplate, copyTemplateSchema } = useSchemaTemplateManager();
|
||||
if (!collectionName) {
|
||||
return null;
|
||||
}
|
||||
if (template) {
|
||||
return (
|
||||
<SchemaSettings.Item
|
||||
onClick={async () => {
|
||||
const schema = await copyTemplateSchema(template);
|
||||
const templateSchema = findBlockTemplateSchema(fieldSchema);
|
||||
const sdn = createDesignable({
|
||||
api,
|
||||
refresh: dn.refresh.bind(dn),
|
||||
current: templateSchema.parent,
|
||||
});
|
||||
sdn.loadAPIClientEvents();
|
||||
sdn.removeWithoutEmit(templateSchema);
|
||||
sdn.insertAdjacent(insertAdjacentPosition, schema, {
|
||||
async onSuccess() {
|
||||
await api.request({
|
||||
url: `/uiSchemas:remove/${templateSchema['x-uid']}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
fieldSchema['x-template-key'] = null;
|
||||
await api.request({
|
||||
url: `uiSchemas:patch`,
|
||||
method: 'post',
|
||||
data: {
|
||||
'x-uid': fieldSchema['x-uid'],
|
||||
'x-template-key': null,
|
||||
},
|
||||
});
|
||||
dn.refresh();
|
||||
}}
|
||||
>
|
||||
{t('Convert reference to duplicate')}
|
||||
</SchemaSettings.Item>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<SchemaSettings.Item
|
||||
onClick={async () => {
|
||||
setVisible(false);
|
||||
const gridSchema = findGridSchema(fieldSchema);
|
||||
console.log('gridSchema', gridSchema);
|
||||
const values = await FormDialog('Save as template', () => {
|
||||
return (
|
||||
<FormLayout layout={'vertical'}>
|
||||
<SchemaComponent
|
||||
components={{ Input, FormItem }}
|
||||
schema={{
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
title: '模板名称',
|
||||
required: true,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormLayout>
|
||||
);
|
||||
}).open({});
|
||||
const sdn = createDesignable({
|
||||
api,
|
||||
refresh: dn.refresh.bind(dn),
|
||||
current: gridSchema.parent,
|
||||
});
|
||||
sdn.loadAPIClientEvents();
|
||||
const { key } = await saveAsTemplate({
|
||||
collectionName,
|
||||
componentName,
|
||||
name: values.name,
|
||||
uid: gridSchema['x-uid'],
|
||||
});
|
||||
sdn.removeWithoutEmit(gridSchema);
|
||||
sdn.insertAdjacent(insertAdjacentPosition, {
|
||||
type: 'void',
|
||||
'x-component': 'BlockTemplate',
|
||||
'x-component-props': {
|
||||
templateId: key,
|
||||
},
|
||||
});
|
||||
fieldSchema['x-template-key'] = key;
|
||||
await api.request({
|
||||
url: `uiSchemas:patch`,
|
||||
method: 'post',
|
||||
data: {
|
||||
'x-uid': fieldSchema['x-uid'],
|
||||
'x-template-key': key,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t('Save as template')}
|
||||
</SchemaSettings.Item>
|
||||
);
|
||||
};
|
||||
|
||||
SchemaSettings.Item = (props) => {
|
||||
let { eventKey } = props;
|
||||
return (
|
||||
|
@ -19,7 +19,7 @@ export const BlockTemplate = observer((props: any) => {
|
||||
return (
|
||||
<div>
|
||||
<BlockTemplateContext.Provider value={{ dn, field, fieldSchema, template }}>
|
||||
<RemoteSchemaComponent uid={template?.uid} />
|
||||
<RemoteSchemaComponent noForm uid={template?.uid} />
|
||||
</BlockTemplateContext.Provider>
|
||||
</div>
|
||||
);
|
||||
|
@ -46,10 +46,12 @@ const regenerateUid = (s: ISchema) => {
|
||||
};
|
||||
|
||||
export const useSchemaTemplate = () => {
|
||||
const { getTemplateBySchemaId } = useSchemaTemplateManager();
|
||||
const { getTemplateBySchema, templates } = useSchemaTemplateManager();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const schemaId = fieldSchema['x-uid'];
|
||||
return useMemo(() => getTemplateBySchemaId(schemaId), [schemaId]);
|
||||
const templateKey = fieldSchema['x-template-key'];
|
||||
console.log('templateKey', { schemaId, templateKey })
|
||||
return useMemo(() => getTemplateBySchema(fieldSchema), [schemaId, templateKey]);
|
||||
};
|
||||
|
||||
export const useSchemaTemplateManager = () => {
|
||||
@ -98,7 +100,20 @@ export const useSchemaTemplateManager = () => {
|
||||
await refresh();
|
||||
return { key };
|
||||
},
|
||||
getTemplateBySchema(schema) {
|
||||
const templateKey = schema['x-template-key'];
|
||||
if (templateKey) {
|
||||
return templates?.find((template) => template.key === templateKey);
|
||||
}
|
||||
const schemaId = schema['x-uid'];
|
||||
if (schemaId) {
|
||||
return templates?.find((template) => template.uid === schemaId);
|
||||
}
|
||||
},
|
||||
getTemplateBySchemaId(schemaId) {
|
||||
if (!schemaId) {
|
||||
return null;
|
||||
}
|
||||
return templates?.find((template) => template.uid === schemaId);
|
||||
},
|
||||
getTemplateById(key) {
|
||||
|
Loading…
Reference in New Issue
Block a user