@arronqzy/vue-blueprint 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/BlueprintNodeConfigSidebar.vue +12 -0
- package/src/blueprintNodeTypes.ts +2 -0
- package/src/components/BlueprintContextMenu.vue +1 -1
- package/src/components/StorageNodeConfigPanel.vue +217 -0
- package/src/graph/blueprint-graph.ts +16 -0
- package/src/graph/document.ts +53 -1
- package/src/graph/node-summary.ts +24 -0
- package/src/graph/node-types.ts +1 -0
- package/src/graph/sync-nodes.ts +5 -1
- package/src/graph/vue-flow-adapter.ts +4 -0
- package/src/nodes/BlueprintNodeCard.vue +6 -1
- package/src/nodes/StorageFlowNode.vue +29 -0
- package/src/runtime/document-to-runnable-graph.ts +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arronqzy/vue-blueprint",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Vue 3 blueprint editor for Abuilder (Vue Flow + Ant Design Vue)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
"ant-design-vue": "^4.2.6",
|
|
20
20
|
"dayjs": "^1.11.13",
|
|
21
21
|
"vue": "^3.5.13",
|
|
22
|
-
"@arronqzy/
|
|
23
|
-
"@arronqzy/
|
|
24
|
-
"@arronqzy/
|
|
22
|
+
"@arronqzy/i18n": "0.1.8",
|
|
23
|
+
"@arronqzy/vue-rx-store": "0.1.3",
|
|
24
|
+
"@arronqzy/blueprint-dsl": "1.0.10"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"vue": "^3.4.0"
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import FetchNodeConfigPanel from "./components/FetchNodeConfigPanel.vue";
|
|
15
15
|
import ClockNodeConfigPanel from "./components/ClockNodeConfigPanel.vue";
|
|
16
16
|
import JsonNodeConfigPanel from "./components/JsonNodeConfigPanel.vue";
|
|
17
|
+
import StorageNodeConfigPanel from "./components/StorageNodeConfigPanel.vue";
|
|
17
18
|
import LogicNodeConfigPanel from "./components/LogicNodeConfigPanel.vue";
|
|
18
19
|
import ViewElementMultiSelect from "./components/ViewElementMultiSelect.vue";
|
|
19
20
|
import ConfigHintIcon from "./components/ConfigHintIcon.vue";
|
|
@@ -69,6 +70,7 @@ export type BlueprintNodeConfigSidebarProps = {
|
|
|
69
70
|
| "lifecyclePhase"
|
|
70
71
|
| "fetchConfig"
|
|
71
72
|
| "jsonConfig"
|
|
73
|
+
| "storageConfig"
|
|
72
74
|
| "logicConfig"
|
|
73
75
|
| "clockConfig"
|
|
74
76
|
| "eventConfig"
|
|
@@ -102,6 +104,7 @@ const ROLE_LABEL_KEYS: Record<BlueprintNodeRole, string> = {
|
|
|
102
104
|
and: "blueprint.config.roleAnd",
|
|
103
105
|
fetch: "blueprint.config.roleFetch",
|
|
104
106
|
json: "blueprint.config.roleJson",
|
|
107
|
+
storage: "blueprint.config.roleStorage",
|
|
105
108
|
logic: "blueprint.config.roleLogic",
|
|
106
109
|
clock: "blueprint.config.roleClock",
|
|
107
110
|
event: "blueprint.config.roleEvent",
|
|
@@ -201,6 +204,7 @@ function toggleEventType(eventType: ViewEventType, checked: boolean) {
|
|
|
201
204
|
<Select.Option value="lifecycle">{{ t("blueprint.config.configLifecycle") }}</Select.Option>
|
|
202
205
|
<Select.Option value="fetch">{{ t("blueprint.config.configFetch") }}</Select.Option>
|
|
203
206
|
<Select.Option value="json">{{ t("blueprint.config.configJson") }}</Select.Option>
|
|
207
|
+
<Select.Option value="storage">{{ t("blueprint.config.configStorage") }}</Select.Option>
|
|
204
208
|
<Select.Option value="clock">{{ t("blueprint.config.configClock") }}</Select.Option>
|
|
205
209
|
<Select.Option value="event">{{ t("blueprint.config.configEvent") }}</Select.Option>
|
|
206
210
|
<Select.Option value="view">{{ t("blueprint.config.configView") }}</Select.Option>
|
|
@@ -381,6 +385,14 @@ function toggleEventType(eventType: ViewEventType, checked: boolean) {
|
|
|
381
385
|
:node="node"
|
|
382
386
|
:on-update-node="onUpdateNode"
|
|
383
387
|
/>
|
|
388
|
+
<StorageNodeConfigPanel
|
|
389
|
+
v-if="configSource === 'storage'"
|
|
390
|
+
:node="node"
|
|
391
|
+
:graph-nodes="graphNodes"
|
|
392
|
+
:graph-edges="graphEdges"
|
|
393
|
+
:trace-entries="traceEntries"
|
|
394
|
+
:on-update-node="onUpdateNode"
|
|
395
|
+
/>
|
|
384
396
|
<ClockNodeConfigPanel
|
|
385
397
|
v-if="configSource === 'clock'"
|
|
386
398
|
:node="node"
|
|
@@ -5,6 +5,7 @@ import BlueprintFlowNode from "./nodes/BlueprintFlowNode.vue";
|
|
|
5
5
|
import ClockFlowNode from "./nodes/ClockFlowNode.vue";
|
|
6
6
|
import FetchFlowNode from "./nodes/FetchFlowNode.vue";
|
|
7
7
|
import JsonFlowNode from "./nodes/JsonFlowNode.vue";
|
|
8
|
+
import StorageFlowNode from "./nodes/StorageFlowNode.vue";
|
|
8
9
|
import LifecycleFlowNode from "./nodes/LifecycleFlowNode.vue";
|
|
9
10
|
import EventFlowNode from "./nodes/EventFlowNode.vue";
|
|
10
11
|
import LogicFlowNode from "./nodes/LogicFlowNode.vue";
|
|
@@ -16,6 +17,7 @@ export const blueprintNodeTypes: Record<string, Component> = {
|
|
|
16
17
|
clock: markRaw(ClockFlowNode),
|
|
17
18
|
fetch: markRaw(FetchFlowNode),
|
|
18
19
|
json: markRaw(JsonFlowNode),
|
|
20
|
+
storage: markRaw(StorageFlowNode),
|
|
19
21
|
logic: markRaw(LogicFlowNode),
|
|
20
22
|
lifecycle: markRaw(LifecycleFlowNode),
|
|
21
23
|
event: markRaw(EventFlowNode),
|
|
@@ -14,7 +14,7 @@ export type BlueprintContextMenuState =
|
|
|
14
14
|
clientX: number;
|
|
15
15
|
clientY: number;
|
|
16
16
|
nodeId: string;
|
|
17
|
-
role: "blueprint" | "logic" | "and" | "lifecycle" | "event" | "fetch" | "json" | "clock";
|
|
17
|
+
role: "blueprint" | "logic" | "and" | "lifecycle" | "event" | "fetch" | "json" | "storage" | "clock";
|
|
18
18
|
}
|
|
19
19
|
| {
|
|
20
20
|
kind: "edge";
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/vue";
|
|
3
|
+
import { computed, ref } from "vue";
|
|
4
|
+
import { Checkbox, Input, Select } from "ant-design-vue";
|
|
5
|
+
import type {
|
|
6
|
+
ExecutionTraceEntry,
|
|
7
|
+
StorageKind,
|
|
8
|
+
StorageReadConfig,
|
|
9
|
+
StorageSetConfig,
|
|
10
|
+
} from "@arronqzy/blueprint-dsl";
|
|
11
|
+
import {
|
|
12
|
+
latestTraceOutputsByNode,
|
|
13
|
+
resolveFetchIncomingScope,
|
|
14
|
+
resolveFetchScopeAutocompleteRoot,
|
|
15
|
+
uniqueStorageKinds,
|
|
16
|
+
} from "@arronqzy/blueprint-dsl";
|
|
17
|
+
|
|
18
|
+
import type { BlueprintGraphEdge, BlueprintGraphNode } from "../graph/document";
|
|
19
|
+
import { resolveNodeStorageConfig } from "../graph/document";
|
|
20
|
+
import ConfigHintIcon from "./ConfigHintIcon.vue";
|
|
21
|
+
import ScopeTemplateAutocompleteHost from "./ScopeTemplateAutocompleteHost.vue";
|
|
22
|
+
|
|
23
|
+
const { t } = useI18n();
|
|
24
|
+
|
|
25
|
+
export type StorageNodeConfigPanelProps = {
|
|
26
|
+
node: BlueprintGraphNode;
|
|
27
|
+
graphNodes?: BlueprintGraphNode[];
|
|
28
|
+
graphEdges?: BlueprintGraphEdge[];
|
|
29
|
+
traceEntries?: ExecutionTraceEntry[];
|
|
30
|
+
onUpdateNode: (
|
|
31
|
+
nodeId: string,
|
|
32
|
+
patch: Partial<Pick<BlueprintGraphNode, "storageConfig" | "configSource">>
|
|
33
|
+
) => void;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const props = withDefaults(defineProps<StorageNodeConfigPanelProps>(), {
|
|
37
|
+
graphNodes: () => [],
|
|
38
|
+
graphEdges: () => [],
|
|
39
|
+
traceEntries: () => [],
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const formRef = ref<HTMLDivElement | null>(null);
|
|
43
|
+
const storageConfig = computed(() => resolveNodeStorageConfig(props.node));
|
|
44
|
+
const incomingScope = computed(() => {
|
|
45
|
+
const outputs = latestTraceOutputsByNode(props.traceEntries);
|
|
46
|
+
return resolveFetchIncomingScope({
|
|
47
|
+
fetchNodeId: props.node.id,
|
|
48
|
+
nodes: props.graphNodes,
|
|
49
|
+
edges: props.graphEdges,
|
|
50
|
+
getOutput: (sourceId, port) => outputs[sourceId]?.[port],
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
const autocompleteScope = computed(() =>
|
|
54
|
+
resolveFetchScopeAutocompleteRoot(incomingScope.value)
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
function patchStorageConfig(
|
|
58
|
+
node: BlueprintGraphNode,
|
|
59
|
+
patch: {
|
|
60
|
+
read?: Partial<StorageReadConfig>;
|
|
61
|
+
set?: Partial<StorageSetConfig>;
|
|
62
|
+
}
|
|
63
|
+
) {
|
|
64
|
+
const current = resolveNodeStorageConfig(node);
|
|
65
|
+
return {
|
|
66
|
+
storageConfig: {
|
|
67
|
+
read: { ...current.read, ...patch.read },
|
|
68
|
+
set: { ...current.set, ...patch.set },
|
|
69
|
+
},
|
|
70
|
+
configSource: "storage" as const,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function handleReadStorageChange(value: string) {
|
|
75
|
+
props.onUpdateNode(
|
|
76
|
+
props.node.id,
|
|
77
|
+
patchStorageConfig(props.node, {
|
|
78
|
+
read: { storage: value === "session" ? "session" : "local" },
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function handleReadKeyChange(value: string) {
|
|
84
|
+
props.onUpdateNode(
|
|
85
|
+
props.node.id,
|
|
86
|
+
patchStorageConfig(props.node, { read: { key: String(value ?? "") } })
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function handleSetStorageToggle(kind: StorageKind, checked: boolean) {
|
|
91
|
+
const next = new Set(storageConfig.value.set.storages);
|
|
92
|
+
if (checked) next.add(kind);
|
|
93
|
+
else next.delete(kind);
|
|
94
|
+
props.onUpdateNode(
|
|
95
|
+
props.node.id,
|
|
96
|
+
patchStorageConfig(props.node, {
|
|
97
|
+
set: { storages: uniqueStorageKinds([...next]) },
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function handleSetKeyChange(value: string) {
|
|
103
|
+
props.onUpdateNode(
|
|
104
|
+
props.node.id,
|
|
105
|
+
patchStorageConfig(props.node, { set: { key: String(value ?? "") } })
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function handleSetValueChange(value: string) {
|
|
110
|
+
props.onUpdateNode(
|
|
111
|
+
props.node.id,
|
|
112
|
+
patchStorageConfig(props.node, { set: { value: String(value ?? "") } })
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
</script>
|
|
116
|
+
|
|
117
|
+
<template>
|
|
118
|
+
<div
|
|
119
|
+
ref="formRef"
|
|
120
|
+
class="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5"
|
|
121
|
+
>
|
|
122
|
+
<ScopeTemplateAutocompleteHost :scope="autocompleteScope" :container-ref="formRef" />
|
|
123
|
+
<div class="flex items-center gap-1.5">
|
|
124
|
+
<div class="font-medium text-foreground">{{ t("blueprint.config.storageTitle") }}</div>
|
|
125
|
+
<ConfigHintIcon :label="t('blueprint.config.storageTitle')">
|
|
126
|
+
{{ t("blueprint.config.storageHint") }}
|
|
127
|
+
</ConfigHintIcon>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<div class="space-y-2 rounded-md border border-border/60 bg-background/70 p-2">
|
|
131
|
+
<div class="flex items-center gap-1.5">
|
|
132
|
+
<div class="font-medium text-foreground">{{ t("blueprint.config.storageReadTitle") }}</div>
|
|
133
|
+
<ConfigHintIcon :label="t('blueprint.config.storageReadTitle')">
|
|
134
|
+
{{ t("blueprint.config.storageReadHint") }}
|
|
135
|
+
</ConfigHintIcon>
|
|
136
|
+
</div>
|
|
137
|
+
<label class="block space-y-1">
|
|
138
|
+
<span class="text-muted-foreground">{{ t("blueprint.config.storageTarget") }}</span>
|
|
139
|
+
<Select
|
|
140
|
+
size="small"
|
|
141
|
+
class="w-full"
|
|
142
|
+
:value="storageConfig.read.storage"
|
|
143
|
+
@change="(v) => handleReadStorageChange(String(v))"
|
|
144
|
+
>
|
|
145
|
+
<Select.Option value="session">{{ t("blueprint.config.storageSession") }}</Select.Option>
|
|
146
|
+
<Select.Option value="local">{{ t("blueprint.config.storageLocal") }}</Select.Option>
|
|
147
|
+
</Select>
|
|
148
|
+
</label>
|
|
149
|
+
<label class="block space-y-1">
|
|
150
|
+
<span class="text-muted-foreground">{{ t("blueprint.config.storageKey") }}</span>
|
|
151
|
+
<Input
|
|
152
|
+
size="small"
|
|
153
|
+
:value="storageConfig.read.key"
|
|
154
|
+
spellcheck="false"
|
|
155
|
+
class="font-mono text-[11px]"
|
|
156
|
+
placeholder="key"
|
|
157
|
+
@update:value="(v) => handleReadKeyChange(String(v ?? ''))"
|
|
158
|
+
/>
|
|
159
|
+
</label>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<div class="space-y-2 rounded-md border border-border/60 bg-background/70 p-2">
|
|
163
|
+
<div class="flex items-center gap-1.5">
|
|
164
|
+
<div class="font-medium text-foreground">{{ t("blueprint.config.storageSetTitle") }}</div>
|
|
165
|
+
<ConfigHintIcon :label="t('blueprint.config.storageSetTitle')">
|
|
166
|
+
{{ t("blueprint.config.storageSetHint") }}
|
|
167
|
+
</ConfigHintIcon>
|
|
168
|
+
</div>
|
|
169
|
+
<div class="space-y-1.5">
|
|
170
|
+
<span class="text-muted-foreground">{{ t("blueprint.config.storageTarget") }}</span>
|
|
171
|
+
<label class="flex items-center gap-2">
|
|
172
|
+
<Checkbox
|
|
173
|
+
:checked="storageConfig.set.storages.includes('session')"
|
|
174
|
+
class="mt-0.5"
|
|
175
|
+
@update:checked="(v) => handleSetStorageToggle('session', Boolean(v))"
|
|
176
|
+
/>
|
|
177
|
+
<span>{{ t("blueprint.config.storageSession") }}</span>
|
|
178
|
+
</label>
|
|
179
|
+
<label class="flex items-center gap-2">
|
|
180
|
+
<Checkbox
|
|
181
|
+
:checked="storageConfig.set.storages.includes('local')"
|
|
182
|
+
class="mt-0.5"
|
|
183
|
+
@update:checked="(v) => handleSetStorageToggle('local', Boolean(v))"
|
|
184
|
+
/>
|
|
185
|
+
<span>{{ t("blueprint.config.storageLocal") }}</span>
|
|
186
|
+
</label>
|
|
187
|
+
</div>
|
|
188
|
+
<label class="block space-y-1">
|
|
189
|
+
<span class="text-muted-foreground">{{ t("blueprint.config.storageKey") }}</span>
|
|
190
|
+
<Input
|
|
191
|
+
size="small"
|
|
192
|
+
:value="storageConfig.set.key"
|
|
193
|
+
spellcheck="false"
|
|
194
|
+
class="font-mono text-[11px]"
|
|
195
|
+
placeholder="key"
|
|
196
|
+
@update:value="(v) => handleSetKeyChange(String(v ?? ''))"
|
|
197
|
+
/>
|
|
198
|
+
</label>
|
|
199
|
+
<label class="block space-y-1">
|
|
200
|
+
<span class="inline-flex items-center gap-1 text-muted-foreground">
|
|
201
|
+
{{ t("blueprint.config.storageValue") }}
|
|
202
|
+
<ConfigHintIcon :label="t('blueprint.config.storageValue')">
|
|
203
|
+
{{ t("blueprint.config.storageValueHint") }}
|
|
204
|
+
</ConfigHintIcon>
|
|
205
|
+
</span>
|
|
206
|
+
<Input
|
|
207
|
+
size="small"
|
|
208
|
+
:value="storageConfig.set.value"
|
|
209
|
+
spellcheck="false"
|
|
210
|
+
class="font-mono text-[11px]"
|
|
211
|
+
placeholder="{scope.value}"
|
|
212
|
+
@update:value="(v) => handleSetValueChange(String(v ?? ''))"
|
|
213
|
+
/>
|
|
214
|
+
</label>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
</template>
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
resolveNodeEventConfig,
|
|
9
9
|
resolveNodeFetchConfig,
|
|
10
10
|
resolveNodeJsonConfig,
|
|
11
|
+
resolveNodeStorageConfig,
|
|
11
12
|
resolveNodeLogicConfig,
|
|
12
13
|
resolveRunnableNodeType,
|
|
13
14
|
sanitizeBlueprintDocument,
|
|
@@ -197,6 +198,7 @@ export class BlueprintGraph {
|
|
|
197
198
|
| "lifecyclePhase"
|
|
198
199
|
| "fetchConfig"
|
|
199
200
|
| "jsonConfig"
|
|
201
|
+
| "storageConfig"
|
|
200
202
|
| "logicConfig"
|
|
201
203
|
| "clockConfig"
|
|
202
204
|
| "eventConfig"
|
|
@@ -241,6 +243,20 @@ export class BlueprintGraph {
|
|
|
241
243
|
...patch.jsonConfig,
|
|
242
244
|
};
|
|
243
245
|
}
|
|
246
|
+
if (patch.storageConfig) {
|
|
247
|
+
nextNode.storageConfig = {
|
|
248
|
+
...resolveNodeStorageConfig(n),
|
|
249
|
+
...patch.storageConfig,
|
|
250
|
+
read: {
|
|
251
|
+
...resolveNodeStorageConfig(n).read,
|
|
252
|
+
...patch.storageConfig.read,
|
|
253
|
+
},
|
|
254
|
+
set: {
|
|
255
|
+
...resolveNodeStorageConfig(n).set,
|
|
256
|
+
...patch.storageConfig.set,
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
}
|
|
244
260
|
if (patch.logicConfig) {
|
|
245
261
|
nextNode.logicConfig = {
|
|
246
262
|
...resolveNodeLogicConfig(n),
|
package/src/graph/document.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
JsonNodeConfig,
|
|
5
5
|
LogicNodeConfig,
|
|
6
6
|
PageLifecyclePhase,
|
|
7
|
+
StorageNodeConfig,
|
|
7
8
|
ViewEventNodeConfig,
|
|
8
9
|
ViewEventType,
|
|
9
10
|
} from "@arronqzy/blueprint-dsl";
|
|
@@ -16,13 +17,16 @@ import {
|
|
|
16
17
|
DEFAULT_JSON_NODE_CONFIG,
|
|
17
18
|
DEFAULT_LOGIC_NODE_CONFIG,
|
|
18
19
|
DEFAULT_LOGIC_NODE_TYPE,
|
|
20
|
+
DEFAULT_STORAGE_NODE_CONFIG,
|
|
19
21
|
DEFAULT_VIEW_EVENT_NODE_CONFIG,
|
|
20
22
|
EVENT_NODE_TYPE,
|
|
21
23
|
FETCH_NODE_TYPE,
|
|
22
24
|
JSON_NODE_TYPE,
|
|
23
25
|
LIFECYCLE_NODE_TYPE,
|
|
24
26
|
normalizeClockConfig,
|
|
27
|
+
normalizeStorageConfig,
|
|
25
28
|
normalizeViewEventConfig,
|
|
29
|
+
STORAGE_NODE_TYPE,
|
|
26
30
|
VIEW_NODE_TYPE,
|
|
27
31
|
} from "@arronqzy/blueprint-dsl";
|
|
28
32
|
|
|
@@ -31,8 +35,9 @@ export type { JsonNodeConfig as BlueprintJsonConfig } from "@arronqzy/blueprint-
|
|
|
31
35
|
export type { LogicNodeConfig as BlueprintLogicConfig } from "@arronqzy/blueprint-dsl";
|
|
32
36
|
export type { ClockNodeConfig as BlueprintClockConfig } from "@arronqzy/blueprint-dsl";
|
|
33
37
|
export type { ViewEventNodeConfig as BlueprintEventConfig } from "@arronqzy/blueprint-dsl";
|
|
38
|
+
export type { StorageNodeConfig as BlueprintStorageConfig } from "@arronqzy/blueprint-dsl";
|
|
34
39
|
|
|
35
|
-
export type BlueprintNodeRole = "blueprint" | "logic" | "and" | "lifecycle" | "fetch" | "json" | "clock" | "event";
|
|
40
|
+
export type BlueprintNodeRole = "blueprint" | "logic" | "and" | "lifecycle" | "fetch" | "json" | "storage" | "clock" | "event";
|
|
36
41
|
|
|
37
42
|
/** 决定右侧配置面板展示哪类配置 */
|
|
38
43
|
export type BlueprintConfigSource =
|
|
@@ -43,6 +48,7 @@ export type BlueprintConfigSource =
|
|
|
43
48
|
| "view"
|
|
44
49
|
| "fetch"
|
|
45
50
|
| "json"
|
|
51
|
+
| "storage"
|
|
46
52
|
| "clock"
|
|
47
53
|
| "event";
|
|
48
54
|
|
|
@@ -71,6 +77,8 @@ export type BlueprintGraphNode = {
|
|
|
71
77
|
fetchConfig?: FetchRequestConfig;
|
|
72
78
|
/** JSON 节点的 JSON 字符串配置 */
|
|
73
79
|
jsonConfig?: JsonNodeConfig;
|
|
80
|
+
/** 存储节点的读写配置 */
|
|
81
|
+
storageConfig?: StorageNodeConfig;
|
|
74
82
|
/** 逻辑节点的 JavaScript 脚本配置 */
|
|
75
83
|
logicConfig?: LogicNodeConfig;
|
|
76
84
|
/** 时钟节点的间隔与时间格式配置 */
|
|
@@ -108,8 +116,10 @@ export function resolveBlueprintConfigSource(
|
|
|
108
116
|
if (node.role === "and") return "and";
|
|
109
117
|
if (node.role === "fetch") return "fetch";
|
|
110
118
|
if (node.role === "json") return "json";
|
|
119
|
+
if (node.role === "storage") return "storage";
|
|
111
120
|
if (node.nodeType === FETCH_NODE_TYPE) return "fetch";
|
|
112
121
|
if (node.nodeType === JSON_NODE_TYPE) return "json";
|
|
122
|
+
if (node.nodeType === STORAGE_NODE_TYPE) return "storage";
|
|
113
123
|
if (node.nodeType === CLOCK_NODE_TYPE) return "clock";
|
|
114
124
|
if (node.nodeType === EVENT_NODE_TYPE) return "event";
|
|
115
125
|
if (node.nodeType === VIEW_NODE_TYPE) return "view";
|
|
@@ -187,6 +197,8 @@ export function resolveRunnableNodeType(
|
|
|
187
197
|
return FETCH_NODE_TYPE;
|
|
188
198
|
case "json":
|
|
189
199
|
return JSON_NODE_TYPE;
|
|
200
|
+
case "storage":
|
|
201
|
+
return STORAGE_NODE_TYPE;
|
|
190
202
|
case "logic":
|
|
191
203
|
return DEFAULT_LOGIC_NODE_TYPE;
|
|
192
204
|
default:
|
|
@@ -210,6 +222,7 @@ export const BLUEPRINT_CONFIG_TYPE_LABEL_KEYS: Record<
|
|
|
210
222
|
event: "blueprint.node.typeEvent",
|
|
211
223
|
fetch: "blueprint.node.typeFetch",
|
|
212
224
|
json: "blueprint.node.typeJson",
|
|
225
|
+
storage: "blueprint.node.typeStorage",
|
|
213
226
|
clock: "blueprint.node.typeClock",
|
|
214
227
|
};
|
|
215
228
|
|
|
@@ -300,6 +313,15 @@ export function resolveNodeJsonConfig(
|
|
|
300
313
|
};
|
|
301
314
|
}
|
|
302
315
|
|
|
316
|
+
export function resolveNodeStorageConfig(
|
|
317
|
+
node: Pick<BlueprintGraphNode, "storageConfig">
|
|
318
|
+
): StorageNodeConfig {
|
|
319
|
+
return normalizeStorageConfig({
|
|
320
|
+
...DEFAULT_STORAGE_NODE_CONFIG,
|
|
321
|
+
...node.storageConfig,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
303
325
|
export function resolveNodeLogicConfig(
|
|
304
326
|
node: Pick<BlueprintGraphNode, "logicConfig">
|
|
305
327
|
): LogicNodeConfig {
|
|
@@ -399,6 +421,9 @@ export function sanitizeBlueprintDocument(
|
|
|
399
421
|
if (next.role === "json") {
|
|
400
422
|
return { ...next, configSource: "json" as const };
|
|
401
423
|
}
|
|
424
|
+
if (next.role === "storage") {
|
|
425
|
+
return { ...next, configSource: "storage" as const };
|
|
426
|
+
}
|
|
402
427
|
if (next.role === "and") {
|
|
403
428
|
return { ...next, configSource: "and" as const };
|
|
404
429
|
}
|
|
@@ -452,6 +477,7 @@ export function patchNodeConfigSource(
|
|
|
452
477
|
logicConfig: undefined,
|
|
453
478
|
clockConfig: undefined,
|
|
454
479
|
eventConfig: undefined,
|
|
480
|
+
storageConfig: undefined,
|
|
455
481
|
};
|
|
456
482
|
}
|
|
457
483
|
|
|
@@ -469,6 +495,7 @@ export function patchNodeConfigSource(
|
|
|
469
495
|
jsonConfig: undefined,
|
|
470
496
|
logicConfig: undefined,
|
|
471
497
|
eventConfig: undefined,
|
|
498
|
+
storageConfig: undefined,
|
|
472
499
|
};
|
|
473
500
|
}
|
|
474
501
|
|
|
@@ -486,6 +513,7 @@ export function patchNodeConfigSource(
|
|
|
486
513
|
logicConfig: undefined,
|
|
487
514
|
clockConfig: undefined,
|
|
488
515
|
eventConfig: undefined,
|
|
516
|
+
storageConfig: undefined,
|
|
489
517
|
};
|
|
490
518
|
}
|
|
491
519
|
|
|
@@ -503,6 +531,25 @@ export function patchNodeConfigSource(
|
|
|
503
531
|
logicConfig: undefined,
|
|
504
532
|
clockConfig: undefined,
|
|
505
533
|
eventConfig: undefined,
|
|
534
|
+
storageConfig: undefined,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (configSource === "storage") {
|
|
539
|
+
return {
|
|
540
|
+
role: "storage",
|
|
541
|
+
nodeType: STORAGE_NODE_TYPE,
|
|
542
|
+
configSource: "storage",
|
|
543
|
+
storageConfig: resolveNodeStorageConfig(node),
|
|
544
|
+
lifecyclePhase: undefined,
|
|
545
|
+
viewElementId: undefined,
|
|
546
|
+
viewElementIds: undefined,
|
|
547
|
+
libraryBlueprintId: undefined,
|
|
548
|
+
fetchConfig: undefined,
|
|
549
|
+
jsonConfig: undefined,
|
|
550
|
+
logicConfig: undefined,
|
|
551
|
+
clockConfig: undefined,
|
|
552
|
+
eventConfig: undefined,
|
|
506
553
|
};
|
|
507
554
|
}
|
|
508
555
|
|
|
@@ -520,6 +567,7 @@ export function patchNodeConfigSource(
|
|
|
520
567
|
logicConfig: undefined,
|
|
521
568
|
clockConfig: undefined,
|
|
522
569
|
eventConfig: undefined,
|
|
570
|
+
storageConfig: undefined,
|
|
523
571
|
};
|
|
524
572
|
}
|
|
525
573
|
|
|
@@ -537,6 +585,7 @@ export function patchNodeConfigSource(
|
|
|
537
585
|
logicConfig: resolveNodeLogicConfig(node),
|
|
538
586
|
clockConfig: undefined,
|
|
539
587
|
eventConfig: undefined,
|
|
588
|
+
storageConfig: undefined,
|
|
540
589
|
};
|
|
541
590
|
}
|
|
542
591
|
|
|
@@ -554,6 +603,7 @@ export function patchNodeConfigSource(
|
|
|
554
603
|
logicConfig: undefined,
|
|
555
604
|
clockConfig: undefined,
|
|
556
605
|
eventConfig: undefined,
|
|
606
|
+
storageConfig: undefined,
|
|
557
607
|
};
|
|
558
608
|
}
|
|
559
609
|
|
|
@@ -569,6 +619,7 @@ export function patchNodeConfigSource(
|
|
|
569
619
|
jsonConfig: undefined,
|
|
570
620
|
logicConfig: undefined,
|
|
571
621
|
clockConfig: undefined,
|
|
622
|
+
storageConfig: undefined,
|
|
572
623
|
};
|
|
573
624
|
}
|
|
574
625
|
|
|
@@ -583,5 +634,6 @@ export function patchNodeConfigSource(
|
|
|
583
634
|
logicConfig: undefined,
|
|
584
635
|
clockConfig: undefined,
|
|
585
636
|
eventConfig: undefined,
|
|
637
|
+
storageConfig: undefined,
|
|
586
638
|
};
|
|
587
639
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
resolveNodeFetchConfig,
|
|
10
10
|
resolveNodeJsonConfig,
|
|
11
11
|
resolveNodeLogicConfig,
|
|
12
|
+
resolveNodeStorageConfig,
|
|
12
13
|
resolveViewElementIds,
|
|
13
14
|
resolveNodeEventConfig,
|
|
14
15
|
getViewEventTypeLabel,
|
|
@@ -65,6 +66,29 @@ export function resolveBlueprintNodeSummary(
|
|
|
65
66
|
if (!json) return t("blueprint.node.summaryEmptyJson");
|
|
66
67
|
return truncate(json.replace(/\s+/g, " "), 44);
|
|
67
68
|
}
|
|
69
|
+
case "storage": {
|
|
70
|
+
const storage = resolveNodeStorageConfig(data);
|
|
71
|
+
const setKey = storage.set.key.trim();
|
|
72
|
+
const readKey = storage.read.key.trim();
|
|
73
|
+
const hasSet = Boolean(setKey) && storage.set.storages.length > 0;
|
|
74
|
+
const hasRead = Boolean(readKey);
|
|
75
|
+
if (hasSet && hasRead) {
|
|
76
|
+
return truncate(
|
|
77
|
+
t("blueprint.node.summaryStorageSetRead", {
|
|
78
|
+
setKey,
|
|
79
|
+
readKey,
|
|
80
|
+
}),
|
|
81
|
+
44
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
if (hasSet) {
|
|
85
|
+
return truncate(t("blueprint.node.summaryStorageSet", { key: setKey }), 44);
|
|
86
|
+
}
|
|
87
|
+
if (hasRead) {
|
|
88
|
+
return truncate(t("blueprint.node.summaryStorageRead", { key: readKey }), 44);
|
|
89
|
+
}
|
|
90
|
+
return t("blueprint.node.summaryStorageUnset");
|
|
91
|
+
}
|
|
68
92
|
case "logic": {
|
|
69
93
|
const code = resolveNodeLogicConfig(data).sourceCode?.trim();
|
|
70
94
|
if (!code) return t("blueprint.node.summaryNoScript");
|
package/src/graph/node-types.ts
CHANGED
package/src/graph/sync-nodes.ts
CHANGED
|
@@ -50,6 +50,10 @@ export function nodeStructureSignature(
|
|
|
50
50
|
viewElementIds?: string[];
|
|
51
51
|
fetchConfig?: { url?: string; method?: string; apiBaseUrl?: string; swaggerDocsUrl?: string };
|
|
52
52
|
jsonConfig?: { jsonString?: string };
|
|
53
|
+
storageConfig?: {
|
|
54
|
+
read?: { storage?: string; key?: string };
|
|
55
|
+
set?: { storages?: string[]; key?: string; value?: string };
|
|
56
|
+
};
|
|
53
57
|
logicConfig?: { sourceCode?: string };
|
|
54
58
|
clockConfig?: { intervalSeconds?: number; timeFormat?: string; outputCount?: number; emitImmediately?: boolean };
|
|
55
59
|
eventConfig?: { eventTypes?: string[] };
|
|
@@ -58,7 +62,7 @@ export function nodeStructureSignature(
|
|
|
58
62
|
return nodes
|
|
59
63
|
.map(
|
|
60
64
|
(n) =>
|
|
61
|
-
`${n.id}:${n.role}:${n.label}:${n.configSource ?? ""}:${n.lifecyclePhase ?? ""}:${n.libraryBlueprintId ?? ""}:${n.nodeType ?? ""}:${(n.viewElementIds ?? (n.viewElementId ? [n.viewElementId] : [])).join(",")}:${n.fetchConfig?.url ?? ""}:${n.fetchConfig?.method ?? ""}:${n.fetchConfig?.apiBaseUrl ?? ""}:${n.fetchConfig?.swaggerDocsUrl ?? ""}:${n.jsonConfig?.jsonString ?? ""}:${n.logicConfig?.sourceCode ?? ""}:${n.clockConfig?.intervalSeconds ?? ""}:${n.clockConfig?.timeFormat ?? ""}:${n.clockConfig?.outputCount ?? ""}:${n.clockConfig?.emitImmediately ?? ""}:${(n.eventConfig?.eventTypes ?? []).join(",")}`
|
|
65
|
+
`${n.id}:${n.role}:${n.label}:${n.configSource ?? ""}:${n.lifecyclePhase ?? ""}:${n.libraryBlueprintId ?? ""}:${n.nodeType ?? ""}:${(n.viewElementIds ?? (n.viewElementId ? [n.viewElementId] : [])).join(",")}:${n.fetchConfig?.url ?? ""}:${n.fetchConfig?.method ?? ""}:${n.fetchConfig?.apiBaseUrl ?? ""}:${n.fetchConfig?.swaggerDocsUrl ?? ""}:${n.jsonConfig?.jsonString ?? ""}:${n.storageConfig?.read?.storage ?? ""}:${n.storageConfig?.read?.key ?? ""}:${(n.storageConfig?.set?.storages ?? []).join(",")}:${n.storageConfig?.set?.key ?? ""}:${n.storageConfig?.set?.value ?? ""}:${n.logicConfig?.sourceCode ?? ""}:${n.clockConfig?.intervalSeconds ?? ""}:${n.clockConfig?.timeFormat ?? ""}:${n.clockConfig?.outputCount ?? ""}:${n.clockConfig?.emitImmediately ?? ""}:${(n.eventConfig?.eventTypes ?? []).join(",")}`
|
|
62
66
|
)
|
|
63
67
|
.sort()
|
|
64
68
|
.join("|");
|
|
@@ -19,6 +19,7 @@ export type BlueprintFlowNodeData = {
|
|
|
19
19
|
lifecyclePhase?: BlueprintGraphNode["lifecyclePhase"];
|
|
20
20
|
fetchConfig?: BlueprintGraphNode["fetchConfig"];
|
|
21
21
|
jsonConfig?: BlueprintGraphNode["jsonConfig"];
|
|
22
|
+
storageConfig?: BlueprintGraphNode["storageConfig"];
|
|
22
23
|
clockConfig?: BlueprintGraphNode["clockConfig"];
|
|
23
24
|
eventConfig?: BlueprintGraphNode["eventConfig"];
|
|
24
25
|
logicConfig?: BlueprintGraphNode["logicConfig"];
|
|
@@ -71,6 +72,8 @@ export function toVueFlowNodes(document: BlueprintDocument): VueFlowNodeView[] {
|
|
|
71
72
|
? "fetch"
|
|
72
73
|
: n.role === "json"
|
|
73
74
|
? "json"
|
|
75
|
+
: n.role === "storage"
|
|
76
|
+
? "storage"
|
|
74
77
|
: n.role === "event"
|
|
75
78
|
? "event"
|
|
76
79
|
: "logic",
|
|
@@ -93,6 +96,7 @@ export function toVueFlowNodes(document: BlueprintDocument): VueFlowNodeView[] {
|
|
|
93
96
|
lifecyclePhase: n.lifecyclePhase,
|
|
94
97
|
fetchConfig: n.fetchConfig,
|
|
95
98
|
jsonConfig: n.jsonConfig,
|
|
99
|
+
storageConfig: n.storageConfig,
|
|
96
100
|
clockConfig: n.clockConfig,
|
|
97
101
|
eventConfig: n.eventConfig,
|
|
98
102
|
logicConfig: n.logicConfig,
|
|
@@ -10,7 +10,7 @@ export type BlueprintNodeCardProps = {
|
|
|
10
10
|
meta?: string;
|
|
11
11
|
subtitle?: string;
|
|
12
12
|
progressLabel?: string;
|
|
13
|
-
variant?: "blueprint" | "logic" | "and" | "lifecycle" | "event" | "fetch" | "json" | "clock";
|
|
13
|
+
variant?: "blueprint" | "logic" | "and" | "lifecycle" | "event" | "fetch" | "json" | "storage" | "clock";
|
|
14
14
|
selected?: boolean;
|
|
15
15
|
hideLeadingDot?: boolean;
|
|
16
16
|
};
|
|
@@ -61,6 +61,11 @@ const variantStyle = {
|
|
|
61
61
|
badge: "bg-teal-500/10 text-teal-700 dark:text-teal-300",
|
|
62
62
|
dot: "bg-teal-500",
|
|
63
63
|
},
|
|
64
|
+
storage: {
|
|
65
|
+
accent: "border-l-emerald-500 dark:border-l-emerald-400",
|
|
66
|
+
badge: "bg-emerald-500/10 text-emerald-700 dark:text-emerald-300",
|
|
67
|
+
dot: "bg-emerald-500",
|
|
68
|
+
},
|
|
64
69
|
clock: {
|
|
65
70
|
accent: "border-l-rose-500 dark:border-l-rose-400",
|
|
66
71
|
badge: "bg-rose-500/10 text-rose-700 dark:text-rose-300",
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/vue";
|
|
3
|
+
import type { NodeProps } from "@vue-flow/core";
|
|
4
|
+
import { resolveBlueprintNodeTypeLabel } from "../graph/document";
|
|
5
|
+
import { resolveBlueprintNodeSummary } from "../graph/node-summary";
|
|
6
|
+
import { useBlueprintNodeSelect } from "../BlueprintCanvasContext";
|
|
7
|
+
import { resolveBlueprintNodeExecutionTone } from "../runtime/execution-overlay";
|
|
8
|
+
import type { BlueprintFlowNodeData } from "../types";
|
|
9
|
+
import BlueprintNodeShell from "./BlueprintNodeShell.vue";
|
|
10
|
+
|
|
11
|
+
const { t } = useI18n();
|
|
12
|
+
|
|
13
|
+
const props = defineProps<NodeProps<BlueprintFlowNodeData>>();
|
|
14
|
+
const onSelect = useBlueprintNodeSelect();
|
|
15
|
+
const nodeData = props.data;
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<BlueprintNodeShell
|
|
20
|
+
:node-id="props.id"
|
|
21
|
+
:label="nodeData.label"
|
|
22
|
+
:meta="resolveBlueprintNodeTypeLabel(nodeData, t)"
|
|
23
|
+
:subtitle="resolveBlueprintNodeSummary(nodeData, t)"
|
|
24
|
+
variant="storage"
|
|
25
|
+
:selected="Boolean(nodeData.isSelected)"
|
|
26
|
+
:execution-tone="resolveBlueprintNodeExecutionTone(nodeData)"
|
|
27
|
+
@select="onSelect"
|
|
28
|
+
/>
|
|
29
|
+
</template>
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
FETCH_NODE_TYPE,
|
|
3
|
+
STORAGE_NODE_TYPE,
|
|
3
4
|
type RunnableGraph,
|
|
4
5
|
} from "@arronqzy/blueprint-dsl";
|
|
5
6
|
|
|
6
7
|
import {
|
|
7
8
|
resolveNodeFetchConfig,
|
|
9
|
+
resolveNodeStorageConfig,
|
|
8
10
|
resolveRunnableNodeType,
|
|
9
11
|
resolveViewElementIds,
|
|
10
12
|
type BlueprintDocument,
|
|
@@ -34,6 +36,10 @@ export function documentToRunnableGraph(
|
|
|
34
36
|
? resolveNodeFetchConfig(node)
|
|
35
37
|
: node.fetchConfig,
|
|
36
38
|
jsonConfig: node.jsonConfig,
|
|
39
|
+
storageConfig:
|
|
40
|
+
nodeType === STORAGE_NODE_TYPE
|
|
41
|
+
? resolveNodeStorageConfig(node)
|
|
42
|
+
: node.storageConfig,
|
|
37
43
|
logicConfig: node.logicConfig,
|
|
38
44
|
clockConfig: node.clockConfig,
|
|
39
45
|
eventConfig: node.eventConfig,
|