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,391 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * 日志与标注 tab — table + filters. Matches Snipaste_09-01-27:
4
+ * - period picker + status filter + search
5
+ * - sortable table (标题 / 用户 / 状态 / 消息数 / 用户反馈 / 管理员反馈 / 更新时间 / 创建时间)
6
+ * - simple pagination footer (host-driven)
7
+ *
8
+ * The host owns the data. This panel is purely presentational.
9
+ */
10
+ import { computed, ref } from 'vue';
11
+
12
+ import type { StudioLogEntry } from '../types';
13
+
14
+ interface Props {
15
+ entries: StudioLogEntry[];
16
+ total?: number;
17
+ page?: number;
18
+ pageSize?: number;
19
+ loading?: boolean;
20
+ }
21
+
22
+ const props = withDefaults(defineProps<Props>(), {
23
+ total: undefined,
24
+ page: 1,
25
+ pageSize: 10,
26
+ loading: false,
27
+ });
28
+
29
+ const emit = defineEmits<{
30
+ (e: 'select', entry: StudioLogEntry): void;
31
+ (e: 'change-page', page: number): void;
32
+ (e: 'change-page-size', size: number): void;
33
+ (e: 'change-period', period: string): void;
34
+ (e: 'change-status', status: string): void;
35
+ (e: 'change-search', q: string): void;
36
+ }>();
37
+
38
+ const period = ref('过去 7 天');
39
+ const statusFilter = ref('全部');
40
+ const search = ref('');
41
+ const sortKey = ref<'createdAt' | 'updatedAt'>('createdAt');
42
+
43
+ const filtered = computed(() => {
44
+ const q = search.value.trim().toLowerCase();
45
+ return props.entries.filter((e) => {
46
+ if (statusFilter.value !== '全部' && e.status !== statusFilter.value) {
47
+ return false;
48
+ }
49
+ if (!q) return true;
50
+ return (
51
+ e.title.toLowerCase().includes(q) ||
52
+ (e.endUser ?? '').toLowerCase().includes(q)
53
+ );
54
+ });
55
+ });
56
+
57
+ function statusClass(s: string) {
58
+ return `logs-status logs-status-${s.toLowerCase()}`;
59
+ }
60
+
61
+ function fmt(iso?: string) {
62
+ if (!iso) return '-';
63
+ const d = new Date(iso);
64
+ if (Number.isNaN(d.getTime())) return iso;
65
+ const pad = (n: number) => n.toString().padStart(2, '0');
66
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
67
+ }
68
+
69
+ const totalPages = computed(() =>
70
+ Math.max(1, Math.ceil((props.total ?? filtered.value.length) / props.pageSize)),
71
+ );
72
+ </script>
73
+
74
+ <template>
75
+ <div class="logs">
76
+ <header class="logs-top">
77
+ <div>
78
+ <h2 class="logs-top-title">日志</h2>
79
+ <div class="logs-top-sub">
80
+ 日志记录了应用的运行情况,包括用户的输入和 AI 的回复。
81
+ </div>
82
+ </div>
83
+ </header>
84
+
85
+ <div class="logs-filters">
86
+ <select
87
+ class="logs-input"
88
+ :value="period"
89
+ @change="(e) => { period = (e.target as HTMLSelectElement).value; emit('change-period', period); }"
90
+ >
91
+ <option>过去 7 天</option>
92
+ <option>过去 30 天</option>
93
+ <option>全部时间</option>
94
+ </select>
95
+ <select
96
+ class="logs-input"
97
+ :value="statusFilter"
98
+ @change="(e) => { statusFilter = (e.target as HTMLSelectElement).value; emit('change-status', statusFilter); }"
99
+ >
100
+ <option>全部</option>
101
+ <option value="SUCCESS">成功</option>
102
+ <option value="FAILED">失败</option>
103
+ <option value="PENDING">处理中</option>
104
+ </select>
105
+ <input
106
+ v-model="search"
107
+ class="logs-input logs-search"
108
+ placeholder="搜索..."
109
+ @input="emit('change-search', search)"
110
+ />
111
+ <div class="logs-sort">
112
+ <span class="logs-sort-label">排序:</span>
113
+ <select
114
+ class="logs-input"
115
+ :value="sortKey"
116
+ @change="(e) => (sortKey = (e.target as HTMLSelectElement).value as any)"
117
+ >
118
+ <option value="createdAt">创建时间</option>
119
+ <option value="updatedAt">更新时间</option>
120
+ </select>
121
+ </div>
122
+ </div>
123
+
124
+ <div class="logs-table-wrap">
125
+ <table class="logs-table">
126
+ <thead>
127
+ <tr>
128
+ <th>标题</th>
129
+ <th>用户/终端用户</th>
130
+ <th>状态</th>
131
+ <th>消息数</th>
132
+ <th>用户反馈</th>
133
+ <th>管理员反馈</th>
134
+ <th>更新时间</th>
135
+ <th>创建时间</th>
136
+ </tr>
137
+ </thead>
138
+ <tbody>
139
+ <tr v-if="loading">
140
+ <td colspan="8" class="logs-tbody-empty">加载中...</td>
141
+ </tr>
142
+ <tr
143
+ v-else-if="filtered.length === 0"
144
+ class="logs-empty-row"
145
+ >
146
+ <td colspan="8" class="logs-tbody-empty">暂无日志</td>
147
+ </tr>
148
+ <tr
149
+ v-for="e in filtered"
150
+ :key="e.id"
151
+ class="logs-row"
152
+ @click="emit('select', e)"
153
+ >
154
+ <td class="logs-title-cell">
155
+ <span class="logs-title-dot" />
156
+ {{ e.title }}
157
+ </td>
158
+ <td class="logs-user-cell">{{ e.endUser ?? '-' }}</td>
159
+ <td>
160
+ <span :class="statusClass(e.status)">{{ e.status }}</span>
161
+ </td>
162
+ <td>{{ e.messageCount ?? '-' }}</td>
163
+ <td>{{ e.userFeedback ?? 'N/A' }}</td>
164
+ <td>{{ e.adminFeedback ?? 'N/A' }}</td>
165
+ <td class="logs-date">{{ fmt(e.updatedAt) }}</td>
166
+ <td class="logs-date">{{ fmt(e.createdAt) }}</td>
167
+ </tr>
168
+ </tbody>
169
+ </table>
170
+ </div>
171
+
172
+ <footer class="logs-foot">
173
+ <button
174
+ type="button"
175
+ class="logs-page-btn"
176
+ :disabled="page <= 1"
177
+ @click="emit('change-page', page - 1)"
178
+ >
179
+
180
+ </button>
181
+ <span class="logs-page-label">{{ page }} / {{ totalPages }}</span>
182
+ <button
183
+ type="button"
184
+ class="logs-page-btn"
185
+ :disabled="page >= totalPages"
186
+ @click="emit('change-page', page + 1)"
187
+ >
188
+
189
+ </button>
190
+ <span class="logs-page-sep">|</span>
191
+ <select
192
+ class="logs-input"
193
+ :value="pageSize"
194
+ @change="emit('change-page-size', Number(($event.target as HTMLSelectElement).value))"
195
+ >
196
+ <option :value="10">10</option>
197
+ <option :value="25">25</option>
198
+ <option :value="50">50</option>
199
+ </select>
200
+ </footer>
201
+ </div>
202
+ </template>
203
+
204
+ <style scoped>
205
+ .logs {
206
+ display: flex;
207
+ flex-direction: column;
208
+ height: 100%;
209
+ min-height: 0;
210
+ }
211
+ .logs-top {
212
+ padding: 16px 24px 8px;
213
+ background: #fff;
214
+ border-bottom: 1px solid #e5e7eb;
215
+ }
216
+ .logs-top-title {
217
+ margin: 0;
218
+ font-size: 15px;
219
+ font-weight: 600;
220
+ color: #0f172a;
221
+ }
222
+ .logs-top-sub {
223
+ margin-top: 2px;
224
+ font-size: 12px;
225
+ color: #94a3b8;
226
+ }
227
+ .logs-filters {
228
+ display: flex;
229
+ gap: 8px;
230
+ padding: 12px 24px;
231
+ background: #fff;
232
+ border-bottom: 1px solid #f1f5f9;
233
+ align-items: center;
234
+ }
235
+ .logs-input {
236
+ padding: 5px 8px;
237
+ border: 1px solid #e2e8f0;
238
+ border-radius: 6px;
239
+ background: #fff;
240
+ font-size: 12px;
241
+ color: #0f172a;
242
+ outline: none;
243
+ }
244
+ .logs-input:focus {
245
+ border-color: #6366f1;
246
+ }
247
+ .logs-search {
248
+ min-width: 220px;
249
+ }
250
+ .logs-sort {
251
+ margin-left: auto;
252
+ display: inline-flex;
253
+ align-items: center;
254
+ gap: 4px;
255
+ }
256
+ .logs-sort-label {
257
+ font-size: 12px;
258
+ color: #94a3b8;
259
+ }
260
+ .logs-table-wrap {
261
+ flex: 1;
262
+ overflow: auto;
263
+ padding: 0 24px;
264
+ background: #fff;
265
+ }
266
+ .logs-table {
267
+ width: 100%;
268
+ border-collapse: collapse;
269
+ font-size: 12px;
270
+ }
271
+ .logs-table th {
272
+ text-align: left;
273
+ padding: 8px 10px;
274
+ color: #64748b;
275
+ font-weight: 500;
276
+ border-bottom: 1px solid #e5e7eb;
277
+ background: #fff;
278
+ position: sticky;
279
+ top: 0;
280
+ }
281
+ .logs-table td {
282
+ padding: 10px;
283
+ border-bottom: 1px solid #f1f5f9;
284
+ color: #334155;
285
+ }
286
+ .logs-row {
287
+ cursor: pointer;
288
+ transition: background 0.15s;
289
+ }
290
+ .logs-row:hover {
291
+ background: #f8fafc;
292
+ }
293
+ .logs-title-cell {
294
+ display: flex;
295
+ align-items: center;
296
+ gap: 6px;
297
+ font-weight: 500;
298
+ color: #0f172a;
299
+ }
300
+ .logs-title-dot {
301
+ width: 6px;
302
+ height: 6px;
303
+ border-radius: 50%;
304
+ background: #cbd5e1;
305
+ }
306
+ .logs-user-cell {
307
+ font-family: ui-monospace, monospace;
308
+ color: #64748b;
309
+ font-size: 11px;
310
+ }
311
+ .logs-status {
312
+ display: inline-flex;
313
+ align-items: center;
314
+ gap: 4px;
315
+ padding: 2px 8px;
316
+ border-radius: 999px;
317
+ font-size: 11px;
318
+ font-weight: 500;
319
+ }
320
+ .logs-status::before {
321
+ content: '';
322
+ width: 6px;
323
+ height: 6px;
324
+ border-radius: 50%;
325
+ }
326
+ .logs-status-success {
327
+ background: #dcfce7;
328
+ color: #15803d;
329
+ }
330
+ .logs-status-success::before {
331
+ background: #16a34a;
332
+ }
333
+ .logs-status-failed {
334
+ background: #fee2e2;
335
+ color: #b91c1c;
336
+ }
337
+ .logs-status-failed::before {
338
+ background: #dc2626;
339
+ }
340
+ .logs-status-pending {
341
+ background: #fef3c7;
342
+ color: #a16207;
343
+ }
344
+ .logs-status-pending::before {
345
+ background: #f59e0b;
346
+ }
347
+ .logs-date {
348
+ color: #64748b;
349
+ font-size: 11px;
350
+ white-space: nowrap;
351
+ }
352
+ .logs-tbody-empty {
353
+ padding: 40px !important;
354
+ text-align: center;
355
+ color: #94a3b8;
356
+ }
357
+ .logs-foot {
358
+ display: flex;
359
+ align-items: center;
360
+ gap: 6px;
361
+ padding: 12px 24px;
362
+ background: #fff;
363
+ border-top: 1px solid #e5e7eb;
364
+ }
365
+ .logs-page-btn {
366
+ width: 26px;
367
+ height: 26px;
368
+ padding: 0;
369
+ border: 1px solid #e2e8f0;
370
+ border-radius: 6px;
371
+ background: #fff;
372
+ cursor: pointer;
373
+ color: #475569;
374
+ }
375
+ .logs-page-btn:disabled {
376
+ opacity: 0.4;
377
+ cursor: not-allowed;
378
+ }
379
+ .logs-page-btn:hover:not(:disabled) {
380
+ background: #f1f5f9;
381
+ }
382
+ .logs-page-label {
383
+ padding: 0 6px;
384
+ font-size: 12px;
385
+ color: #64748b;
386
+ }
387
+ .logs-page-sep {
388
+ margin: 0 6px;
389
+ color: #cbd5e1;
390
+ }
391
+ </style>
@@ -0,0 +1,204 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * 监测 tab — 2-column grid of metric cards, each backed by a SparkChart.
4
+ * Matches Snipaste_09-01-41 (全部会话 / 活跃用户数 / 平均会话互动数 / Token 输出速度 /
5
+ * 用户满意度 / 费用消耗).
6
+ */
7
+ import { ref } from 'vue';
8
+
9
+ import type { StudioMonitorMetric } from '../types';
10
+ import SparkChart from './SparkChart.vue';
11
+
12
+ interface Props {
13
+ metrics: StudioMonitorMetric[];
14
+ loading?: boolean;
15
+ }
16
+
17
+ withDefaults(defineProps<Props>(), {
18
+ loading: false,
19
+ });
20
+
21
+ const emit = defineEmits<{
22
+ (e: 'change-period', period: string): void;
23
+ (e: 'refresh'): void;
24
+ }>();
25
+
26
+ const period = ref('过去 7 天');
27
+ </script>
28
+
29
+ <template>
30
+ <div class="mon">
31
+ <header class="mon-top">
32
+ <h2 class="mon-top-title">监测</h2>
33
+ <div class="mon-top-right">
34
+ <select
35
+ class="mon-select"
36
+ :value="period"
37
+ @change="(e) => { period = (e.target as HTMLSelectElement).value; emit('change-period', period); }"
38
+ >
39
+ <option>过去 7 天</option>
40
+ <option>过去 30 天</option>
41
+ <option>过去 90 天</option>
42
+ </select>
43
+ <button
44
+ type="button"
45
+ class="mon-btn"
46
+ @click="emit('refresh')"
47
+ title="刷新"
48
+ >
49
+
50
+ </button>
51
+ </div>
52
+ </header>
53
+
54
+ <div class="mon-body">
55
+ <div v-if="loading" class="mon-loading">加载中...</div>
56
+ <div v-else-if="metrics.length === 0" class="mon-loading">
57
+ 暂无监测数据
58
+ </div>
59
+ <div v-else class="mon-grid">
60
+ <div v-for="m in metrics" :key="m.id" class="mon-card">
61
+ <div class="mon-card-head">
62
+ <div class="mon-card-title">
63
+ {{ m.title }}
64
+ <span class="mon-help" title="过去所选时间段内的指标">ⓘ</span>
65
+ </div>
66
+ <div class="mon-card-period">过去 7 天</div>
67
+ </div>
68
+ <div class="mon-card-value">
69
+ <span class="mon-card-num">{{ m.value }}</span>
70
+ <span v-if="m.unit" class="mon-card-unit">{{ m.unit }}</span>
71
+ </div>
72
+ <div v-if="m.subtitle" class="mon-card-sub">{{ m.subtitle }}</div>
73
+ <div class="mon-card-chart">
74
+ <SparkChart
75
+ :series="m.series"
76
+ :labels="m.labels"
77
+ :color="m.color"
78
+ :height="120"
79
+ />
80
+ </div>
81
+ </div>
82
+ </div>
83
+ </div>
84
+ </div>
85
+ </template>
86
+
87
+ <style scoped>
88
+ .mon {
89
+ display: flex;
90
+ flex-direction: column;
91
+ height: 100%;
92
+ min-height: 0;
93
+ }
94
+ .mon-top {
95
+ display: flex;
96
+ align-items: center;
97
+ justify-content: space-between;
98
+ padding: 12px 24px;
99
+ border-bottom: 1px solid #e5e7eb;
100
+ background: #fff;
101
+ }
102
+ .mon-top-title {
103
+ margin: 0;
104
+ font-size: 15px;
105
+ font-weight: 600;
106
+ color: #0f172a;
107
+ }
108
+ .mon-top-right {
109
+ display: flex;
110
+ align-items: center;
111
+ gap: 6px;
112
+ }
113
+ .mon-select {
114
+ padding: 5px 8px;
115
+ border: 1px solid #e2e8f0;
116
+ border-radius: 6px;
117
+ background: #fff;
118
+ font-size: 12px;
119
+ color: #0f172a;
120
+ outline: none;
121
+ }
122
+ .mon-select:focus {
123
+ border-color: #6366f1;
124
+ }
125
+ .mon-btn {
126
+ width: 28px;
127
+ height: 28px;
128
+ padding: 0;
129
+ border: 1px solid #e2e8f0;
130
+ border-radius: 6px;
131
+ background: #fff;
132
+ color: #475569;
133
+ cursor: pointer;
134
+ }
135
+ .mon-btn:hover {
136
+ background: #f1f5f9;
137
+ }
138
+ .mon-body {
139
+ flex: 1;
140
+ overflow-y: auto;
141
+ padding: 20px 24px;
142
+ background: #f8fafc;
143
+ }
144
+ .mon-loading {
145
+ padding: 60px;
146
+ text-align: center;
147
+ color: #94a3b8;
148
+ font-size: 13px;
149
+ }
150
+ .mon-grid {
151
+ display: grid;
152
+ grid-template-columns: repeat(2, minmax(0, 1fr));
153
+ gap: 16px;
154
+ }
155
+ .mon-card {
156
+ padding: 14px 18px 12px;
157
+ background: #fff;
158
+ border: 1px solid #e5e7eb;
159
+ border-radius: 12px;
160
+ }
161
+ .mon-card-head {
162
+ display: flex;
163
+ align-items: center;
164
+ justify-content: space-between;
165
+ }
166
+ .mon-card-title {
167
+ font-size: 13px;
168
+ color: #334155;
169
+ font-weight: 500;
170
+ }
171
+ .mon-help {
172
+ margin-left: 4px;
173
+ color: #94a3b8;
174
+ font-size: 10px;
175
+ cursor: help;
176
+ }
177
+ .mon-card-period {
178
+ font-size: 10px;
179
+ color: #94a3b8;
180
+ }
181
+ .mon-card-value {
182
+ margin-top: 6px;
183
+ display: flex;
184
+ align-items: baseline;
185
+ gap: 6px;
186
+ }
187
+ .mon-card-num {
188
+ font-size: 26px;
189
+ font-weight: 600;
190
+ color: #0f172a;
191
+ }
192
+ .mon-card-unit {
193
+ font-size: 12px;
194
+ color: #64748b;
195
+ }
196
+ .mon-card-sub {
197
+ margin-top: 2px;
198
+ font-size: 11px;
199
+ color: #94a3b8;
200
+ }
201
+ .mon-card-chart {
202
+ margin-top: 8px;
203
+ }
204
+ </style>