@arronqzy/vue-blueprint 0.1.3 → 0.1.5
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 +111 -23
- package/src/blueprintNodeTypes.ts +2 -0
- package/src/components/BlueprintCanvas.vue +2 -0
- package/src/components/BlueprintContextMenu.vue +1 -1
- package/src/components/ClockNodeConfigPanel.vue +25 -14
- package/src/components/ConfigHintIcon.vue +38 -0
- package/src/components/FetchNodeConfigPanel.vue +191 -31
- package/src/components/FetchUrlAutocomplete.vue +1 -0
- package/src/components/JsonNodeConfigPanel.vue +7 -2
- package/src/components/LogicNodeConfigPanel.vue +7 -2
- package/src/components/ScopeTemplateAutocompleteHost.vue +231 -0
- package/src/composables/useBlueprintFlowState.ts +51 -5
- package/src/composables/useBlueprintPageLifecycle.ts +99 -10
- package/src/graph/blueprint-graph.ts +8 -0
- package/src/graph/document.ts +136 -8
- package/src/graph/node-summary.ts +20 -0
- package/src/graph/node-types.ts +1 -0
- package/src/graph/sync-nodes.ts +2 -1
- package/src/graph/vue-flow-adapter.ts +4 -0
- package/src/nodes/BlueprintNodeCard.vue +12 -2
- package/src/nodes/BlueprintNodeShell.vue +4 -2
- package/src/nodes/EventFlowNode.vue +31 -0
- package/src/runtime/document-to-runnable-graph.ts +3 -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.5",
|
|
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.7",
|
|
23
|
+
"@arronqzy/vue-rx-store": "0.1.2",
|
|
24
|
+
"@arronqzy/i18n": "0.1.6"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"vue": "^3.4.0"
|
|
@@ -5,7 +5,10 @@ import { Checkbox, Input, Select } from "ant-design-vue";
|
|
|
5
5
|
import {
|
|
6
6
|
LIFECYCLE_NODE_TYPE,
|
|
7
7
|
PAGE_LIFECYCLE_PHASES,
|
|
8
|
+
VIEW_EVENT_TYPES,
|
|
9
|
+
type ExecutionTraceEntry,
|
|
8
10
|
type PageLifecyclePhase,
|
|
11
|
+
type ViewEventType,
|
|
9
12
|
} from "@arronqzy/blueprint-dsl";
|
|
10
13
|
|
|
11
14
|
import FetchNodeConfigPanel from "./components/FetchNodeConfigPanel.vue";
|
|
@@ -13,13 +16,18 @@ import ClockNodeConfigPanel from "./components/ClockNodeConfigPanel.vue";
|
|
|
13
16
|
import JsonNodeConfigPanel from "./components/JsonNodeConfigPanel.vue";
|
|
14
17
|
import LogicNodeConfigPanel from "./components/LogicNodeConfigPanel.vue";
|
|
15
18
|
import ViewElementMultiSelect from "./components/ViewElementMultiSelect.vue";
|
|
19
|
+
import ConfigHintIcon from "./components/ConfigHintIcon.vue";
|
|
16
20
|
import {
|
|
17
21
|
getLifecyclePhaseLabel,
|
|
22
|
+
getViewEventTypeLabel,
|
|
23
|
+
inspectEventNodeInputs,
|
|
18
24
|
patchNodeConfigSource,
|
|
19
25
|
pruneViewElementIds,
|
|
20
26
|
resolveBlueprintConfigSource,
|
|
27
|
+
resolveNodeEventConfig,
|
|
21
28
|
resolveViewElementIds,
|
|
22
29
|
type BlueprintConfigSource,
|
|
30
|
+
type BlueprintGraphEdge,
|
|
23
31
|
type BlueprintGraphNode,
|
|
24
32
|
type BlueprintNodeRole,
|
|
25
33
|
} from "./graph/document";
|
|
@@ -38,6 +46,9 @@ export type BlueprintLibraryOption = {
|
|
|
38
46
|
|
|
39
47
|
export type BlueprintNodeConfigSidebarProps = {
|
|
40
48
|
node: BlueprintGraphNode;
|
|
49
|
+
graphNodes?: BlueprintGraphNode[];
|
|
50
|
+
graphEdges?: BlueprintGraphEdge[];
|
|
51
|
+
traceEntries?: ExecutionTraceEntry[];
|
|
41
52
|
viewElementOptions?: BlueprintViewElementOption[];
|
|
42
53
|
blueprintLibraryOptions?: BlueprintLibraryOption[];
|
|
43
54
|
allowFalseSignalPropagation?: boolean;
|
|
@@ -60,6 +71,7 @@ export type BlueprintNodeConfigSidebarProps = {
|
|
|
60
71
|
| "jsonConfig"
|
|
61
72
|
| "logicConfig"
|
|
62
73
|
| "clockConfig"
|
|
74
|
+
| "eventConfig"
|
|
63
75
|
>
|
|
64
76
|
>
|
|
65
77
|
) => void;
|
|
@@ -68,11 +80,15 @@ export type BlueprintNodeConfigSidebarProps = {
|
|
|
68
80
|
const props = withDefaults(defineProps<BlueprintNodeConfigSidebarProps>(), {
|
|
69
81
|
viewElementOptions: () => [],
|
|
70
82
|
blueprintLibraryOptions: () => [],
|
|
83
|
+
graphNodes: () => [],
|
|
84
|
+
graphEdges: () => [],
|
|
85
|
+
traceEntries: () => [],
|
|
71
86
|
allowFalseSignalPropagation: false,
|
|
72
87
|
});
|
|
73
88
|
|
|
74
89
|
const configSource = computed(() => resolveBlueprintConfigSource(props.node));
|
|
75
90
|
const linkedViewElementIds = computed(() => resolveViewElementIds(props.node));
|
|
91
|
+
const selectedEventTypes = computed(() => resolveNodeEventConfig(props.node).eventTypes);
|
|
76
92
|
const existingViewElementIdSet = computed(
|
|
77
93
|
() => new Set(props.viewElementOptions.map((opt) => opt.id))
|
|
78
94
|
);
|
|
@@ -88,12 +104,17 @@ const ROLE_LABEL_KEYS: Record<BlueprintNodeRole, string> = {
|
|
|
88
104
|
json: "blueprint.config.roleJson",
|
|
89
105
|
logic: "blueprint.config.roleLogic",
|
|
90
106
|
clock: "blueprint.config.roleClock",
|
|
107
|
+
event: "blueprint.config.roleEvent",
|
|
91
108
|
};
|
|
92
109
|
|
|
93
110
|
const roleLabel = computed(() =>
|
|
94
111
|
t(ROLE_LABEL_KEYS[props.node.role] ?? "blueprint.config.roleLogic")
|
|
95
112
|
);
|
|
96
113
|
|
|
114
|
+
const eventInputs = computed(() =>
|
|
115
|
+
inspectEventNodeInputs(props.node.id, props.graphNodes, props.graphEdges)
|
|
116
|
+
);
|
|
117
|
+
|
|
97
118
|
watch(
|
|
98
119
|
[
|
|
99
120
|
configSource,
|
|
@@ -104,7 +125,7 @@ watch(
|
|
|
104
125
|
() => props.viewElementOptions.length,
|
|
105
126
|
],
|
|
106
127
|
() => {
|
|
107
|
-
if (configSource.value !== "view") return;
|
|
128
|
+
if (configSource.value !== "view" && configSource.value !== "event") return;
|
|
108
129
|
if (props.viewElementOptions.length === 0) return;
|
|
109
130
|
const linked = resolveViewElementIds(props.node);
|
|
110
131
|
if (linked.length === 0) return;
|
|
@@ -113,7 +134,7 @@ watch(
|
|
|
113
134
|
props.onUpdateNode(props.node.id, {
|
|
114
135
|
viewElementIds: pruned.length > 0 ? pruned : undefined,
|
|
115
136
|
viewElementId: undefined,
|
|
116
|
-
configSource:
|
|
137
|
+
configSource: configSource.value,
|
|
117
138
|
});
|
|
118
139
|
}
|
|
119
140
|
);
|
|
@@ -122,7 +143,7 @@ function handleConfigSourceChange(value: string) {
|
|
|
122
143
|
const nextSource = value as BlueprintConfigSource;
|
|
123
144
|
props.onUpdateNode(props.node.id, {
|
|
124
145
|
...patchNodeConfigSource(props.node, nextSource),
|
|
125
|
-
...(nextSource === "view"
|
|
146
|
+
...(nextSource === "view" || nextSource === "event"
|
|
126
147
|
? {
|
|
127
148
|
viewElementIds: linkedViewElementIds.value,
|
|
128
149
|
viewElementId: undefined,
|
|
@@ -133,6 +154,19 @@ function handleConfigSourceChange(value: string) {
|
|
|
133
154
|
}),
|
|
134
155
|
});
|
|
135
156
|
}
|
|
157
|
+
|
|
158
|
+
function toggleEventType(eventType: ViewEventType, checked: boolean) {
|
|
159
|
+
const current = selectedEventTypes.value;
|
|
160
|
+
const nextTypes: ViewEventType[] = checked
|
|
161
|
+
? [...new Set([...current, eventType])]
|
|
162
|
+
: current.filter((type) => type !== eventType);
|
|
163
|
+
props.onUpdateNode(props.node.id, {
|
|
164
|
+
eventConfig: {
|
|
165
|
+
eventTypes: nextTypes.length > 0 ? nextTypes : ["click"],
|
|
166
|
+
},
|
|
167
|
+
configSource: "event",
|
|
168
|
+
});
|
|
169
|
+
}
|
|
136
170
|
</script>
|
|
137
171
|
|
|
138
172
|
<template>
|
|
@@ -168,12 +202,18 @@ function handleConfigSourceChange(value: string) {
|
|
|
168
202
|
<Select.Option value="fetch">{{ t("blueprint.config.configFetch") }}</Select.Option>
|
|
169
203
|
<Select.Option value="json">{{ t("blueprint.config.configJson") }}</Select.Option>
|
|
170
204
|
<Select.Option value="clock">{{ t("blueprint.config.configClock") }}</Select.Option>
|
|
205
|
+
<Select.Option value="event">{{ t("blueprint.config.configEvent") }}</Select.Option>
|
|
171
206
|
<Select.Option value="view">{{ t("blueprint.config.configView") }}</Select.Option>
|
|
172
207
|
</Select>
|
|
173
208
|
</label>
|
|
174
209
|
|
|
175
|
-
<div v-if="configSource === 'view'" class="block space-y-1">
|
|
176
|
-
<span class="text-muted-foreground">
|
|
210
|
+
<div v-if="configSource === 'view' || configSource === 'event'" class="block space-y-1">
|
|
211
|
+
<span class="inline-flex items-center gap-1 text-muted-foreground">
|
|
212
|
+
{{ t("blueprint.config.linkedViewNodes") }}
|
|
213
|
+
<ConfigHintIcon :label="t('blueprint.config.linkedViewNodes')">
|
|
214
|
+
{{ configSource === 'event' ? t("blueprint.config.eventViewHint") : t("blueprint.config.viewMultiHint") }}
|
|
215
|
+
</ConfigHintIcon>
|
|
216
|
+
</span>
|
|
177
217
|
<ViewElementMultiSelect
|
|
178
218
|
:options="viewElementOptions"
|
|
179
219
|
:value="linkedViewElementIds"
|
|
@@ -183,14 +223,11 @@ function handleConfigSourceChange(value: string) {
|
|
|
183
223
|
onUpdateNode(node.id, {
|
|
184
224
|
viewElementIds: next,
|
|
185
225
|
viewElementId: undefined,
|
|
186
|
-
configSource
|
|
226
|
+
configSource,
|
|
187
227
|
})
|
|
188
228
|
"
|
|
189
229
|
/>
|
|
190
|
-
<p v-if="linkedViewElementIds.length
|
|
191
|
-
{{ t("blueprint.config.viewMultiHint") }}
|
|
192
|
-
</p>
|
|
193
|
-
<p v-else class="text-[11px] text-muted-foreground">
|
|
230
|
+
<p v-if="linkedViewElementIds.length > 0" class="text-[11px] text-muted-foreground">
|
|
194
231
|
{{
|
|
195
232
|
t("blueprint.config.linkedViewCount", {
|
|
196
233
|
count: linkedViewElementIds.length,
|
|
@@ -206,8 +243,12 @@ function handleConfigSourceChange(value: string) {
|
|
|
206
243
|
v-if="configSource === 'blueprint'"
|
|
207
244
|
class="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5"
|
|
208
245
|
>
|
|
209
|
-
<div class="
|
|
210
|
-
|
|
246
|
+
<div class="flex items-center gap-1.5">
|
|
247
|
+
<div class="font-medium text-foreground">{{ t("blueprint.config.blueprintAttrs") }}</div>
|
|
248
|
+
<ConfigHintIcon :label="t('blueprint.config.blueprintAttrs')">
|
|
249
|
+
{{ t("blueprint.config.blueprintAttrsHint") }}
|
|
250
|
+
</ConfigHintIcon>
|
|
251
|
+
</div>
|
|
211
252
|
<label class="block space-y-1">
|
|
212
253
|
<span class="text-muted-foreground">{{ t("blueprint.config.refLibrary") }}</span>
|
|
213
254
|
<Select
|
|
@@ -249,18 +290,58 @@ function handleConfigSourceChange(value: string) {
|
|
|
249
290
|
</div>
|
|
250
291
|
|
|
251
292
|
<div
|
|
252
|
-
v-if="configSource === '
|
|
293
|
+
v-if="configSource === 'event'"
|
|
253
294
|
class="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5"
|
|
254
295
|
>
|
|
255
|
-
<div class="
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
296
|
+
<div class="flex items-center gap-1.5">
|
|
297
|
+
<div class="font-medium text-foreground">{{ t("blueprint.config.eventListen") }}</div>
|
|
298
|
+
<ConfigHintIcon :label="t('blueprint.config.eventListen')">
|
|
299
|
+
<p>{{ t("blueprint.config.eventNoInput") }}</p>
|
|
300
|
+
<p>{{ t("blueprint.config.eventHint") }}</p>
|
|
301
|
+
</ConfigHintIcon>
|
|
302
|
+
</div>
|
|
303
|
+
<p
|
|
304
|
+
v-if="eventInputs.nonLifecycleCount > 0"
|
|
305
|
+
class="rounded border border-destructive/40 bg-destructive/10 px-2 py-1.5 text-[11px] leading-relaxed text-destructive"
|
|
306
|
+
>
|
|
307
|
+
{{ t("blueprint.config.eventInputNotLifecycle") }}
|
|
263
308
|
</p>
|
|
309
|
+
<p
|
|
310
|
+
v-else-if="eventInputs.lifecycleCount === 0"
|
|
311
|
+
class="rounded border border-amber-500/40 bg-amber-500/10 px-2 py-1.5 text-[11px] leading-relaxed text-amber-700"
|
|
312
|
+
>
|
|
313
|
+
{{ t("blueprint.config.eventInputMissing") }}
|
|
314
|
+
</p>
|
|
315
|
+
<label
|
|
316
|
+
v-for="eventType in VIEW_EVENT_TYPES"
|
|
317
|
+
:key="eventType"
|
|
318
|
+
class="flex items-center gap-2"
|
|
319
|
+
>
|
|
320
|
+
<Checkbox
|
|
321
|
+
:checked="selectedEventTypes.includes(eventType)"
|
|
322
|
+
@update:checked="(v) => toggleEventType(eventType, Boolean(v))"
|
|
323
|
+
/>
|
|
324
|
+
<span>{{ getViewEventTypeLabel(t, eventType) }}</span>
|
|
325
|
+
</label>
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
<div
|
|
329
|
+
v-if="configSource === 'lifecycle'"
|
|
330
|
+
class="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5"
|
|
331
|
+
>
|
|
332
|
+
<div class="flex items-center gap-1.5">
|
|
333
|
+
<div class="font-medium text-foreground">{{ t("blueprint.config.lifecycleHook") }}</div>
|
|
334
|
+
<ConfigHintIcon :label="t('blueprint.config.lifecycleHook')">
|
|
335
|
+
<p>{{ t("blueprint.config.lifecycleNoInput") }}</p>
|
|
336
|
+
<p>
|
|
337
|
+
{{
|
|
338
|
+
node.lifecyclePhase === "blueprintActivated"
|
|
339
|
+
? t("blueprint.config.lifecycleBlueprintActivatedHint")
|
|
340
|
+
: t("blueprint.config.lifecyclePageHint")
|
|
341
|
+
}}
|
|
342
|
+
</p>
|
|
343
|
+
</ConfigHintIcon>
|
|
344
|
+
</div>
|
|
264
345
|
<label class="block space-y-1">
|
|
265
346
|
<span class="text-muted-foreground">{{ t("blueprint.config.listenPhase") }}</span>
|
|
266
347
|
<Select
|
|
@@ -290,6 +371,9 @@ function handleConfigSourceChange(value: string) {
|
|
|
290
371
|
<FetchNodeConfigPanel
|
|
291
372
|
v-if="configSource === 'fetch'"
|
|
292
373
|
:node="node"
|
|
374
|
+
:graph-nodes="graphNodes"
|
|
375
|
+
:graph-edges="graphEdges"
|
|
376
|
+
:trace-entries="traceEntries"
|
|
293
377
|
:on-update-node="onUpdateNode"
|
|
294
378
|
/>
|
|
295
379
|
<JsonNodeConfigPanel
|
|
@@ -307,8 +391,12 @@ function handleConfigSourceChange(value: string) {
|
|
|
307
391
|
v-if="configSource === 'and'"
|
|
308
392
|
class="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5"
|
|
309
393
|
>
|
|
310
|
-
<div class="
|
|
311
|
-
|
|
394
|
+
<div class="flex items-center gap-1.5">
|
|
395
|
+
<div class="font-medium text-foreground">{{ t("blueprint.config.andTitle") }}</div>
|
|
396
|
+
<ConfigHintIcon :label="t('blueprint.config.andTitle')">
|
|
397
|
+
{{ t("blueprint.config.andHint") }}
|
|
398
|
+
</ConfigHintIcon>
|
|
399
|
+
</div>
|
|
312
400
|
</div>
|
|
313
401
|
|
|
314
402
|
<template v-if="configSource === 'logic'">
|
|
@@ -6,6 +6,7 @@ import ClockFlowNode from "./nodes/ClockFlowNode.vue";
|
|
|
6
6
|
import FetchFlowNode from "./nodes/FetchFlowNode.vue";
|
|
7
7
|
import JsonFlowNode from "./nodes/JsonFlowNode.vue";
|
|
8
8
|
import LifecycleFlowNode from "./nodes/LifecycleFlowNode.vue";
|
|
9
|
+
import EventFlowNode from "./nodes/EventFlowNode.vue";
|
|
9
10
|
import LogicFlowNode from "./nodes/LogicFlowNode.vue";
|
|
10
11
|
|
|
11
12
|
/** 稳定引用,避免 Vue Flow 因 nodeTypes 变化反复卸载节点 */
|
|
@@ -17,4 +18,5 @@ export const blueprintNodeTypes: Record<string, Component> = {
|
|
|
17
18
|
json: markRaw(JsonFlowNode),
|
|
18
19
|
logic: markRaw(LogicFlowNode),
|
|
19
20
|
lifecycle: markRaw(LifecycleFlowNode),
|
|
21
|
+
event: markRaw(EventFlowNode),
|
|
20
22
|
};
|
|
@@ -105,6 +105,7 @@ const {
|
|
|
105
105
|
onEdgesChange,
|
|
106
106
|
onNodeDragStop,
|
|
107
107
|
onConnect,
|
|
108
|
+
onConnectEnd,
|
|
108
109
|
isValidConnection,
|
|
109
110
|
} = flowState;
|
|
110
111
|
|
|
@@ -201,6 +202,7 @@ function handleDeleteEdge(edgeId: string) {
|
|
|
201
202
|
@nodes-change="onNodesChange"
|
|
202
203
|
@edges-change="onEdgesChange"
|
|
203
204
|
@connect="onConnect"
|
|
205
|
+
@connect-end="onConnectEnd"
|
|
204
206
|
:is-valid-connection="isValidConnection"
|
|
205
207
|
@node-drag-stop="onNodeDragStop"
|
|
206
208
|
@pane-context-menu="onPaneContextMenu"
|
|
@@ -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" | "fetch" | "json" | "clock";
|
|
17
|
+
role: "blueprint" | "logic" | "and" | "lifecycle" | "event" | "fetch" | "json" | "clock";
|
|
18
18
|
}
|
|
19
19
|
| {
|
|
20
20
|
kind: "edge";
|
|
@@ -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 { computed } from "vue";
|
|
3
|
+
import { useI18n } from "@arronqzy/i18n/vue";
|
|
4
|
+
import { Tooltip } from "ant-design-vue";
|
|
5
|
+
|
|
6
|
+
const { t } = useI18n();
|
|
7
|
+
const props = defineProps<{
|
|
8
|
+
label?: string;
|
|
9
|
+
contentClass?: string;
|
|
10
|
+
buttonClass?: string;
|
|
11
|
+
}>();
|
|
12
|
+
|
|
13
|
+
const resolvedLabel = computed(() => props.label || t("common.hint"));
|
|
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: resolvedLabel })"
|
|
34
|
+
>
|
|
35
|
+
?
|
|
36
|
+
</button>
|
|
37
|
+
</Tooltip>
|
|
38
|
+
</template>
|