vue-agent-start 0.1.0

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.
Files changed (169) hide show
  1. package/package.json +30 -0
  2. package/pnpm-workspace.yaml +3 -0
  3. package/src/agent-flow/adapter/backend.ts +99 -0
  4. package/src/agent-flow/components/ChatIframePanel.vue +773 -0
  5. package/src/agent-flow/components/DatasetItemCard.vue +313 -0
  6. package/src/agent-flow/components/MemoryWindow.vue +104 -0
  7. package/src/agent-flow/components/PromptEditor.vue +804 -0
  8. package/src/agent-flow/components/PromptEditorTagPanel.vue +460 -0
  9. package/src/agent-flow/components/SvgIcon.vue +32 -0
  10. package/src/agent-flow/components/ToolItemCard.vue +169 -0
  11. package/src/agent-flow/components/VariableCard.vue +161 -0
  12. package/src/agent-flow/components/VariableSelector.vue +162 -0
  13. package/src/agent-flow/components/chat-iframe-types.ts +35 -0
  14. package/src/agent-flow/components/stubs/DatasetChooser.vue +70 -0
  15. package/src/agent-flow/components/stubs/Embedding.vue +22 -0
  16. package/src/agent-flow/components/stubs/ModelSelect.vue +24 -0
  17. package/src/agent-flow/components/stubs/RestfulSelectCard.vue +24 -0
  18. package/src/agent-flow/components/stubs/ToolChooser.vue +96 -0
  19. package/src/agent-flow/components/stubs/TriggersItemCard.vue +126 -0
  20. package/src/agent-flow/components/stubs/VariableModal.vue +115 -0
  21. package/src/agent-flow/index.ts +73 -0
  22. package/src/agent-flow/stores/workflow.ts +72 -0
  23. package/src/agent-flow/utils/langUtils.ts +173 -0
  24. package/src/agent-flow/workflow/Ability.vue +65 -0
  25. package/src/agent-flow/workflow/AuthorizationItem.vue +101 -0
  26. package/src/agent-flow/workflow/ConversationVar.vue +42 -0
  27. package/src/agent-flow/workflow/CustomEdge.vue +94 -0
  28. package/src/agent-flow/workflow/EnvironmentVar.vue +40 -0
  29. package/src/agent-flow/workflow/FlowDesigner.vue +2802 -0
  30. package/src/agent-flow/workflow/Icon.vue +179 -0
  31. package/src/agent-flow/workflow/NodeConfig.vue +509 -0
  32. package/src/agent-flow/workflow/NodeConfigCard.vue +460 -0
  33. package/src/agent-flow/workflow/NodeHandle.vue +147 -0
  34. package/src/agent-flow/workflow/NodeHeaderToolbar.vue +77 -0
  35. package/src/agent-flow/workflow/NodeHoverToolbar.vue +176 -0
  36. package/src/agent-flow/workflow/OutputItemCard.vue +668 -0
  37. package/src/agent-flow/workflow/ParamTableItem.vue +200 -0
  38. package/src/agent-flow/workflow/StructTable.vue +202 -0
  39. package/src/agent-flow/workflow/VarInsertField.vue +517 -0
  40. package/src/agent-flow/workflow/VarSelectField.vue +223 -0
  41. package/src/agent-flow/workflow/VariableSelectorItem.vue +48 -0
  42. package/src/agent-flow/workflow/WfField.vue +206 -0
  43. package/src/agent-flow/workflow/_config-base.css +470 -0
  44. package/src/agent-flow/workflow/composables/useNode.js +55 -0
  45. package/src/agent-flow/workflow/index.vue +53 -0
  46. package/src/agent-flow/workflow/nodeCard/AgentNodeCard.vue +180 -0
  47. package/src/agent-flow/workflow/nodeCard/AnswerNodeCard.vue +65 -0
  48. package/src/agent-flow/workflow/nodeCard/ClassifierCard.vue +152 -0
  49. package/src/agent-flow/workflow/nodeCard/CodeNodeCard.vue +139 -0
  50. package/src/agent-flow/workflow/nodeCard/ConditionNodeCard.vue +323 -0
  51. package/src/agent-flow/workflow/nodeCard/DocumentExtractorCard.vue +83 -0
  52. package/src/agent-flow/workflow/nodeCard/EndNodeCard.vue +123 -0
  53. package/src/agent-flow/workflow/nodeCard/HttpNodeCard.vue +451 -0
  54. package/src/agent-flow/workflow/nodeCard/HumanInputCard.vue +152 -0
  55. package/src/agent-flow/workflow/nodeCard/IterationCard.vue +66 -0
  56. package/src/agent-flow/workflow/nodeCard/KnowledgeRetrievalCard.vue +48 -0
  57. package/src/agent-flow/workflow/nodeCard/LLMNodeCard.vue +206 -0
  58. package/src/agent-flow/workflow/nodeCard/ListOperatorCard.vue +188 -0
  59. package/src/agent-flow/workflow/nodeCard/ParameterExtractorCard.vue +352 -0
  60. package/src/agent-flow/workflow/nodeCard/ServiceApiNodeCard.vue +188 -0
  61. package/src/agent-flow/workflow/nodeCard/StartNodeCard.vue +216 -0
  62. package/src/agent-flow/workflow/nodeCard/VariableAssignerCard.vue +125 -0
  63. package/src/agent-flow/workflow/nodeCard/VariableNodeCard.vue +121 -0
  64. package/src/agent-flow/workflow/nodes/AgentNode.vue +122 -0
  65. package/src/agent-flow/workflow/nodes/AnswerNode.vue +99 -0
  66. package/src/agent-flow/workflow/nodes/ClassifierNode.vue +199 -0
  67. package/src/agent-flow/workflow/nodes/CodeNode.vue +152 -0
  68. package/src/agent-flow/workflow/nodes/ConditionNode.vue +232 -0
  69. package/src/agent-flow/workflow/nodes/DocumentExtractorNode.vue +96 -0
  70. package/src/agent-flow/workflow/nodes/EndNode.vue +119 -0
  71. package/src/agent-flow/workflow/nodes/FileUploadNode.vue +97 -0
  72. package/src/agent-flow/workflow/nodes/HttpNode.vue +204 -0
  73. package/src/agent-flow/workflow/nodes/HumanInputNode.vue +110 -0
  74. package/src/agent-flow/workflow/nodes/IterationNode.vue +98 -0
  75. package/src/agent-flow/workflow/nodes/KnowledgeRetrievalNode.vue +253 -0
  76. package/src/agent-flow/workflow/nodes/LLMNode.vue +134 -0
  77. package/src/agent-flow/workflow/nodes/ListOperatorNode.vue +111 -0
  78. package/src/agent-flow/workflow/nodes/LoopNode.vue +94 -0
  79. package/src/agent-flow/workflow/nodes/ParameterExtractorNode.vue +147 -0
  80. package/src/agent-flow/workflow/nodes/ServiceApiNode.vue +161 -0
  81. package/src/agent-flow/workflow/nodes/StartNode.vue +199 -0
  82. package/src/agent-flow/workflow/nodes/TemplateNode.vue +103 -0
  83. package/src/agent-flow/workflow/nodes/UserInputNode.vue +92 -0
  84. package/src/agent-flow/workflow/nodes/VariableAssignerNode.vue +145 -0
  85. package/src/agent-flow/workflow/nodes/VariableNode.vue +130 -0
  86. package/src/agent-flow/workflow/nodes/_node-base.css +338 -0
  87. package/src/agent-flow/workflow/utils/node_card_form.ts +335 -0
  88. package/src/agent-flow/workflow/utils/node_config.ts +63 -0
  89. package/src/agent-flow/workflow/utils/workflow_utils.ts +151 -0
  90. package/src/agent-studio/adapters/springAgentStart.ts +390 -0
  91. package/src/agent-studio/api/index.ts +1 -0
  92. package/src/agent-studio/api/types.ts +173 -0
  93. package/src/agent-studio/components/AgentApiDocs.vue +500 -0
  94. package/src/agent-studio/components/AgentAppsPage.vue +1601 -0
  95. package/src/agent-studio/components/AgentCardGrid.vue +259 -0
  96. package/src/agent-studio/components/AgentChatPage.vue +553 -0
  97. package/src/agent-studio/components/AgentDebugPanel.vue +350 -0
  98. package/src/agent-studio/components/AgentLogsPanel.vue +391 -0
  99. package/src/agent-studio/components/AgentMonitorPanel.vue +204 -0
  100. package/src/agent-studio/components/AgentOrchestrate.vue +505 -0
  101. package/src/agent-studio/components/AgentPromptEditor.vue +237 -0
  102. package/src/agent-studio/components/AgentStudioShell.vue +227 -0
  103. package/src/agent-studio/components/AgentToolsPanel.vue +394 -0
  104. package/src/agent-studio/components/AgentVariablesPanel.vue +205 -0
  105. package/src/agent-studio/components/ApiKeyManager.vue +251 -0
  106. package/src/agent-studio/components/AppDesignDrawer.vue +3391 -0
  107. package/src/agent-studio/components/CreateAppModal.vue +737 -0
  108. package/src/agent-studio/components/SparkChart.vue +200 -0
  109. package/src/agent-studio/components/VariableEditorModal.vue +499 -0
  110. package/src/agent-studio/composables/useAgentStudio.ts +347 -0
  111. package/src/agent-studio/index.ts +60 -0
  112. package/src/agent-studio/panels/DrawerFlowDesigner.vue +203 -0
  113. package/src/agent-studio/panels/LogAnnotationPanel.vue +490 -0
  114. package/src/agent-studio/panels/MonitorPanel.vue +535 -0
  115. package/src/agent-studio/types.ts +242 -0
  116. package/src/index.ts +155 -0
  117. package/src/knowledge-hub/adapters/springAgentStart.ts +472 -0
  118. package/src/knowledge-hub/components/AddDocumentsWizard.vue +1452 -0
  119. package/src/knowledge-hub/components/CreateDatasetWizard.vue +1723 -0
  120. package/src/knowledge-hub/components/DatasetCardGrid.vue +467 -0
  121. package/src/knowledge-hub/components/DatasetDetailDrawer.vue +888 -0
  122. package/src/knowledge-hub/components/DatasetPicker.vue +65 -0
  123. package/src/knowledge-hub/components/DatasetPickerModal.vue +526 -0
  124. package/src/knowledge-hub/components/DatasetSettingsPanel.vue +908 -0
  125. package/src/knowledge-hub/components/DatasetSidebar.vue +781 -0
  126. package/src/knowledge-hub/components/DocumentChunksView.vue +802 -0
  127. package/src/knowledge-hub/components/DocumentTable.vue +552 -0
  128. package/src/knowledge-hub/components/HitTestingPanel.vue +74 -0
  129. package/src/knowledge-hub/components/KnowledgeApp.vue +66 -0
  130. package/src/knowledge-hub/components/KnowledgeHubApp.vue +504 -0
  131. package/src/knowledge-hub/components/RecallTestingPanelV2.vue +515 -0
  132. package/src/knowledge-hub/components/RetrievalConfigPopover.vue +270 -0
  133. package/src/knowledge-hub/components/RetrievalMethodPicker.vue +547 -0
  134. package/src/knowledge-hub/components/RetrievedList.vue +24 -0
  135. package/src/knowledge-hub/components/internal/KhDialog.vue +195 -0
  136. package/src/knowledge-hub/composables/useKnowledge.ts +89 -0
  137. package/src/knowledge-hub/i18n/index.ts +2 -0
  138. package/src/knowledge-hub/i18n/messages.ts +422 -0
  139. package/src/knowledge-hub/i18n/useKhI18n.ts +107 -0
  140. package/src/knowledge-hub/index.ts +46 -0
  141. package/src/knowledge-hub/styles/index.css +4 -0
  142. package/src/knowledge-hub/styles/tokens.css +138 -0
  143. package/src/knowledge-hub/types/api.ts +213 -0
  144. package/src/knowledge-hub/types/dataset.ts +88 -0
  145. package/src/knowledge-hub/types/document.ts +45 -0
  146. package/src/knowledge-hub/types/index.ts +9 -0
  147. package/src/knowledge-hub/types/retrieval.ts +46 -0
  148. package/src/provider-hub/components/CredentialForm.vue +103 -0
  149. package/src/provider-hub/components/DefaultModelsPanel.vue +270 -0
  150. package/src/provider-hub/components/GroupedModelSelect.vue +445 -0
  151. package/src/provider-hub/components/ModelCardGrid.vue +332 -0
  152. package/src/provider-hub/components/ModelParameterDrawer.vue +739 -0
  153. package/src/provider-hub/components/ModelPickerPopover.vue +1282 -0
  154. package/src/provider-hub/components/ProviderApp.vue +159 -0
  155. package/src/provider-hub/components/ProviderCredentialModal.vue +321 -0
  156. package/src/provider-hub/components/ProviderGallery.vue +131 -0
  157. package/src/provider-hub/components/ProviderHubShell.vue +1992 -0
  158. package/src/provider-hub/components/ProviderIcon.vue +130 -0
  159. package/src/provider-hub/composables/useProviderHub.ts +358 -0
  160. package/src/provider-hub/index.ts +43 -0
  161. package/src/provider-hub/svg/MiniMax.svg +1 -0
  162. package/src/provider-hub/svg/Volcengine.svg +1 -0
  163. package/src/provider-hub/svg/Zhipu.svg +1 -0
  164. package/src/provider-hub/svg/deepseek.svg +1 -0
  165. package/src/provider-hub/svg/moonshot.svg +1 -0
  166. package/src/provider-hub/svg/openai.svg +1 -0
  167. package/src/provider-hub/svg/qwen.svg +1 -0
  168. package/src/provider-hub/svg/siliconflow.svg +1 -0
  169. package/src/provider-hub/types.ts +256 -0
