mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
fix: flowDocument fromJSON will reload the node json data (#240)
* fix: flowDocument fromJSON update node data * feat: getNodeForm add updateFormValues
This commit is contained in:
parent
d9e805b167
commit
766fdc1597
@ -13,7 +13,7 @@ doc.toJSON() // TODO This is the old version data, not yet optimized. Business l
|
|||||||
|
|
||||||
doc.addFromNode(targetNode, json) // Insert after the specified node
|
doc.addFromNode(targetNode, json) // Insert after the specified node
|
||||||
|
|
||||||
doc.onNodeCreate(({ node, data}) => {}) // Listen to node creation, data is the JSON data at creation time
|
doc.onNodeCreate(({ node, json }) => {}) // Listen to node creation, data is the JSON data at creation time
|
||||||
doc.onNodeDispose(({ node }) => {}) // Listen to node deletion
|
doc.onNodeDispose(({ node }) => {}) // Listen to node deletion
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ doc.toJSON() // TODO 这里老版本的数据,还没优化,业务最好自
|
|||||||
|
|
||||||
doc.addFromNode(targetNode, json) // 插入到指定节点的后边
|
doc.addFromNode(targetNode, json) // 插入到指定节点的后边
|
||||||
|
|
||||||
doc.onNodeCreate(({ node, data}) => {}) // 监听节点创建,data 为创建时候的json数据
|
doc.onNodeCreate(({ node, json }) => {}) // 监听节点创建,data 为创建时候的json数据
|
||||||
doc.onNodeDispose(({ node }) => {}) // 监听节点删除
|
doc.onNodeDispose(({ node }) => {}) // 监听节点删除
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export interface FlowDocumentOptions {
|
|||||||
*/
|
*/
|
||||||
allNodesDefaultExpanded?: boolean;
|
allNodesDefaultExpanded?: boolean;
|
||||||
toNodeJSON?(node: FlowNodeEntity): FlowNodeJSON;
|
toNodeJSON?(node: FlowNodeEntity): FlowNodeJSON;
|
||||||
fromNodeJSON?(node: FlowNodeEntity, json: FlowNodeJSON): void;
|
fromNodeJSON?(node: FlowNodeEntity, json: FlowNodeJSON, isFirstCreate: boolean): void;
|
||||||
constants?: Record<string, any>;
|
constants?: Record<string, any>;
|
||||||
formatNodeLines?: (node: FlowNodeEntity, lines: FlowTransitionLine[]) => FlowTransitionLine[];
|
formatNodeLines?: (node: FlowNodeEntity, lines: FlowTransitionLine[]) => FlowTransitionLine[];
|
||||||
formatNodeLabels?: (node: FlowNodeEntity, lines: FlowTransitionLabel[]) => FlowTransitionLabel[];
|
formatNodeLabels?: (node: FlowNodeEntity, lines: FlowTransitionLabel[]) => FlowTransitionLabel[];
|
||||||
|
|||||||
@ -57,12 +57,22 @@ export class FlowDocument<T = FlowDocumentJSON> implements Disposable {
|
|||||||
|
|
||||||
protected onNodeUpdateEmitter = new Emitter<{
|
protected onNodeUpdateEmitter = new Emitter<{
|
||||||
node: FlowNodeEntity;
|
node: FlowNodeEntity;
|
||||||
|
/**
|
||||||
|
* use 'json' instead
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
data: FlowNodeJSON;
|
data: FlowNodeJSON;
|
||||||
|
json: FlowNodeJSON;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
protected onNodeCreateEmitter = new Emitter<{
|
protected onNodeCreateEmitter = new Emitter<{
|
||||||
node: FlowNodeEntity;
|
node: FlowNodeEntity;
|
||||||
|
/**
|
||||||
|
* use 'json' instead
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
data: FlowNodeJSON;
|
data: FlowNodeJSON;
|
||||||
|
json: FlowNodeJSON;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
protected onNodeDisposeEmitter = new Emitter<{
|
protected onNodeDisposeEmitter = new Emitter<{
|
||||||
@ -219,7 +229,7 @@ export class FlowDocument<T = FlowDocumentJSON> implements Disposable {
|
|||||||
addNode(
|
addNode(
|
||||||
data: AddNodeData,
|
data: AddNodeData,
|
||||||
addedNodes?: FlowNodeEntity[],
|
addedNodes?: FlowNodeEntity[],
|
||||||
ignoreCreateEvent?: boolean
|
ignoreCreateAndUpdateEvent?: boolean
|
||||||
): FlowNodeEntity {
|
): FlowNodeEntity {
|
||||||
const { id, type = 'block', originParent, parent, meta, hidden, index } = data;
|
const { id, type = 'block', originParent, parent, meta, hidden, index } = data;
|
||||||
let node = this.getNode(id);
|
let node = this.getNode(id);
|
||||||
@ -244,10 +254,10 @@ export class FlowDocument<T = FlowDocumentJSON> implements Disposable {
|
|||||||
: this.nodeDataRegistries;
|
: this.nodeDataRegistries;
|
||||||
node.addInitializeData(datas);
|
node.addInitializeData(datas);
|
||||||
node.onDispose(() => this.onNodeDisposeEmitter.fire({ node: node! }));
|
node.onDispose(() => this.onNodeDisposeEmitter.fire({ node: node! }));
|
||||||
if (this.options.fromNodeJSON) {
|
this.options.fromNodeJSON?.(node, data, true);
|
||||||
this.options.fromNodeJSON(node, data);
|
|
||||||
}
|
|
||||||
isNew = true;
|
isNew = true;
|
||||||
|
} else {
|
||||||
|
this.options.fromNodeJSON?.(node, data, false);
|
||||||
}
|
}
|
||||||
// 初始化数据重制
|
// 初始化数据重制
|
||||||
node.initData({
|
node.initData({
|
||||||
@ -261,7 +271,6 @@ export class FlowDocument<T = FlowDocumentJSON> implements Disposable {
|
|||||||
if (node.isStart) {
|
if (node.isStart) {
|
||||||
this.root.addChild(node);
|
this.root.addChild(node);
|
||||||
}
|
}
|
||||||
this.onNodeUpdateEmitter.fire({ node, data });
|
|
||||||
addedNodes?.push(node);
|
addedNodes?.push(node);
|
||||||
// 自定义创建逻辑
|
// 自定义创建逻辑
|
||||||
if (register.onCreate) {
|
if (register.onCreate) {
|
||||||
@ -278,11 +287,16 @@ export class FlowDocument<T = FlowDocumentJSON> implements Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNew && !ignoreCreateEvent) {
|
if (!ignoreCreateAndUpdateEvent) {
|
||||||
this.onNodeCreateEmitter.fire({
|
if (isNew) {
|
||||||
node,
|
this.onNodeCreateEmitter.fire({
|
||||||
data,
|
node,
|
||||||
});
|
data,
|
||||||
|
json: data,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.onNodeUpdateEmitter.fire({ node, data, json: data });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
|
|||||||
@ -14,19 +14,27 @@ export function toFormJSON(node: FlowNodeEntity) {
|
|||||||
return formData.toJSON();
|
return formData.toJSON();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initFormDataFromJSON(node: FlowNodeEntity, json: FlowNodeJSON) {
|
export function initFormDataFromJSON(
|
||||||
|
node: FlowNodeEntity,
|
||||||
|
json: FlowNodeJSON,
|
||||||
|
isFirstCreate: boolean
|
||||||
|
) {
|
||||||
const formData = node.getData(FlowNodeFormData)!;
|
const formData = node.getData(FlowNodeFormData)!;
|
||||||
const registry = node.getNodeRegistry();
|
const registry = node.getNodeRegistry();
|
||||||
const { formMeta } = registry;
|
const { formMeta } = registry;
|
||||||
|
|
||||||
if (formData && formMeta) {
|
if (formData && formMeta) {
|
||||||
formData.createForm(formMeta, json.data);
|
if (isFirstCreate) {
|
||||||
formData.onDataChange(() => {
|
formData.createForm(formMeta, json.data);
|
||||||
(node.document as WorkflowDocument).fireContentChange({
|
formData.onDataChange(() => {
|
||||||
type: WorkflowContentChangeType.NODE_DATA_CHANGE,
|
(node.document as WorkflowDocument).fireContentChange({
|
||||||
toJSON: () => formData.toJSON(),
|
type: WorkflowContentChangeType.NODE_DATA_CHANGE,
|
||||||
entity: node,
|
toJSON: () => formData.toJSON(),
|
||||||
|
entity: node,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
} else {
|
||||||
|
formData.updateFormValues(json.data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -92,8 +92,8 @@ export const WorkflowDocumentOptionsDefault: WorkflowDocumentOptions = {
|
|||||||
'url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMCAyMSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik02LjYxODE3IDUuNTk4NzVDNi42MTgxNyA0LjgxOTIgNy4yNTAxMiA0LjE4NzI2IDguMDI5NjcgNC4xODcyNkM4Ljc3ODczIDQuMTg3MjYgOS4zOTE1MiA0Ljc3MDc1IDkuNDM4MjkgNS41MDgwMUM5LjQ1OTkyIDQuOTQ3MSA5LjkyMTQ3IDQuNDk5MDIgMTAuNDg3NyA0LjQ5OTAyQzExLjA2NzcgNC40OTkwMiAxMS41Mzc4IDQuOTY5MiAxMS41Mzc4IDUuNTQ5MTlWOC43NjI0NkwxMS41Mzc5IDYuNzExNUMxMS41Mzc5IDYuMDMwOTUgMTIuMDg5NiA1LjQ3OTI2IDEyLjc3MDIgNS40NzkyNkMxMy40NTA3IDUuNDc5MjYgMTQuMDAyNCA2LjAzMDk1IDE0LjAwMjQgNi43MTE1TDE0LjAwMjQgOC43NjI0NkwxNC4wMDI5IDguMDE5ODNDMTQuMDAyOSA3LjMzOTI5IDE0LjU1NDYgNi43ODc1OSAxNS4yMzUyIDYuNzg3NTlDMTUuOTE1NyA2Ljc4NzU5IDE2LjQ2NzQgNy4zMzkyOCAxNi40Njc0IDguMDE5ODNWMTEuNDk3TDE2LjQ2NzUgMTIuNDQ2N0MxNi40Njc1IDE0LjA4MjEgMTUuODA5NiAxNS42NDg3IDE0LjY0MiAxNi43OTM4VjE3LjMyNjJDMTQuNjQyIDE3Ljc4NjQgMTQuMjY4OSAxOC4xNTk1IDEzLjgwODcgMTguMTU5NUg4LjE3MzE3QzcuNzEyOTMgMTguMTU5NSA3LjMzOTg0IDE3Ljc4NjQgNy4zMzk4NCAxNy4zMjYyVjE1Ljk0MjRMNS4zNDU2MiAxNC43NTM0QzQuNTg5MjQgMTQuMzAyNCA0LjEyNTkxIDEzLjQ4NjcgNC4xMjU4OSAxMi42MDYxTDQuMTI1ODMgOS4yODM4M0M0LjEyNTgyIDguOTU0MjcgNC4zMjAwMyA4LjY1NTY2IDQuNjIxMjkgOC41MjIwNUw2LjYxODE3IDcuNjM2MzRWNS41OTg3NVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTAuNDg3OCAzLjI0OTAyQzExLjI3OTYgMy4yNDkwMiAxMS45Nzc4IDMuNjQ5MDIgMTIuMzkxNyA0LjI1Nzk2QzEyLjUxNTEgNC4yMzkwNiAxMi42NDE2IDQuMjI5MjYgMTIuNzcwMyA0LjIyOTI2QzEzLjcyMjQgNC4yMjkyNiAxNC41NDkzIDQuNzY1MzEgMTQuOTY1NyA1LjU1MjA3QzE1LjA1NDMgNS41NDI1IDE1LjE0NDIgNS41Mzc1OSAxNS4yMzUzIDUuNTM3NTlDMTYuNjA2MiA1LjUzNzU5IDE3LjcxNzYgNi42NDg5MyAxNy43MTc2IDguMDE5ODNMMTcuNzE3NyAxMi40NDY3QzE3LjcxNzcgMTQuMjM1NSAxNy4wNjQ3IDE1Ljk1NTkgMTUuODkyMSAxNy4yOTA0VjE3LjMyNjJDMTUuODkyMSAxOC40NzY4IDE0Ljk1OTQgMTkuNDA5NSAxMy44MDg4IDE5LjQwOTVIOC4xNzMzMkM3LjAyMjczIDE5LjQwOTUgNi4wODk5OCAxOC40NzY4IDYuMDg5OTggMTcuMzI2MlYxNi42NTI0TDQuNzA1NjMgMTUuODI3QzMuNTcxMDYgMTUuMTUwNSAyLjg3NjA3IDEzLjkyNzEgMi44NzYwNCAxMi42MDYxTDIuODc1OTggOS4yODM4NUMyLjg3NTk2IDguNDU5OTYgMy4zNjE0OSA3LjcxMzQ1IDQuMTE0NjIgNy4zNzk0TDUuMzY4MzIgNi44MjMzM1Y1LjU5ODc1QzUuMzY4MzIgNC4xMjg4NSA2LjU1OTkxIDIuOTM3MjYgOC4wMjk4MiAyLjkzNzI2QzguNjA4MzEgMi45MzcyNiA5LjE0MzU1IDMuMTIxNyA5LjU4MDA1IDMuNDM1MDVDOS44NTg1MyAzLjMxNTMyIDEwLjE2NTQgMy4yNDkwMiAxMC40ODc4IDMuMjQ5MDJaTTEyLjQ0NzkgNS41MjE4NlY5LjQ3NTU3QzEyLjQ0NzkgOS43Mjc2MiAxMi4yNDM2IDkuOTMxOTUgMTEuOTkxNiA5LjkzMTk1QzExLjc1NjggOS45MzE5NSAxMS41NjM0IDkuNzU0NjUgMTEuNTM4IDkuNTI2NjNDMTEuNTM2MSA5LjUwOTg3IDExLjUzNTIgOS40OTI4MyAxMS41MzUyIDkuNDc1NTdWNS40NzE1OEMxMS41MTU0IDUuMjAwODMgMTEuMzkzIDQuOTU4NTggMTEuMjA2NiA0Ljc4MzU4QzExLjAxODggNC42MDcxMSAxMC43NjU5IDQuNDk5MDIgMTAuNDg3OCA0LjQ5OTAyQzEwLjQ3NjYgNC40OTkwMiAxMC40NjU0IDQuNDk5MTkgMTAuNDU0MiA0LjQ5OTU0QzkuOTAzNDcgNC41MTY4NCA5LjQ1OTY0IDQuOTU4MjQgOS40Mzg0NCA1LjUwODAxQzkuNDM4MiA1LjUwNDMgOS40Mzc5NSA1LjUwMDU4IDkuNDM3NjkgNS40OTY4OFY5LjkwMjQzQzkuNDM3NjkgMTAuMTU0NSA5LjIzMzM2IDEwLjM1ODggOC45ODEzMSAxMC4zNTg4QzguNzI5MjUgMTAuMzU4OCA4LjUyNDkyIDEwLjE1NDUgOC41MjQ5MiA5LjkwMjQzVjQuMjc2NTNDOC4zNzA4NiA0LjIxODgyIDguMjA0MDIgNC4xODcyNiA4LjAyOTgyIDQuMTg3MjZDNy4yNTAyNyA0LjE4NzI2IDYuNjE4MzIgNC44MTkyIDYuNjE4MzIgNS41OTg3NUw2LjYxODI3IDkuOTc1OTlDNi42MTgyNyAxMC4yMjggNi40MTM5NCAxMC40MzI0IDYuMTYxODkgMTAuNDMyNEM1LjkwOTgzIDEwLjQzMjQgNS43MDU1IDEwLjIyOCA1LjcwNTUgOS45NzU5OVY4LjA0MTIyTDQuNjIxNDQgOC41MjIwNUM0LjMyMDE4IDguNjU1NjYgNC4xMjU5NyA4Ljk1NDI3IDQuMTI1OTggOS4yODM4M0w0LjEyNjA0IDEyLjYwNjFDNC4xMjYwNiAxMy40ODY3IDQuNTg5MzkgMTQuMzAyNCA1LjM0NTc2IDE0Ljc1MzRMNy4zMzk5OCAxNS45NDI0VjE3LjMyNjJDNy4zMzk5OCAxNy43ODY0IDcuNzEzMDggMTguMTU5NSA4LjE3MzMyIDE4LjE1OTVIMTMuODA4OEMxNC4yNjkgMTguMTU5NSAxNC42NDIxIDE3Ljc4NjQgMTQuNjQyMSAxNy4zMjYyVjE2Ljc5MzhDMTUuNzM4MSAxNS43MTkgMTYuMzg1IDE0LjI3MjggMTYuNDYwMyAxMi43NDdDMTYuNDY0NiAxMi42NiAxNi40NjcgMTIuNTcyOCAxNi40Njc2IDEyLjQ4NTRMMTYuNDY3NyAxMi40NDY3TDE2LjQ2NzYgOC4wMTk4M0MxNi40Njc2IDcuMzQ1MDQgMTUuOTI1MiA2Ljc5NjkzIDE1LjI1MjUgNi43ODc3MUwxNS4yMzUzIDYuNzg3NTlDMTUuMTI1IDYuNzg3NTkgMTUuMDE4IDYuODAyMSAxNC45MTYyIDYuODI5MzFWOS42MDEzNkMxNC45MTYyIDkuODUzNDIgMTQuNzExOSAxMC4wNTc3IDE0LjQ1OTggMTAuMDU3N0MxNC4yMDc4IDEwLjA1NzcgMTQuMDAzNCA5Ljg1MzQxIDE0LjAwMzQgOS42MDEzNlY3Ljk4OTg1QzE0LjAwMzIgNy45OTk4MiAxNC4wMDMxIDguMDA5ODEgMTQuMDAzMSA4LjAxOTgzTDE0LjAwMzQgOS42MDEzNkwxNC4wMDI1IDYuNzExNUMxNC4wMDI1IDYuNDQ5NzQgMTMuOTIwOSA2LjIwNzA1IDEzLjc4MTggNi4wMDc0OEMxMy41NjIgNS42OTI0MiAxMy4xOTg5IDUuNDg0ODMgMTIuNzg3IDUuNDc5MzdMMTIuNzcwMyA1LjQ3OTI2QzEyLjY1ODggNS40NzkyNiAxMi41NTA3IDUuNDk0MDggMTIuNDQ3OSA1LjUyMTg2WiIgZmlsbD0iIzFEMUMyMyIvPgo8L3N2Zz4="), auto',
|
'url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMCAyMSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik02LjYxODE3IDUuNTk4NzVDNi42MTgxNyA0LjgxOTIgNy4yNTAxMiA0LjE4NzI2IDguMDI5NjcgNC4xODcyNkM4Ljc3ODczIDQuMTg3MjYgOS4zOTE1MiA0Ljc3MDc1IDkuNDM4MjkgNS41MDgwMUM5LjQ1OTkyIDQuOTQ3MSA5LjkyMTQ3IDQuNDk5MDIgMTAuNDg3NyA0LjQ5OTAyQzExLjA2NzcgNC40OTkwMiAxMS41Mzc4IDQuOTY5MiAxMS41Mzc4IDUuNTQ5MTlWOC43NjI0NkwxMS41Mzc5IDYuNzExNUMxMS41Mzc5IDYuMDMwOTUgMTIuMDg5NiA1LjQ3OTI2IDEyLjc3MDIgNS40NzkyNkMxMy40NTA3IDUuNDc5MjYgMTQuMDAyNCA2LjAzMDk1IDE0LjAwMjQgNi43MTE1TDE0LjAwMjQgOC43NjI0NkwxNC4wMDI5IDguMDE5ODNDMTQuMDAyOSA3LjMzOTI5IDE0LjU1NDYgNi43ODc1OSAxNS4yMzUyIDYuNzg3NTlDMTUuOTE1NyA2Ljc4NzU5IDE2LjQ2NzQgNy4zMzkyOCAxNi40Njc0IDguMDE5ODNWMTEuNDk3TDE2LjQ2NzUgMTIuNDQ2N0MxNi40Njc1IDE0LjA4MjEgMTUuODA5NiAxNS42NDg3IDE0LjY0MiAxNi43OTM4VjE3LjMyNjJDMTQuNjQyIDE3Ljc4NjQgMTQuMjY4OSAxOC4xNTk1IDEzLjgwODcgMTguMTU5NUg4LjE3MzE3QzcuNzEyOTMgMTguMTU5NSA3LjMzOTg0IDE3Ljc4NjQgNy4zMzk4NCAxNy4zMjYyVjE1Ljk0MjRMNS4zNDU2MiAxNC43NTM0QzQuNTg5MjQgMTQuMzAyNCA0LjEyNTkxIDEzLjQ4NjcgNC4xMjU4OSAxMi42MDYxTDQuMTI1ODMgOS4yODM4M0M0LjEyNTgyIDguOTU0MjcgNC4zMjAwMyA4LjY1NTY2IDQuNjIxMjkgOC41MjIwNUw2LjYxODE3IDcuNjM2MzRWNS41OTg3NVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTAuNDg3OCAzLjI0OTAyQzExLjI3OTYgMy4yNDkwMiAxMS45Nzc4IDMuNjQ5MDIgMTIuMzkxNyA0LjI1Nzk2QzEyLjUxNTEgNC4yMzkwNiAxMi42NDE2IDQuMjI5MjYgMTIuNzcwMyA0LjIyOTI2QzEzLjcyMjQgNC4yMjkyNiAxNC41NDkzIDQuNzY1MzEgMTQuOTY1NyA1LjU1MjA3QzE1LjA1NDMgNS41NDI1IDE1LjE0NDIgNS41Mzc1OSAxNS4yMzUzIDUuNTM3NTlDMTYuNjA2MiA1LjUzNzU5IDE3LjcxNzYgNi42NDg5MyAxNy43MTc2IDguMDE5ODNMMTcuNzE3NyAxMi40NDY3QzE3LjcxNzcgMTQuMjM1NSAxNy4wNjQ3IDE1Ljk1NTkgMTUuODkyMSAxNy4yOTA0VjE3LjMyNjJDMTUuODkyMSAxOC40NzY4IDE0Ljk1OTQgMTkuNDA5NSAxMy44MDg4IDE5LjQwOTVIOC4xNzMzMkM3LjAyMjczIDE5LjQwOTUgNi4wODk5OCAxOC40NzY4IDYuMDg5OTggMTcuMzI2MlYxNi42NTI0TDQuNzA1NjMgMTUuODI3QzMuNTcxMDYgMTUuMTUwNSAyLjg3NjA3IDEzLjkyNzEgMi44NzYwNCAxMi42MDYxTDIuODc1OTggOS4yODM4NUMyLjg3NTk2IDguNDU5OTYgMy4zNjE0OSA3LjcxMzQ1IDQuMTE0NjIgNy4zNzk0TDUuMzY4MzIgNi44MjMzM1Y1LjU5ODc1QzUuMzY4MzIgNC4xMjg4NSA2LjU1OTkxIDIuOTM3MjYgOC4wMjk4MiAyLjkzNzI2QzguNjA4MzEgMi45MzcyNiA5LjE0MzU1IDMuMTIxNyA5LjU4MDA1IDMuNDM1MDVDOS44NTg1MyAzLjMxNTMyIDEwLjE2NTQgMy4yNDkwMiAxMC40ODc4IDMuMjQ5MDJaTTEyLjQ0NzkgNS41MjE4NlY5LjQ3NTU3QzEyLjQ0NzkgOS43Mjc2MiAxMi4yNDM2IDkuOTMxOTUgMTEuOTkxNiA5LjkzMTk1QzExLjc1NjggOS45MzE5NSAxMS41NjM0IDkuNzU0NjUgMTEuNTM4IDkuNTI2NjNDMTEuNTM2MSA5LjUwOTg3IDExLjUzNTIgOS40OTI4MyAxMS41MzUyIDkuNDc1NTdWNS40NzE1OEMxMS41MTU0IDUuMjAwODMgMTEuMzkzIDQuOTU4NTggMTEuMjA2NiA0Ljc4MzU4QzExLjAxODggNC42MDcxMSAxMC43NjU5IDQuNDk5MDIgMTAuNDg3OCA0LjQ5OTAyQzEwLjQ3NjYgNC40OTkwMiAxMC40NjU0IDQuNDk5MTkgMTAuNDU0MiA0LjQ5OTU0QzkuOTAzNDcgNC41MTY4NCA5LjQ1OTY0IDQuOTU4MjQgOS40Mzg0NCA1LjUwODAxQzkuNDM4MiA1LjUwNDMgOS40Mzc5NSA1LjUwMDU4IDkuNDM3NjkgNS40OTY4OFY5LjkwMjQzQzkuNDM3NjkgMTAuMTU0NSA5LjIzMzM2IDEwLjM1ODggOC45ODEzMSAxMC4zNTg4QzguNzI5MjUgMTAuMzU4OCA4LjUyNDkyIDEwLjE1NDUgOC41MjQ5MiA5LjkwMjQzVjQuMjc2NTNDOC4zNzA4NiA0LjIxODgyIDguMjA0MDIgNC4xODcyNiA4LjAyOTgyIDQuMTg3MjZDNy4yNTAyNyA0LjE4NzI2IDYuNjE4MzIgNC44MTkyIDYuNjE4MzIgNS41OTg3NUw2LjYxODI3IDkuOTc1OTlDNi42MTgyNyAxMC4yMjggNi40MTM5NCAxMC40MzI0IDYuMTYxODkgMTAuNDMyNEM1LjkwOTgzIDEwLjQzMjQgNS43MDU1IDEwLjIyOCA1LjcwNTUgOS45NzU5OVY4LjA0MTIyTDQuNjIxNDQgOC41MjIwNUM0LjMyMDE4IDguNjU1NjYgNC4xMjU5NyA4Ljk1NDI3IDQuMTI1OTggOS4yODM4M0w0LjEyNjA0IDEyLjYwNjFDNC4xMjYwNiAxMy40ODY3IDQuNTg5MzkgMTQuMzAyNCA1LjM0NTc2IDE0Ljc1MzRMNy4zMzk5OCAxNS45NDI0VjE3LjMyNjJDNy4zMzk5OCAxNy43ODY0IDcuNzEzMDggMTguMTU5NSA4LjE3MzMyIDE4LjE1OTVIMTMuODA4OEMxNC4yNjkgMTguMTU5NSAxNC42NDIxIDE3Ljc4NjQgMTQuNjQyMSAxNy4zMjYyVjE2Ljc5MzhDMTUuNzM4MSAxNS43MTkgMTYuMzg1IDE0LjI3MjggMTYuNDYwMyAxMi43NDdDMTYuNDY0NiAxMi42NiAxNi40NjcgMTIuNTcyOCAxNi40Njc2IDEyLjQ4NTRMMTYuNDY3NyAxMi40NDY3TDE2LjQ2NzYgOC4wMTk4M0MxNi40Njc2IDcuMzQ1MDQgMTUuOTI1MiA2Ljc5NjkzIDE1LjI1MjUgNi43ODc3MUwxNS4yMzUzIDYuNzg3NTlDMTUuMTI1IDYuNzg3NTkgMTUuMDE4IDYuODAyMSAxNC45MTYyIDYuODI5MzFWOS42MDEzNkMxNC45MTYyIDkuODUzNDIgMTQuNzExOSAxMC4wNTc3IDE0LjQ1OTggMTAuMDU3N0MxNC4yMDc4IDEwLjA1NzcgMTQuMDAzNCA5Ljg1MzQxIDE0LjAwMzQgOS42MDEzNlY3Ljk4OTg1QzE0LjAwMzIgNy45OTk4MiAxNC4wMDMxIDguMDA5ODEgMTQuMDAzMSA4LjAxOTgzTDE0LjAwMzQgOS42MDEzNkwxNC4wMDI1IDYuNzExNUMxNC4wMDI1IDYuNDQ5NzQgMTMuOTIwOSA2LjIwNzA1IDEzLjc4MTggNi4wMDc0OEMxMy41NjIgNS42OTI0MiAxMy4xOTg5IDUuNDg0ODMgMTIuNzg3IDUuNDc5MzdMMTIuNzcwMyA1LjQ3OTI2QzEyLjY1ODggNS40NzkyNiAxMi41NTA3IDUuNDk0MDggMTIuNDQ3OSA1LjUyMTg2WiIgZmlsbD0iIzFEMUMyMyIvPgo8L3N2Zz4="), auto',
|
||||||
},
|
},
|
||||||
|
|
||||||
fromNodeJSON(node, json) {
|
fromNodeJSON(node, json, isFirstCreate) {
|
||||||
initFormDataFromJSON(node, json);
|
initFormDataFromJSON(node, json, isFirstCreate);
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
toNodeJSON(node: WorkflowNodeEntity): WorkflowNodeJSON {
|
toNodeJSON(node: WorkflowNodeEntity): WorkflowNodeJSON {
|
||||||
|
|||||||
@ -180,10 +180,12 @@ export class WorkflowDocument extends FlowDocument {
|
|||||||
|
|
||||||
const transform = node.getData<FlowNodeTransformData>(FlowNodeTransformData)!;
|
const transform = node.getData<FlowNodeTransformData>(FlowNodeTransformData)!;
|
||||||
const freeLayout = this.layout as FreeLayout;
|
const freeLayout = this.layout as FreeLayout;
|
||||||
transform.onDataChange(() => {
|
if (!isExistedNode) {
|
||||||
// TODO 这个有点难以理解,其实是为了同步size 数据
|
transform.onDataChange(() => {
|
||||||
freeLayout.syncTransform(node);
|
// TODO 这个有点难以理解,其实是为了同步size 数据
|
||||||
});
|
freeLayout.syncTransform(node);
|
||||||
|
});
|
||||||
|
}
|
||||||
let { position } = meta;
|
let { position } = meta;
|
||||||
if (!position) {
|
if (!position) {
|
||||||
// 获取默认的位置
|
// 获取默认的位置
|
||||||
@ -210,13 +212,15 @@ export class WorkflowDocument extends FlowDocument {
|
|||||||
}
|
}
|
||||||
// 位置变更
|
// 位置变更
|
||||||
const positionData = node.getData<PositionData>(PositionData)!;
|
const positionData = node.getData<PositionData>(PositionData)!;
|
||||||
positionData.onDataChange(() => {
|
if (!isExistedNode) {
|
||||||
this.fireContentChange({
|
positionData.onDataChange(() => {
|
||||||
type: WorkflowContentChangeType.MOVE_NODE,
|
this.fireContentChange({
|
||||||
toJSON: () => positionData.toJSON(),
|
type: WorkflowContentChangeType.MOVE_NODE,
|
||||||
entity: node,
|
toJSON: () => positionData.toJSON(),
|
||||||
|
entity: node,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
const subCanvas = this.getNodeSubCanvas(node);
|
const subCanvas = this.getNodeSubCanvas(node);
|
||||||
|
|
||||||
@ -265,19 +269,29 @@ export class WorkflowDocument extends FlowDocument {
|
|||||||
canvasTransform.update({
|
canvasTransform.update({
|
||||||
position: subCanvas.parentNode.getNodeMeta()?.canvasPosition,
|
position: subCanvas.parentNode.getNodeMeta()?.canvasPosition,
|
||||||
});
|
});
|
||||||
subCanvas.parentNode.onDispose(() => {
|
if (!isExistedNode) {
|
||||||
subCanvas.canvasNode.dispose();
|
subCanvas.parentNode.onDispose(() => {
|
||||||
|
subCanvas.canvasNode.dispose();
|
||||||
|
});
|
||||||
|
subCanvas.canvasNode.onDispose(() => {
|
||||||
|
subCanvas.parentNode.dispose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isExistedNode) {
|
||||||
|
this.onNodeCreateEmitter.fire({
|
||||||
|
node,
|
||||||
|
data: json,
|
||||||
|
json,
|
||||||
});
|
});
|
||||||
subCanvas.canvasNode.onDispose(() => {
|
} else {
|
||||||
subCanvas.parentNode.dispose();
|
this.onNodeUpdateEmitter.fire({
|
||||||
|
node,
|
||||||
|
data: json,
|
||||||
|
json,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onNodeCreateEmitter.fire({
|
|
||||||
node,
|
|
||||||
data: json,
|
|
||||||
});
|
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -554,8 +568,6 @@ export class WorkflowDocument extends FlowDocument {
|
|||||||
* 导出数据
|
* 导出数据
|
||||||
*/
|
*/
|
||||||
toJSON(): WorkflowJSON {
|
toJSON(): WorkflowJSON {
|
||||||
// 要等 一些节点的 dispose 触发结束
|
|
||||||
// await delay(10);
|
|
||||||
const rootJSON = this.toNodeJSON(this.root);
|
const rootJSON = this.toNodeJSON(this.root);
|
||||||
return {
|
return {
|
||||||
nodes: rootJSON.blocks ?? [],
|
nodes: rootJSON.blocks ?? [],
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
export const EditorOptions = Symbol('EditorOptions');
|
|
||||||
@ -10,7 +10,6 @@ import { FlowDocumentContainerModule } from '@flowgram.ai/document';
|
|||||||
import { createPlaygroundPlugin, Plugin, PluginsProvider } from '@flowgram.ai/core';
|
import { createPlaygroundPlugin, Plugin, PluginsProvider } from '@flowgram.ai/core';
|
||||||
|
|
||||||
import { compose } from '../utils/compose';
|
import { compose } from '../utils/compose';
|
||||||
import { EditorOptions } from '../constants';
|
|
||||||
import { createFlowEditorClientPlugins } from '../clients/flow-editor-client-plugins';
|
import { createFlowEditorClientPlugins } from '../clients/flow-editor-client-plugins';
|
||||||
import { EditorPluginContext, EditorProps } from './editor-props';
|
import { EditorPluginContext, EditorProps } from './editor-props';
|
||||||
|
|
||||||
@ -20,7 +19,6 @@ export function createDefaultPreset<CTX extends EditorPluginContext = EditorPlug
|
|||||||
): PluginsProvider<CTX> {
|
): PluginsProvider<CTX> {
|
||||||
return (ctx: CTX) => {
|
return (ctx: CTX) => {
|
||||||
opts = { ...EditorProps.DEFAULT, ...opts };
|
opts = { ...EditorProps.DEFAULT, ...opts };
|
||||||
ctx.container.bind(EditorOptions).toConstantValue(opts);
|
|
||||||
/**
|
/**
|
||||||
* i18n support
|
* i18n support
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { NodeCorePluginOptions } from '@flowgram.ai/node-core-plugin';
|
|||||||
import { MaterialsPluginOptions } from '@flowgram.ai/materials-plugin';
|
import { MaterialsPluginOptions } from '@flowgram.ai/materials-plugin';
|
||||||
import { I18nPluginOptions } from '@flowgram.ai/i18n-plugin';
|
import { I18nPluginOptions } from '@flowgram.ai/i18n-plugin';
|
||||||
import { HistoryPluginOptions } from '@flowgram.ai/history';
|
import { HistoryPluginOptions } from '@flowgram.ai/history';
|
||||||
import { FlowNodeFormData, FormMetaOrFormMetaGenerator } from '@flowgram.ai/form-core';
|
import { FormMetaOrFormMetaGenerator } from '@flowgram.ai/form-core';
|
||||||
import {
|
import {
|
||||||
FlowDocument,
|
FlowDocument,
|
||||||
FlowDocumentJSON,
|
FlowDocumentJSON,
|
||||||
@ -18,8 +18,6 @@ import {
|
|||||||
} from '@flowgram.ai/document';
|
} from '@flowgram.ai/document';
|
||||||
import { PluginContext } from '@flowgram.ai/core';
|
import { PluginContext } from '@flowgram.ai/core';
|
||||||
|
|
||||||
import { EditorOptions } from '../constants';
|
|
||||||
|
|
||||||
export interface EditorPluginContext extends PluginContext {
|
export interface EditorPluginContext extends PluginContext {
|
||||||
document: FlowDocument;
|
document: FlowDocument;
|
||||||
selection: SelectionService;
|
selection: SelectionService;
|
||||||
@ -94,12 +92,18 @@ export interface EditorProps<
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 节点转
|
* 节点数据导出
|
||||||
* @param node
|
* - node 当前节点
|
||||||
|
* - json 当前节点数据
|
||||||
*/
|
*/
|
||||||
toNodeJSON?(node: FlowNodeEntity): FlowNodeJSON;
|
toNodeJSON?(node: FlowNodeEntity, json: FlowNodeJSON): FlowNodeJSON;
|
||||||
fromNodeJSON?(node: FlowNodeEntity, json: FlowNodeJSON): void;
|
/**
|
||||||
|
* 节点数据导入
|
||||||
|
* - node 当前节点
|
||||||
|
* - json 当前节点数据
|
||||||
|
* - isFirstCreate 是否是第一次创建
|
||||||
|
*/
|
||||||
|
fromNodeJSON?(node: FlowNodeEntity, json: FlowNodeJSON, isFirstCreate: boolean): FlowNodeJSON;
|
||||||
/**
|
/**
|
||||||
* 画布内部常量自定义
|
* 画布内部常量自定义
|
||||||
*/
|
*/
|
||||||
@ -124,54 +128,5 @@ export namespace EditorProps {
|
|||||||
*/
|
*/
|
||||||
export const DEFAULT: EditorProps = {
|
export const DEFAULT: EditorProps = {
|
||||||
background: {},
|
background: {},
|
||||||
fromNodeJSON(node: FlowNodeEntity, json: FlowNodeJSON) {
|
|
||||||
const formData = node.getData(FlowNodeFormData)!;
|
|
||||||
// 如果没有使用表单引擎,将 data 数据填入 extInfo
|
|
||||||
if (!formData) {
|
|
||||||
if (json.data) {
|
|
||||||
node.updateExtInfo(json.data);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const defaultFormMeta = node
|
|
||||||
.getService<EditorProps>(EditorOptions)
|
|
||||||
.nodeEngine?.createDefaultFormMeta?.(node);
|
|
||||||
|
|
||||||
const formMeta = node.getNodeRegistry()?.formMeta || defaultFormMeta;
|
|
||||||
|
|
||||||
if (formMeta) {
|
|
||||||
formData.createForm(formMeta, json.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toNodeJSON(node: FlowNodeEntity): FlowNodeJSON {
|
|
||||||
const nodesMap: Record<string, FlowNodeJSON> = {};
|
|
||||||
let startNodeJSON: FlowNodeJSON;
|
|
||||||
node.document.traverse((node) => {
|
|
||||||
const isSystemNode = node.id.startsWith('$');
|
|
||||||
if (isSystemNode) return;
|
|
||||||
const formData = node.getData(FlowNodeFormData);
|
|
||||||
let formJSON =
|
|
||||||
formData && formData.formModel && formData.formModel.initialized
|
|
||||||
? formData.toJSON()
|
|
||||||
: undefined;
|
|
||||||
const nodeJSON = {
|
|
||||||
id: node.id,
|
|
||||||
type: node.flowNodeType,
|
|
||||||
data: formData ? formJSON : node.getExtInfo(),
|
|
||||||
blocks: [],
|
|
||||||
};
|
|
||||||
if (!startNodeJSON) startNodeJSON = nodeJSON;
|
|
||||||
let { parent } = node;
|
|
||||||
if (parent && parent.id.startsWith('$')) {
|
|
||||||
parent = parent.originParent;
|
|
||||||
}
|
|
||||||
const parentJSON = parent ? nodesMap[parent.id] : undefined;
|
|
||||||
if (parentJSON) {
|
|
||||||
parentJSON.blocks?.push(nodeJSON);
|
|
||||||
}
|
|
||||||
nodesMap[node.id] = nodeJSON;
|
|
||||||
}, node);
|
|
||||||
return startNodeJSON!;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,6 +26,17 @@ export const formMock: FlowDocumentJSON = {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const formMock2: FlowDocumentJSON = {
|
||||||
|
nodes: [{
|
||||||
|
id: 'noop_0',
|
||||||
|
type: 'noop',
|
||||||
|
data: {
|
||||||
|
title: 'noop title changed',
|
||||||
|
},
|
||||||
|
blocks: [],
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
export const baseWithDataMock: FlowDocumentJSON = {
|
export const baseWithDataMock: FlowDocumentJSON = {
|
||||||
nodes: [ {
|
nodes: [ {
|
||||||
id: 'start_0',
|
id: 'start_0',
|
||||||
@ -58,6 +69,39 @@ export const baseWithDataMock: FlowDocumentJSON = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const baseWithDataMock2: FlowDocumentJSON = {
|
||||||
|
nodes: [ {
|
||||||
|
id: 'start_0',
|
||||||
|
type: 'start',
|
||||||
|
data: {
|
||||||
|
title: 'start title changed',
|
||||||
|
},
|
||||||
|
blocks: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'dynamicSplit_0',
|
||||||
|
type: 'dynamicSplit',
|
||||||
|
data: {
|
||||||
|
title: 'dynamic title changed',
|
||||||
|
},
|
||||||
|
blocks: [
|
||||||
|
{ id: 'block_3', data: { title: '' }, blocks: [], type: 'block' },
|
||||||
|
{ id: 'block_4',data: { title: '' }, blocks: [], type: 'block'},
|
||||||
|
{ id: 'block_2',data: { title: 'title changed' },blocks: [], type: 'block' }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'end_0',
|
||||||
|
type: 'end',
|
||||||
|
data: {
|
||||||
|
title: 'end title changed',
|
||||||
|
},
|
||||||
|
blocks: [],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export const baseMock: FlowDocumentJSON = {
|
export const baseMock: FlowDocumentJSON = {
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,5 +1,141 @@
|
|||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`fixed-layout-preset > custom fromNodeJSON and toNodeJSON 1`] = `
|
||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"blocks": [],
|
||||||
|
"data": {
|
||||||
|
"isFirstCreate": true,
|
||||||
|
"runningTimes": 1,
|
||||||
|
"title": "start title",
|
||||||
|
},
|
||||||
|
"id": "start_0",
|
||||||
|
"type": "start",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"blocks": [],
|
||||||
|
"data": {
|
||||||
|
"isFirstCreate": true,
|
||||||
|
"runningTimes": 1,
|
||||||
|
"title": "",
|
||||||
|
},
|
||||||
|
"id": "block_0",
|
||||||
|
"type": "block",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [],
|
||||||
|
"data": {
|
||||||
|
"isFirstCreate": true,
|
||||||
|
"runningTimes": 1,
|
||||||
|
"title": "",
|
||||||
|
},
|
||||||
|
"id": "block_1",
|
||||||
|
"type": "block",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [],
|
||||||
|
"data": {
|
||||||
|
"isFirstCreate": true,
|
||||||
|
"runningTimes": 1,
|
||||||
|
"title": "",
|
||||||
|
},
|
||||||
|
"id": "block_2",
|
||||||
|
"type": "block",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"data": {
|
||||||
|
"isFirstCreate": true,
|
||||||
|
"runningTimes": 1,
|
||||||
|
"title": "dynamic title",
|
||||||
|
},
|
||||||
|
"id": "dynamicSplit_0",
|
||||||
|
"type": "dynamicSplit",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [],
|
||||||
|
"data": {
|
||||||
|
"isFirstCreate": true,
|
||||||
|
"runningTimes": 1,
|
||||||
|
"title": "end title",
|
||||||
|
},
|
||||||
|
"id": "end_0",
|
||||||
|
"type": "end",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`fixed-layout-preset > custom fromNodeJSON and toNodeJSON 2`] = `
|
||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"blocks": [],
|
||||||
|
"data": {
|
||||||
|
"isFirstCreate": false,
|
||||||
|
"runningTimes": 1,
|
||||||
|
"title": "start title changed",
|
||||||
|
},
|
||||||
|
"id": "start_0",
|
||||||
|
"type": "start",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"blocks": [],
|
||||||
|
"data": {
|
||||||
|
"isFirstCreate": true,
|
||||||
|
"runningTimes": 1,
|
||||||
|
"title": "",
|
||||||
|
},
|
||||||
|
"id": "block_3",
|
||||||
|
"type": "block",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [],
|
||||||
|
"data": {
|
||||||
|
"isFirstCreate": true,
|
||||||
|
"runningTimes": 1,
|
||||||
|
"title": "",
|
||||||
|
},
|
||||||
|
"id": "block_4",
|
||||||
|
"type": "block",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [],
|
||||||
|
"data": {
|
||||||
|
"isFirstCreate": false,
|
||||||
|
"runningTimes": 1,
|
||||||
|
"title": "title changed",
|
||||||
|
},
|
||||||
|
"id": "block_2",
|
||||||
|
"type": "block",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"data": {
|
||||||
|
"isFirstCreate": false,
|
||||||
|
"runningTimes": 1,
|
||||||
|
"title": "dynamic title changed",
|
||||||
|
},
|
||||||
|
"id": "dynamicSplit_0",
|
||||||
|
"type": "dynamicSplit",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [],
|
||||||
|
"data": {
|
||||||
|
"isFirstCreate": false,
|
||||||
|
"runningTimes": 1,
|
||||||
|
"title": "end title changed",
|
||||||
|
},
|
||||||
|
"id": "end_0",
|
||||||
|
"type": "end",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`fixed-layout-preset > nodeEngine(v2) toJSON 1`] = `
|
exports[`fixed-layout-preset > nodeEngine(v2) toJSON 1`] = `
|
||||||
{
|
{
|
||||||
"nodes": [
|
"nodes": [
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
import { FlowDocument, FlowNodeFormData } from '@flowgram.ai/editor';
|
import { FlowDocument, FlowNodeFormData } from '@flowgram.ai/editor';
|
||||||
|
|
||||||
import { baseWithDataMock, formMock } from '../__mocks__/flow.mock';
|
import { baseWithDataMock, baseWithDataMock2, formMock, formMock2 } from '../__mocks__/flow.mock';
|
||||||
import { createContainer } from './create-container';
|
import { createContainer } from './create-container';
|
||||||
|
|
||||||
describe('fixed-layout-preset', () => {
|
describe('fixed-layout-preset', () => {
|
||||||
@ -13,6 +13,28 @@ describe('fixed-layout-preset', () => {
|
|||||||
it('fromJSON and toJSON', () => {
|
it('fromJSON and toJSON', () => {
|
||||||
flowDocument.fromJSON(baseWithDataMock);
|
flowDocument.fromJSON(baseWithDataMock);
|
||||||
expect(flowDocument.toJSON()).toEqual(baseWithDataMock);
|
expect(flowDocument.toJSON()).toEqual(baseWithDataMock);
|
||||||
|
// reload data
|
||||||
|
flowDocument.fromJSON(baseWithDataMock2);
|
||||||
|
expect(flowDocument.toJSON()).toEqual(baseWithDataMock2);
|
||||||
|
});
|
||||||
|
it('custom fromNodeJSON and toNodeJSON', () => {
|
||||||
|
const container = createContainer({
|
||||||
|
fromNodeJSON: (node, json, isFirstCreate) => {
|
||||||
|
if (!json.data) {
|
||||||
|
json.data = {};
|
||||||
|
}
|
||||||
|
json.data = { ...json.data, isFirstCreate };
|
||||||
|
return json;
|
||||||
|
},
|
||||||
|
toNodeJSON(node, json) {
|
||||||
|
json.data.runningTimes = (json.data.runningTimes || 0) + 1;
|
||||||
|
return json;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
container.get(FlowDocument).fromJSON(baseWithDataMock);
|
||||||
|
expect(container.get(FlowDocument).toJSON()).toMatchSnapshot();
|
||||||
|
container.get(FlowDocument).fromJSON(baseWithDataMock2);
|
||||||
|
expect(container.get(FlowDocument).toJSON()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
it('nodeEngine(v2) toJSON', async () => {
|
it('nodeEngine(v2) toJSON', async () => {
|
||||||
const container = createContainer({
|
const container = createContainer({
|
||||||
@ -33,5 +55,7 @@ describe('fixed-layout-preset', () => {
|
|||||||
expect(formModel.getFormItemByPath('title').value).toEqual('noop title');
|
expect(formModel.getFormItemByPath('title').value).toEqual('noop title');
|
||||||
formModel.getFormItemByPath('title').value = 'noop title2';
|
formModel.getFormItemByPath('title').value = 'noop title2';
|
||||||
expect(flowDocument.toJSON()).toMatchSnapshot();
|
expect(flowDocument.toJSON()).toMatchSnapshot();
|
||||||
|
flowDocument.fromJSON(formMock2);
|
||||||
|
expect(flowDocument.toJSON()).toEqual(formMock2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { beforeEach, describe, it, expect } from 'vitest';
|
import { beforeEach, describe, it, expect } from 'vitest';
|
||||||
import { FlowNodeEntity, FlowNodeFormData, FlowNodeJSON, FormModelV2 } from '@flowgram.ai/editor';
|
import { FlowNodeFormData, FormModelV2 } from '@flowgram.ai/editor';
|
||||||
|
|
||||||
import { createHistoryContainer } from '../../create-container';
|
import { createHistoryContainer } from '../../create-container';
|
||||||
import { formMock } from '../../../__mocks__/form.mock';
|
import { formMock } from '../../../__mocks__/form.mock';
|
||||||
@ -7,14 +7,6 @@ import { emptyMock } from '../../../__mocks__/flow.mock';
|
|||||||
|
|
||||||
describe('history-operation-service changeFormData', () => {
|
describe('history-operation-service changeFormData', () => {
|
||||||
const { flowDocument, flowOperationService, historyService } = createHistoryContainer({
|
const { flowDocument, flowOperationService, historyService } = createHistoryContainer({
|
||||||
fromNodeJSON(node: FlowNodeEntity, json: FlowNodeJSON) {
|
|
||||||
const formData = node.getData(FlowNodeFormData)!;
|
|
||||||
const formMeta = node.getNodeRegistry()?.formMeta;
|
|
||||||
|
|
||||||
if (formMeta) {
|
|
||||||
formData.createForm(formMeta, json.data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
nodeEngine: {},
|
nodeEngine: {},
|
||||||
nodeRegistries: [
|
nodeRegistries: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import {
|
|||||||
|
|
||||||
import { FlowOperationService } from '../types';
|
import { FlowOperationService } from '../types';
|
||||||
import { createOperationPlugin } from '../plugins/create-operation-plugin';
|
import { createOperationPlugin } from '../plugins/create-operation-plugin';
|
||||||
|
import { fromNodeJSON, toNodeJSON } from './node-serialize';
|
||||||
import { FixedLayoutPluginContext, FixedLayoutProps } from './fixed-layout-props';
|
import { FixedLayoutPluginContext, FixedLayoutProps } from './fixed-layout-props';
|
||||||
|
|
||||||
export function createFixedLayoutPreset(
|
export function createFixedLayoutPreset(
|
||||||
@ -128,8 +129,9 @@ export function createFixedLayoutPreset(
|
|||||||
bindConfig.bind(FlowDocumentOptions).toConstantValue({
|
bindConfig.bind(FlowDocumentOptions).toConstantValue({
|
||||||
...FlowDocumentOptionsDefault,
|
...FlowDocumentOptionsDefault,
|
||||||
defaultLayout: opts.defaultLayout,
|
defaultLayout: opts.defaultLayout,
|
||||||
toNodeJSON: opts.toNodeJSON,
|
toNodeJSON: (node) => toNodeJSON(opts, node),
|
||||||
fromNodeJSON: opts.fromNodeJSON,
|
fromNodeJSON: (node, json, isFirstCreate) =>
|
||||||
|
fromNodeJSON(opts, node, json, isFirstCreate),
|
||||||
allNodesDefaultExpanded: opts.allNodesDefaultExpanded,
|
allNodesDefaultExpanded: opts.allNodesDefaultExpanded,
|
||||||
} as FlowDocumentOptions);
|
} as FlowDocumentOptions);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,67 @@
|
|||||||
|
import { FlowNodeEntity, FlowNodeJSON, FlowNodeFormData } from '@flowgram.ai/editor';
|
||||||
|
|
||||||
|
import { FixedLayoutProps } from './fixed-layout-props';
|
||||||
|
|
||||||
|
export function fromNodeJSON(
|
||||||
|
opts: FixedLayoutProps,
|
||||||
|
node: FlowNodeEntity,
|
||||||
|
json: FlowNodeJSON,
|
||||||
|
isFirstCreate: boolean
|
||||||
|
) {
|
||||||
|
json = opts.fromNodeJSON ? opts.fromNodeJSON(node, json, isFirstCreate) : json;
|
||||||
|
const formData = node.getData(FlowNodeFormData)!;
|
||||||
|
// 如果没有使用表单引擎,将 data 数据填入 extInfo
|
||||||
|
if (!formData) {
|
||||||
|
if (json.data) {
|
||||||
|
node.updateExtInfo(json.data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const defaultFormMeta = opts.nodeEngine?.createDefaultFormMeta?.(node);
|
||||||
|
|
||||||
|
const formMeta = node.getNodeRegistry()?.formMeta || defaultFormMeta;
|
||||||
|
|
||||||
|
if (formMeta) {
|
||||||
|
if (isFirstCreate) {
|
||||||
|
formData.createForm(formMeta, json.data);
|
||||||
|
} else {
|
||||||
|
formData.updateFormValues(json.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toNodeJSON(opts: FixedLayoutProps, node: FlowNodeEntity): FlowNodeJSON {
|
||||||
|
const nodesMap: Record<string, FlowNodeJSON> = {};
|
||||||
|
let startNodeJSON: FlowNodeJSON;
|
||||||
|
node.document.traverse((node) => {
|
||||||
|
const isSystemNode = node.id.startsWith('$');
|
||||||
|
if (isSystemNode) return;
|
||||||
|
const formData = node.getData(FlowNodeFormData);
|
||||||
|
let formJSON =
|
||||||
|
formData && formData.formModel && formData.formModel.initialized
|
||||||
|
? formData.toJSON()
|
||||||
|
: undefined;
|
||||||
|
let nodeJSON: FlowNodeJSON = {
|
||||||
|
id: node.id,
|
||||||
|
type: node.flowNodeType,
|
||||||
|
data: formData ? formJSON : node.getExtInfo(),
|
||||||
|
blocks: [],
|
||||||
|
};
|
||||||
|
if (opts.toNodeJSON) {
|
||||||
|
nodeJSON = opts.toNodeJSON(node, nodeJSON);
|
||||||
|
}
|
||||||
|
if (!startNodeJSON) startNodeJSON = nodeJSON;
|
||||||
|
let { parent } = node;
|
||||||
|
if (parent && parent.id.startsWith('$')) {
|
||||||
|
parent = parent.originParent;
|
||||||
|
}
|
||||||
|
const parentJSON = parent ? nodesMap[parent.id] : undefined;
|
||||||
|
if (parentJSON) {
|
||||||
|
parentJSON.blocks?.push(nodeJSON);
|
||||||
|
}
|
||||||
|
nodesMap[node.id] = nodeJSON;
|
||||||
|
}, node);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return startNodeJSON;
|
||||||
|
}
|
||||||
440
packages/client/free-layout-editor/__mocks__/flow.mocks.ts
Normal file
440
packages/client/free-layout-editor/__mocks__/flow.mocks.ts
Normal file
@ -0,0 +1,440 @@
|
|||||||
|
import { WorkflowJSON } from '@flowgram.ai/free-layout-core';
|
||||||
|
|
||||||
|
export const mockJSON: WorkflowJSON = {
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'start_0',
|
||||||
|
type: 'start',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 180,
|
||||||
|
y: 381.75,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'Start',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'condition_0',
|
||||||
|
type: 'condition',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 640,
|
||||||
|
y: 363.25,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'Condition',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'end_0',
|
||||||
|
type: 'end',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 2220,
|
||||||
|
y: 381.75,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'End',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'loop_H8M3U',
|
||||||
|
type: 'loop',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 1020,
|
||||||
|
y: 547.96875,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'Loop_2',
|
||||||
|
},
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
id: 'llm_CBdCg',
|
||||||
|
type: 'llm',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 180,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'LLM_4',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'llm_gZafu',
|
||||||
|
type: 'llm',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 640,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'LLM_5',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
sourceNodeID: 'llm_CBdCg',
|
||||||
|
targetNodeID: 'llm_gZafu',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '159623',
|
||||||
|
type: 'comment',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 640,
|
||||||
|
y: 522.46875,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
size: {
|
||||||
|
width: 240,
|
||||||
|
height: 150,
|
||||||
|
},
|
||||||
|
note: 'hi ~\n\nthis is a comment node\n\n- flowgram.ai',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'group_V-_st',
|
||||||
|
type: 'group',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 1020,
|
||||||
|
y: 96.25,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'LLM_Group',
|
||||||
|
color: 'Violet',
|
||||||
|
},
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
id: 'llm_0',
|
||||||
|
type: 'llm',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 640,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'LLM_0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'llm_l_TcE',
|
||||||
|
type: 'llm',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 180,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'LLM_1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
sourceNodeID: 'llm_l_TcE',
|
||||||
|
targetNodeID: 'llm_0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceNodeID: 'llm_0',
|
||||||
|
targetNodeID: 'end_0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceNodeID: 'condition_0',
|
||||||
|
targetNodeID: 'llm_l_TcE',
|
||||||
|
sourcePortID: 'if_0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
sourceNodeID: 'start_0',
|
||||||
|
targetNodeID: 'condition_0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceNodeID: 'condition_0',
|
||||||
|
targetNodeID: 'llm_l_TcE',
|
||||||
|
sourcePortID: 'if_0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceNodeID: 'condition_0',
|
||||||
|
targetNodeID: 'loop_H8M3U',
|
||||||
|
sourcePortID: 'if_f0rOAt',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceNodeID: 'llm_0',
|
||||||
|
targetNodeID: 'end_0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceNodeID: 'loop_H8M3U',
|
||||||
|
targetNodeID: 'end_0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mockJSON2: WorkflowJSON = {
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'start_0',
|
||||||
|
type: 'start',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'Start changed',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'condition_0',
|
||||||
|
type: 'condition',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'Condition changed',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'end_0',
|
||||||
|
type: 'end',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'End',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'loop_H8M3U',
|
||||||
|
type: 'loop',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 1020,
|
||||||
|
y: 547.96875,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'Loop_2 changed',
|
||||||
|
},
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
id: 'llm_CBdCg',
|
||||||
|
type: 'llm',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 180,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'LLM_4 chnaged',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'llm_gZafu',
|
||||||
|
type: 'llm changed',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 6,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'LLM_5',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
sourceNodeID: 'llm_CBdCg',
|
||||||
|
targetNodeID: 'llm_gZafu',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '159623',
|
||||||
|
type: 'comment',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 640,
|
||||||
|
y: 522.46875,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
size: {
|
||||||
|
width: 240,
|
||||||
|
height: 150,
|
||||||
|
},
|
||||||
|
note: 'hi ~\n\nthis is a comment node changed\n\n- flowgram.ai',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'group_V-_st',
|
||||||
|
type: 'group',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 1020,
|
||||||
|
y: 96.25,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'LLM_Group changed',
|
||||||
|
color: 'Violet',
|
||||||
|
},
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
id: 'llm_0',
|
||||||
|
type: 'llm',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 640,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'LLM_0 changed',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'llm_l_TcE',
|
||||||
|
type: 'llm',
|
||||||
|
meta: {
|
||||||
|
position: {
|
||||||
|
x: 180,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'LLM_1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
sourceNodeID: 'llm_l_TcE',
|
||||||
|
targetNodeID: 'llm_0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceNodeID: 'llm_0',
|
||||||
|
targetNodeID: 'end_0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceNodeID: 'condition_0',
|
||||||
|
targetNodeID: 'llm_l_TcE',
|
||||||
|
sourcePortID: 'if_0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
sourceNodeID: 'start_0',
|
||||||
|
targetNodeID: 'condition_0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceNodeID: 'condition_0',
|
||||||
|
targetNodeID: 'llm_l_TcE',
|
||||||
|
sourcePortID: 'if_0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceNodeID: 'condition_0',
|
||||||
|
targetNodeID: 'loop_H8M3U',
|
||||||
|
sourcePortID: 'if_f0rOAt',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceNodeID: 'llm_0',
|
||||||
|
targetNodeID: 'end_0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceNodeID: 'loop_H8M3U',
|
||||||
|
targetNodeID: 'end_0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
export const mockSimpleJSON: WorkflowJSON = {
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'start_0',
|
||||||
|
type: 'start',
|
||||||
|
meta: {
|
||||||
|
position: { x: 0, y: 0 },
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'start'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'end_0',
|
||||||
|
type: 'end',
|
||||||
|
meta: {
|
||||||
|
position: { x: 800, y: 0 },
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'end'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
sourceNodeID: 'start_0',
|
||||||
|
targetNodeID: 'end_0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mockSimpleJSON2: WorkflowJSON = {
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'start_0',
|
||||||
|
type: 'start',
|
||||||
|
meta: {
|
||||||
|
position: { x: 1, y: 1 },
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'start changed'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'end_0',
|
||||||
|
type: 'end',
|
||||||
|
meta: {
|
||||||
|
position: { x: 801, y: 1 },
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'end changed'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
sourceNodeID: 'start_0',
|
||||||
|
targetNodeID: 'end_0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`free-layout-preset > custom fromNodeJSON and toNodeJSON 1`] = `
|
||||||
|
{
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"sourceNodeID": "start_0",
|
||||||
|
"targetNodeID": "end_0",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"isFirstCreate": true,
|
||||||
|
"runningTimes": 1,
|
||||||
|
"title": "start",
|
||||||
|
},
|
||||||
|
"id": "start_0",
|
||||||
|
"meta": {
|
||||||
|
"position": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "start",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"isFirstCreate": true,
|
||||||
|
"runningTimes": 1,
|
||||||
|
"title": "end",
|
||||||
|
},
|
||||||
|
"id": "end_0",
|
||||||
|
"meta": {
|
||||||
|
"position": {
|
||||||
|
"x": 800,
|
||||||
|
"y": 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "end",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`free-layout-preset > custom fromNodeJSON and toNodeJSON 2`] = `
|
||||||
|
{
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"sourceNodeID": "start_0",
|
||||||
|
"targetNodeID": "end_0",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"isFirstCreate": false,
|
||||||
|
"runningTimes": 1,
|
||||||
|
"title": "start changed",
|
||||||
|
},
|
||||||
|
"id": "start_0",
|
||||||
|
"meta": {
|
||||||
|
"position": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "start",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"isFirstCreate": false,
|
||||||
|
"runningTimes": 1,
|
||||||
|
"title": "end changed",
|
||||||
|
},
|
||||||
|
"id": "end_0",
|
||||||
|
"meta": {
|
||||||
|
"position": {
|
||||||
|
"x": 801,
|
||||||
|
"y": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "end",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`;
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
import { interfaces } from 'inversify';
|
||||||
|
import {
|
||||||
|
createPlaygroundContainer,
|
||||||
|
Playground,
|
||||||
|
loadPlugins,
|
||||||
|
PluginContext,
|
||||||
|
createPluginContextDefault,
|
||||||
|
FlowDocument,
|
||||||
|
} from '@flowgram.ai/editor';
|
||||||
|
|
||||||
|
import { FreeLayoutPluginContext, FreeLayoutProps, createFreeLayoutPreset } from '../src';
|
||||||
|
|
||||||
|
export function createEditor(opts: FreeLayoutProps): interfaces.Container {
|
||||||
|
const container = createPlaygroundContainer();
|
||||||
|
|
||||||
|
const playground = container.get(Playground);
|
||||||
|
const preset = createFreeLayoutPreset(opts);
|
||||||
|
const customPluginContext = (container: interfaces.Container) =>
|
||||||
|
({
|
||||||
|
...createPluginContextDefault(container),
|
||||||
|
get document(): FlowDocument {
|
||||||
|
return container.get<FlowDocument>(FlowDocument);
|
||||||
|
},
|
||||||
|
} as FreeLayoutPluginContext);
|
||||||
|
|
||||||
|
const ctx = customPluginContext(container);
|
||||||
|
container.rebind(PluginContext).toConstantValue(ctx);
|
||||||
|
loadPlugins(preset(ctx), container);
|
||||||
|
playground.init();
|
||||||
|
return container;
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { FlowDocument, FlowNodeFormData } from '@flowgram.ai/editor';
|
||||||
|
|
||||||
|
import { mockJSON, mockJSON2, mockSimpleJSON, mockSimpleJSON2 } from '../__mocks__/flow.mocks';
|
||||||
|
import { createEditor } from './create-editor';
|
||||||
|
|
||||||
|
describe('free-layout-preset', () => {
|
||||||
|
it('fromJSON and toJSON', () => {
|
||||||
|
const editor = createEditor({});
|
||||||
|
const document = editor.get(FlowDocument);
|
||||||
|
document.fromJSON(mockJSON);
|
||||||
|
expect(document.toJSON()).toEqual(mockJSON);
|
||||||
|
document.fromJSON(mockJSON2);
|
||||||
|
expect(document.toJSON()).toEqual(mockJSON2);
|
||||||
|
});
|
||||||
|
it('custom fromNodeJSON and toNodeJSON', () => {
|
||||||
|
const container = createEditor({
|
||||||
|
fromNodeJSON: (node, json, isFirstCreate) => {
|
||||||
|
if (!json.data) {
|
||||||
|
json.data = {};
|
||||||
|
}
|
||||||
|
json.data = { ...json.data, isFirstCreate };
|
||||||
|
return json;
|
||||||
|
},
|
||||||
|
toNodeJSON(node, json) {
|
||||||
|
json.data.runningTimes = (json.data.runningTimes || 0) + 1;
|
||||||
|
return json;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
container.get(FlowDocument).fromJSON(mockSimpleJSON);
|
||||||
|
expect(container.get(FlowDocument).toJSON()).toMatchSnapshot();
|
||||||
|
container.get(FlowDocument).fromJSON(mockSimpleJSON2);
|
||||||
|
expect(container.get(FlowDocument).toJSON()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
it('nodeEngine(v2) toJSON', async () => {
|
||||||
|
const container = createEditor({
|
||||||
|
nodeEngine: {},
|
||||||
|
nodeRegistries: [
|
||||||
|
{
|
||||||
|
type: 'start',
|
||||||
|
formMeta: {
|
||||||
|
render: () => undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'end',
|
||||||
|
formMeta: {
|
||||||
|
render: () => undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const flowDocument = container.get(FlowDocument);
|
||||||
|
flowDocument.fromJSON(mockSimpleJSON);
|
||||||
|
expect(flowDocument.toJSON()).toEqual(mockSimpleJSON);
|
||||||
|
flowDocument.fromJSON(mockSimpleJSON2);
|
||||||
|
expect(flowDocument.toJSON()).toEqual(mockSimpleJSON2);
|
||||||
|
const { formModel } = flowDocument.getNode('start_0').getData(FlowNodeFormData);
|
||||||
|
expect(formModel.getFormItemByPath('title').value).toEqual('start changed');
|
||||||
|
formModel.getFormItemByPath('title').value = 'start changed 2';
|
||||||
|
expect(formModel.toJSON()).toEqual({
|
||||||
|
title: 'start changed 2',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -32,6 +32,7 @@ import {
|
|||||||
createPlaygroundReactPreset,
|
createPlaygroundReactPreset,
|
||||||
} from '@flowgram.ai/editor';
|
} from '@flowgram.ai/editor';
|
||||||
|
|
||||||
|
import { fromNodeJSON, toNodeJSON } from './node-serialize';
|
||||||
import { FreeLayoutProps, FreeLayoutPluginContext } from './free-layout-props';
|
import { FreeLayoutProps, FreeLayoutPluginContext } from './free-layout-props';
|
||||||
|
|
||||||
const renderElement = (ctx: PluginContext) => {
|
const renderElement = (ctx: PluginContext) => {
|
||||||
@ -162,8 +163,9 @@ export function createFreeLayoutPreset(
|
|||||||
cursors: opts.cursors ?? WorkflowDocumentOptionsDefault.cursors,
|
cursors: opts.cursors ?? WorkflowDocumentOptionsDefault.cursors,
|
||||||
lineColor: opts.lineColor ?? WorkflowDocumentOptionsDefault.lineColor,
|
lineColor: opts.lineColor ?? WorkflowDocumentOptionsDefault.lineColor,
|
||||||
allNodesDefaultExpanded: opts.allNodesDefaultExpanded,
|
allNodesDefaultExpanded: opts.allNodesDefaultExpanded,
|
||||||
toNodeJSON: opts.toNodeJSON,
|
toNodeJSON: (node) => toNodeJSON(opts, node),
|
||||||
fromNodeJSON: opts.fromNodeJSON,
|
fromNodeJSON: (node, json, isFirstCreate) =>
|
||||||
|
fromNodeJSON(opts, node, json, isFirstCreate),
|
||||||
} as WorkflowDocumentOptions);
|
} as WorkflowDocumentOptions);
|
||||||
},
|
},
|
||||||
onInit: (ctx) => {
|
onInit: (ctx) => {
|
||||||
|
|||||||
@ -5,9 +5,7 @@ import {
|
|||||||
LineRenderType,
|
LineRenderType,
|
||||||
onDragLineEndParams,
|
onDragLineEndParams,
|
||||||
WorkflowContentChangeEvent,
|
WorkflowContentChangeEvent,
|
||||||
WorkflowContentChangeType,
|
|
||||||
WorkflowDocument,
|
WorkflowDocument,
|
||||||
WorkflowDocumentOptionsDefault,
|
|
||||||
WorkflowJSON,
|
WorkflowJSON,
|
||||||
WorkflowLineEntity,
|
WorkflowLineEntity,
|
||||||
WorkflowLinePortInfo,
|
WorkflowLinePortInfo,
|
||||||
@ -20,9 +18,6 @@ import {
|
|||||||
ClipboardService,
|
ClipboardService,
|
||||||
EditorPluginContext,
|
EditorPluginContext,
|
||||||
EditorProps,
|
EditorProps,
|
||||||
FlowNodeEntity,
|
|
||||||
FlowNodeFormData,
|
|
||||||
type FlowNodeJSON,
|
|
||||||
SelectionService,
|
SelectionService,
|
||||||
PluginContext,
|
PluginContext,
|
||||||
} from '@flowgram.ai/editor';
|
} from '@flowgram.ai/editor';
|
||||||
@ -203,41 +198,5 @@ export namespace FreeLayoutProps {
|
|||||||
*/
|
*/
|
||||||
export const DEFAULT: FreeLayoutProps = {
|
export const DEFAULT: FreeLayoutProps = {
|
||||||
...EditorProps.DEFAULT,
|
...EditorProps.DEFAULT,
|
||||||
fromNodeJSON(node: FlowNodeEntity, json: FlowNodeJSON) {
|
|
||||||
const formData = node.getData(FlowNodeFormData)!;
|
|
||||||
// 如果没有使用表单引擎,将 data 数据填入 extInfo
|
|
||||||
if (!formData) {
|
|
||||||
if (json.data) {
|
|
||||||
node.updateExtInfo(json.data);
|
|
||||||
}
|
|
||||||
// extInfo 数据更新则触发内容更新
|
|
||||||
node.onExtInfoChange(() => {
|
|
||||||
(node.document as WorkflowDocument).fireContentChange({
|
|
||||||
type: WorkflowContentChangeType.NODE_DATA_CHANGE,
|
|
||||||
toJSON: () => node.getExtInfo(),
|
|
||||||
entity: node,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return WorkflowDocumentOptionsDefault.fromNodeJSON?.(node, json);
|
|
||||||
},
|
|
||||||
toNodeJSON(node: FlowNodeEntity): FlowNodeJSON {
|
|
||||||
const formData = node.getData(FlowNodeFormData)!;
|
|
||||||
const position = node.transform.position;
|
|
||||||
// 不使用节点引擎则采用 extInfo
|
|
||||||
if (!formData) {
|
|
||||||
return {
|
|
||||||
id: node.id,
|
|
||||||
type: node.flowNodeType,
|
|
||||||
meta: {
|
|
||||||
position: { x: position.x, y: position.y },
|
|
||||||
},
|
|
||||||
data: node.getExtInfo(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return WorkflowDocumentOptionsDefault.toNodeJSON!(node);
|
|
||||||
},
|
|
||||||
} as FreeLayoutProps;
|
} as FreeLayoutProps;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,57 @@
|
|||||||
|
import {
|
||||||
|
WorkflowContentChangeType,
|
||||||
|
WorkflowDocument,
|
||||||
|
WorkflowDocumentOptionsDefault,
|
||||||
|
} from '@flowgram.ai/free-layout-core';
|
||||||
|
import { FlowNodeEntity, FlowNodeFormData, type FlowNodeJSON } from '@flowgram.ai/editor';
|
||||||
|
|
||||||
|
import { FreeLayoutProps } from './free-layout-props';
|
||||||
|
|
||||||
|
export function fromNodeJSON(
|
||||||
|
opts: FreeLayoutProps,
|
||||||
|
node: FlowNodeEntity,
|
||||||
|
json: FlowNodeJSON,
|
||||||
|
isFirstCreate: boolean
|
||||||
|
) {
|
||||||
|
json = opts.fromNodeJSON ? opts.fromNodeJSON(node, json, isFirstCreate) : json;
|
||||||
|
const formData = node.getData(FlowNodeFormData)!;
|
||||||
|
// 如果没有使用表单引擎,将 data 数据填入 extInfo
|
||||||
|
if (!formData) {
|
||||||
|
if (json.data) {
|
||||||
|
node.updateExtInfo(json.data);
|
||||||
|
}
|
||||||
|
// extInfo 数据更新则触发内容更新
|
||||||
|
if (isFirstCreate) {
|
||||||
|
node.onExtInfoChange(() => {
|
||||||
|
(node.document as WorkflowDocument).fireContentChange({
|
||||||
|
type: WorkflowContentChangeType.NODE_DATA_CHANGE,
|
||||||
|
toJSON: () => node.getExtInfo(),
|
||||||
|
entity: node,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return WorkflowDocumentOptionsDefault.fromNodeJSON?.(node, json, isFirstCreate);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toNodeJSON(opts: FreeLayoutProps, node: FlowNodeEntity): FlowNodeJSON {
|
||||||
|
const formData = node.getData(FlowNodeFormData)!;
|
||||||
|
const position = node.transform.position;
|
||||||
|
let json: FlowNodeJSON;
|
||||||
|
// 不使用节点引擎则采用 extInfo
|
||||||
|
if (!formData) {
|
||||||
|
json = {
|
||||||
|
id: node.id,
|
||||||
|
type: node.flowNodeType,
|
||||||
|
meta: {
|
||||||
|
position: { x: position.x, y: position.y },
|
||||||
|
},
|
||||||
|
data: node.getExtInfo(),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
json = WorkflowDocumentOptionsDefault.toNodeJSON!(node);
|
||||||
|
}
|
||||||
|
return opts.toNodeJSON ? opts.toNodeJSON(node, json) : json;
|
||||||
|
}
|
||||||
@ -78,6 +78,10 @@ export class FlowNodeFormData extends EntityData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateFormValues(value: any) {
|
||||||
|
this.formModel.updateFormValues(value);
|
||||||
|
}
|
||||||
|
|
||||||
recreateForm(formMetaOrFormMetaGenerator: FormMetaOrFormMetaGenerator, initialValue?: any): void {
|
recreateForm(formMetaOrFormMetaGenerator: FormMetaOrFormMetaGenerator, initialValue?: any): void {
|
||||||
this.createForm(formMetaOrFormMetaGenerator, initialValue);
|
this.createForm(formMetaOrFormMetaGenerator, initialValue);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,6 +39,8 @@ export abstract class FormModel {
|
|||||||
|
|
||||||
abstract get valid(): FormModelValid;
|
abstract get valid(): FormModelValid;
|
||||||
|
|
||||||
|
abstract updateFormValues(value: any): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
* use `formModel.getFieldIn` instead in FormModelV2 to get the model of a form field
|
* use `formModel.getFieldIn` instead in FormModelV2 to get the model of a form field
|
||||||
|
|||||||
@ -145,6 +145,12 @@ export class FormModelV2 extends FormModel implements Disposable {
|
|||||||
return this._feedbacks;
|
return this._feedbacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateFormValues(value: any) {
|
||||||
|
if (this.nativeFormModel) {
|
||||||
|
this.nativeFormModel.values = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private set feedbacks(feedbacks: FormFeedback[]) {
|
private set feedbacks(feedbacks: FormFeedback[]) {
|
||||||
this._feedbacks = feedbacks;
|
this._feedbacks = feedbacks;
|
||||||
this.onFeedbacksChangeEmitter.fire(feedbacks);
|
this.onFeedbacksChangeEmitter.fire(feedbacks);
|
||||||
|
|||||||
@ -33,6 +33,10 @@ export interface NodeFormProps<TValues> {
|
|||||||
* @param name path
|
* @param name path
|
||||||
*/
|
*/
|
||||||
setValueIn<TValue>(name: FieldName, value: TValue): void;
|
setValueIn<TValue>(name: FieldName, value: TValue): void;
|
||||||
|
/**
|
||||||
|
* set form values
|
||||||
|
*/
|
||||||
|
updateFormValues(values: any): void;
|
||||||
/**
|
/**
|
||||||
* Render form
|
* Render form
|
||||||
*/
|
*/
|
||||||
@ -75,9 +79,13 @@ export function getNodeForm<TValues = FieldValue>(
|
|||||||
get values() {
|
get values() {
|
||||||
return nativeFormModel.values;
|
return nativeFormModel.values;
|
||||||
},
|
},
|
||||||
|
|
||||||
state: nativeFormModel.state,
|
state: nativeFormModel.state,
|
||||||
getValueIn: (name: FieldName) => nativeFormModel.getValueIn(name),
|
getValueIn: (name: FieldName) => nativeFormModel.getValueIn(name),
|
||||||
setValueIn: (name: FieldName, value: any) => nativeFormModel.setValueIn(name, value),
|
setValueIn: (name: FieldName, value: any) => nativeFormModel.setValueIn(name, value),
|
||||||
|
updateFormValues: (values: any) => {
|
||||||
|
formModel.updateFormValues(values);
|
||||||
|
},
|
||||||
render: () => <NodeRender node={node} />,
|
render: () => <NodeRender node={node} />,
|
||||||
onFormValuesChange: formModel.onFormValuesChange.bind(formModel),
|
onFormValuesChange: formModel.onFormValuesChange.bind(formModel),
|
||||||
onFormValueChangeIn: formModel.onFormValueChangeIn.bind(formModel),
|
onFormValueChangeIn: formModel.onFormValueChangeIn.bind(formModel),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user