@arronqzy/vue-blueprint 0.1.3 → 0.1.4
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 +44 -18
- package/src/components/ClockNodeConfigPanel.vue +25 -14
- package/src/components/ConfigHintIcon.vue +38 -0
- package/src/components/FetchNodeConfigPanel.vue +127 -25
- package/src/components/JsonNodeConfigPanel.vue +7 -2
- package/src/components/LogicNodeConfigPanel.vue +7 -2
- package/src/graph/document.ts +1 -0
- package/src/runtime/document-to-runnable-graph.ts +2 -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.4",
|
|
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/blueprint-dsl": "1.0.
|
|
23
|
-
"@arronqzy/
|
|
24
|
-
"@arronqzy/
|
|
22
|
+
"@arronqzy/blueprint-dsl": "1.0.6",
|
|
23
|
+
"@arronqzy/vue-rx-store": "0.1.2",
|
|
24
|
+
"@arronqzy/i18n": "0.1.5"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"vue": "^3.4.0"
|
|
@@ -5,6 +5,7 @@ import { Checkbox, Input, Select } from "ant-design-vue";
|
|
|
5
5
|
import {
|
|
6
6
|
LIFECYCLE_NODE_TYPE,
|
|
7
7
|
PAGE_LIFECYCLE_PHASES,
|
|
8
|
+
type ExecutionTraceEntry,
|
|
8
9
|
type PageLifecyclePhase,
|
|
9
10
|
} from "@arronqzy/blueprint-dsl";
|
|
10
11
|
|
|
@@ -13,6 +14,7 @@ import ClockNodeConfigPanel from "./components/ClockNodeConfigPanel.vue";
|
|
|
13
14
|
import JsonNodeConfigPanel from "./components/JsonNodeConfigPanel.vue";
|
|
14
15
|
import LogicNodeConfigPanel from "./components/LogicNodeConfigPanel.vue";
|
|
15
16
|
import ViewElementMultiSelect from "./components/ViewElementMultiSelect.vue";
|
|
17
|
+
import ConfigHintIcon from "./components/ConfigHintIcon.vue";
|
|
16
18
|
import {
|
|
17
19
|
getLifecyclePhaseLabel,
|
|
18
20
|
patchNodeConfigSource,
|
|
@@ -20,6 +22,7 @@ import {
|
|
|
20
22
|
resolveBlueprintConfigSource,
|
|
21
23
|
resolveViewElementIds,
|
|
22
24
|
type BlueprintConfigSource,
|
|
25
|
+
type BlueprintGraphEdge,
|
|
23
26
|
type BlueprintGraphNode,
|
|
24
27
|
type BlueprintNodeRole,
|
|
25
28
|
} from "./graph/document";
|
|
@@ -38,6 +41,9 @@ export type BlueprintLibraryOption = {
|
|
|
38
41
|
|
|
39
42
|
export type BlueprintNodeConfigSidebarProps = {
|
|
40
43
|
node: BlueprintGraphNode;
|
|
44
|
+
graphNodes?: BlueprintGraphNode[];
|
|
45
|
+
graphEdges?: BlueprintGraphEdge[];
|
|
46
|
+
traceEntries?: ExecutionTraceEntry[];
|
|
41
47
|
viewElementOptions?: BlueprintViewElementOption[];
|
|
42
48
|
blueprintLibraryOptions?: BlueprintLibraryOption[];
|
|
43
49
|
allowFalseSignalPropagation?: boolean;
|
|
@@ -68,6 +74,9 @@ export type BlueprintNodeConfigSidebarProps = {
|
|
|
68
74
|
const props = withDefaults(defineProps<BlueprintNodeConfigSidebarProps>(), {
|
|
69
75
|
viewElementOptions: () => [],
|
|
70
76
|
blueprintLibraryOptions: () => [],
|
|
77
|
+
graphNodes: () => [],
|
|
78
|
+
graphEdges: () => [],
|
|
79
|
+
traceEntries: () => [],
|
|
71
80
|
allowFalseSignalPropagation: false,
|
|
72
81
|
});
|
|
73
82
|
|
|
@@ -173,7 +182,12 @@ function handleConfigSourceChange(value: string) {
|
|
|
173
182
|
</label>
|
|
174
183
|
|
|
175
184
|
<div v-if="configSource === 'view'" class="block space-y-1">
|
|
176
|
-
<span class="text-muted-foreground">
|
|
185
|
+
<span class="inline-flex items-center gap-1 text-muted-foreground">
|
|
186
|
+
{{ t("blueprint.config.linkedViewNodes") }}
|
|
187
|
+
<ConfigHintIcon :label="t('blueprint.config.linkedViewNodes')">
|
|
188
|
+
{{ t("blueprint.config.viewMultiHint") }}
|
|
189
|
+
</ConfigHintIcon>
|
|
190
|
+
</span>
|
|
177
191
|
<ViewElementMultiSelect
|
|
178
192
|
:options="viewElementOptions"
|
|
179
193
|
:value="linkedViewElementIds"
|
|
@@ -187,10 +201,7 @@ function handleConfigSourceChange(value: string) {
|
|
|
187
201
|
})
|
|
188
202
|
"
|
|
189
203
|
/>
|
|
190
|
-
<p v-if="linkedViewElementIds.length
|
|
191
|
-
{{ t("blueprint.config.viewMultiHint") }}
|
|
192
|
-
</p>
|
|
193
|
-
<p v-else class="text-[11px] text-muted-foreground">
|
|
204
|
+
<p v-if="linkedViewElementIds.length > 0" class="text-[11px] text-muted-foreground">
|
|
194
205
|
{{
|
|
195
206
|
t("blueprint.config.linkedViewCount", {
|
|
196
207
|
count: linkedViewElementIds.length,
|
|
@@ -206,8 +217,12 @@ function handleConfigSourceChange(value: string) {
|
|
|
206
217
|
v-if="configSource === 'blueprint'"
|
|
207
218
|
class="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5"
|
|
208
219
|
>
|
|
209
|
-
<div class="
|
|
210
|
-
|
|
220
|
+
<div class="flex items-center gap-1.5">
|
|
221
|
+
<div class="font-medium text-foreground">{{ t("blueprint.config.blueprintAttrs") }}</div>
|
|
222
|
+
<ConfigHintIcon :label="t('blueprint.config.blueprintAttrs')">
|
|
223
|
+
{{ t("blueprint.config.blueprintAttrsHint") }}
|
|
224
|
+
</ConfigHintIcon>
|
|
225
|
+
</div>
|
|
211
226
|
<label class="block space-y-1">
|
|
212
227
|
<span class="text-muted-foreground">{{ t("blueprint.config.refLibrary") }}</span>
|
|
213
228
|
<Select
|
|
@@ -252,15 +267,19 @@ function handleConfigSourceChange(value: string) {
|
|
|
252
267
|
v-if="configSource === 'lifecycle'"
|
|
253
268
|
class="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5"
|
|
254
269
|
>
|
|
255
|
-
<div class="
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
270
|
+
<div class="flex items-center gap-1.5">
|
|
271
|
+
<div class="font-medium text-foreground">{{ t("blueprint.config.lifecycleHook") }}</div>
|
|
272
|
+
<ConfigHintIcon :label="t('blueprint.config.lifecycleHook')">
|
|
273
|
+
<p>{{ t("blueprint.config.lifecycleNoInput") }}</p>
|
|
274
|
+
<p>
|
|
275
|
+
{{
|
|
276
|
+
node.lifecyclePhase === "blueprintActivated"
|
|
277
|
+
? t("blueprint.config.lifecycleBlueprintActivatedHint")
|
|
278
|
+
: t("blueprint.config.lifecyclePageHint")
|
|
279
|
+
}}
|
|
280
|
+
</p>
|
|
281
|
+
</ConfigHintIcon>
|
|
282
|
+
</div>
|
|
264
283
|
<label class="block space-y-1">
|
|
265
284
|
<span class="text-muted-foreground">{{ t("blueprint.config.listenPhase") }}</span>
|
|
266
285
|
<Select
|
|
@@ -290,6 +309,9 @@ function handleConfigSourceChange(value: string) {
|
|
|
290
309
|
<FetchNodeConfigPanel
|
|
291
310
|
v-if="configSource === 'fetch'"
|
|
292
311
|
:node="node"
|
|
312
|
+
:graph-nodes="graphNodes"
|
|
313
|
+
:graph-edges="graphEdges"
|
|
314
|
+
:trace-entries="traceEntries"
|
|
293
315
|
:on-update-node="onUpdateNode"
|
|
294
316
|
/>
|
|
295
317
|
<JsonNodeConfigPanel
|
|
@@ -307,8 +329,12 @@ function handleConfigSourceChange(value: string) {
|
|
|
307
329
|
v-if="configSource === 'and'"
|
|
308
330
|
class="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5"
|
|
309
331
|
>
|
|
310
|
-
<div class="
|
|
311
|
-
|
|
332
|
+
<div class="flex items-center gap-1.5">
|
|
333
|
+
<div class="font-medium text-foreground">{{ t("blueprint.config.andTitle") }}</div>
|
|
334
|
+
<ConfigHintIcon :label="t('blueprint.config.andTitle')">
|
|
335
|
+
{{ t("blueprint.config.andHint") }}
|
|
336
|
+
</ConfigHintIcon>
|
|
337
|
+
</div>
|
|
312
338
|
</div>
|
|
313
339
|
|
|
314
340
|
<template v-if="configSource === 'logic'">
|
|
@@ -4,6 +4,7 @@ import { computed } from "vue";
|
|
|
4
4
|
import { Checkbox, Input } from "ant-design-vue";
|
|
5
5
|
import type { ClockNodeConfig } from "@arronqzy/blueprint-dsl";
|
|
6
6
|
|
|
7
|
+
import ConfigHintIcon from "./ConfigHintIcon.vue";
|
|
7
8
|
import type { BlueprintGraphNode } from "../graph/document";
|
|
8
9
|
import { resolveNodeClockConfig } from "../graph/document";
|
|
9
10
|
|
|
@@ -60,11 +61,20 @@ function handleEmitImmediatelyChange(checked: boolean) {
|
|
|
60
61
|
|
|
61
62
|
<template>
|
|
62
63
|
<div class="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
|
|
63
|
-
<div class="
|
|
64
|
-
|
|
64
|
+
<div class="flex items-center gap-1.5">
|
|
65
|
+
<div class="font-medium text-foreground">{{ t("blueprint.config.clockTitle") }}</div>
|
|
66
|
+
<ConfigHintIcon :label="t('blueprint.config.clockTitle')">
|
|
67
|
+
{{ t("blueprint.config.clockHint") }}
|
|
68
|
+
</ConfigHintIcon>
|
|
69
|
+
</div>
|
|
65
70
|
|
|
66
71
|
<label class="block space-y-1">
|
|
67
|
-
<span class="text-muted-foreground">
|
|
72
|
+
<span class="inline-flex items-center gap-1 text-muted-foreground">
|
|
73
|
+
{{ t("blueprint.config.clockIntervalSeconds") }}
|
|
74
|
+
<ConfigHintIcon :label="t('blueprint.config.clockIntervalSeconds')">
|
|
75
|
+
{{ t("blueprint.config.clockIntervalHint") }}
|
|
76
|
+
</ConfigHintIcon>
|
|
77
|
+
</span>
|
|
68
78
|
<Input
|
|
69
79
|
type="number"
|
|
70
80
|
:min="0"
|
|
@@ -73,13 +83,15 @@ function handleEmitImmediatelyChange(checked: boolean) {
|
|
|
73
83
|
:value="clockConfig.intervalSeconds"
|
|
74
84
|
@update:value="handleIntervalChange"
|
|
75
85
|
/>
|
|
76
|
-
<p class="text-[11px] text-muted-foreground">
|
|
77
|
-
{{ t("blueprint.config.clockIntervalHint") }}
|
|
78
|
-
</p>
|
|
79
86
|
</label>
|
|
80
87
|
|
|
81
88
|
<label class="block space-y-1">
|
|
82
|
-
<span class="text-muted-foreground">
|
|
89
|
+
<span class="inline-flex items-center gap-1 text-muted-foreground">
|
|
90
|
+
{{ t("blueprint.config.outputCount") }}
|
|
91
|
+
<ConfigHintIcon :label="t('blueprint.config.outputCount')">
|
|
92
|
+
{{ t("blueprint.config.outputCountHint") }}
|
|
93
|
+
</ConfigHintIcon>
|
|
94
|
+
</span>
|
|
83
95
|
<Input
|
|
84
96
|
type="number"
|
|
85
97
|
:min="1"
|
|
@@ -88,9 +100,6 @@ function handleEmitImmediatelyChange(checked: boolean) {
|
|
|
88
100
|
:value="clockConfig.outputCount"
|
|
89
101
|
@update:value="handleOutputCountChange"
|
|
90
102
|
/>
|
|
91
|
-
<p class="text-[11px] text-muted-foreground">
|
|
92
|
-
{{ t("blueprint.config.outputCountHint") }}
|
|
93
|
-
</p>
|
|
94
103
|
</label>
|
|
95
104
|
|
|
96
105
|
<label class="flex items-start gap-2">
|
|
@@ -105,7 +114,12 @@ function handleEmitImmediatelyChange(checked: boolean) {
|
|
|
105
114
|
</label>
|
|
106
115
|
|
|
107
116
|
<label class="block space-y-1">
|
|
108
|
-
<span class="text-muted-foreground">
|
|
117
|
+
<span class="inline-flex items-center gap-1 text-muted-foreground">
|
|
118
|
+
{{ t("blueprint.config.timeFormat") }}
|
|
119
|
+
<ConfigHintIcon :label="t('blueprint.config.timeFormat')">
|
|
120
|
+
{{ t("blueprint.config.timeFormatHint") }}
|
|
121
|
+
</ConfigHintIcon>
|
|
122
|
+
</span>
|
|
109
123
|
<Input
|
|
110
124
|
size="small"
|
|
111
125
|
:value="clockConfig.timeFormat"
|
|
@@ -114,9 +128,6 @@ function handleEmitImmediatelyChange(checked: boolean) {
|
|
|
114
128
|
placeholder="YYYY-MM-DD HH:mm:ss"
|
|
115
129
|
@input="handleFormatChange"
|
|
116
130
|
/>
|
|
117
|
-
<p class="text-[11px] text-muted-foreground">
|
|
118
|
-
{{ t("blueprint.config.timeFormatHint") }}
|
|
119
|
-
</p>
|
|
120
131
|
</label>
|
|
121
132
|
</div>
|
|
122
133
|
</template>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/vue";
|
|
3
|
+
import { Tooltip } from "ant-design-vue";
|
|
4
|
+
|
|
5
|
+
const { t } = useI18n();
|
|
6
|
+
withDefaults(
|
|
7
|
+
defineProps<{
|
|
8
|
+
label?: string;
|
|
9
|
+
contentClass?: string;
|
|
10
|
+
buttonClass?: string;
|
|
11
|
+
}>(),
|
|
12
|
+
{ label: t("common.hint") }
|
|
13
|
+
);
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<Tooltip
|
|
18
|
+
placement="top"
|
|
19
|
+
:overlay-class-name="`z-[10120] max-w-[360px] text-[11px] leading-5 ${contentClass ?? ''}`"
|
|
20
|
+
:mouse-enter-delay="0.12"
|
|
21
|
+
>
|
|
22
|
+
<template #title>
|
|
23
|
+
<div class="space-y-1.5">
|
|
24
|
+
<slot />
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
27
|
+
<button
|
|
28
|
+
type="button"
|
|
29
|
+
:class="
|
|
30
|
+
buttonClass ??
|
|
31
|
+
'inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full border border-border text-[10px] leading-none text-muted-foreground hover:bg-accent/50'
|
|
32
|
+
"
|
|
33
|
+
:aria-label="t('common.hintAria', { label })"
|
|
34
|
+
>
|
|
35
|
+
?
|
|
36
|
+
</button>
|
|
37
|
+
</Tooltip>
|
|
38
|
+
</template>
|
|
@@ -3,11 +3,14 @@ import { useI18n } from "@arronqzy/i18n/vue";
|
|
|
3
3
|
import { computed, ref } from "vue";
|
|
4
4
|
import { Button, Input, Select } from "ant-design-vue";
|
|
5
5
|
import type {
|
|
6
|
+
ExecutionTraceEntry,
|
|
6
7
|
FetchHttpMethod,
|
|
7
8
|
FetchRequestConfig,
|
|
8
9
|
FetchResponseType,
|
|
9
10
|
} from "@arronqzy/blueprint-dsl";
|
|
10
11
|
import {
|
|
12
|
+
applyFetchConfigScope,
|
|
13
|
+
draftFetchHeadersText,
|
|
11
14
|
FETCH_CACHES,
|
|
12
15
|
FETCH_CREDENTIALS,
|
|
13
16
|
FETCH_HTTP_METHODS,
|
|
@@ -15,9 +18,14 @@ import {
|
|
|
15
18
|
FETCH_NODE_TYPE,
|
|
16
19
|
FETCH_REDIRECTS,
|
|
17
20
|
FETCH_RESPONSE_TYPES,
|
|
21
|
+
fetchConfigHasScopeTemplate,
|
|
22
|
+
latestTraceOutputsByNode,
|
|
23
|
+
parseFetchHeadersJson,
|
|
24
|
+
resolveFetchIncomingScope,
|
|
25
|
+
resolveFetchRequestUrl,
|
|
18
26
|
} from "@arronqzy/blueprint-dsl";
|
|
19
27
|
|
|
20
|
-
import type { BlueprintGraphNode } from "../graph/document";
|
|
28
|
+
import type { BlueprintGraphEdge, BlueprintGraphNode } from "../graph/document";
|
|
21
29
|
import { resolveNodeFetchConfig } from "../graph/document";
|
|
22
30
|
import {
|
|
23
31
|
cancelFetchDebugTask,
|
|
@@ -27,25 +35,80 @@ import {
|
|
|
27
35
|
useFetchDebugTask,
|
|
28
36
|
useSwaggerLoadTask,
|
|
29
37
|
} from "../fetch-config-task-store";
|
|
38
|
+
import ConfigHintIcon from "./ConfigHintIcon.vue";
|
|
30
39
|
import FetchUrlAutocomplete from "./FetchUrlAutocomplete.vue";
|
|
31
40
|
|
|
32
41
|
const { t } = useI18n();
|
|
33
42
|
|
|
34
43
|
export type FetchNodeConfigPanelProps = {
|
|
35
44
|
node: BlueprintGraphNode;
|
|
45
|
+
graphNodes?: BlueprintGraphNode[];
|
|
46
|
+
graphEdges?: BlueprintGraphEdge[];
|
|
47
|
+
traceEntries?: ExecutionTraceEntry[];
|
|
36
48
|
onUpdateNode: (
|
|
37
49
|
nodeId: string,
|
|
38
50
|
patch: Partial<Pick<BlueprintGraphNode, "fetchConfig" | "configSource">>
|
|
39
51
|
) => void;
|
|
40
52
|
};
|
|
41
53
|
|
|
42
|
-
const props = defineProps<FetchNodeConfigPanelProps>()
|
|
54
|
+
const props = withDefaults(defineProps<FetchNodeConfigPanelProps>(), {
|
|
55
|
+
graphNodes: () => [],
|
|
56
|
+
graphEdges: () => [],
|
|
57
|
+
traceEntries: () => [],
|
|
58
|
+
});
|
|
43
59
|
|
|
44
60
|
const validationError = ref<string | null>(null);
|
|
45
61
|
const fetchValidationError = ref<string | null>(null);
|
|
46
62
|
const debugExpanded = ref(false);
|
|
47
63
|
|
|
48
64
|
const fetchConfig = computed(() => resolveNodeFetchConfig(props.node));
|
|
65
|
+
const incomingScope = computed(() => {
|
|
66
|
+
const outputs = latestTraceOutputsByNode(props.traceEntries);
|
|
67
|
+
return resolveFetchIncomingScope({
|
|
68
|
+
fetchNodeId: props.node.id,
|
|
69
|
+
nodes: props.graphNodes,
|
|
70
|
+
edges: props.graphEdges,
|
|
71
|
+
getOutput: (sourceId, port) => outputs[sourceId]?.[port],
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
const usesScopeTemplate = computed(() => fetchConfigHasScopeTemplate(fetchConfig.value));
|
|
75
|
+
const resolvedFetchConfig = computed(() =>
|
|
76
|
+
applyFetchConfigScope(fetchConfig.value, incomingScope.value)
|
|
77
|
+
);
|
|
78
|
+
const resolvedUrlPreview = computed(() => {
|
|
79
|
+
if (!usesScopeTemplate.value) return "";
|
|
80
|
+
try {
|
|
81
|
+
return resolveFetchRequestUrl(resolvedFetchConfig.value);
|
|
82
|
+
} catch {
|
|
83
|
+
return resolvedFetchConfig.value.url?.trim() ?? "";
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
const incomingScopeJson = computed(() => {
|
|
87
|
+
if (incomingScope.value === undefined) return "";
|
|
88
|
+
try {
|
|
89
|
+
return JSON.stringify(incomingScope.value, null, 2);
|
|
90
|
+
} catch {
|
|
91
|
+
return String(incomingScope.value);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
const headersDraft = computed(() => draftFetchHeadersText(fetchConfig.value));
|
|
95
|
+
const resolvedHeadersPreview = computed(() => {
|
|
96
|
+
if (!usesScopeTemplate.value) return "";
|
|
97
|
+
const headers = resolvedFetchConfig.value.headers;
|
|
98
|
+
if (!headers || Object.keys(headers).length === 0) return "";
|
|
99
|
+
try {
|
|
100
|
+
return JSON.stringify(headers, null, 2);
|
|
101
|
+
} catch {
|
|
102
|
+
return "";
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
const incomingHasPendingValue = computed(() => {
|
|
106
|
+
const scope = incomingScope.value;
|
|
107
|
+
if (!scope || typeof scope !== "object") return false;
|
|
108
|
+
const entries =
|
|
109
|
+
"kind" in scope ? [scope] : Object.values(scope as Record<string, { kind?: string }>);
|
|
110
|
+
return entries.some((entry) => entry && entry.kind === "pending");
|
|
111
|
+
});
|
|
49
112
|
const endpoints = computed(() => fetchConfig.value.swaggerEndpoints ?? []);
|
|
50
113
|
const hasSwaggerEndpoints = computed(() => endpoints.value.length > 0);
|
|
51
114
|
const urlInputMode = computed(
|
|
@@ -105,16 +168,20 @@ function handleAbortSwagger() {
|
|
|
105
168
|
}
|
|
106
169
|
|
|
107
170
|
function handleSendFetchDebug() {
|
|
108
|
-
const
|
|
171
|
+
const resolved = applyFetchConfigScope(fetchConfig.value, incomingScope.value);
|
|
172
|
+
const url = resolved.url?.trim();
|
|
109
173
|
if (!url) {
|
|
110
|
-
fetchValidationError.value =
|
|
174
|
+
fetchValidationError.value =
|
|
175
|
+
usesScopeTemplate.value && fetchConfig.value.url?.trim()
|
|
176
|
+
? t("blueprint.config.fetchScopeUnresolved")
|
|
177
|
+
: t("blueprint.config.fillRequestUrlFirst");
|
|
111
178
|
return;
|
|
112
179
|
}
|
|
113
180
|
|
|
114
181
|
fetchValidationError.value = null;
|
|
115
182
|
startFetchDebugTask({
|
|
116
183
|
nodeId: props.node.id,
|
|
117
|
-
config:
|
|
184
|
+
config: resolved,
|
|
118
185
|
});
|
|
119
186
|
}
|
|
120
187
|
|
|
@@ -149,24 +216,42 @@ const debugOk = computed(() => {
|
|
|
149
216
|
});
|
|
150
217
|
|
|
151
218
|
function handleHeadersBlur(event: Event) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
219
|
+
const headersJson = (event.target as HTMLTextAreaElement).value;
|
|
220
|
+
const parsed = parseFetchHeadersJson(headersJson);
|
|
221
|
+
props.onUpdateNode(
|
|
222
|
+
props.node.id,
|
|
223
|
+
patchFetchConfig(props.node, {
|
|
224
|
+
headersJson,
|
|
225
|
+
...(parsed ? { headers: parsed } : {}),
|
|
226
|
+
})
|
|
227
|
+
);
|
|
161
228
|
}
|
|
162
229
|
</script>
|
|
163
230
|
|
|
164
231
|
<template>
|
|
165
232
|
<div class="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
|
|
166
|
-
<div class="
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
233
|
+
<div class="flex items-center gap-1.5">
|
|
234
|
+
<div class="font-medium text-foreground">{{ t("blueprint.config.fetchTitle") }}</div>
|
|
235
|
+
<ConfigHintIcon :label="t('blueprint.config.fetchTitle')">
|
|
236
|
+
{{ t("blueprint.config.fetchHint") }}
|
|
237
|
+
</ConfigHintIcon>
|
|
238
|
+
</div>
|
|
239
|
+
<div class="space-y-1 rounded-md border border-gray-200/80 bg-white/80 p-2">
|
|
240
|
+
<div class="flex items-center gap-1.5 text-[11px] font-medium text-gray-800">
|
|
241
|
+
{{ t("blueprint.config.fetchScopeTitle") }}
|
|
242
|
+
<ConfigHintIcon :label="t('blueprint.config.fetchScopeTitle')">
|
|
243
|
+
<p>{{ t("blueprint.config.fetchScopeHint") }}</p>
|
|
244
|
+
<p>{{ t("blueprint.config.fetchScopeEmpty") }}</p>
|
|
245
|
+
<p>{{ t("blueprint.config.fetchScopePending") }}</p>
|
|
246
|
+
</ConfigHintIcon>
|
|
247
|
+
</div>
|
|
248
|
+
<template v-if="incomingScope !== undefined">
|
|
249
|
+
<p v-if="incomingHasPendingValue" class="text-[11px] text-amber-700">
|
|
250
|
+
{{ t("blueprint.config.fetchScopePending") }}
|
|
251
|
+
</p>
|
|
252
|
+
<pre class="max-h-[180px] overflow-auto rounded border border-gray-200 bg-gray-50 p-2 font-mono text-[10px] leading-relaxed text-gray-800">{{ incomingScopeJson }}</pre>
|
|
253
|
+
</template>
|
|
254
|
+
</div>
|
|
170
255
|
|
|
171
256
|
<label class="block space-y-1">
|
|
172
257
|
<span class="text-muted-foreground">{{ t("blueprint.config.swaggerUrlOptional") }}</span>
|
|
@@ -254,7 +339,7 @@ function handleHeadersBlur(event: Event) {
|
|
|
254
339
|
<Input
|
|
255
340
|
size="small"
|
|
256
341
|
:value="fetchConfig.apiBaseUrl ?? ''"
|
|
257
|
-
placeholder="https://api.example.com/v1"
|
|
342
|
+
placeholder="https://api.example.com/v1/{scope?.value?.tenant}"
|
|
258
343
|
class="font-mono text-[11px]"
|
|
259
344
|
@update:value="
|
|
260
345
|
(v) => onUpdateNode(node.id, patchFetchConfig(node, { apiBaseUrl: String(v) }))
|
|
@@ -313,7 +398,7 @@ function handleHeadersBlur(event: Event) {
|
|
|
313
398
|
size="small"
|
|
314
399
|
:value="fetchConfig.url"
|
|
315
400
|
:disabled="loadingFetchDebug"
|
|
316
|
-
placeholder="https://api.example.com/
|
|
401
|
+
placeholder="https://api.example.com/users/{scope?.value?.id}"
|
|
317
402
|
class="flex-1 font-mono text-[11px]"
|
|
318
403
|
@update:value="(v) => onUpdateNode(node.id, patchFetchConfig(node, { url: String(v) }))"
|
|
319
404
|
/>
|
|
@@ -338,6 +423,12 @@ function handleHeadersBlur(event: Event) {
|
|
|
338
423
|
</Button>
|
|
339
424
|
</div>
|
|
340
425
|
|
|
426
|
+
<p
|
|
427
|
+
v-if="usesScopeTemplate && resolvedUrlPreview"
|
|
428
|
+
class="break-all font-mono text-[10px] text-gray-500"
|
|
429
|
+
>
|
|
430
|
+
{{ t("blueprint.config.fetchScopeResolvedUrl") }}: {{ resolvedUrlPreview }}
|
|
431
|
+
</p>
|
|
341
432
|
<p v-if="loadingFetchDebug" class="text-[11px] text-muted-foreground">
|
|
342
433
|
{{ t("blueprint.config.sendingDebug") }}
|
|
343
434
|
</p>
|
|
@@ -418,15 +509,26 @@ function handleHeadersBlur(event: Event) {
|
|
|
418
509
|
</label>
|
|
419
510
|
|
|
420
511
|
<label class="block space-y-1">
|
|
421
|
-
<span class="text-muted-foreground">
|
|
512
|
+
<span class="inline-flex items-center gap-1 text-muted-foreground">
|
|
513
|
+
{{ t("blueprint.config.requestHeadersJson") }}
|
|
514
|
+
<ConfigHintIcon :label="t('blueprint.config.requestHeadersJson')">
|
|
515
|
+
{{ t("blueprint.config.fetchHeadersHint") }}
|
|
516
|
+
</ConfigHintIcon>
|
|
517
|
+
</span>
|
|
422
518
|
<textarea
|
|
423
|
-
:key="`${node.id}-headers-${
|
|
424
|
-
:value="
|
|
519
|
+
:key="`${node.id}-headers-${headersDraft}`"
|
|
520
|
+
:value="headersDraft"
|
|
425
521
|
rows="4"
|
|
426
522
|
class="w-full rounded-md border border-input bg-background px-2 py-1.5 font-mono text-[11px] leading-relaxed text-foreground shadow-sm outline-none focus-visible:ring-1 focus-visible:ring-primary"
|
|
427
|
-
placeholder='{"
|
|
523
|
+
placeholder='{"Authorization":"Bearer {scope?.value?.token}"}'
|
|
428
524
|
@blur="handleHeadersBlur"
|
|
429
525
|
/>
|
|
526
|
+
<p
|
|
527
|
+
v-if="usesScopeTemplate && resolvedHeadersPreview"
|
|
528
|
+
class="whitespace-pre-wrap break-all font-mono text-[10px] text-gray-500"
|
|
529
|
+
>
|
|
530
|
+
{{ t("blueprint.config.fetchScopeResolvedHeaders") }}: {{ resolvedHeadersPreview }}
|
|
531
|
+
</p>
|
|
430
532
|
</label>
|
|
431
533
|
|
|
432
534
|
<label class="block space-y-1">
|
|
@@ -435,7 +537,7 @@ function handleHeadersBlur(event: Event) {
|
|
|
435
537
|
:value="fetchConfig.body ?? ''"
|
|
436
538
|
rows="4"
|
|
437
539
|
class="w-full rounded-md border border-input bg-background px-2 py-1.5 font-mono text-[11px] leading-relaxed text-foreground shadow-sm outline-none focus-visible:ring-1 focus-visible:ring-primary"
|
|
438
|
-
placeholder='{"
|
|
540
|
+
placeholder='{"id":"{scope?.value?.id}"}'
|
|
439
541
|
@input="
|
|
440
542
|
(e) =>
|
|
441
543
|
onUpdateNode(
|
|
@@ -4,6 +4,7 @@ import { computed, ref } from "vue";
|
|
|
4
4
|
import { validateJsonString } from "@arronqzy/blueprint-dsl";
|
|
5
5
|
import type { JsonNodeConfig } from "@arronqzy/blueprint-dsl";
|
|
6
6
|
|
|
7
|
+
import ConfigHintIcon from "./ConfigHintIcon.vue";
|
|
7
8
|
import type { BlueprintGraphNode } from "../graph/document";
|
|
8
9
|
import { resolveNodeJsonConfig } from "../graph/document";
|
|
9
10
|
|
|
@@ -48,8 +49,12 @@ function handleChange(event: Event) {
|
|
|
48
49
|
|
|
49
50
|
<template>
|
|
50
51
|
<div class="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
|
|
51
|
-
<div class="
|
|
52
|
-
|
|
52
|
+
<div class="flex items-center gap-1.5">
|
|
53
|
+
<div class="font-medium text-foreground">{{ t("blueprint.config.jsonTitle") }}</div>
|
|
54
|
+
<ConfigHintIcon :label="t('blueprint.config.jsonTitle')">
|
|
55
|
+
{{ t("blueprint.config.jsonHint") }}
|
|
56
|
+
</ConfigHintIcon>
|
|
57
|
+
</div>
|
|
53
58
|
|
|
54
59
|
<label class="block space-y-1">
|
|
55
60
|
<span class="text-muted-foreground">{{ t("blueprint.config.jsonContent") }}</span>
|
|
@@ -4,6 +4,7 @@ import { computed, ref } from "vue";
|
|
|
4
4
|
import { validateLogicSourceCode } from "@arronqzy/blueprint-dsl";
|
|
5
5
|
import type { LogicNodeConfig } from "@arronqzy/blueprint-dsl";
|
|
6
6
|
|
|
7
|
+
import ConfigHintIcon from "./ConfigHintIcon.vue";
|
|
7
8
|
import type { BlueprintGraphNode } from "../graph/document";
|
|
8
9
|
import { resolveNodeLogicConfig } from "../graph/document";
|
|
9
10
|
|
|
@@ -48,8 +49,12 @@ function handleChange(event: Event) {
|
|
|
48
49
|
|
|
49
50
|
<template>
|
|
50
51
|
<div class="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
|
|
51
|
-
<div class="
|
|
52
|
-
|
|
52
|
+
<div class="flex items-center gap-1.5">
|
|
53
|
+
<div class="font-medium text-foreground">{{ t("blueprint.config.logicTitle") }}</div>
|
|
54
|
+
<ConfigHintIcon :label="t('blueprint.config.logicTitle')">
|
|
55
|
+
{{ t("blueprint.config.logicHint") }}
|
|
56
|
+
</ConfigHintIcon>
|
|
57
|
+
</div>
|
|
53
58
|
|
|
54
59
|
<label class="block space-y-1">
|
|
55
60
|
<span class="text-muted-foreground">{{ t("blueprint.config.jsCode") }}</span>
|
package/src/graph/document.ts
CHANGED