@@ -0,0 +1,460 @@
1
+ <script setup lang="ts">
2
+ import { computed, defineEmits, onBeforeMount, ref, watch } from 'vue';
3
+
4
+ import {
5
+ BlockOutlined,
6
+ CloseOutlined,
7
+ CopyOutlined,
8
+ DeleteOutlined,
9
+ EllipsisOutlined,
10
+ PlayCircleOutlined,
11
+ ReadOutlined,
12
+ RetweetOutlined,
13
+ } from '@ant-design/icons-vue';
14
+
15
+ import Icon from './Icon.vue';
16
+ import AgentNodeCard from './nodeCard/AgentNodeCard.vue';
17
+ import AnswerNodeCard from './nodeCard/AnswerNodeCard.vue';
18
+ import ClassifierCard from './nodeCard/ClassifierCard.vue';
19
+ import CodeNodeCard from './nodeCard/CodeNodeCard.vue';
20
+ import ConditionNodeCard from './nodeCard/ConditionNodeCard.vue';
21
+ import EndNodeCard from './nodeCard/EndNodeCard.vue';
22
+ import DocumentExtractorCard from './nodeCard/DocumentExtractorCard.vue';
23
+ import HttpNodeCard from './nodeCard/HttpNodeCard.vue';
24
+ import HumanInputCard from './nodeCard/HumanInputCard.vue';
25
+ import IterationCard from './nodeCard/IterationCard.vue';
26
+ import KnowledgeRetrievalCard from './nodeCard/KnowledgeRetrievalCard.vue';
27
+ import ListOperatorCard from './nodeCard/ListOperatorCard.vue';
28
+ import LLMNodeCard from './nodeCard/LLMNodeCard.vue';
29
+ import ParameterExtractorCard from './nodeCard/ParameterExtractorCard.vue';
30
+ import ServiceApiNodeCard from './nodeCard/ServiceApiNodeCard.vue';
31
+ import StartNodeCard from './nodeCard/StartNodeCard.vue';
32
+ import VariableAssignerCard from './nodeCard/VariableAssignerCard.vue';
33
+ import VariableNodeCard from './nodeCard/VariableNodeCard.vue';
34
+
35
+ import './_config-base.css';
36
+
37
+ const props = defineProps({
38
+ selectNode: { type: Object, default: () => ({}) },
39
+ parentHierarchy: { type: Array, default: () => [] },
40
+ mainData: { type: Object, default: () => ({}) },
41
+ });
42
+ const emit = defineEmits([
43
+ 'onClose',
44
+ 'dataChange',
45
+ 'runStep',
46
+ 'changeType',
47
+ 'copyNode',
48
+ 'duplicateNode',
49
+ 'deleteNode',
50
+ ]);
51
+
52
+ const menuOpen = ref(false);
53
+
54
+ function menuAction(action: 'run' | 'change' | 'copy' | 'duplicate' | 'delete') {
55
+ menuOpen.value = false;
56
+ const id = props.selectNode?.id;
57
+ if (!id) return;
58
+ switch (action) {
59
+ case 'run':
60
+ emit('runStep', id);
61
+ break;
62
+ case 'change':
63
+ emit('changeType', id);
64
+ break;
65
+ case 'copy':
66
+ emit('copyNode', id);
67
+ break;
68
+ case 'duplicate':
69
+ emit('duplicateNode', id);
70
+ break;
71
+ case 'delete':
72
+ emit('deleteNode', id);
73
+ break;
74
+ }
75
+ }
76
+
77
+ /** 是否允许删除:START 节点不能删 */
78
+ const canDelete = computed(() => props.selectNode?.type !== 'START');
79
+ const canRun = computed(() =>
80
+ ['LLM', 'AGENT', 'CODE', 'HTTP_REQUEST', 'SERVICE_API', 'KNOWLEDGE_RETRIEVAL', 'QUESTION_CLASSIFIER'].includes(
81
+ props.selectNode?.type,
82
+ ),
83
+ );
84
+
85
+ const formData: any = ref({});
86
+ const attrListGroup: any = ref([]);
87
+ let suppressWatch = false;
88
+
89
+ /** 节点类型(backend NodeType 名) → 图标 + 主色(用于面板头部 accent 色) */
90
+ const nodeTypeTheme: Record<string, { icon: string; accent: string }> = {
91
+ START: { icon: 'start', accent: '#10b981' },
92
+ END: { icon: 'end', accent: '#ef4444' },
93
+ LLM: { icon: 'llm', accent: '#6366f1' },
94
+ AGENT: { icon: 'agent', accent: '#8b5cf6' },
95
+ KNOWLEDGE_RETRIEVAL: { icon: 'knowledge', accent: '#14b8a6' },
96
+ QUESTION_CLASSIFIER: { icon: 'classifier', accent: '#7c3aed' },
97
+ IF_ELSE: { icon: 'condition', accent: '#f59e0b' },
98
+ CODE: { icon: 'code', accent: '#84cc16' },
99
+ ANSWER: { icon: 'answer', accent: '#f97316' },
100
+ HTTP_REQUEST: { icon: 'http', accent: '#06b6d4' },
101
+ SERVICE_API: { icon: 'service', accent: '#0d9488' },
102
+ VARIABLE_AGGREGATOR: { icon: 'variable', accent: '#0ea5e9' },
103
+ LOOP: { icon: 'loop', accent: '#dc2626' },
104
+ TEMPLATE_TRANSFORM: { icon: 'template', accent: '#a855f7' },
105
+ USER_INPUT: { icon: 'user', accent: '#3b82f6' },
106
+ FILE_UPLOAD: { icon: 'file', accent: '#059669' },
107
+ ITERATION: { icon: 'loop', accent: '#f472b6' },
108
+ PARAMETER_EXTRACTOR: { icon: 'variable', accent: '#eab308' },
109
+ LIST_OPERATOR: { icon: 'variable', accent: '#22d3ee' },
110
+ DOCUMENT_EXTRACTOR: { icon: 'file', accent: '#0891b2' },
111
+ HUMAN_INPUT: { icon: 'user', accent: '#f59e0b' },
112
+ VARIABLE_ASSIGNER: { icon: 'variable', accent: '#059669' },
113
+ };
114
+
115
+ const currentTheme = computed(
116
+ () => nodeTypeTheme[props.selectNode?.type] || nodeTypeTheme.LLM,
117
+ );
118
+
119
+ /* -------- 面板宽度:可通过左边缘手柄拖动来调整,localStorage 记忆 --------
120
+ * 用 pointer capture 而不是全局 window 监听:move / up 事件即使指针滑出手柄
121
+ * 也照样发到手柄元素上,避免遗漏 up 造成的"拖动粘滞",也不用在 unmount 时
122
+ * 手动清理监听器。 */
123
+ const PANEL_WIDTH_KEY = 'wf-config-panel-width';
124
+ const PANEL_MIN_WIDTH = 320;
125
+ const PANEL_MAX_WIDTH = 960;
126
+ const PANEL_DEFAULT_WIDTH = 420;
127
+
128
+ function clampPanelWidth(w: number): number {
129
+ return Math.min(PANEL_MAX_WIDTH, Math.max(PANEL_MIN_WIDTH, w));
130
+ }
131
+
132
+ function loadInitialPanelWidth(): number {
133
+ if (typeof window === 'undefined') return PANEL_DEFAULT_WIDTH;
134
+ const raw = window.localStorage?.getItem(PANEL_WIDTH_KEY);
135
+ const parsed = raw == null ? Number.NaN : Number(raw);
136
+ if (Number.isFinite(parsed) && parsed >= PANEL_MIN_WIDTH && parsed <= PANEL_MAX_WIDTH) {
137
+ return parsed;
138
+ }
139
+ return PANEL_DEFAULT_WIDTH;
140
+ }
141
+
142
+ const panelWidth = ref(loadInitialPanelWidth());
143
+ const resizing = ref(false);
144
+ const resizeStart = { clientX: 0, width: 0, pointerId: -1 };
145
+
146
+ function onResizeStart(e: PointerEvent) {
147
+ if (e.button !== 0) return;
148
+ resizing.value = true;
149
+ resizeStart.clientX = e.clientX;
150
+ resizeStart.width = panelWidth.value;
151
+ resizeStart.pointerId = e.pointerId;
152
+ (e.currentTarget as Element).setPointerCapture?.(e.pointerId);
153
+ document.body.style.userSelect = 'none';
154
+ document.body.style.cursor = 'col-resize';
155
+ e.preventDefault();
156
+ }
157
+
158
+ function onResizeMove(e: PointerEvent) {
159
+ if (!resizing.value || e.pointerId !== resizeStart.pointerId) return;
160
+ // 手柄在面板左边缘:向左拖 → clientX 变小 → delta 为正 → 面板变宽
161
+ const delta = resizeStart.clientX - e.clientX;
162
+ panelWidth.value = clampPanelWidth(resizeStart.width + delta);
163
+ }
164
+
165
+ function onResizeEnd(e: PointerEvent) {
166
+ if (!resizing.value || e.pointerId !== resizeStart.pointerId) return;
167
+ resizing.value = false;
168
+ const target = e.currentTarget as Element;
169
+ if (target.hasPointerCapture?.(e.pointerId)) {
170
+ target.releasePointerCapture(e.pointerId);
171
+ }
172
+ document.body.style.userSelect = '';
173
+ document.body.style.cursor = '';
174
+ try {
175
+ window.localStorage?.setItem(PANEL_WIDTH_KEY, String(panelWidth.value));
176
+ } catch {
177
+ // 无 storage 权限(隐私模式)直接忽略
178
+ }
179
+ }
180
+
181
+ const panelStyle = computed(() => ({
182
+ '--wf-accent': currentTheme.value.accent,
183
+ width: `${panelWidth.value}px`,
184
+ }));
185
+
186
+ const closeCard = () => emit('onClose', false);
187
+
188
+ const panelInit = () => {
189
+ if (props.selectNode) {
190
+ suppressWatch = true;
191
+ // 深拷贝以免面板编辑直接改动画布内部数据;改动通过 dataChange 事件回写
192
+ formData.value = JSON.parse(JSON.stringify(props.selectNode.data ?? {}));
193
+ // 下一轮 tick 才恢复监听,避免 init 时 watch 立刻触发一次
194
+ setTimeout(() => (suppressWatch = false), 0);
195
+ }
196
+ };
197
+
198
+ /** 深度监听 formData,任何修改都通过 dataChange 事件冒泡到宿主,
199
+ * 由 FlowDesigner.patchNodeData 落回画布节点 */
200
+ watch(
201
+ () => formData.value,
202
+ (val) => {
203
+ if (suppressWatch || !props.selectNode?.id) return;
204
+ emit('dataChange', {
205
+ nodeId: props.selectNode.id,
206
+ data: JSON.parse(JSON.stringify(val)),
207
+ });
208
+ },
209
+ { deep: true },
210
+ );
211
+
212
+ onBeforeMount(panelInit);
213
+ watch(() => props.selectNode, panelInit);
214
+ </script>
215
+
216
+ <template>
217
+ <div class="wf-config-panel" :style="panelStyle">
218
+ <!-- 左边缘拖拽手柄:向左拖动可加宽面板 -->
219
+ <div
220
+ class="wf-config-resize"
221
+ :class="{ 'wf-config-resize-active': resizing }"
222
+ title="拖动可调整面板宽度"
223
+ @pointerdown="onResizeStart"
224
+ @pointermove="onResizeMove"
225
+ @pointerup="onResizeEnd"
226
+ @pointercancel="onResizeEnd"
227
+ >
228
+ <div class="wf-config-resize-bar" />
229
+ </div>
230
+
231
+ <!-- Header:图标 + 可编辑标题 + 操作 -->
232
+ <div class="wf-config-header">
233
+ <div class="wf-config-header-icon">
234
+ <Icon :name="currentTheme.icon" />
235
+ </div>
236
+ <a-input
237
+ v-model:value="formData.label"
238
+ :bordered="false"
239
+ size="small"
240
+ class="wf-config-header-title-input"
241
+ />
242
+ <div class="wf-config-header-actions">
243
+ <a-popover
244
+ v-model:open="menuOpen"
245
+ placement="bottomRight"
246
+ trigger="click"
247
+ :overlay-class-name="'wf-config-menu-overlay'"
248
+ >
249
+ <template #content>
250
+ <div class="wf-config-menu">
251
+ <div
252
+ v-if="canRun"
253
+ class="wf-config-menu-item"
254
+ @click="menuAction('run')"
255
+ >
256
+ <PlayCircleOutlined class="wf-config-menu-icon" />
257
+ <span>运行此步骤</span>
258
+ </div>
259
+ <div class="wf-config-menu-item" @click="menuAction('change')">
260
+ <RetweetOutlined class="wf-config-menu-icon" />
261
+ <span>更改节点类型</span>
262
+ </div>
263
+ <div class="wf-config-menu-divider" />
264
+ <div class="wf-config-menu-item" @click="menuAction('copy')">
265
+ <CopyOutlined class="wf-config-menu-icon" />
266
+ <span>复制</span>
267
+ <span class="wf-config-menu-shortcut">Ctrl+C</span>
268
+ </div>
269
+ <div class="wf-config-menu-item" @click="menuAction('duplicate')">
270
+ <BlockOutlined class="wf-config-menu-icon" />
271
+ <span>创建副本</span>
272
+ <span class="wf-config-menu-shortcut">Ctrl+D</span>
273
+ </div>
274
+ <template v-if="canDelete">
275
+ <div class="wf-config-menu-divider" />
276
+ <div
277
+ class="wf-config-menu-item wf-config-menu-item-danger"
278
+ @click="menuAction('delete')"
279
+ >
280
+ <DeleteOutlined class="wf-config-menu-icon" />
281
+ <span>删除节点</span>
282
+ <span class="wf-config-menu-shortcut">Del</span>
283
+ </div>
284
+ </template>
285
+ </div>
286
+ </template>
287
+ <button class="wf-config-header-btn" title="更多">
288
+ <EllipsisOutlined />
289
+ </button>
290
+ </a-popover>
291
+ <a-tooltip title="帮助文档" placement="bottom">
292
+ <button class="wf-config-header-btn">
293
+ <ReadOutlined />
294
+ </button>
295
+ </a-tooltip>
296
+ <div class="wf-config-header-divider" />
297
+ <a-tooltip title="关闭" placement="bottom">
298
+ <button class="wf-config-header-btn" @click="closeCard">
299
+ <CloseOutlined />
300
+ </button>
301
+ </a-tooltip>
302
+ </div>
303
+ </div>
304
+
305
+ <!-- 描述行 -->
306
+ <div class="wf-config-desc">
307
+ <a-input
308
+ v-model:value="formData.description"
309
+ :bordered="false"
310
+ placeholder="添加描述"
311
+ />
312
+ </div>
313
+
314
+ <!-- Body:按 type 分发到具体表单。所有 card 统一收到 nodeId=selectNode.id,
315
+ 这样卡片内部 PromptEditor / VariableSelector / VarInsertField 弹出的
316
+ "上游变量选择" 面板能通过 workflowStore.getParentNodeList(nodeId) 正确
317
+ 定位上游节点。formData 是 node.data 的深拷贝,不带 id,所以不能直接
318
+ 用 formState.id —— 那个字段常年 undefined,导致变量面板打开时是空的。 -->
319
+ <div class="wf-config-body">
320
+ <AgentNodeCard
321
+ v-if="selectNode.type === 'AGENT'"
322
+ v-model="formData"
323
+ :attr-list-group="attrListGroup"
324
+ :node-id="selectNode.id"
325
+ />
326
+ <LLMNodeCard
327
+ v-else-if="selectNode.type === 'LLM'"
328
+ v-model="formData"
329
+ :attr-list-group="attrListGroup"
330
+ :node-id="selectNode.id"
331
+ />
332
+ <KnowledgeRetrievalCard
333
+ v-else-if="selectNode.type === 'KNOWLEDGE_RETRIEVAL'"
334
+ v-model="formData"
335
+ :attr-list-group="attrListGroup"
336
+ :node-id="selectNode.id"
337
+ />
338
+ <ClassifierCard
339
+ v-else-if="selectNode.type === 'QUESTION_CLASSIFIER'"
340
+ v-model="formData"
341
+ :node-id="selectNode.id"
342
+ />
343
+ <ConditionNodeCard
344
+ v-else-if="selectNode.type === 'IF_ELSE'"
345
+ v-model="formData"
346
+ :node-id="selectNode.id"
347
+ />
348
+ <StartNodeCard
349
+ v-else-if="selectNode.type === 'START'"
350
+ v-model="formData"
351
+ :node-id="selectNode.id"
352
+ />
353
+ <CodeNodeCard
354
+ v-else-if="selectNode.type === 'CODE'"
355
+ v-model="formData"
356
+ :node-id="selectNode.id"
357
+ />
358
+ <AnswerNodeCard
359
+ v-else-if="selectNode.type === 'ANSWER'"
360
+ v-model="formData"
361
+ :node-id="selectNode.id"
362
+ />
363
+ <HttpNodeCard
364
+ v-else-if="selectNode.type === 'HTTP_REQUEST'"
365
+ v-model="formData"
366
+ :node-id="selectNode.id"
367
+ />
368
+ <ServiceApiNodeCard
369
+ v-else-if="selectNode.type === 'SERVICE_API'"
370
+ v-model="formData"
371
+ :node-id="selectNode.id"
372
+ />
373
+ <EndNodeCard
374
+ v-else-if="selectNode.type === 'END'"
375
+ v-model="formData"
376
+ :node-id="selectNode.id"
377
+ />
378
+ <VariableNodeCard
379
+ v-else-if="selectNode.type === 'VARIABLE_AGGREGATOR'"
380
+ v-model="formData"
381
+ :node-id="selectNode.id"
382
+ />
383
+ <IterationCard
384
+ v-else-if="selectNode.type === 'ITERATION'"
385
+ v-model="formData"
386
+ :node-id="selectNode.id"
387
+ />
388
+ <ParameterExtractorCard
389
+ v-else-if="selectNode.type === 'PARAMETER_EXTRACTOR'"
390
+ v-model="formData"
391
+ :node-id="selectNode.id"
392
+ />
393
+ <ListOperatorCard
394
+ v-else-if="selectNode.type === 'LIST_OPERATOR'"
395
+ v-model="formData"
396
+ :node-id="selectNode.id"
397
+ />
398
+ <DocumentExtractorCard
399
+ v-else-if="selectNode.type === 'DOCUMENT_EXTRACTOR'"
400
+ v-model="formData"
401
+ :node-id="selectNode.id"
402
+ />
403
+ <HumanInputCard
404
+ v-else-if="selectNode.type === 'HUMAN_INPUT'"
405
+ v-model="formData"
406
+ :node-id="selectNode.id"
407
+ />
408
+ <VariableAssignerCard
409
+ v-else-if="selectNode.type === 'VARIABLE_ASSIGNER'"
410
+ v-model="formData"
411
+ :node-id="selectNode.id"
412
+ />
413
+ <div v-else class="wf-config-section">
414
+ <div class="wf-config-section-title">此节点暂无配置项</div>
415
+ <div class="wf-config-hint">
416
+ 节点类型 <code>{{ selectNode.type }}</code> 的配置表单尚未实现。
417
+ </div>
418
+ </div>
419
+ </div>
420
+ </div>
421
+ </template>
422
+
423
+ <style scoped>
424
+ /* 让内嵌的 antd input 在 header 里视觉一致 */
425
+ :deep(.wf-config-header-title-input .ant-input) {
426
+ padding: 0 !important;
427
+ font-size: 16px !important;
428
+ font-weight: 600 !important;
429
+ color: #1f2937;
430
+ }
431
+
432
+ /* 左边缘拖拽手柄:完全嵌在面板内 6px 触发区,中央一条 2px 竖线
433
+ * hover / 拖动时点亮为节点主题色。父容器 overflow: hidden,因此不能用负 left。*/
434
+ .wf-config-resize {
435
+ position: absolute;
436
+ top: 0;
437
+ bottom: 0;
438
+ left: 0;
439
+ width: 6px;
440
+ z-index: 20;
441
+ cursor: col-resize;
442
+ user-select: none;
443
+ touch-action: none;
444
+ }
445
+
446
+ .wf-config-resize-bar {
447
+ position: absolute;
448
+ top: 0;
449
+ bottom: 0;
450
+ left: 2px;
451
+ width: 2px;
452
+ background: transparent;
453
+ transition: background 0.15s ease;
454
+ }
455
+
456
+ .wf-config-resize:hover .wf-config-resize-bar,
457
+ .wf-config-resize-active .wf-config-resize-bar {
458
+ background: var(--wf-accent, #6366f1);
459
+ }
460
+ </style>
@@ -0,0 +1,147 @@
1
+ <script setup>
2
+ import { Handle, Position } from '@vue-flow/core';
3
+
4
+ const props = defineProps({
5
+ id: String,
6
+ type: {
7
+ type: String,
8
+ default: 'source',
9
+ validator: (value) => ['source', 'target'].includes(value)
10
+ },
11
+ position: {
12
+ type: String,
13
+ default: 'Right',
14
+ validator: (value) => ['Top', 'Right', 'Bottom', 'Left'].includes(value)
15
+ },
16
+ handleId: {
17
+ type: String,
18
+ default: null
19
+ },
20
+ style: {
21
+ type: Object,
22
+ default: () => ({})
23
+ },
24
+ className: {
25
+ type: String,
26
+ default: ''
27
+ },
28
+ isHovered: {
29
+ type: Boolean,
30
+ default: false
31
+ }
32
+ });
33
+
34
+ const emit = defineEmits(['connection-plus-click']);
35
+
36
+ const handleClick = (event) => {
37
+ if (props.type === 'source') {
38
+ emit('connection-plus-click', event, props.id, props.handleId);
39
+ }
40
+ };
41
+
42
+ const getPositionValue = () => {
43
+ return Position[props.position] || Position.Right;
44
+ };
45
+
46
+ const getHandleClass = () => {
47
+ let baseClass = 'flow-node-handle';
48
+ if (props.type === 'source') {
49
+ baseClass += ` flow-node-handle-${props.position.toLowerCase()}`;
50
+ }
51
+ if (props.className) {
52
+ baseClass += ` ${props.className}`;
53
+ }
54
+ if (props.isHovered && props.type === 'source') {
55
+ baseClass += ' hovered';
56
+ }
57
+ return baseClass;
58
+ };
59
+ </script>
60
+
61
+ <template>
62
+ <Handle
63
+ :type="type"
64
+ :position="getPositionValue()"
65
+ :id="handleId"
66
+ :class="getHandleClass()"
67
+ :style="style"
68
+ @click="handleClick"
69
+ />
70
+ </template>
71
+
72
+ <style scoped>
73
+ /* 基础handle样式 */
74
+ .flow-node-handle {
75
+ width: 12px;
76
+ height: 12px;
77
+ border: 2px solid #ffffff;
78
+ border-radius: 50%;
79
+ transition: all 0.3s ease;
80
+ cursor: crosshair;
81
+ position: absolute;
82
+ z-index: 10;
83
+ }
84
+
85
+ /* source handle 特殊样式 */
86
+ .flow-node-handle.flow-node-handle-right {
87
+ background: #10b981;
88
+ }
89
+
90
+ .flow-node-handle.flow-node-handle-right.hovered {
91
+ width: 30px;
92
+ height: 30px;
93
+ background: #3b82f6;
94
+ box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.2);
95
+ cursor: pointer;
96
+ }
97
+
98
+ /* hover 时显示 + 号:用 CSS 十字形代替字符 + ,居中精确 */
99
+ .flow-node-handle.flow-node-handle-right.hovered::before,
100
+ .flow-node-handle.flow-node-handle-right.hovered::after {
101
+ content: '';
102
+ position: absolute;
103
+ top: 50%;
104
+ left: 50%;
105
+ background: #ffffff;
106
+ border-radius: 1px;
107
+ z-index: 11;
108
+ pointer-events: none;
109
+ }
110
+
111
+ /* 横向短杠 */
112
+ .flow-node-handle.flow-node-handle-right.hovered::before {
113
+ width: 12px;
114
+ height: 2px;
115
+ transform: translate(-50%, -50%);
116
+ }
117
+
118
+ /* 纵向短杠 */
119
+ .flow-node-handle.flow-node-handle-right.hovered::after {
120
+ width: 2px;
121
+ height: 12px;
122
+ transform: translate(-50%, -50%);
123
+ }
124
+
125
+ /* 其他方向的handle */
126
+ .flow-node-handle.flow-node-handle-top {
127
+ background: #8b5cf6;
128
+ }
129
+
130
+ .flow-node-handle.flow-node-handle-bottom {
131
+ background: #f59e0b;
132
+ }
133
+
134
+ .flow-node-handle.flow-node-handle-left {
135
+ background: #ef4444;
136
+ }
137
+
138
+ /* target handle 样式 */
139
+ .flow-node-handle[data-handletype="target"] {
140
+ background: #6b7280;
141
+ }
142
+
143
+ .flow-node-handle[data-handletype="target"]:hover {
144
+ background: #4b5563;
145
+ box-shadow: 0 0 0 3px rgba(107, 114, 128, 0.2);
146
+ }
147
+ </style>
@@ -0,0 +1,77 @@
1
+ <script setup lang="ts">
2
+ import { h, ref } from 'vue';
3
+
4
+ import {
5
+ CopyOutlined,
6
+ DeleteOutlined,
7
+ MoreOutlined,
8
+ PlayCircleOutlined,
9
+ } from '@ant-design/icons-vue';
10
+
11
+ defineProps<{ nodeId?: string }>();
12
+
13
+ const emit = defineEmits(['duplicate', 'delete', 'run']);
14
+ const morePopoverOpen = ref(false);
15
+
16
+ const onDuplicate = () => emit('duplicate');
17
+ const onDelete = () => emit('delete');
18
+ const onRun = () => emit('run');
19
+
20
+ const closeMore = () => (morePopoverOpen.value = false);
21
+ </script>
22
+
23
+ <template>
24
+ <div class="wf-header-toolbar">
25
+ <a-tooltip title="运行到此节点">
26
+ <a-button :icon="h(PlayCircleOutlined)" size="small" @click="onRun" />
27
+ </a-tooltip>
28
+ <a-tooltip title="复制 (Ctrl+D)">
29
+ <a-button :icon="h(CopyOutlined)" size="small" @click="onDuplicate" />
30
+ </a-tooltip>
31
+ <a-tooltip title="删除 (Del)">
32
+ <a-button :icon="h(DeleteOutlined)" size="small" danger @click="onDelete" />
33
+ </a-tooltip>
34
+ <a-popover v-model:open="morePopoverOpen" trigger="click" placement="bottom">
35
+ <template #content>
36
+ <div class="wf-header-toolbar-menu">
37
+ <div class="wf-header-toolbar-menu-item" @click="closeMore">
38
+ 重命名
39
+ </div>
40
+ <div class="wf-header-toolbar-menu-item" @click="closeMore">
41
+ 查看变量
42
+ </div>
43
+ <div class="wf-header-toolbar-menu-item" @click="closeMore">
44
+ 关于此节点
45
+ </div>
46
+ </div>
47
+ </template>
48
+ <a-button :icon="h(MoreOutlined)" size="small" />
49
+ </a-popover>
50
+ </div>
51
+ </template>
52
+
53
+ <style scoped>
54
+ .wf-header-toolbar {
55
+ display: flex;
56
+ align-items: center;
57
+ gap: 4px;
58
+ width: 100%;
59
+ }
60
+
61
+ .wf-header-toolbar-menu {
62
+ min-width: 140px;
63
+ padding: 4px 0;
64
+ }
65
+
66
+ .wf-header-toolbar-menu-item {
67
+ padding: 6px 12px;
68
+ font-size: 13px;
69
+ color: #1f2937;
70
+ cursor: pointer;
71
+ transition: background 0.15s;
72
+ }
73
+
74
+ .wf-header-toolbar-menu-item:hover {
75
+ background: #f3f4f6;
76
+ }
77
+ </style>