v1 zu v2 clientseitige Migrationsanleitung
Diese Anleitung beschreibt, wie der clientseitige Code eines Workflow-Erweiterungs-Plugins von v1 zu v2 migriert wird. Die zentrale Veränderung im v2-Client besteht darin, dass deklarative Konfigurationsoberflächen auf Basis von Formily Schema durch einen Loader- und reinen React/antd-Komponentenansatz ersetzt werden.
Überblick
Wesentliche Änderungen
- Änderung der Importpfade:
@nocobase/plugin-workflow/client → @nocobase/plugin-workflow/client-v2, Plugin-Basisklasse @nocobase/client → @nocobase/client-v2
- Änderung des Konfigurationsoberflächen-Musters: Von Formily-Schema-Objekten (
fieldset) zu per Loader lazy-geladenen React-Komponenten (FieldsetLoader)
scope/components-Eigenschaften entfernt: Es ist nicht mehr notwendig, Scope-Objekte oder Komponenten in das Schema zu injizieren; sie werden einfach direkt in React-Komponenten importiert und verwendet
Importpfad-Zuordnung
// v1
import { Plugin } from '@nocobase/client';
import WorkflowPlugin, { Trigger, Instruction } from '@nocobase/plugin-workflow/client';
// v2
import { Plugin } from '@nocobase/client-v2';
import WorkflowPlugin, { Trigger, Instruction } from '@nocobase/plugin-workflow/client-v2';
Allgemeine Regeln
Loader-Muster
v2 verwendet Eigenschaften vom Typ LoaderOf, um die fieldset- und andere Formily-Schema-Objekte aus v1 zu ersetzen. Ein Loader ist im Wesentlichen eine Funktion, die Promise<{ default: ComponentType }> zurückgibt und damit Code-Splitting und Lazy-Loading über dynamisches import() ermöglicht:
// v1: Formily Schema object
fieldset = {
interval: {
type: 'number',
title: 'Interval',
'x-decorator': 'FormItem',
'x-component': 'InputNumber',
default: 60000,
},
};
// v2: Loader pointing to a React component
FieldsetLoader = () => import('./MyConfig');
Wenn Sie auf einen benannten Export (anstelle des Standard-Exports) verweisen müssen, verwenden Sie .then() zur Umleitung:
FieldsetLoader = () => import('./MyConfig').then((m) => ({ default: m.MyPresetConfig }));
Syntax der Konfigurationskomponente
Die durch einen Loader geladene Komponente ist eine standardmäßige React-Funktionskomponente, die antd's Form.Item zum Aufbau von Formularen verwendet. Feldpfade verwenden durchgehend das verschachtelte Array-Format ['config', 'fieldName']:
// v1: Formily Schema
fieldset = {
interval: {
type: 'number',
title: '{{t("Interval")}}',
name: 'config.interval',
'x-decorator': 'FormItem',
'x-component': 'InputNumber',
default: 60000,
},
};
// v2: React component
import { Form, InputNumber } from 'antd';
export default function MyConfig() {
const { t } = useWorkflowTranslation();
return (
<Form.Item
name={['config', 'interval']}
label={t('Interval')}
initialValue={60000}
>
<InputNumber />
</Form.Item>
);
}
Trigger-Migration
Eigenschafts-Zuordnungstabelle
Migrationsbeispiel
v1-Syntax:
import { Trigger } from '@nocobase/plugin-workflow/client';
class MyTrigger extends Trigger {
title = '{{t("My Trigger")}}';
fieldset = {
collection: {
type: 'string',
title: '{{t("Collection")}}',
'x-decorator': 'FormItem',
'x-component': 'CollectionSelect',
required: true,
},
mode: {
type: 'number',
title: '{{t("Mode")}}',
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {
options: [
{ label: '{{t("Created")}}', value: 1 },
{ label: '{{t("Updated")}}', value: 2 },
],
},
},
};
scope = { /* ... */ };
components = { CollectionSelect };
}
v2-Syntax:
import { Trigger } from '@nocobase/plugin-workflow/client-v2';
class MyTrigger extends Trigger {
title = '{{t("My Trigger")}}';
PresetFieldsetLoader = () =>
import('./MyTriggerConfig').then((m) => ({ default: m.MyPresetConfig }));
FieldsetLoader = () => import('./MyTriggerConfig');
TriggerFieldsetLoader = () => import('./TriggerMyConfig');
validate(config) {
return Boolean(config?.collection && config?.mode);
}
}
// MyTriggerConfig.tsx
import { Form, Select } from 'antd';
import { CollectionCascader } from '@nocobase/plugin-workflow/client-v2';
export function MyPresetConfig() {
return (
<Form.Item name={['config', 'collection']} label="Collection" rules={[{ required: true }]}>
<CollectionCascader />
</Form.Item>
);
}
export default function MyTriggerConfig() {
return (
<>
<Form.Item name={['config', 'collection']} label="Collection">
<CollectionCascader disabled />
</Form.Item>
<Form.Item name={['config', 'mode']} label="Mode">
<Select
options={[
{ label: 'Created', value: 1 },
{ label: 'Updated', value: 2 },
]}
/>
</Form.Item>
</>
);
}
Plugin-Registrierung
// v1
import { Plugin } from '@nocobase/client';
import WorkflowPlugin from '@nocobase/plugin-workflow/client';
export default class extends Plugin {
async load() {
const workflow = this.app.pm.get(WorkflowPlugin);
workflow.registerTrigger('myTrigger', MyTrigger);
}
}
// v2
import { Plugin } from '@nocobase/client-v2';
export default class extends Plugin {
async load() {
const workflow = this.app.pm.get('workflow');
workflow.registerTrigger('myTrigger', MyTrigger);
}
}
Knoten-Migration
Eigenschafts-Zuordnungstabelle
Migrationsbeispiel
v1-Syntax:
import WorkflowPlugin, { Instruction } from '@nocobase/plugin-workflow/client';
class MyInstruction extends Instruction {
title = 'Random string';
type = 'randomString';
group = 'extended';
fieldset = {
digit: {
type: 'number',
title: 'Digit',
name: 'digit',
'x-decorator': 'FormItem',
'x-component': 'InputNumber',
'x-component-props': { min: 1, max: 10 },
default: 6,
},
};
useVariables(node, options) {
return { value: node.key, label: node.title };
}
}
v2-Syntax:
import { Instruction } from '@nocobase/plugin-workflow/client-v2';
class MyInstruction extends Instruction {
title = 'Random string';
type = 'randomString';
group = 'extended';
FieldsetLoader = () => import('./components/RandomStringConfig');
useVariables(node, options) {
return { value: node.key, label: node.title };
}
}
// components/RandomStringConfig.tsx
import { Form, InputNumber } from 'antd';
export default function RandomStringConfig() {
return (
<Form.Item
name={['config', 'digit']}
label="Digit"
initialValue={6}
rules={[{ required: true }]}
>
<InputNumber min={1} max={10} />
</Form.Item>
);
}
Weitere Hinweise
Unveränderte Teile
Die folgenden Eigenschaften und Methoden haben in v1 und v2 im Wesentlichen die gleichen Signaturen und können bei der Migration unverändert beibehalten werden:
useVariables(node/config, options) — Stellt Variablenoptionen bereit
useScopeVariables(node, options) — Stellt branchspezifische Variablen bereit
isAvailable(ctx) — Verfügbarkeitsprüfung des Knotens (der v2-NodeAvailableContext fügt eine neue engine-Eigenschaft hinzu)
Neue Eigenschaften in v2
getCreateModelMenuItem — Definiert die Konfiguration zum Erstellen von Untermodell-Menüeinträgen für Knoten/Trigger auf der v2-Arbeitsfläche
useTempAssociationSource — Stellt Informationen zur temporären Assoziationsdatenquelle bereit
validate(config) — Validierung der Trigger-Konfiguration (nur Trigger)
branching — Deklariert, ob der Knoten ein Verzweigungsknoten ist (nur Knoten)
end — Deklariert, ob der Knoten ein Endknoten ist (nur Knoten)
testable — Deklariert, ob der Knoten Testläufe unterstützt (nur Knoten)
Wertsemantische Konsistenz
Stellen Sie bei der Migration sicher, dass die von v2-Komponenten erzeugten Formularwerte mit denen aus v1 konsistent sind, insbesondere die Payload-Struktur bei der manuellen Ausführung. Wenn beispielsweise das v1-Formular für die manuelle Ausführung ein vollständiges Datensatzobjekt speichert, muss die v2-Version die gleiche Wertstruktur beibehalten, anstatt nur den Primärschlüssel zu speichern.