refactor(plugin-workflow): manual collection block migration (#2064)
* refactor(plugin-workflow): change all collection node initializer in manual config * refactor(plugin-workflow): clean code * fix(plugin-workflow): fix custom form field in manual config * refactor(plugin-workflow): add migration * fix(plugin-workflow): fix custom form block migration * fix(plugin-workflow): fix detail read-pretty for association fields
This commit is contained in:
parent
4faf64f905
commit
e5bab3249a
@ -1,44 +1,71 @@
|
||||
import React from 'react';
|
||||
|
||||
import { SchemaInitializer, useCollectionManager } from '@nocobase/client';
|
||||
import {
|
||||
CollectionProvider,
|
||||
SchemaInitializer,
|
||||
SchemaInitializerItemOptions,
|
||||
useCollectionManager,
|
||||
useRecordCollectionDataSourceItems,
|
||||
useSchemaTemplateManager,
|
||||
} from '@nocobase/client';
|
||||
import { traverseSchema } from '../nodes/manual/utils';
|
||||
|
||||
export function CollectionBlockInitializer({ insert, collection, dataSource, ...props }) {
|
||||
function InnerCollectionBlockInitializer({ insert, collection, dataSource, ...props }) {
|
||||
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
|
||||
const { getCollection } = useCollectionManager();
|
||||
const items = useRecordCollectionDataSourceItems('FormItem') as SchemaInitializerItemOptions[];
|
||||
const resovledCollection = getCollection(collection);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
{...props}
|
||||
onClick={() => {
|
||||
insert({
|
||||
|
||||
async function onConfirm({ item }) {
|
||||
const template = item.template ? await getTemplateSchemaByMode(item) : null;
|
||||
const result = {
|
||||
type: 'void',
|
||||
name: resovledCollection.name,
|
||||
title: resovledCollection.title,
|
||||
'x-decorator': 'DetailsBlockProvider',
|
||||
'x-decorator-props': {
|
||||
collection,
|
||||
dataSource,
|
||||
},
|
||||
'x-component': 'CardItem',
|
||||
'x-component-props': {
|
||||
title: props.title,
|
||||
},
|
||||
'x-designer': 'SimpleDesigner',
|
||||
properties: {
|
||||
grid: {
|
||||
type: 'void',
|
||||
name: resovledCollection.name,
|
||||
title: resovledCollection.title,
|
||||
'x-decorator': 'CollectionProvider',
|
||||
'x-decorator-props': {
|
||||
collection,
|
||||
},
|
||||
'x-component': 'CardItem',
|
||||
'x-component': 'FormV2',
|
||||
'x-component-props': {
|
||||
// title: props.title
|
||||
},
|
||||
'x-designer': 'SimpleDesigner',
|
||||
'x-designer-props': {
|
||||
type: 'record',
|
||||
useProps: '{{useDetailsBlockProps}}',
|
||||
},
|
||||
'x-read-pretty': true,
|
||||
properties: {
|
||||
grid: {
|
||||
grid: template || {
|
||||
type: 'void',
|
||||
'x-decorator': 'Form',
|
||||
'x-decorator-props': {
|
||||
useValues: '{{ useFlowRecordFromBlock }}',
|
||||
},
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'CollectionFieldInitializers',
|
||||
'x-context-datasource': dataSource,
|
||||
'x-initializer': 'ReadPrettyFormItemInitializers',
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
},
|
||||
},
|
||||
};
|
||||
traverseSchema(result, (node) => {
|
||||
if (node['x-uid']) {
|
||||
delete node['x-uid'];
|
||||
}
|
||||
});
|
||||
insert(result);
|
||||
}
|
||||
|
||||
return <SchemaInitializer.Item {...props} onClick={onConfirm} items={items} />;
|
||||
}
|
||||
|
||||
export function CollectionBlockInitializer(props) {
|
||||
return (
|
||||
<CollectionProvider collection={props.collection}>
|
||||
<InnerCollectionBlockInitializer {...props} />
|
||||
</CollectionProvider>
|
||||
);
|
||||
}
|
||||
|
@ -1,39 +0,0 @@
|
||||
import React from 'react';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { SchemaInitializer, useCollection, InitializerWithSwitch, gridRowColWrap } from '@nocobase/client';
|
||||
|
||||
function CollectionFieldInitializer({ field, ...props }) {
|
||||
const uiSchema = cloneDeep(field.uiSchema);
|
||||
delete uiSchema['x-uid'];
|
||||
|
||||
return (
|
||||
<InitializerWithSwitch
|
||||
{...props}
|
||||
schema={{
|
||||
...uiSchema,
|
||||
name: field.name,
|
||||
title: uiSchema.title ?? field.name,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-read-pretty': true,
|
||||
'x-designer': 'FormItem.Designer',
|
||||
'x-collection-field': field.name,
|
||||
}}
|
||||
type="x-collection-field"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function CollectionFieldInitializers(props) {
|
||||
const { fields } = useCollection();
|
||||
const items = fields
|
||||
.filter((field) => !['belongsTo', 'hasOne', 'hasMany', 'belongsToMany'].includes(field.type) && field.uiSchema)
|
||||
.map((field) => ({
|
||||
key: field.name,
|
||||
type: 'item',
|
||||
title: field.uiSchema.title,
|
||||
component: CollectionFieldInitializer,
|
||||
field,
|
||||
}));
|
||||
|
||||
return <SchemaInitializer.Button {...props} items={items} title="{{t('Configure fields')}}" wrap={gridRowColWrap} />;
|
||||
}
|
@ -4,7 +4,6 @@ import { appends, collection, values } from '../schemas/collection';
|
||||
import CollectionFieldset from '../components/CollectionFieldset';
|
||||
import { NAMESPACE } from '../locale';
|
||||
import { CollectionBlockInitializer } from '../components/CollectionBlockInitializer';
|
||||
import { CollectionFieldInitializers } from '../components/CollectionFieldInitializers';
|
||||
import { useCollectionFieldOptions } from '../variable';
|
||||
import { FieldsSelect } from '../components/FieldsSelect';
|
||||
|
||||
@ -63,7 +62,5 @@ export default {
|
||||
dataSource: `{{$jobsMapByNodeId.${node.id}}}`,
|
||||
};
|
||||
},
|
||||
initializers: {
|
||||
CollectionFieldInitializers,
|
||||
},
|
||||
initializers: {},
|
||||
};
|
||||
|
@ -0,0 +1,80 @@
|
||||
import React, { useRef, useMemo, useContext } from 'react';
|
||||
import { createForm } from '@formily/core';
|
||||
import { useField } from '@formily/react';
|
||||
import {
|
||||
BlockRequestContext,
|
||||
CollectionProvider,
|
||||
FormBlockContext,
|
||||
RecordProvider,
|
||||
useAPIClient,
|
||||
useAssociationNames,
|
||||
} from '@nocobase/client';
|
||||
import { useFlowContext } from '../../FlowContext';
|
||||
import { parse } from '@nocobase/utils/client';
|
||||
|
||||
function useFlowContextData(dataSource) {
|
||||
const { execution } = useFlowContext();
|
||||
const data = useMemo(
|
||||
() => ({
|
||||
$context: execution?.context,
|
||||
$jobsMapByNodeId: (execution?.jobs ?? []).reduce(
|
||||
(map, job) => Object.assign(map, { [job.nodeId]: job.result }),
|
||||
{},
|
||||
),
|
||||
}),
|
||||
[execution],
|
||||
);
|
||||
|
||||
const result = useMemo(() => parse(dataSource)(data), [execution]);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function DetailsBlockProvider(props) {
|
||||
const field = useField();
|
||||
const formBlockRef = useRef(null);
|
||||
const { appends, updateAssociationValues } = useAssociationNames();
|
||||
const values = useFlowContextData(props.dataSource);
|
||||
|
||||
const form = useMemo(
|
||||
() =>
|
||||
createForm({
|
||||
values,
|
||||
readPretty: true,
|
||||
}),
|
||||
[values],
|
||||
);
|
||||
|
||||
const params = {
|
||||
appends,
|
||||
};
|
||||
const service = {
|
||||
loading: false,
|
||||
data: {
|
||||
data: values,
|
||||
},
|
||||
};
|
||||
const api = useAPIClient();
|
||||
const resource = api.resource(props.collection);
|
||||
const __parent = useContext(BlockRequestContext);
|
||||
|
||||
return (
|
||||
<CollectionProvider collection={props.collection}>
|
||||
<RecordProvider record={values} parent={false}>
|
||||
<BlockRequestContext.Provider value={{ block: 'form', field, service, resource, __parent }}>
|
||||
<FormBlockContext.Provider
|
||||
value={{
|
||||
params,
|
||||
form,
|
||||
field,
|
||||
service,
|
||||
updateAssociationValues,
|
||||
formBlockRef,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</FormBlockContext.Provider>
|
||||
</BlockRequestContext.Provider>
|
||||
</RecordProvider>
|
||||
</CollectionProvider>
|
||||
);
|
||||
}
|
@ -35,6 +35,9 @@ function InternalFormBlockInitializer({ insert, schema, ...others }) {
|
||||
useAction: '{{ useSubmit }}',
|
||||
},
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-designer-props': {
|
||||
type: 'record',
|
||||
},
|
||||
'x-action': `${JOB_STATUS.RESOLVED}`,
|
||||
},
|
||||
},
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
GeneralSchemaDesigner,
|
||||
SchemaSettings,
|
||||
useCompile,
|
||||
useFormBlockContext,
|
||||
} from '@nocobase/client';
|
||||
import { Registry } from '@nocobase/utils/client';
|
||||
|
||||
@ -27,6 +28,7 @@ import customForm from './forms/custom';
|
||||
import createForm from './forms/create';
|
||||
import updateForm from './forms/update';
|
||||
import { FormBlockProvider } from './FormBlockProvider';
|
||||
import { DetailsBlockProvider } from './DetailsBlockProvider';
|
||||
|
||||
type ValueOf<T> = T[keyof T];
|
||||
|
||||
@ -229,10 +231,6 @@ function useSubmit() {
|
||||
};
|
||||
}
|
||||
|
||||
function useFlowRecordFromBlock() {
|
||||
return {};
|
||||
}
|
||||
|
||||
export function SchemaConfig({ value, onChange }) {
|
||||
const ctx = useContext(SchemaComponentContext);
|
||||
const trigger = useTrigger();
|
||||
@ -333,6 +331,7 @@ export function SchemaConfig({ value, onChange }) {
|
||||
{},
|
||||
),
|
||||
FormBlockProvider,
|
||||
DetailsBlockProvider,
|
||||
// NOTE: fake provider component
|
||||
ManualActionStatusProvider(props) {
|
||||
return props.children;
|
||||
@ -344,7 +343,7 @@ export function SchemaConfig({ value, onChange }) {
|
||||
}}
|
||||
scope={{
|
||||
useSubmit,
|
||||
useFlowRecordFromBlock,
|
||||
useDetailsBlockProps: useFormBlockContext,
|
||||
}}
|
||||
/>
|
||||
</SchemaInitializerProvider>
|
||||
|
@ -14,7 +14,6 @@ import {
|
||||
useCollectionManager,
|
||||
useCurrentUserContext,
|
||||
useRecord,
|
||||
useRequest,
|
||||
useTableBlockContext,
|
||||
FormBlockContext,
|
||||
useFormBlockContext,
|
||||
@ -28,6 +27,7 @@ import { instructions, useAvailableUpstreams } from '..';
|
||||
import { linkNodes } from '../../utils';
|
||||
import { manualFormTypes } from './SchemaConfig';
|
||||
import { FormBlockProvider } from './FormBlockProvider';
|
||||
import { DetailsBlockProvider } from './DetailsBlockProvider';
|
||||
|
||||
const nodeCollection = {
|
||||
title: `{{t("Task", { ns: "${NAMESPACE}" })}}`,
|
||||
@ -421,23 +421,6 @@ function useSubmit() {
|
||||
};
|
||||
}
|
||||
|
||||
// parse datasource block from execution context
|
||||
function useFlowRecordFromBlock(opts) {
|
||||
const { ['x-context-datasource']: dataSource } = useFieldSchema();
|
||||
const { execution } = useFlowContext();
|
||||
const result = parse(dataSource)({
|
||||
$context: execution?.context,
|
||||
$jobsMapByNodeId: (execution?.jobs ?? []).reduce(
|
||||
(map, job) => Object.assign(map, { [job.nodeId]: job.result }),
|
||||
{},
|
||||
),
|
||||
});
|
||||
|
||||
return useRequest(() => {
|
||||
return Promise.resolve({ data: result });
|
||||
}, opts);
|
||||
}
|
||||
|
||||
function FlowContextProvider(props) {
|
||||
const api = useAPIClient();
|
||||
const { id } = useRecord();
|
||||
@ -479,6 +462,7 @@ function FlowContextProvider(props) {
|
||||
<SchemaComponent
|
||||
components={{
|
||||
FormBlockProvider,
|
||||
DetailsBlockProvider,
|
||||
ActionBarProvider,
|
||||
ManualActionStatusProvider,
|
||||
...Array.from(manualFormTypes.getValues()).reduce(
|
||||
@ -490,6 +474,7 @@ function FlowContextProvider(props) {
|
||||
scope={{
|
||||
useSubmit,
|
||||
useFormBlockProps,
|
||||
useDetailsBlockProps,
|
||||
...Array.from(manualFormTypes.getValues()).reduce(
|
||||
(result, item) => Object.assign(result, item.block.scope),
|
||||
{},
|
||||
@ -529,6 +514,11 @@ function useFormBlockProps() {
|
||||
return { form };
|
||||
}
|
||||
|
||||
function useDetailsBlockProps() {
|
||||
const { form } = useFormBlockContext();
|
||||
return { form };
|
||||
}
|
||||
|
||||
function Drawer() {
|
||||
const ctx = useContext(SchemaComponentContext);
|
||||
const { id, node, workflow, status, updatedAt } = useRecord();
|
||||
@ -585,9 +575,6 @@ function Drawer() {
|
||||
},
|
||||
},
|
||||
}}
|
||||
scope={{
|
||||
useFlowRecordFromBlock,
|
||||
}}
|
||||
/>
|
||||
</SchemaComponentContext.Provider>
|
||||
);
|
||||
|
@ -1,19 +1,22 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import React, { useState, useContext, useMemo } from 'react';
|
||||
|
||||
import { cloneDeep, set } from 'lodash';
|
||||
import { Field } from '@formily/core';
|
||||
import { useForm } from '@formily/react';
|
||||
import { Field, createForm } from '@formily/core';
|
||||
import { useForm, useField, useFieldSchema } from '@formily/react';
|
||||
import { ArrayTable } from '@formily/antd';
|
||||
|
||||
import {
|
||||
ActionContextProvider,
|
||||
CollectionContext,
|
||||
CollectionProvider,
|
||||
FormBlockContext,
|
||||
gridRowColWrap,
|
||||
RecordProvider,
|
||||
SchemaComponent,
|
||||
SchemaInitializer,
|
||||
SchemaInitializerItemOptions,
|
||||
useCollectionManager,
|
||||
useRecord,
|
||||
} from '@nocobase/client';
|
||||
import { merge, uid } from '@nocobase/utils/client';
|
||||
|
||||
@ -22,22 +25,41 @@ import { lang, NAMESPACE } from '../../../locale';
|
||||
import { findSchema } from '../utils';
|
||||
import { ManualFormType } from '../SchemaConfig';
|
||||
|
||||
const FormCollectionContext = React.createContext<any>(null);
|
||||
|
||||
function FormCollectionProvider(props) {
|
||||
function CustomFormBlockProvider(props) {
|
||||
const [fields, setCollectionFields] = useState(props.collection?.fields ?? []);
|
||||
const userJob = useRecord();
|
||||
const field = useField();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const [formKey] = Object.keys(fieldSchema.toJSON().properties ?? {});
|
||||
const values = userJob?.result?.[formKey];
|
||||
|
||||
const form = useMemo(
|
||||
() =>
|
||||
createForm({
|
||||
initialValues: values,
|
||||
}),
|
||||
[values],
|
||||
);
|
||||
|
||||
return (
|
||||
<FormCollectionContext.Provider value={{ setCollectionFields }}>
|
||||
<CollectionProvider
|
||||
collection={{
|
||||
...props.collection,
|
||||
fields,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</CollectionProvider>
|
||||
</FormCollectionContext.Provider>
|
||||
<CollectionProvider
|
||||
collection={{
|
||||
...props.collection,
|
||||
fields,
|
||||
}}
|
||||
>
|
||||
<RecordProvider record={values} parent={false}>
|
||||
<FormBlockContext.Provider
|
||||
value={{
|
||||
form,
|
||||
field,
|
||||
setCollectionFields,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</FormBlockContext.Provider>
|
||||
</RecordProvider>
|
||||
</CollectionProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@ -48,7 +70,7 @@ function CustomFormBlockInitializer({ insert, ...props }) {
|
||||
onClick={() => {
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-decorator': 'FormCollectionProvider',
|
||||
'x-decorator': 'CustomFormBlockProvider',
|
||||
'x-decorator-props': {
|
||||
collection: {
|
||||
name: uid(),
|
||||
@ -180,7 +202,7 @@ function AddCustomFormField(props) {
|
||||
const collection = useContext(CollectionContext);
|
||||
const [interfaceOptions, setInterface] = useState<any>(null);
|
||||
const [insert, setCallback] = useState<any>();
|
||||
const { setCollectionFields } = useContext(FormCollectionContext);
|
||||
const { setCollectionFields } = useContext(FormBlockContext);
|
||||
|
||||
return (
|
||||
<AddCustomFormFieldButtonContext.Provider
|
||||
@ -271,7 +293,9 @@ function AddCustomFormField(props) {
|
||||
type: options.uiSchema.type,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'CollectionField',
|
||||
'x-interface-options': newField,
|
||||
'x-component-props': {
|
||||
field: newField,
|
||||
},
|
||||
'x-collection-field': `${collection.name}.${options.name}`,
|
||||
'x-designer': 'FormItem.Designer',
|
||||
});
|
||||
@ -329,11 +353,11 @@ export default {
|
||||
AddCustomFormField,
|
||||
},
|
||||
components: {
|
||||
FormCollectionProvider,
|
||||
CustomFormBlockProvider,
|
||||
},
|
||||
parseFormOptions(root) {
|
||||
const forms = {};
|
||||
const formBlocks: any[] = findSchema(root, (item) => item['x-decorator'] === 'FormCollectionProvider');
|
||||
const formBlocks: any[] = findSchema(root, (item) => item['x-decorator'] === 'CustomFormBlockProvider');
|
||||
formBlocks.forEach((formBlock) => {
|
||||
const [formKey] = Object.keys(formBlock.properties);
|
||||
const formSchema = formBlock.properties[formKey];
|
||||
@ -342,7 +366,9 @@ export default {
|
||||
(item) => item['x-component'] === 'CollectionField',
|
||||
true,
|
||||
);
|
||||
formBlock['x-decorator-props'].collection.fields = fields.map((field) => field['x-interface-options']);
|
||||
formBlock['x-decorator-props'].collection.fields = fields.map(
|
||||
(field) => field['x-component-props']?.field ?? field['x-interface-options'],
|
||||
);
|
||||
forms[formKey] = {
|
||||
type: 'custom',
|
||||
title: formBlock['x-component-props']?.title || formKey,
|
||||
@ -358,7 +384,7 @@ export default {
|
||||
block: {
|
||||
scope: {},
|
||||
components: {
|
||||
FormCollectionProvider: CollectionProvider,
|
||||
CustomFormBlockProvider,
|
||||
},
|
||||
},
|
||||
} as ManualFormType;
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { BlockInitializers, SchemaInitializerItemOptions, useCollectionManager } from '@nocobase/client';
|
||||
|
||||
import { CollectionBlockInitializer } from '../../components/CollectionBlockInitializer';
|
||||
import { CollectionFieldInitializers } from '../../components/CollectionFieldInitializers';
|
||||
import { filterTypedFields, useCollectionFieldOptions } from '../../variable';
|
||||
import { useCollectionFieldOptions } from '../../variable';
|
||||
import { NAMESPACE } from '../../locale';
|
||||
import { SchemaConfig, SchemaConfigButton } from './SchemaConfig';
|
||||
import { ModeConfig } from './ModeConfig';
|
||||
@ -149,7 +148,5 @@ export default {
|
||||
}
|
||||
: null;
|
||||
},
|
||||
initializers: {
|
||||
CollectionFieldInitializers,
|
||||
},
|
||||
initializers: {},
|
||||
};
|
||||
|
@ -3,7 +3,6 @@ import { SchemaInitializerItemOptions, useCollectionDataSource } from '@nocobase
|
||||
import { appends, collection, filter } from '../schemas/collection';
|
||||
import { NAMESPACE } from '../locale';
|
||||
import { CollectionBlockInitializer } from '../components/CollectionBlockInitializer';
|
||||
import { CollectionFieldInitializers } from '../components/CollectionFieldInitializers';
|
||||
import { FilterDynamicComponent } from '../components/FilterDynamicComponent';
|
||||
import { useCollectionFieldOptions } from '../variable';
|
||||
import { FieldsSelect } from '../components/FieldsSelect';
|
||||
@ -54,7 +53,7 @@ export default {
|
||||
return result?.length ? result : null;
|
||||
},
|
||||
useInitializers(node): SchemaInitializerItemOptions | null {
|
||||
if (!node.config.collection) {
|
||||
if (!node.config.collection || node.config.multiple) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -66,7 +65,5 @@ export default {
|
||||
dataSource: `{{$jobsMapByNodeId.${node.id}}}`,
|
||||
};
|
||||
},
|
||||
initializers: {
|
||||
CollectionFieldInitializers,
|
||||
},
|
||||
initializers: {},
|
||||
};
|
||||
|
@ -3,7 +3,6 @@ import { SchemaInitializerItemOptions, useCollectionDataSource } from '@nocobase
|
||||
import { appends, collection, filter } from '../schemas/collection';
|
||||
import { useCollectionFieldOptions } from '../variable';
|
||||
import { CollectionBlockInitializer } from '../components/CollectionBlockInitializer';
|
||||
import { CollectionFieldInitializers } from '../components/CollectionFieldInitializers';
|
||||
import { NAMESPACE, useWorkflowTranslation } from '../locale';
|
||||
import { FieldsSelect } from '../components/FieldsSelect';
|
||||
|
||||
@ -168,7 +167,5 @@ export default {
|
||||
dataSource: '{{$context.data}}',
|
||||
};
|
||||
},
|
||||
initializers: {
|
||||
CollectionFieldInitializers,
|
||||
},
|
||||
initializers: {},
|
||||
};
|
||||
|
@ -3,7 +3,6 @@ import { useCollectionDataSource, SchemaInitializerItemOptions } from '@nocobase
|
||||
import { ScheduleConfig } from './ScheduleConfig';
|
||||
import { SCHEDULE_MODE } from './constants';
|
||||
import { NAMESPACE, useWorkflowTranslation } from '../../locale';
|
||||
import { CollectionFieldInitializers } from '../../components/CollectionFieldInitializers';
|
||||
import { CollectionBlockInitializer } from '../../components/CollectionBlockInitializer';
|
||||
import { useCollectionFieldOptions } from '../../variable';
|
||||
import { appends } from '../../schemas/collection';
|
||||
@ -72,7 +71,5 @@ export default {
|
||||
dataSource: '{{$context.data}}',
|
||||
};
|
||||
},
|
||||
initializers: {
|
||||
CollectionFieldInitializers,
|
||||
},
|
||||
initializers: {},
|
||||
};
|
||||
|
@ -0,0 +1,138 @@
|
||||
import { Migration } from '@nocobase/server';
|
||||
import { uid } from '@nocobase/utils';
|
||||
|
||||
function findSchema(root, filter, onlyLeaf = false) {
|
||||
const result = [];
|
||||
|
||||
if (!root) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (filter(root) && (!onlyLeaf || !root.properties)) {
|
||||
result.push(root);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (root.properties) {
|
||||
Object.keys(root.properties).forEach((key) => {
|
||||
result.push(...findSchema(root.properties[key], filter));
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function migrateSchema(schema = {}): object {
|
||||
const root = { properties: schema };
|
||||
const collectionBlocks = findSchema(root, (item) => {
|
||||
return (
|
||||
item['x-component'] === 'CardItem' &&
|
||||
item['x-designer'] === 'SimpleDesigner' &&
|
||||
item['x-decorator'] === 'CollectionProvider'
|
||||
);
|
||||
});
|
||||
|
||||
collectionBlocks.forEach((block) => {
|
||||
const id = uid();
|
||||
const { grid } = block.properties;
|
||||
delete block.properties.grid;
|
||||
const fields = findSchema(grid, (item) => {
|
||||
return item['x-decorator'] === 'FormItem';
|
||||
});
|
||||
fields.forEach((field) => {
|
||||
Object.assign(field, {
|
||||
'x-component': 'CollectionField',
|
||||
'x-collection-field': `${block['x-decorator-props'].collection}.${field['x-collection-field']}`,
|
||||
});
|
||||
});
|
||||
|
||||
Object.assign(block, {
|
||||
'x-decorator': 'DetailsBlockProvider',
|
||||
'x-decorator-props': {
|
||||
...block['x-decorator-props'],
|
||||
dataSource: grid['x-context-datasource'],
|
||||
},
|
||||
properties: {
|
||||
[id]: {
|
||||
type: 'void',
|
||||
name: id,
|
||||
'x-component': 'FormV2',
|
||||
'x-component-props': {
|
||||
useProps: '{{useDetailsBlockProps}}',
|
||||
},
|
||||
properties: {
|
||||
grid: {
|
||||
type: 'void',
|
||||
name: 'grid',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'ReadPrettyFormItemInitializers',
|
||||
properties: grid.properties,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const customForms = findSchema(root, (item) => {
|
||||
return item['x-decorator'] === 'FormCollectionProvider';
|
||||
});
|
||||
customForms.forEach((item) => {
|
||||
Object.assign(item, {
|
||||
'x-decorator': 'CustomFormBlockProvider',
|
||||
});
|
||||
});
|
||||
|
||||
const customFormFields = findSchema(root, (item) => {
|
||||
return item['x-interface-options'];
|
||||
});
|
||||
customFormFields.forEach((field) => {
|
||||
const options = field['x-interface-options'];
|
||||
delete field['x-interface-options'];
|
||||
Object.assign(field, {
|
||||
'x-component-props': {
|
||||
field: options,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
export default class extends Migration {
|
||||
async up() {
|
||||
const match = await this.app.version.satisfies('<0.9.4-alpha.3');
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
const { db } = this.context;
|
||||
const NodeRepo = db.getRepository('flow_nodes');
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
const nodes = await NodeRepo.find({
|
||||
filter: {
|
||||
type: 'manual',
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
console.log('%d nodes need to be migrated.', nodes.length);
|
||||
|
||||
await nodes.reduce(
|
||||
(promise, node) =>
|
||||
promise.then(() => {
|
||||
const { schema, ...config } = node.config;
|
||||
node.set('config', {
|
||||
...config,
|
||||
schema: {
|
||||
...migrateSchema(schema),
|
||||
},
|
||||
});
|
||||
node.changed('config', true);
|
||||
return node.save({
|
||||
silent: true,
|
||||
transaction,
|
||||
});
|
||||
}),
|
||||
Promise.resolve(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user