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,552 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * DocumentTable — the 文档 tab of the dataset detail (screenshot 4).
4
+ *
5
+ * Top toolbar: 全部 filter dropdown, search input, sort dropdown, 元数据 button,
6
+ * "+ 添加文件" primary button.
7
+ * Body: table with # / 名称 / 分段模式 / 字数 / 召回次数 / 上传时间 / 状态 /
8
+ * 操作 (toggle + ⋯ menu).
9
+ * Footer: pagination (« 1/1 » and 10/25/50 page-size selector).
10
+ */
11
+ import { computed, ref } from 'vue';
12
+
13
+ import type { DocumentRow } from '../types';
14
+
15
+ interface Props {
16
+ documents: DocumentRow[];
17
+ loading?: boolean;
18
+ page?: number;
19
+ pageSize?: number;
20
+ total?: number;
21
+ }
22
+
23
+ const props = withDefaults(defineProps<Props>(), {
24
+ loading: false,
25
+ page: 1,
26
+ pageSize: 10,
27
+ });
28
+
29
+ const emit = defineEmits<{
30
+ (e: 'add-file'): void;
31
+ (e: 'open', doc: DocumentRow): void;
32
+ (e: 'toggle-enabled', doc: DocumentRow, next: boolean): void;
33
+ (e: 'delete', doc: DocumentRow): void;
34
+ (e: 'edit-metadata'): void;
35
+ (e: 'update:page', p: number): void;
36
+ (e: 'update:pageSize', s: number): void;
37
+ }>();
38
+
39
+ const filter = ref<'ALL' | 'AVAILABLE' | 'FAILED'>('ALL');
40
+ const search = ref('');
41
+ const sort = ref<'-uploaded' | 'name' | 'uploaded'>('-uploaded');
42
+
43
+ const filtered = computed(() => {
44
+ let out = [...props.documents];
45
+ if (filter.value !== 'ALL') out = out.filter((d) => d.status === filter.value);
46
+ const q = search.value.trim().toLowerCase();
47
+ if (q) out = out.filter((d) => d.name.toLowerCase().includes(q));
48
+ if (sort.value === 'name') out.sort((a, b) => a.name.localeCompare(b.name));
49
+ else {
50
+ out.sort((a, b) => {
51
+ const ta = a.uploadedAt ? +new Date(a.uploadedAt) : 0;
52
+ const tb = b.uploadedAt ? +new Date(b.uploadedAt) : 0;
53
+ return sort.value === 'uploaded' ? ta - tb : tb - ta;
54
+ });
55
+ }
56
+ return out;
57
+ });
58
+
59
+ const total = computed(() => props.total ?? filtered.value.length);
60
+ const totalPages = computed(() =>
61
+ Math.max(1, Math.ceil(total.value / props.pageSize)),
62
+ );
63
+
64
+ function goPage(p: number) {
65
+ emit('update:page', Math.max(1, Math.min(p, totalPages.value)));
66
+ }
67
+
68
+ const openMenuFor = ref<null | string>(null);
69
+
70
+ function toggle(d: DocumentRow) {
71
+ emit('toggle-enabled', d, !d.enabled);
72
+ }
73
+ function fmt(iso?: string): string {
74
+ if (!iso) return '';
75
+ const d = new Date(iso);
76
+ if (Number.isNaN(d.getTime())) return iso;
77
+ const pad = (n: number) => String(n).padStart(2, '0');
78
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
79
+ }
80
+ function fmtNum(n?: number): string {
81
+ if (n == null) return '';
82
+ if (n >= 10_000) return `${(n / 1000).toFixed(1)}k`;
83
+ return String(n);
84
+ }
85
+ function statusColor(s: DocumentRow['status']): string {
86
+ if (s === 'AVAILABLE') return '#059669';
87
+ if (s === 'FAILED') return '#dc2626';
88
+ return '#0284c7';
89
+ }
90
+ function statusLabel(s: DocumentRow['status'], enabled: boolean): string {
91
+ if (s === 'FAILED') return '失败';
92
+ if (s === 'PROCESSING') return '处理中';
93
+ return enabled ? '可用' : '已停用';
94
+ }
95
+ </script>
96
+
97
+ <template>
98
+ <div class="kh-doctab">
99
+ <!-- Intro text -->
100
+ <div class="kh-doctab-intro">
101
+ 知识库的所有文件都在这里显示,可通过 API 引用或在应用中被智能体检索。
102
+ <a href="#" class="kh-doctab-more">了解更多 ↗</a>
103
+ </div>
104
+
105
+ <!-- toolbar -->
106
+ <div class="kh-doctab-toolbar">
107
+ <div class="kh-doctab-left">
108
+ <select v-model="filter" class="kh-doctab-select">
109
+ <option value="ALL">全部</option>
110
+ <option value="AVAILABLE">可用</option>
111
+ <option value="FAILED">失败</option>
112
+ </select>
113
+ <div class="kh-doctab-search">
114
+ <span class="kh-doctab-search-icon">🔍</span>
115
+ <input
116
+ v-model="search"
117
+ class="kh-doctab-search-input"
118
+ placeholder="搜索"
119
+ />
120
+ </div>
121
+ <select v-model="sort" class="kh-doctab-select">
122
+ <option value="-uploaded">排序:上传时间 ↓</option>
123
+ <option value="uploaded">排序:上传时间 ↑</option>
124
+ <option value="name">排序:名称</option>
125
+ </select>
126
+ </div>
127
+ <div class="kh-doctab-right">
128
+ <button class="kh-btn kh-btn-secondary" @click="emit('edit-metadata')">
129
+ ≡ 元数据
130
+ </button>
131
+ <button class="kh-btn kh-btn-primary" @click="emit('add-file')">
132
+ + 添加文件
133
+ </button>
134
+ </div>
135
+ </div>
136
+
137
+ <!-- table -->
138
+ <div class="kh-doctab-table">
139
+ <div class="kh-doctab-thead">
140
+ <div class="kh-col kh-col-check">
141
+ <input type="checkbox" />
142
+ </div>
143
+ <div class="kh-col kh-col-num">#</div>
144
+ <div class="kh-col kh-col-name">名称</div>
145
+ <div class="kh-col kh-col-mode">分段模式</div>
146
+ <div class="kh-col kh-col-num2">字数</div>
147
+ <div class="kh-col kh-col-num2">召回次数</div>
148
+ <div class="kh-col kh-col-time">上传时间 ↓</div>
149
+ <div class="kh-col kh-col-status">状态</div>
150
+ <div class="kh-col kh-col-actions">操作</div>
151
+ </div>
152
+
153
+ <div v-if="loading" class="kh-doctab-empty">加载中...</div>
154
+ <div v-else-if="filtered.length === 0" class="kh-doctab-empty">
155
+ 📄 还没有文档。点击右上角"添加文件"上传。
156
+ </div>
157
+
158
+ <div
159
+ v-for="(d, i) in filtered"
160
+ :key="d.id"
161
+ class="kh-doctab-row"
162
+ @click="emit('open', d)"
163
+ >
164
+ <div class="kh-col kh-col-check" @click.stop>
165
+ <input type="checkbox" />
166
+ </div>
167
+ <div class="kh-col kh-col-num">{{ i + 1 }}</div>
168
+ <div class="kh-col kh-col-name">
169
+ <span class="kh-doc-icon">📄</span>
170
+ <span class="kh-doc-name">{{ d.name }}</span>
171
+ </div>
172
+ <div class="kh-col kh-col-mode">
173
+ <span class="kh-mode-chip">{{ d.chunkMode ?? '通用' }}</span>
174
+ </div>
175
+ <div class="kh-col kh-col-num2">{{ fmtNum(d.wordCount) }}</div>
176
+ <div class="kh-col kh-col-num2">{{ d.hitCount ?? 0 }}</div>
177
+ <div class="kh-col kh-col-time">{{ fmt(d.uploadedAt) }}</div>
178
+ <div class="kh-col kh-col-status">
179
+ <span class="kh-status-dot" :style="{ background: statusColor(d.status) }" />
180
+ <span>{{ statusLabel(d.status, d.enabled) }}</span>
181
+ </div>
182
+ <div class="kh-col kh-col-actions" @click.stop>
183
+ <label class="kh-switch" :title="d.enabled ? '已启用 · 点击停用' : '已停用 · 点击启用'">
184
+ <input
185
+ type="checkbox"
186
+ :checked="d.enabled"
187
+ @change="toggle(d)"
188
+ />
189
+ <span class="kh-switch-slider" />
190
+ </label>
191
+ <button
192
+ class="kh-more-btn"
193
+ @click="openMenuFor = openMenuFor === d.id ? null : d.id"
194
+ >
195
+
196
+ </button>
197
+ <div
198
+ v-if="openMenuFor === d.id"
199
+ class="kh-more-menu"
200
+ @click.stop
201
+ >
202
+ <div class="kh-more-item" @click="emit('open', d); openMenuFor = null">
203
+ 查看片段
204
+ </div>
205
+ <div class="kh-more-item kh-more-danger" @click="emit('delete', d); openMenuFor = null">
206
+ 删除
207
+ </div>
208
+ </div>
209
+ </div>
210
+ </div>
211
+ </div>
212
+
213
+ <!-- footer pagination -->
214
+ <div class="kh-doctab-foot">
215
+ <div class="kh-doctab-pager">
216
+ <button class="kh-pager-btn" @click="goPage(page - 1)">‹</button>
217
+ <span class="kh-pager-page">{{ page }} / {{ totalPages }}</span>
218
+ <button class="kh-pager-btn" @click="goPage(page + 1)">›</button>
219
+ </div>
220
+ <div class="kh-pager-center">{{ page }}</div>
221
+ <div class="kh-pager-sizes">
222
+ <button
223
+ v-for="s in [10, 25, 50]"
224
+ :key="s"
225
+ class="kh-pager-size"
226
+ :class="{ 'kh-pager-size-active': pageSize === s }"
227
+ @click="emit('update:pageSize', s)"
228
+ >
229
+ {{ s }}
230
+ </button>
231
+ </div>
232
+ </div>
233
+ </div>
234
+ </template>
235
+
236
+ <style scoped>
237
+ .kh-doctab {
238
+ padding: 20px 24px;
239
+ display: flex;
240
+ flex-direction: column;
241
+ height: 100%;
242
+ }
243
+ .kh-doctab-intro {
244
+ font-size: 12px;
245
+ color: #64748b;
246
+ margin-bottom: 12px;
247
+ line-height: 1.5;
248
+ }
249
+ .kh-doctab-more {
250
+ color: #4338ca;
251
+ text-decoration: none;
252
+ }
253
+
254
+ /* Toolbar */
255
+ .kh-doctab-toolbar {
256
+ display: flex;
257
+ justify-content: space-between;
258
+ align-items: center;
259
+ gap: 10px;
260
+ margin-bottom: 14px;
261
+ }
262
+ .kh-doctab-left {
263
+ display: flex;
264
+ gap: 8px;
265
+ align-items: center;
266
+ }
267
+ .kh-doctab-right {
268
+ display: flex;
269
+ gap: 8px;
270
+ }
271
+ .kh-doctab-select {
272
+ padding: 6px 24px 6px 10px;
273
+ border: 1px solid #e2e8f0;
274
+ border-radius: 6px;
275
+ background: #fff;
276
+ font-size: 12px;
277
+ color: #475569;
278
+ cursor: pointer;
279
+ outline: none;
280
+ }
281
+ .kh-doctab-select:focus {
282
+ border-color: #6366f1;
283
+ }
284
+ .kh-doctab-search {
285
+ position: relative;
286
+ }
287
+ .kh-doctab-search-icon {
288
+ position: absolute;
289
+ left: 8px;
290
+ top: 50%;
291
+ transform: translateY(-50%);
292
+ font-size: 11px;
293
+ color: #94a3b8;
294
+ }
295
+ .kh-doctab-search-input {
296
+ padding: 6px 10px 6px 26px;
297
+ border: 1px solid #e2e8f0;
298
+ border-radius: 6px;
299
+ background: #fff;
300
+ font-size: 12px;
301
+ outline: none;
302
+ min-width: 200px;
303
+ }
304
+ .kh-doctab-search-input:focus {
305
+ border-color: #6366f1;
306
+ }
307
+
308
+ .kh-btn {
309
+ padding: 6px 12px;
310
+ border: none;
311
+ border-radius: 6px;
312
+ font-size: 12px;
313
+ font-weight: 500;
314
+ cursor: pointer;
315
+ }
316
+ .kh-btn-primary {
317
+ background: linear-gradient(135deg, #6366f1, #4f46e5);
318
+ color: #fff;
319
+ box-shadow: 0 2px 4px rgba(79, 70, 229, 0.25);
320
+ }
321
+ .kh-btn-primary:hover {
322
+ transform: translateY(-1px);
323
+ }
324
+ .kh-btn-secondary {
325
+ background: #f1f5f9;
326
+ color: #475569;
327
+ }
328
+ .kh-btn-secondary:hover {
329
+ background: #e2e8f0;
330
+ }
331
+
332
+ /* Table */
333
+ .kh-doctab-table {
334
+ flex: 1;
335
+ border: 1px solid #f1f5f9;
336
+ border-radius: 8px;
337
+ overflow: auto;
338
+ background: #fff;
339
+ }
340
+ .kh-doctab-thead,
341
+ .kh-doctab-row {
342
+ display: grid;
343
+ grid-template-columns: 32px 32px 2fr 120px 80px 90px 140px 80px 100px;
344
+ align-items: center;
345
+ padding: 0 10px;
346
+ gap: 0;
347
+ }
348
+ .kh-doctab-thead {
349
+ height: 40px;
350
+ background: #f8fafc;
351
+ border-bottom: 1px solid #f1f5f9;
352
+ font-size: 11px;
353
+ color: #64748b;
354
+ font-weight: 500;
355
+ }
356
+ .kh-doctab-row {
357
+ height: 44px;
358
+ border-bottom: 1px solid #f8fafc;
359
+ font-size: 12px;
360
+ color: #334155;
361
+ cursor: pointer;
362
+ transition: background 0.15s;
363
+ }
364
+ .kh-doctab-row:hover {
365
+ background: #f8fafc;
366
+ }
367
+ .kh-col {
368
+ overflow: hidden;
369
+ text-overflow: ellipsis;
370
+ white-space: nowrap;
371
+ }
372
+ .kh-col-num {
373
+ color: #94a3b8;
374
+ }
375
+ .kh-col-name {
376
+ display: flex;
377
+ align-items: center;
378
+ gap: 6px;
379
+ }
380
+ .kh-doc-icon {
381
+ color: #4338ca;
382
+ }
383
+ .kh-doc-name {
384
+ overflow: hidden;
385
+ text-overflow: ellipsis;
386
+ white-space: nowrap;
387
+ }
388
+ .kh-mode-chip {
389
+ padding: 2px 8px;
390
+ background: #eef2ff;
391
+ color: #4338ca;
392
+ border-radius: 4px;
393
+ font-size: 11px;
394
+ }
395
+ .kh-col-status {
396
+ display: flex;
397
+ align-items: center;
398
+ gap: 6px;
399
+ }
400
+ .kh-status-dot {
401
+ width: 6px;
402
+ height: 6px;
403
+ border-radius: 50%;
404
+ display: inline-block;
405
+ }
406
+ .kh-col-actions {
407
+ position: relative;
408
+ display: flex;
409
+ gap: 6px;
410
+ align-items: center;
411
+ }
412
+
413
+ /* Switch */
414
+ .kh-switch {
415
+ position: relative;
416
+ display: inline-block;
417
+ width: 32px;
418
+ height: 18px;
419
+ cursor: pointer;
420
+ }
421
+ .kh-switch input {
422
+ display: none;
423
+ }
424
+ .kh-switch-slider {
425
+ position: absolute;
426
+ inset: 0;
427
+ background: #cbd5e1;
428
+ border-radius: 9px;
429
+ transition: background 0.15s;
430
+ }
431
+ .kh-switch-slider::before {
432
+ content: '';
433
+ position: absolute;
434
+ top: 2px;
435
+ left: 2px;
436
+ width: 14px;
437
+ height: 14px;
438
+ background: #fff;
439
+ border-radius: 50%;
440
+ transition: transform 0.15s;
441
+ }
442
+ .kh-switch input:checked + .kh-switch-slider {
443
+ background: #4f46e5;
444
+ }
445
+ .kh-switch input:checked + .kh-switch-slider::before {
446
+ transform: translateX(14px);
447
+ }
448
+
449
+ .kh-more-btn {
450
+ padding: 0 6px;
451
+ border: none;
452
+ border-radius: 4px;
453
+ background: transparent;
454
+ font-size: 16px;
455
+ color: #64748b;
456
+ cursor: pointer;
457
+ line-height: 1;
458
+ }
459
+ .kh-more-btn:hover {
460
+ background: #f1f5f9;
461
+ }
462
+ .kh-more-menu {
463
+ position: absolute;
464
+ right: 0;
465
+ top: 24px;
466
+ z-index: 20;
467
+ min-width: 140px;
468
+ padding: 4px;
469
+ background: #fff;
470
+ border: 1px solid #e2e8f0;
471
+ border-radius: 6px;
472
+ box-shadow: 0 6px 20px rgba(15, 23, 42, 0.1);
473
+ }
474
+ .kh-more-item {
475
+ padding: 6px 10px;
476
+ border-radius: 4px;
477
+ font-size: 12px;
478
+ color: #475569;
479
+ cursor: pointer;
480
+ }
481
+ .kh-more-item:hover {
482
+ background: #f8fafc;
483
+ }
484
+ .kh-more-danger {
485
+ color: #dc2626;
486
+ }
487
+
488
+ .kh-doctab-empty {
489
+ padding: 40px 20px;
490
+ text-align: center;
491
+ color: #94a3b8;
492
+ font-size: 12px;
493
+ }
494
+
495
+ /* Footer */
496
+ .kh-doctab-foot {
497
+ display: grid;
498
+ grid-template-columns: 1fr auto 1fr;
499
+ align-items: center;
500
+ padding: 12px 4px 0;
501
+ gap: 12px;
502
+ }
503
+ .kh-doctab-pager {
504
+ display: flex;
505
+ align-items: center;
506
+ gap: 6px;
507
+ font-size: 12px;
508
+ color: #64748b;
509
+ }
510
+ .kh-pager-btn {
511
+ padding: 2px 8px;
512
+ border: 1px solid #e2e8f0;
513
+ border-radius: 4px;
514
+ background: #fff;
515
+ cursor: pointer;
516
+ color: #64748b;
517
+ }
518
+ .kh-pager-btn:hover {
519
+ background: #f1f5f9;
520
+ }
521
+ .kh-pager-page {
522
+ min-width: 40px;
523
+ text-align: center;
524
+ }
525
+ .kh-pager-center {
526
+ text-align: center;
527
+ font-size: 12px;
528
+ color: #64748b;
529
+ }
530
+ .kh-pager-sizes {
531
+ display: flex;
532
+ justify-content: flex-end;
533
+ gap: 4px;
534
+ }
535
+ .kh-pager-size {
536
+ padding: 2px 8px;
537
+ border: none;
538
+ background: transparent;
539
+ font-size: 12px;
540
+ color: #94a3b8;
541
+ cursor: pointer;
542
+ border-radius: 4px;
543
+ }
544
+ .kh-pager-size:hover {
545
+ background: #f1f5f9;
546
+ color: #475569;
547
+ }
548
+ .kh-pager-size-active {
549
+ background: #4f46e5;
550
+ color: #fff;
551
+ }
552
+ </style>
@@ -0,0 +1,74 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue';
3
+
4
+ import { useKnowledge } from '../composables/useKnowledge';
5
+ import type { RetrievalMethod, RetrievedSegment } from '../types';
6
+
7
+ import RetrievedList from './RetrievedList.vue';
8
+
9
+ interface Props {
10
+ datasetId: string;
11
+ initialQuery?: string;
12
+ initialMethod?: RetrievalMethod;
13
+ topK?: number;
14
+ }
15
+
16
+ const props = withDefaults(defineProps<Props>(), {
17
+ initialQuery: '',
18
+ initialMethod: 'HYBRID',
19
+ topK: 5,
20
+ });
21
+
22
+ const query = ref(props.initialQuery);
23
+ const method = ref<RetrievalMethod>(props.initialMethod);
24
+ const hits = ref<RetrievedSegment[]>([]);
25
+ const loading = ref(false);
26
+ const error = ref<string | null>(null);
27
+
28
+ async function run() {
29
+ if (!props.datasetId || !query.value.trim()) return;
30
+ loading.value = true;
31
+ error.value = null;
32
+ try {
33
+ const { retrieve } = useKnowledge();
34
+ hits.value = await retrieve(props.datasetId, {
35
+ query: query.value,
36
+ method: method.value,
37
+ topK: props.topK,
38
+ });
39
+ } catch (e: any) {
40
+ error.value = e?.message ?? String(e);
41
+ } finally {
42
+ loading.value = false;
43
+ }
44
+ }
45
+ </script>
46
+
47
+ <template>
48
+ <div class="knowledge-hub-panel space-y-2">
49
+ <textarea
50
+ v-model="query"
51
+ :rows="2"
52
+ placeholder="输入检索问题"
53
+ class="w-full rounded border px-2 py-1"
54
+ ></textarea>
55
+ <div class="flex items-center justify-between">
56
+ <label class="text-sm">
57
+ <select v-model="method" class="rounded border px-1 py-0.5">
58
+ <option value="HYBRID">混合</option>
59
+ <option value="VECTOR">仅向量</option>
60
+ <option value="FULL_TEXT">仅关键词</option>
61
+ </select>
62
+ </label>
63
+ <button
64
+ :disabled="loading"
65
+ class="rounded bg-blue-500 px-3 py-1 text-white disabled:opacity-50"
66
+ @click="run"
67
+ >
68
+ {{ loading ? '检索中...' : '检索' }}
69
+ </button>
70
+ </div>
71
+ <div v-if="error" class="text-sm text-red-500">❌ {{ error }}</div>
72
+ <RetrievedList :hits="hits" />
73
+ </div>
74
+ </template>
@@ -0,0 +1,66 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * KnowledgeApp — the single-drop-in knowledge base UI.
4
+ *
5
+ * Give it an `api-base` (default `/api`) and it wires everything for you:
6
+ * • card grid + create wizard drawer + detail drawer + chunks browser
7
+ * • the "先注册 Embedding 模型" empty-state nudge
8
+ * • toasts via ant-design-vue's `message` (or your own via slots/events)
9
+ * • dataset URL copy via navigator.clipboard
10
+ *
11
+ * Zero adapter wiring on the host side:
12
+ *
13
+ * <KnowledgeApp /> // uses /api
14
+ * <KnowledgeApp api-base="/my-api" /> // custom prefix
15
+ *
16
+ * Hosts wanting to override any single behaviour can:
17
+ * • pass an explicit `api` prop (skips the built-in factory), or
18
+ * • listen to `@go-to-embedding-setup` to intercept the router push.
19
+ */
20
+ import { computed } from 'vue';
21
+
22
+ import { message } from 'ant-design-vue';
23
+
24
+ import { createSpringAgentStartAdapter } from '../adapters/springAgentStart';
25
+ import type { KnowledgeHubApi } from '../types';
26
+ import KnowledgeHubApp from './KnowledgeHubApp.vue';
27
+
28
+ interface Props {
29
+ apiBase?: string;
30
+ api?: KnowledgeHubApi;
31
+ showEmbeddingSetupHint?: boolean;
32
+ }
33
+
34
+ const props = withDefaults(defineProps<Props>(), {
35
+ apiBase: '/api',
36
+ api: undefined,
37
+ showEmbeddingSetupHint: true,
38
+ });
39
+
40
+ const emit = defineEmits<{
41
+ (e: 'go-to-embedding-setup'): void;
42
+ }>();
43
+
44
+ const resolvedApi = computed<KnowledgeHubApi>(() => {
45
+ if (props.api) return props.api;
46
+ return createSpringAgentStartAdapter({
47
+ baseUrl: props.apiBase,
48
+ onSuccess: (msg: string) => message.success(msg),
49
+ onError: (msg: string) => message.error(msg),
50
+ onCopyApi: (id: string) => {
51
+ const url = `${window.location.origin}${props.apiBase.replace(/\/+$/, '')}/datasets/${id}`;
52
+ navigator.clipboard.writeText(url).then(
53
+ () => message.success('已复制 API 地址'),
54
+ () => message.error('复制失败'),
55
+ );
56
+ },
57
+ onGoToEmbeddingSetup: props.showEmbeddingSetupHint
58
+ ? () => emit('go-to-embedding-setup')
59
+ : undefined,
60
+ });
61
+ });
62
+ </script>
63
+
64
+ <template>
65
+ <KnowledgeHubApp :api="resolvedApi" />
66
+ </template>