koishi-plugin-media-luna 0.0.4 → 0.0.6

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 (49) hide show
  1. package/client/api.ts +36 -86
  2. package/client/components/ChannelsView.vue +28 -208
  3. package/client/components/GenerateView.vue +306 -38
  4. package/client/components/HistoryGallery.vue +47 -12
  5. package/client/components/PresetsView.vue +26 -200
  6. package/client/components/SettingsView.vue +26 -0
  7. package/client/components/TasksView.vue +15 -68
  8. package/client/composables/index.ts +14 -0
  9. package/client/composables/useDataFetch.ts +102 -0
  10. package/client/composables/useDialog.ts +58 -0
  11. package/client/composables/useLoading.ts +84 -0
  12. package/client/composables/usePagination.ts +110 -0
  13. package/client/constants/categories.ts +36 -0
  14. package/client/constants/index.ts +5 -0
  15. package/client/constants/phases.ts +44 -0
  16. package/client/styles/shared.css +42 -0
  17. package/client/types.ts +73 -0
  18. package/dist/index.js +1 -1
  19. package/dist/style.css +1 -1
  20. package/lib/core/api/plugin-api.d.ts.map +1 -1
  21. package/lib/core/api/plugin-api.js +31 -0
  22. package/lib/core/api/plugin-api.js.map +1 -1
  23. package/lib/core/medialuna.service.d.ts.map +1 -1
  24. package/lib/core/medialuna.service.js +16 -12
  25. package/lib/core/medialuna.service.js.map +1 -1
  26. package/lib/core/pipeline/generation-pipeline.d.ts +4 -0
  27. package/lib/core/pipeline/generation-pipeline.d.ts.map +1 -1
  28. package/lib/core/pipeline/generation-pipeline.js +61 -20
  29. package/lib/core/pipeline/generation-pipeline.js.map +1 -1
  30. package/lib/core/plugin-loader.d.ts +42 -0
  31. package/lib/core/plugin-loader.d.ts.map +1 -1
  32. package/lib/core/plugin-loader.js +204 -1
  33. package/lib/core/plugin-loader.js.map +1 -1
  34. package/lib/core/request.service.d.ts +4 -1
  35. package/lib/core/request.service.d.ts.map +1 -1
  36. package/lib/core/request.service.js +11 -1
  37. package/lib/core/request.service.js.map +1 -1
  38. package/lib/core/types.d.ts +10 -0
  39. package/lib/core/types.d.ts.map +1 -1
  40. package/lib/plugins/README.md +716 -0
  41. package/lib/plugins/cache/middleware.d.ts.map +1 -1
  42. package/lib/plugins/cache/middleware.js +2 -0
  43. package/lib/plugins/cache/middleware.js.map +1 -1
  44. package/lib/plugins/task/middleware.d.ts.map +1 -1
  45. package/lib/plugins/task/middleware.js +20 -2
  46. package/lib/plugins/task/middleware.js.map +1 -1
  47. package/lib/types/index.d.ts +4 -0
  48. package/lib/types/index.d.ts.map +1 -1
  49. package/package.json +1 -1
@@ -97,9 +97,26 @@
97
97
 
98
98
  <!-- 右侧预览区 -->
99
99
  <div class="preview-panel">
100
- <div v-if="result" class="result-container">
101
- <div v-if="result.success" class="success-result">
102
- <div class="output-grid" v-if="result.output && result.output.length">
100
+ <!-- 生成中状态 -->
101
+ <div v-if="generating" class="generating-state">
102
+ <div class="generating-content">
103
+ <div class="loader"></div>
104
+ <div class="generating-info">
105
+ <p class="generating-title">正在生成中...</p>
106
+ <p class="generating-timer">
107
+ <k-icon name="stopwatch"></k-icon>
108
+ 已用时间: {{ formatElapsedTime(elapsedTime) }}
109
+ </p>
110
+ <p class="generating-hint" v-if="currentTaskId">任务 ID: {{ currentTaskId }}</p>
111
+ </div>
112
+ </div>
113
+ </div>
114
+
115
+ <!-- 有结果 -->
116
+ <div v-else-if="result" class="result-container">
117
+ <!-- 成功状态 -->
118
+ <div v-if="result.success && result.output && result.output.length" class="success-result">
119
+ <div class="output-grid">
103
120
  <div v-for="(asset, idx) in result.output" :key="idx" class="output-wrapper">
