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.
- package/client/api.ts +36 -86
- package/client/components/ChannelsView.vue +28 -208
- package/client/components/GenerateView.vue +306 -38
- package/client/components/HistoryGallery.vue +47 -12
- package/client/components/PresetsView.vue +26 -200
- package/client/components/SettingsView.vue +26 -0
- package/client/components/TasksView.vue +15 -68
- package/client/composables/index.ts +14 -0
- package/client/composables/useDataFetch.ts +102 -0
- package/client/composables/useDialog.ts +58 -0
- package/client/composables/useLoading.ts +84 -0
- package/client/composables/usePagination.ts +110 -0
- package/client/constants/categories.ts +36 -0
- package/client/constants/index.ts +5 -0
- package/client/constants/phases.ts +44 -0
- package/client/styles/shared.css +42 -0
- package/client/types.ts +73 -0
- package/dist/index.js +1 -1
- package/dist/style.css +1 -1
- package/lib/core/api/plugin-api.d.ts.map +1 -1
- package/lib/core/api/plugin-api.js +31 -0
- package/lib/core/api/plugin-api.js.map +1 -1
- package/lib/core/medialuna.service.d.ts.map +1 -1
- package/lib/core/medialuna.service.js +16 -12
- package/lib/core/medialuna.service.js.map +1 -1
- package/lib/core/pipeline/generation-pipeline.d.ts +4 -0
- package/lib/core/pipeline/generation-pipeline.d.ts.map +1 -1
- package/lib/core/pipeline/generation-pipeline.js +61 -20
- package/lib/core/pipeline/generation-pipeline.js.map +1 -1
- package/lib/core/plugin-loader.d.ts +42 -0
- package/lib/core/plugin-loader.d.ts.map +1 -1
- package/lib/core/plugin-loader.js +204 -1
- package/lib/core/plugin-loader.js.map +1 -1
- package/lib/core/request.service.d.ts +4 -1
- package/lib/core/request.service.d.ts.map +1 -1
- package/lib/core/request.service.js +11 -1
- package/lib/core/request.service.js.map +1 -1
- package/lib/core/types.d.ts +10 -0
- package/lib/core/types.d.ts.map +1 -1
- package/lib/plugins/README.md +716 -0
- package/lib/plugins/cache/middleware.d.ts.map +1 -1
- package/lib/plugins/cache/middleware.js +2 -0
- package/lib/plugins/cache/middleware.js.map +1 -1
- package/lib/plugins/task/middleware.d.ts.map +1 -1
- package/lib/plugins/task/middleware.js +20 -2
- package/lib/plugins/task/middleware.js.map +1 -1
- package/lib/types/index.d.ts +4 -0
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -97,9 +97,26 @@
|
|
|
97
97
|
|
|
98
98
|
<!-- 右侧预览区 -->
|
|
99
99
|
<div class="preview-panel">
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
|
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
|
-
<
|
|
149
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
570
|
+
border: 1px solid var(--k-color-border);
|
|
434
571
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
|
435
|
-
transition:
|
|
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
|
-
/*
|
|
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:
|
|
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
|
|
518
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
|
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:
|
|
558
|
-
height:
|
|
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
|
-
|
|
664
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
405
|
-
|
|
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:
|
|
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:
|
|
485
|
-
height:
|
|
486
|
-
background-color: var(--k-color-
|
|
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-
|
|
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.
|
|
516
|
-
opacity: 0.
|
|
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
|
/* 任务信息 */
|