104
121
  <!-- 图片 -->
105
122
  <template v-if="asset.kind === 'image'">
@@ -136,25 +153,37 @@
136
153
  </div>
137
154
  </div>
138
155
  <div class="result-meta">
156
+ <span class="meta-item success-badge">
157
+ <k-icon name="check-circle"></k-icon> 生成成功
158
+ </span>
139
159
  <span class="meta-item" v-if="result.duration">
140
- <k-icon name="stopwatch"></k-icon> 耗时: {{ (result.duration / 1000).toFixed(2) }}s
160
+ <k-icon name="stopwatch"></k-icon> 耗时: {{ formatElapsedTime(result.duration) }}
141
161
  </span>
142
162
  <span class="meta-item" v-if="result.taskId">
143
163
  <k-icon name="list-alt"></k-icon> 任务 ID: {{ result.taskId }}
144
164
  </span>
145
165
  </div>
146
166
  </div>
167
+
168
+ <!-- 失败状态 -->
147
169
  <div v-else class="error-result">
148
- <k-icon name="warning" class="error-icon"></k-icon>
149
- <div class="error-msg">{{ result.error || '生成失败' }}</div>
170
+ <div class="error-content">
171
+ <k-icon name="exclamation-triangle" class="error-icon"></k-icon>
172
+ <div class="error-info">
173
+ <p class="error-title">生成失败</p>
174
+ <p class="error-msg">{{ result.error || '未知错误' }}</p>
175
+ <p class="error-meta" v-if="result.taskId">任务 ID: {{ result.taskId }}</p>
176
+ <p class="error-meta" v-if="result.duration">耗时: {{ formatElapsedTime(result.duration) }}</p>
177
+ </div>
178
+ </div>
179
+ <k-button class="retry-btn" @click="generate">
180
+ <template #icon><k-icon name="refresh"></k-icon></template>
181
+ 重新生成
182
+ </k-button>
150
183
  </div>
151
184
  </div>
152
185
 
153
- <div v-else-if="generating" class="generating-state">
154
- <div class="loader"></div>
155
- <p>正在生成中,请稍候...</p>
156
- </div>
157
-
186
+ <!-- 空状态 -->
158
187
  <div v-else class="empty-state">
159
188
  <k-icon name="image" class="empty-icon"></k-icon>
160
189
  <p>在左侧配置并点击生成</p>
@@ -178,7 +207,7 @@
178
207
  </template>
179
208
 
180
209
  <script setup lang="ts">
181
- import { ref, onMounted } from 'vue'
210
+ import { ref, onMounted, onUnmounted } from 'vue'
182
211
  import { message } from '@koishijs/client'
183
212
  import { ChannelConfig, PresetData, GenerationResult, ClientFileData } from '../types'
184
213
  import { channelApi, presetApi, generateApi, taskApi } from '../api'
@@ -202,6 +231,12 @@ const fileInput = ref<HTMLInputElement>()
202
231
  const historyGalleryRef = ref<InstanceType<typeof HistoryGallery>>()
203
232
  let fileUid = 0
204
233
 
234
+ // 计时器相关
235
+ const elapsedTime = ref(0)
236
+ const currentTaskId = ref<number | null>(null)
237
+ let timerInterval: ReturnType<typeof setInterval> | null = null
238
+ let startTime = 0
239
+
205
240
  const form = ref({
206
241
  channel: undefined as number | undefined,
207
242
  prompt: '',
@@ -216,6 +251,33 @@ const lightboxImages = ref<string[]>([])
216
251
  const lightboxIndex = ref(0)
217
252
  const lightboxPrompt = ref('') // 存储最终提示词
218
253
 
254
+ // 格式化耗时
255
+ const formatElapsedTime = (ms: number) => {
256
+ if (ms < 1000) return `${ms}ms`
257
+ const seconds = ms / 1000
258
+ if (seconds < 60) return `${seconds.toFixed(1)}s`
259
+ const minutes = Math.floor(seconds / 60)
260
+ const remainingSeconds = (seconds % 60).toFixed(0)
261
+ return `${minutes}m ${remainingSeconds}s`
262
+ }
263
+
264
+ // 开始计时
265
+ const startTimer = () => {
266
+ startTime = Date.now()
267
+ elapsedTime.value = 0
268
+ timerInterval = setInterval(() => {
269
+ elapsedTime.value = Date.now() - startTime
270
+ }, 100)
271
+ }
272
+
273
+ // 停止计时
274
+ const stopTimer = () => {
275
+ if (timerInterval) {
276
+ clearInterval(timerInterval)
277
+ timerInterval = null
278
+ }
279
+ }
280
+
219
281
  // 打开图片预览
220
282
  const openImagePreview = (index: number) => {
221
283
  if (result.value?.output) {
@@ -324,6 +386,38 @@ const removeFile = (index: number) => {
324
386
  }
325
387
  }
326
388
 
389
+ // 尝试通过 taskId 获取结果
390
+ const fetchTaskResult = async (taskId: number): Promise<GenerationResult | null> => {
391
+ try {
392
+ const task = await taskApi.get(taskId)
393
+
394
+ // 获取最终提示词
395
+ lightboxPrompt.value = (task.middlewareLogs as any)?.preset?.transformedPrompt
396
+ || task.requestSnapshot?.prompt
397
+ || ''
398
+
399
+ if (task.status === 'success' && task.responseSnapshot && task.responseSnapshot.length > 0) {
400
+ return {
401
+ success: true,
402
+ output: task.responseSnapshot,
403
+ taskId: task.id,
404
+ duration: task.duration || undefined
405
+ }
406
+ } else if (task.status === 'failed') {
407
+ const errorInfo = (task.middlewareLogs as any)?._error
408
+ return {
409
+ success: false,
410
+ error: errorInfo?.message || '生成失败',
411
+ taskId: task.id,
412
+ duration: task.duration || undefined
413
+ }
414
+ }
415
+ return null
416
+ } catch {
417
+ return null
418
+ }
419
+ }
420
+
327
421
  const generate = async () => {
328
422
  if (!form.value.channel) {
329
423
  message.warning('请选择渠道')
@@ -332,6 +426,8 @@ const generate = async () => {
332
426
 
333
427
  generating.value = true
334
428
  result.value = null
429
+ currentTaskId.value = null
430
+ startTimer()
335
431
 
336
432
  try {
337
433
  const params: any = {
@@ -353,30 +449,64 @@ const generate = async () => {
353
449
  }
354
450
 
355
451
  const res = await generateApi.generate(params)
356
- result.value = res
357
452
 
358
- // 生成成功后刷新历史画廊,并获取最终提示词
453
+ // 更新 taskId
454
+ if (res.taskId) {
455
+ currentTaskId.value = res.taskId
456
+ }
457
+
458
+ // 如果成功,直接使用结果
359
459
  if (res.success) {
460
+ result.value = res
360
461
  historyGalleryRef.value?.refresh()
361
- // 获取任务详情以获得最终提示词
462
+
463
+ // 获取最终提示词
362
464
  if (res.taskId) {
363
465
  try {
364
466
  const task = await taskApi.get(res.taskId)
365
- // 优先使用预设中间件处理后的最终提示词
366
467
  lightboxPrompt.value = (task.middlewareLogs as any)?.preset?.transformedPrompt
367
468
  || task.requestSnapshot?.prompt
368
469
  || ''
369
470
  } catch {
370
- // 如果获取失败,使用输入的提示词
371
471
  lightboxPrompt.value = form.value.prompt
372
472
  }
373
473
  } else {
374
474
  lightboxPrompt.value = form.value.prompt
375
475
  }
476
+ } else {
477
+ // API 返回失败,但可能任务实际成功了,尝试通过 taskId 获取
478
+ if (res.taskId) {
479
+ // 等待一小段时间让后端完成处理
480
+ await new Promise(resolve => setTimeout(resolve, 500))
481
+ const taskResult = await fetchTaskResult(res.taskId)
482
+ if (taskResult && taskResult.success) {
483
+ result.value = taskResult
484
+ historyGalleryRef.value?.refresh()
485
+ } else {
486
+ result.value = res
487
+ }
488
+ } else {
489
+ result.value = res
490
+ }
491
+ }
492
+ } catch (e: any) {
493
+ // 请求异常,尝试通过 taskId 恢复
494
+ if (currentTaskId.value) {
495
+ await new Promise(resolve => setTimeout(resolve, 500))
496
+ const taskResult = await fetchTaskResult(currentTaskId.value)
497
+ if (taskResult) {
498
+ result.value = taskResult
499
+ if (taskResult.success) {
500
+ historyGalleryRef.value?.refresh()
501
+ }
502
+ } else {
503
+ result.value = { success: false, error: e.message || '请求失败' }
504
+ }
505
+ } else {
506
+ result.value = { success: false, error: e.message || '请求失败' }
376
507
  }
377
- } catch (e) {
378
- result.value = { success: false, error: '请求失败' }
379
508
  } finally {
509
+ stopTimer()
380
510
  generating.value = false
381
511
  }
382
512
  }
@@ -391,6 +521,10 @@ const handleHistorySelect = (task: { prompt: string }) => {
391
521
  onMounted(() => {
392
522
  fetchData()
393
523
  })
524
+
525
+ onUnmounted(() => {
526
+ stopTimer()
527
+ })
394
528
  </script>
395
529
 
396
530
  <style scoped>
@@ -406,11 +540,12 @@ onMounted(() => {
406
540
  flex: 1 1 0;
407
541
  min-height: 0;
408
542
  overflow: hidden;
543
+ padding: 0.5rem;
409
544
  }
410
545
 
411
546
  .generate-layout {
412
547
  display: flex;
413
- gap: 20px;
548
+ gap: 1rem;
414
549
  height: 100%;
415
550
  min-height: 0;
416
551
  }
@@ -420,26 +555,33 @@ onMounted(() => {
420
555
  flex-shrink: 0;
421
556
  display: flex;
422
557
  flex-direction: column;
558
+ min-height: 0;
423
559
  }
424
560
 
425
561
  .config-card {
426
562
  padding: 1.5rem;
427
- height: 100%;
563
+ flex: 1 1 0;
564
+ min-height: 0;
428
565
  overflow-y: auto;
429
566
  display: flex;
430
567
  flex-direction: column;
431
568
  border-radius: 12px;
432
569
  background-color: var(--k-card-bg);
433
- border: 1px solid transparent;
570
+ border: 1px solid var(--k-color-border);
434
571
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
435
- transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
572
+ transition: border-color 0.2s, box-shadow 0.2s;
573
+ /* 隐藏式滚动条 */
574
+ scrollbar-width: thin;
575
+ scrollbar-color: transparent transparent;
436
576
  }
437
577
 
438
578
  .config-card:hover {
579
+ border-color: var(--k-color-active);
439
580
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
581
+ scrollbar-color: var(--k-color-border) transparent;
440
582
  }
441
583
 
442
- /* Scrollbar styling for config card */
584
+ /* Webkit 隐藏式滚动条 */
443
585
  .config-card::-webkit-scrollbar {
444
586
  width: 6px;
445
587
  }
@@ -447,8 +589,12 @@ onMounted(() => {
447
589
  background: transparent;
448
590
  }
449
591
  .config-card::-webkit-scrollbar-thumb {
450
- background-color: var(--k-color-border);
592
+ background-color: transparent;
451
593
  border-radius: 3px;
594
+ transition: background-color 0.2s;
595
+ }
596
+ .config-card:hover::-webkit-scrollbar-thumb {
597
+ background-color: var(--k-color-border);
452
598
  }
453
599
 
454
600
  .form-section {
@@ -514,10 +660,12 @@ onMounted(() => {
514
660
  }
515
661
 
516
662
  .preview-panel {
517
- flex-grow: 1;
518
- background-color: var(--k-color-bg-2);
663
+ flex: 1 1 0;
664
+ min-height: 0;
665
+ min-width: 0;
666
+ background-color: var(--k-card-bg);
519
667
  border-radius: 12px;
520
- border: 1px dashed var(--k-color-border);
668
+ border: 1px solid var(--k-color-border);
521
669
  display: flex;
522
670
  flex-direction: column;
523
671
  align-items: center;
@@ -525,15 +673,36 @@ onMounted(() => {
525
673
  padding: 2rem;
526
674
  position: relative;
527
675
  overflow-y: auto;
528
- transition: background-color 0.3s;
676
+ transition: border-color 0.2s, box-shadow 0.2s;
677
+ /* 隐藏式滚动条 */
678
+ scrollbar-width: thin;
679
+ scrollbar-color: transparent transparent;
529
680
  }
530
681
 
531
682
  .preview-panel:hover {
532
- background-color: var(--k-color-bg-1);
683
+ border-color: var(--k-color-active);
684
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
685
+ scrollbar-color: var(--k-color-border) transparent;
686
+ }
687
+
688
+ /* Webkit 隐藏式滚动条 */
689
+ .preview-panel::-webkit-scrollbar {
690
+ width: 6px;
691
+ }
692
+ .preview-panel::-webkit-scrollbar-track {
693
+ background: transparent;
694
+ }
695
+ .preview-panel::-webkit-scrollbar-thumb {
696
+ background-color: transparent;
697
+ border-radius: 3px;
698
+ transition: background-color 0.2s;
699
+ }
700
+ .preview-panel:hover::-webkit-scrollbar-thumb {
701
+ background-color: var(--k-color-border);
533
702
  }
534
703
 
535
704
  /* States */
536
- .empty-state, .generating-state {
705
+ .empty-state {
537
706
  text-align: center;
538
707
  color: var(--k-color-text-description);
539
708
  display: flex;
@@ -550,14 +719,34 @@ onMounted(() => {
550
719
  color: var(--k-color-text);
551
720
  }
552
721
 
722
+ /* 生成中状态 - 增强样式 */
723
+ .generating-state {
724
+ display: flex;
725
+ flex-direction: column;
726
+ align-items: center;
727
+ justify-content: center;
728
+ height: 100%;
729
+ width: 100%;
730
+ }
731
+
732
+ .generating-content {
733
+ display: flex;
734
+ flex-direction: column;
735
+ align-items: center;
736
+ gap: 1.5rem;
737
+ padding: 2rem;
738
+ background: linear-gradient(135deg, rgba(var(--k-color-primary-rgb), 0.05) 0%, rgba(var(--k-color-primary-rgb), 0.02) 100%);
739
+ border-radius: 16px;
740
+ border: 1px solid rgba(var(--k-color-primary-rgb), 0.1);
741
+ }
742
+
553
743
  .loader {
554
744
  border: 4px solid var(--k-color-bg-2);
555
745
  border-top: 4px solid var(--k-color-active);
556
746
  border-radius: 50%;
557
- width: 40px;
558
- height: 40px;
747
+ width: 48px;
748
+ height: 48px;
559
749
  animation: spin 1s linear infinite;
560
- margin: 0 auto 1rem;
561
750
  }
562
751
 
563
752
  @keyframes spin {
@@ -565,6 +754,35 @@ onMounted(() => {
565
754
  100% { transform: rotate(360deg); }
566
755
  }
567
756
 
757
+ .generating-info {
758
+ text-align: center;
759
+ }
760
+
761
+ .generating-title {
762
+ font-size: 1.1rem;
763
+ font-weight: 600;
764
+ color: var(--k-color-text);
765
+ margin: 0 0 0.75rem 0;
766
+ }
767
+
768
+ .generating-timer {
769
+ display: flex;
770
+ align-items: center;
771
+ justify-content: center;
772
+ gap: 0.5rem;
773
+ font-size: 1.5rem;
774
+ font-weight: 700;
775
+ color: var(--k-color-active);
776
+ margin: 0 0 0.5rem 0;
777
+ font-variant-numeric: tabular-nums;
778
+ }
779
+
780
+ .generating-hint {
781
+ font-size: 0.85rem;
782
+ color: var(--k-color-text-description);
783
+ margin: 0;
784
+ }
785
+
568
786
  /* Result */
569
787
  .result-container {
570
788
  width: 100%;
@@ -647,6 +865,7 @@ onMounted(() => {
647
865
  margin-top: 1.5rem;
648
866
  display: flex;
649
867
  gap: 1.5rem;
868
+ flex-wrap: wrap;
650
869
  color: var(--k-color-text-description);
651
870
  font-size: 0.9rem;
652
871
  border-top: 1px solid var(--k-color-border);
@@ -659,14 +878,63 @@ onMounted(() => {
659
878
  gap: 0.5rem;
660
879
  }
661
880
 
881
+ .success-badge {
882
+ color: var(--k-color-success, #67c23a);
883
+ font-weight: 600;
884
+ }
885
+
886
+ /* 错误状态 - 增强样式 */
662
887
  .error-result {
663
- text-align: center;
664
- color: var(--k-color-error);
888
+ display: flex;
889
+ flex-direction: column;
890
+ align-items: center;
891
+ justify-content: center;
892
+ height: 100%;
893
+ gap: 1.5rem;
894
+ }
895
+
896
+ .error-content {
897
+ display: flex;
898
+ flex-direction: column;
899
+ align-items: center;
900
+ gap: 1rem;
901
+ padding: 2rem;
902
+ background: linear-gradient(135deg, rgba(245, 108, 108, 0.08) 0%, rgba(245, 108, 108, 0.02) 100%);
903
+ border-radius: 16px;
904
+ border: 1px solid rgba(245, 108, 108, 0.2);
665
905
  }
666
906
 
667
907
  .error-icon {
668
908
  font-size: 3rem;
669
- margin-bottom: 1rem;
909
+ color: var(--k-color-error, #f56c6c);
910
+ }
911
+
912
+ .error-info {
913
+ text-align: center;
914
+ }
915
+
916
+ .error-title {
917
+ font-size: 1.1rem;
918
+ font-weight: 600;
919
+ color: var(--k-color-error, #f56c6c);
920
+ margin: 0 0 0.5rem 0;
921
+ }
922
+
923
+ .error-msg {
924
+ color: var(--k-color-text);
925
+ margin: 0 0 0.5rem 0;
926
+ max-width: 400px;
927
+ word-break: break-word;
928
+ }
929
+
930
+ .error-meta {
931
+ font-size: 0.85rem;
932
+ color: var(--k-color-text-description);
933
+ margin: 0;
934
+ }
935
+
936
+ .retry-btn {
937
+ margin-top: 0.5rem;
670
938
  }
671
939
 
672
940
  /* Upload Area */
@@ -775,4 +1043,4 @@ onMounted(() => {
775
1043
  .file-link:hover {
776
1044
  background-color: var(--k-color-bg-3);
777
1045
  }
778
- </style>
1046
+ </style>
@@ -292,15 +292,20 @@ onUnmounted(() => {
292
292
  <style scoped>
293
293
  .history-gallery {
294
294
  width: 240px;
295
- max-height: 100%;
296
295
  flex-shrink: 0;
296
+ min-height: 0;
297
297
  display: flex;
298
298
  flex-direction: column;
299
299
  background-color: var(--k-card-bg);
300
300
  border-radius: 12px;
301
301
  border: 1px solid var(--k-color-border);
302
302
  overflow: hidden;
303
- transition: width 0.3s ease;
303
+ transition: border-color 0.2s, box-shadow 0.2s;
304
+ }
305
+
306
+ .history-gallery:not(.collapsed):hover {
307
+ border-color: var(--k-color-active);
308
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
304
309
  }
305
310
 
306
311
  .history-gallery.collapsed {
@@ -401,13 +406,20 @@ onUnmounted(() => {
401
406
 
402
407
  /* 任务列表 */
403
408
  .gallery-list {
404
- flex: 1 1 auto;
405
- max-height: calc(100vh - 200px);
409
+ flex: 1 1 0;
410
+ min-height: 0;
406
411
  overflow-y: auto;
407
412
  padding: 0.5rem;
408
413
  display: flex;
409
414
  flex-direction: column;
410
415
  gap: 0.5rem;
416
+ /* 隐藏式滚动条 */
417
+ scrollbar-width: thin;
418
+ scrollbar-color: transparent transparent;
419
+ }
420
+
421
+ .gallery-list:hover {
422
+ scrollbar-color: var(--k-color-border) transparent;
411
423
  }
412
424
 
413
425
  .gallery-list::-webkit-scrollbar {
@@ -419,8 +431,13 @@ onUnmounted(() => {
419
431
  }
420
432
 
421
433
  .gallery-list::-webkit-scrollbar-thumb {
422
- background-color: var(--k-color-border);
434
+ background-color: transparent;
423
435
  border-radius: 2px;
436
+ transition: background-color 0.2s;
437
+ }
438
+
439
+ .gallery-list:hover::-webkit-scrollbar-thumb {
440
+ background-color: var(--k-color-border);
424
441
  }
425
442
 
426
443
  /* 任务卡片 */
@@ -431,6 +448,7 @@ onUnmounted(() => {
431
448
  overflow: hidden;
432
449
  transition: all 0.2s;
433
450
  position: relative;
451
+ border: 1px solid var(--k-color-border);
434
452
  }
435
453
 
436
454
  .task-card.clickable {
@@ -440,6 +458,19 @@ onUnmounted(() => {
440
458
  .task-card.clickable:hover {
441
459
  background-color: var(--k-color-bg-1);
442
460
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
461
+ border-color: var(--k-color-active);
462
+ }
463
+
464
+ /* 处理中状态的卡片 */
465
+ .task-card:has(.task-processing) {
466
+ border-color: var(--k-color-warning, #e6a23c);
467
+ background-color: rgba(230, 162, 60, 0.05);
468
+ }
469
+
470
+ /* 失败状态的卡片 */
471
+ .task-card:has(.task-failed) {
472
+ border-color: var(--k-color-error, #f56c6c);
473
+ background-color: rgba(245, 108, 108, 0.05);
443
474
  }
444
475
 
445
476
  /* 图片区域 - 单张图片,完整显示不裁剪 */
@@ -473,6 +504,7 @@ onUnmounted(() => {
473
504
  flex-direction: column;
474
505
  align-items: center;
475
506
  gap: 0.5rem;
507
+ background: linear-gradient(135deg, rgba(230, 162, 60, 0.08) 0%, rgba(230, 162, 60, 0.02) 100%);
476
508
  }
477
509
 
478
510
  .processing-animation {
@@ -481,9 +513,9 @@ onUnmounted(() => {
481
513
  }
482
514
 
483
515
  .processing-animation .dot {
484
- width: 6px;
485
- height: 6px;
486
- background-color: var(--k-color-active);
516
+ width: 8px;
517
+ height: 8px;
518
+ background-color: var(--k-color-warning, #e6a23c);
487
519
  border-radius: 50%;
488
520
  animation: bounce 1.4s ease-in-out infinite both;
489
521
  }
@@ -498,7 +530,8 @@ onUnmounted(() => {
498
530
 
499
531
  .processing-text {
500
532
  font-size: 0.75rem;
501
- color: var(--k-color-text-description);
533
+ color: var(--k-color-warning, #e6a23c);
534
+ font-weight: 500;
502
535
  }
503
536
 
504
537
  /* 失败状态 */
@@ -508,16 +541,18 @@ onUnmounted(() => {
508
541
  flex-direction: column;
509
542
  align-items: center;
510
543
  gap: 0.5rem;
511
- color: var(--k-color-error);
544
+ color: var(--k-color-error, #f56c6c);
545
+ background: linear-gradient(135deg, rgba(245, 108, 108, 0.08) 0%, rgba(245, 108, 108, 0.02) 100%);
512
546
  }
513
547
 
514
548
  .failed-icon {
515
- font-size: 1.25rem;
516
- opacity: 0.7;
549
+ font-size: 1.5rem;
550
+ opacity: 0.8;
517
551
  }
518
552
 
519
553
  .task-failed span {
520
554
  font-size: 0.75rem;
555
+ font-weight: 500;
521
556
  }
522
557
 
523
558
  /* 任务信息 */