koishi-plugin-media-luna 0.0.5 → 0.0.7
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/components/GenerateView.vue +294 -62
- package/client/components/PresetPicker.vue +738 -0
- package/dist/index.js +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
|
@@ -24,14 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
<div class="form-item">
|
|
26
26
|
<div class="label">预设模板</div>
|
|
27
|
-
<
|
|
28
|
-
<el-option
|
|
29
|
-
v-for="preset in presets"
|
|
30
|
-
:key="preset.id"
|
|
31
|
-
:label="preset.name"
|
|
32
|
-
:value="preset.id"
|
|
33
|
-
/>
|
|
34
|
-
</el-select>
|
|
27
|
+
<PresetPicker v-model="presetId" :presets="presets" />
|
|
35
28
|
</div>
|
|
36
29
|
</div>
|
|
37
30
|
|
|
@@ -97,9 +90,26 @@
|
|
|
97
90
|
|
|
98
91
|
<!-- 右侧预览区 -->
|
|
99
92
|
<div class="preview-panel">
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
93
|
+
<!-- 生成中状态 -->
|
|
94
|
+
<div v-if="generating" class="generating-state">
|
|
95
|
+
<div class="generating-content">
|
|
96
|
+
<div class="loader"></div>
|
|
97
|
+
<div class="generating-info">
|
|
98
|
+
<p class="generating-title">正在生成中...</p>
|
|
99
|
+
<p class="generating-timer">
|
|
100
|
+
<k-icon name="stopwatch"></k-icon>
|
|
101
|
+
已用时间: {{ formatElapsedTime(elapsedTime) }}
|
|
102
|
+
</p>
|
|
103
|
+
<p class="generating-hint" v-if="currentTaskId">任务 ID: {{ currentTaskId }}</p>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<!-- 有结果 -->
|
|
109
|
+
<div v-else-if="result" class="result-container">
|
|
110
|
+
<!-- 成功状态 -->
|
|
111
|
+
<div v-if="result.success && result.output && result.output.length" class="success-result">
|
|
112
|
+
<div class="output-grid">
|
|
103
113
|
<div v-for="(asset, idx) in result.output" :key="idx" class="output-wrapper">
|
|
104
114
|
<!-- 图片 -->
|
|
105
115
|
<template v-if="asset.kind === 'image'">
|
|
@@ -136,25 +146,37 @@
|
|
|
136
146
|
</div>
|
|
137
147
|
</div>
|
|
138
148
|
<div class="result-meta">
|
|
149
|
+
<span class="meta-item success-badge">
|
|
150
|
+
<k-icon name="check-circle"></k-icon> 生成成功
|
|
151
|
+
</span>
|
|
139
152
|
<span class="meta-item" v-if="result.duration">
|
|
140
|
-
<k-icon name="stopwatch"></k-icon> 耗时: {{ (result.duration
|
|
153
|
+
<k-icon name="stopwatch"></k-icon> 耗时: {{ formatElapsedTime(result.duration) }}
|
|
141
154
|
</span>
|
|
142
155
|
<span class="meta-item" v-if="result.taskId">
|
|
143
156
|
<k-icon name="list-alt"></k-icon> 任务 ID: {{ result.taskId }}
|
|
144
157
|
</span>
|
|
145
158
|
</div>
|
|
146
159
|
</div>
|
|
160
|
+
|
|
161
|
+
<!-- 失败状态 -->
|
|
147
162
|
<div v-else class="error-result">
|
|
148
|
-
<
|
|
149
|
-
|
|
163
|
+
<div class="error-content">
|
|
164
|
+
<k-icon name="exclamation-triangle" class="error-icon"></k-icon>
|
|
165
|
+
<div class="error-info">
|
|
166
|
+
<p class="error-title">生成失败</p>
|
|
167
|
+
<p class="error-msg">{{ result.error || '未知错误' }}</p>
|
|
168
|
+
<p class="error-meta" v-if="result.taskId">任务 ID: {{ result.taskId }}</p>
|
|
169
|
+
<p class="error-meta" v-if="result.duration">耗时: {{ formatElapsedTime(result.duration) }}</p>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
<k-button class="retry-btn" @click="generate">
|
|
173
|
+
<template #icon><k-icon name="refresh"></k-icon></template>
|
|
174
|
+
重新生成
|
|
175
|
+
</k-button>
|
|
150
176
|
</div>
|
|
151
177
|
</div>
|
|
152
178
|
|
|
153
|
-
|
|
154
|
-
<div class="loader"></div>
|
|
155
|
-
<p>正在生成中,请稍候...</p>
|
|
156
|
-
</div>
|
|
157
|
-
|
|
179
|
+
<!-- 空状态 -->
|
|
158
180
|
<div v-else class="empty-state">
|
|
159
181
|
<k-icon name="image" class="empty-icon"></k-icon>
|
|
160
182
|
<p>在左侧配置并点击生成</p>
|
|
@@ -178,12 +200,13 @@
|
|
|
178
200
|
</template>
|
|
179
201
|
|
|
180
202
|
<script setup lang="ts">
|
|
181
|
-
import { ref, onMounted } from 'vue'
|
|
203
|
+
import { ref, onMounted, onUnmounted } from 'vue'
|
|
182
204
|
import { message } from '@koishijs/client'
|
|
183
205
|
import { ChannelConfig, PresetData, GenerationResult, ClientFileData } from '../types'
|
|
184
206
|
import { channelApi, presetApi, generateApi, taskApi } from '../api'
|
|
185
207
|
import HistoryGallery from './HistoryGallery.vue'
|
|
186
208
|
import ImageLightbox from './ImageLightbox.vue'
|
|
209
|
+
import PresetPicker from './PresetPicker.vue'
|
|
187
210
|
|
|
188
211
|
/** 本地文件项 */
|
|
189
212
|
interface LocalFile {
|
|
@@ -202,6 +225,12 @@ const fileInput = ref<HTMLInputElement>()
|
|
|
202
225
|
const historyGalleryRef = ref<InstanceType<typeof HistoryGallery>>()
|
|
203
226
|
let fileUid = 0
|
|
204
227
|
|
|
228
|
+
// 计时器相关
|
|
229
|
+
const elapsedTime = ref(0)
|
|
230
|
+
const currentTaskId = ref<number | null>(null)
|
|
231
|
+
let timerInterval: ReturnType<typeof setInterval> | null = null
|
|
232
|
+
let startTime = 0
|
|
233
|
+
|
|
205
234
|
const form = ref({
|
|
206
235
|
channel: undefined as number | undefined,
|
|
207
236
|
prompt: '',
|
|
@@ -216,6 +245,33 @@ const lightboxImages = ref<string[]>([])
|
|
|
216
245
|
const lightboxIndex = ref(0)
|
|
217
246
|
const lightboxPrompt = ref('') // 存储最终提示词
|
|
218
247
|
|
|
248
|
+
// 格式化耗时
|
|
249
|
+
const formatElapsedTime = (ms: number) => {
|
|
250
|
+
if (ms < 1000) return `${ms}ms`
|
|
251
|
+
const seconds = ms / 1000
|
|
252
|
+
if (seconds < 60) return `${seconds.toFixed(1)}s`
|
|
253
|
+
const minutes = Math.floor(seconds / 60)
|
|
254
|
+
const remainingSeconds = (seconds % 60).toFixed(0)
|
|
255
|
+
return `${minutes}m ${remainingSeconds}s`
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// 开始计时
|
|
259
|
+
const startTimer = () => {
|
|
260
|
+
startTime = Date.now()
|
|
261
|
+
elapsedTime.value = 0
|
|
262
|
+
timerInterval = setInterval(() => {
|
|
263
|
+
elapsedTime.value = Date.now() - startTime
|
|
264
|
+
}, 100)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// 停止计时
|
|
268
|
+
const stopTimer = () => {
|
|
269
|
+
if (timerInterval) {
|
|
270
|
+
clearInterval(timerInterval)
|
|
271
|
+
timerInterval = null
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
219
275
|
// 打开图片预览
|
|
220
276
|
const openImagePreview = (index: number) => {
|
|
221
277
|
if (result.value?.output) {
|
|
@@ -241,12 +297,6 @@ const fetchData = async () => {
|
|
|
241
297
|
}
|
|
242
298
|
}
|
|
243
299
|
|
|
244
|
-
const applyPreset = () => {
|
|
245
|
-
// Logic to pre-fill prompt or params based on preset could go here
|
|
246
|
-
// But mostly the backend handles preset logic via ID or name
|
|
247
|
-
// The API 'media-luna/generate' accepts parameters.preset name
|
|
248
|
-
}
|
|
249
|
-
|
|
250
300
|
// 文件转 base64
|
|
251
301
|
const fileToBase64 = (file: File): Promise<string> => {
|
|
252
302
|
return new Promise((resolve, reject) => {
|
|
@@ -324,6 +374,38 @@ const removeFile = (index: number) => {
|
|
|
324
374
|
}
|
|
325
375
|
}
|
|
326
376
|
|
|
377
|
+
// 尝试通过 taskId 获取结果
|
|
378
|
+
const fetchTaskResult = async (taskId: number): Promise<GenerationResult | null> => {
|
|
379
|
+
try {
|
|
380
|
+
const task = await taskApi.get(taskId)
|
|
381
|
+
|
|
382
|
+
// 获取最终提示词
|
|
383
|
+
lightboxPrompt.value = (task.middlewareLogs as any)?.preset?.transformedPrompt
|
|
384
|
+
|| task.requestSnapshot?.prompt
|
|
385
|
+
|| ''
|
|
386
|
+
|
|
387
|
+
if (task.status === 'success' && task.responseSnapshot && task.responseSnapshot.length > 0) {
|
|
388
|
+
return {
|
|
389
|
+
success: true,
|
|
390
|
+
output: task.responseSnapshot,
|
|
391
|
+
taskId: task.id,
|
|
392
|
+
duration: task.duration || undefined
|
|
393
|
+
}
|
|
394
|
+
} else if (task.status === 'failed') {
|
|
395
|
+
const errorInfo = (task.middlewareLogs as any)?._error
|
|
396
|
+
return {
|
|
397
|
+
success: false,
|
|
398
|
+
error: errorInfo?.message || '生成失败',
|
|
399
|
+
taskId: task.id,
|
|
400
|
+
duration: task.duration || undefined
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return null
|
|
404
|
+
} catch {
|
|
405
|
+
return null
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
327
409
|
const generate = async () => {
|
|
328
410
|
if (!form.value.channel) {
|
|
329
411
|
message.warning('请选择渠道')
|
|
@@ -332,6 +414,8 @@ const generate = async () => {
|
|
|
332
414
|
|
|
333
415
|
generating.value = true
|
|
334
416
|
result.value = null
|
|
417
|
+
currentTaskId.value = null
|
|
418
|
+
startTimer()
|
|
335
419
|
|
|
336
420
|
try {
|
|
337
421
|
const params: any = {
|
|
@@ -353,30 +437,64 @@ const generate = async () => {
|
|
|
353
437
|
}
|
|
354
438
|
|
|
355
439
|
const res = await generateApi.generate(params)
|
|
356
|
-
result.value = res
|
|
357
440
|
|
|
358
|
-
//
|
|
441
|
+
// 更新 taskId
|
|
442
|
+
if (res.taskId) {
|
|
443
|
+
currentTaskId.value = res.taskId
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// 如果成功,直接使用结果
|
|
359
447
|
if (res.success) {
|
|
448
|
+
result.value = res
|
|
360
449
|
historyGalleryRef.value?.refresh()
|
|
361
|
-
|
|
450
|
+
|
|
451
|
+
// 获取最终提示词
|
|
362
452
|
if (res.taskId) {
|
|
363
453
|
try {
|
|
364
454
|
const task = await taskApi.get(res.taskId)
|
|
365
|
-
// 优先使用预设中间件处理后的最终提示词
|
|
366
455
|
lightboxPrompt.value = (task.middlewareLogs as any)?.preset?.transformedPrompt
|
|
367
456
|
|| task.requestSnapshot?.prompt
|
|
368
457
|
|| ''
|
|
369
458
|
} catch {
|
|
370
|
-
// 如果获取失败,使用输入的提示词
|
|
371
459
|
lightboxPrompt.value = form.value.prompt
|
|
372
460
|
}
|
|
373
461
|
} else {
|
|
374
462
|
lightboxPrompt.value = form.value.prompt
|
|
375
463
|
}
|
|
464
|
+
} else {
|
|
465
|
+
// API 返回失败,但可能任务实际成功了,尝试通过 taskId 获取
|
|
466
|
+
if (res.taskId) {
|
|
467
|
+
// 等待一小段时间让后端完成处理
|
|
468
|
+
await new Promise(resolve => setTimeout(resolve, 500))
|
|
469
|
+
const taskResult = await fetchTaskResult(res.taskId)
|
|
470
|
+
if (taskResult && taskResult.success) {
|
|
471
|
+
result.value = taskResult
|
|
472
|
+
historyGalleryRef.value?.refresh()
|
|
473
|
+
} else {
|
|
474
|
+
result.value = res
|
|
475
|
+
}
|
|
476
|
+
} else {
|
|
477
|
+
result.value = res
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
} catch (e: any) {
|
|
481
|
+
// 请求异常,尝试通过 taskId 恢复
|
|
482
|
+
if (currentTaskId.value) {
|
|
483
|
+
await new Promise(resolve => setTimeout(resolve, 500))
|
|
484
|
+
const taskResult = await fetchTaskResult(currentTaskId.value)
|
|
485
|
+
if (taskResult) {
|
|
486
|
+
result.value = taskResult
|
|
487
|
+
if (taskResult.success) {
|
|
488
|
+
historyGalleryRef.value?.refresh()
|
|
489
|
+
}
|
|
490
|
+
} else {
|
|
491
|
+
result.value = { success: false, error: e.message || '请求失败' }
|
|
492
|
+
}
|
|
493
|
+
} else {
|
|
494
|
+
result.value = { success: false, error: e.message || '请求失败' }
|
|
376
495
|
}
|
|
377
|
-
} catch (e) {
|
|
378
|
-
result.value = { success: false, error: '请求失败' }
|
|
379
496
|
} finally {
|
|
497
|
+
stopTimer()
|
|
380
498
|
generating.value = false
|
|
381
499
|
}
|
|
382
500
|
}
|
|
@@ -391,6 +509,10 @@ const handleHistorySelect = (task: { prompt: string }) => {
|
|
|
391
509
|
onMounted(() => {
|
|
392
510
|
fetchData()
|
|
393
511
|
})
|
|
512
|
+
|
|
513
|
+
onUnmounted(() => {
|
|
514
|
+
stopTimer()
|
|
515
|
+
})
|
|
394
516
|
</script>
|
|
395
517
|
|
|
396
518
|
<style scoped>
|
|
@@ -417,7 +539,7 @@ onMounted(() => {
|
|
|
417
539
|
}
|
|
418
540
|
|
|
419
541
|
.config-panel {
|
|
420
|
-
width:
|
|
542
|
+
width: 320px;
|
|
421
543
|
flex-shrink: 0;
|
|
422
544
|
display: flex;
|
|
423
545
|
flex-direction: column;
|
|
@@ -425,7 +547,7 @@ onMounted(() => {
|
|
|
425
547
|
}
|
|
426
548
|
|
|
427
549
|
.config-card {
|
|
428
|
-
padding: 1.
|
|
550
|
+
padding: 1.25rem;
|
|
429
551
|
flex: 1 1 0;
|
|
430
552
|
min-height: 0;
|
|
431
553
|
overflow-y: auto;
|
|
@@ -464,7 +586,11 @@ onMounted(() => {
|
|
|
464
586
|
}
|
|
465
587
|
|
|
466
588
|
.form-section {
|
|
467
|
-
margin-bottom: 1.
|
|
589
|
+
margin-bottom: 1.25rem;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.form-section:last-of-type {
|
|
593
|
+
margin-bottom: 0;
|
|
468
594
|
}
|
|
469
595
|
|
|
470
596
|
.form-section.flex-grow {
|
|
@@ -483,12 +609,12 @@ onMounted(() => {
|
|
|
483
609
|
|
|
484
610
|
.section-title {
|
|
485
611
|
font-weight: 600;
|
|
486
|
-
margin-bottom: 0.
|
|
612
|
+
margin-bottom: 0.5rem;
|
|
487
613
|
color: var(--k-color-text);
|
|
488
|
-
font-size: 0.
|
|
614
|
+
font-size: 0.9rem;
|
|
489
615
|
display: flex;
|
|
490
616
|
align-items: center;
|
|
491
|
-
gap: 0.
|
|
617
|
+
gap: 0.4rem;
|
|
492
618
|
}
|
|
493
619
|
|
|
494
620
|
.section-title .k-icon {
|
|
@@ -496,23 +622,29 @@ onMounted(() => {
|
|
|
496
622
|
}
|
|
497
623
|
|
|
498
624
|
.form-item {
|
|
499
|
-
margin-bottom:
|
|
625
|
+
margin-bottom: 0.75rem;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.form-item:last-child {
|
|
629
|
+
margin-bottom: 0;
|
|
500
630
|
}
|
|
501
631
|
|
|
502
632
|
.label {
|
|
503
|
-
font-size: 0.
|
|
633
|
+
font-size: 0.8rem;
|
|
504
634
|
color: var(--k-color-text-description);
|
|
505
|
-
margin-bottom: 0.
|
|
635
|
+
margin-bottom: 0.25rem;
|
|
506
636
|
}
|
|
507
637
|
|
|
508
638
|
.form-actions {
|
|
509
|
-
margin-top: 1.
|
|
639
|
+
margin-top: 1.25rem;
|
|
640
|
+
padding-top: 1rem;
|
|
641
|
+
border-top: 1px solid var(--k-color-border);
|
|
510
642
|
}
|
|
511
643
|
|
|
512
644
|
.generate-btn {
|
|
513
645
|
width: 100%;
|
|
514
|
-
height:
|
|
515
|
-
font-size:
|
|
646
|
+
height: 40px;
|
|
647
|
+
font-size: 0.95rem;
|
|
516
648
|
font-weight: 600;
|
|
517
649
|
transition: all 0.2s;
|
|
518
650
|
background: linear-gradient(135deg, var(--k-color-primary) 0%, var(--k-color-primary-dark, var(--k-color-primary)) 100%);
|
|
@@ -568,7 +700,7 @@ onMounted(() => {
|
|
|
568
700
|
}
|
|
569
701
|
|
|
570
702
|
/* States */
|
|
571
|
-
.empty-state
|
|
703
|
+
.empty-state {
|
|
572
704
|
text-align: center;
|
|
573
705
|
color: var(--k-color-text-description);
|
|
574
706
|
display: flex;
|
|
@@ -585,14 +717,34 @@ onMounted(() => {
|
|
|
585
717
|
color: var(--k-color-text);
|
|
586
718
|
}
|
|
587
719
|
|
|
720
|
+
/* 生成中状态 - 增强样式 */
|
|
721
|
+
.generating-state {
|
|
722
|
+
display: flex;
|
|
723
|
+
flex-direction: column;
|
|
724
|
+
align-items: center;
|
|
725
|
+
justify-content: center;
|
|
726
|
+
height: 100%;
|
|
727
|
+
width: 100%;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
.generating-content {
|
|
731
|
+
display: flex;
|
|
732
|
+
flex-direction: column;
|
|
733
|
+
align-items: center;
|
|
734
|
+
gap: 1.5rem;
|
|
735
|
+
padding: 2rem;
|
|
736
|
+
background: linear-gradient(135deg, rgba(var(--k-color-primary-rgb), 0.05) 0%, rgba(var(--k-color-primary-rgb), 0.02) 100%);
|
|
737
|
+
border-radius: 16px;
|
|
738
|
+
border: 1px solid rgba(var(--k-color-primary-rgb), 0.1);
|
|
739
|
+
}
|
|
740
|
+
|
|
588
741
|
.loader {
|
|
589
742
|
border: 4px solid var(--k-color-bg-2);
|
|
590
743
|
border-top: 4px solid var(--k-color-active);
|
|
591
744
|
border-radius: 50%;
|
|
592
|
-
width:
|
|
593
|
-
height:
|
|
745
|
+
width: 48px;
|
|
746
|
+
height: 48px;
|
|
594
747
|
animation: spin 1s linear infinite;
|
|
595
|
-
margin: 0 auto 1rem;
|
|
596
748
|
}
|
|
597
749
|
|
|
598
750
|
@keyframes spin {
|
|
@@ -600,6 +752,35 @@ onMounted(() => {
|
|
|
600
752
|
100% { transform: rotate(360deg); }
|
|
601
753
|
}
|
|
602
754
|
|
|
755
|
+
.generating-info {
|
|
756
|
+
text-align: center;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
.generating-title {
|
|
760
|
+
font-size: 1.1rem;
|
|
761
|
+
font-weight: 600;
|
|
762
|
+
color: var(--k-color-text);
|
|
763
|
+
margin: 0 0 0.75rem 0;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
.generating-timer {
|
|
767
|
+
display: flex;
|
|
768
|
+
align-items: center;
|
|
769
|
+
justify-content: center;
|
|
770
|
+
gap: 0.5rem;
|
|
771
|
+
font-size: 1.5rem;
|
|
772
|
+
font-weight: 700;
|
|
773
|
+
color: var(--k-color-active);
|
|
774
|
+
margin: 0 0 0.5rem 0;
|
|
775
|
+
font-variant-numeric: tabular-nums;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
.generating-hint {
|
|
779
|
+
font-size: 0.85rem;
|
|
780
|
+
color: var(--k-color-text-description);
|
|
781
|
+
margin: 0;
|
|
782
|
+
}
|
|
783
|
+
|
|
603
784
|
/* Result */
|
|
604
785
|
.result-container {
|
|
605
786
|
width: 100%;
|
|
@@ -682,6 +863,7 @@ onMounted(() => {
|
|
|
682
863
|
margin-top: 1.5rem;
|
|
683
864
|
display: flex;
|
|
684
865
|
gap: 1.5rem;
|
|
866
|
+
flex-wrap: wrap;
|
|
685
867
|
color: var(--k-color-text-description);
|
|
686
868
|
font-size: 0.9rem;
|
|
687
869
|
border-top: 1px solid var(--k-color-border);
|
|
@@ -694,19 +876,68 @@ onMounted(() => {
|
|
|
694
876
|
gap: 0.5rem;
|
|
695
877
|
}
|
|
696
878
|
|
|
879
|
+
.success-badge {
|
|
880
|
+
color: var(--k-color-success, #67c23a);
|
|
881
|
+
font-weight: 600;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/* 错误状态 - 增强样式 */
|
|
697
885
|
.error-result {
|
|
698
|
-
|
|
699
|
-
|
|
886
|
+
display: flex;
|
|
887
|
+
flex-direction: column;
|
|
888
|
+
align-items: center;
|
|
889
|
+
justify-content: center;
|
|
890
|
+
height: 100%;
|
|
891
|
+
gap: 1.5rem;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
.error-content {
|
|
895
|
+
display: flex;
|
|
896
|
+
flex-direction: column;
|
|
897
|
+
align-items: center;
|
|
898
|
+
gap: 1rem;
|
|
899
|
+
padding: 2rem;
|
|
900
|
+
background: linear-gradient(135deg, rgba(245, 108, 108, 0.08) 0%, rgba(245, 108, 108, 0.02) 100%);
|
|
901
|
+
border-radius: 16px;
|
|
902
|
+
border: 1px solid rgba(245, 108, 108, 0.2);
|
|
700
903
|
}
|
|
701
904
|
|
|
702
905
|
.error-icon {
|
|
703
906
|
font-size: 3rem;
|
|
704
|
-
|
|
907
|
+
color: var(--k-color-error, #f56c6c);
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
.error-info {
|
|
911
|
+
text-align: center;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
.error-title {
|
|
915
|
+
font-size: 1.1rem;
|
|
916
|
+
font-weight: 600;
|
|
917
|
+
color: var(--k-color-error, #f56c6c);
|
|
918
|
+
margin: 0 0 0.5rem 0;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
.error-msg {
|
|
922
|
+
color: var(--k-color-text);
|
|
923
|
+
margin: 0 0 0.5rem 0;
|
|
924
|
+
max-width: 400px;
|
|
925
|
+
word-break: break-word;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
.error-meta {
|
|
929
|
+
font-size: 0.85rem;
|
|
930
|
+
color: var(--k-color-text-description);
|
|
931
|
+
margin: 0;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
.retry-btn {
|
|
935
|
+
margin-top: 0.5rem;
|
|
705
936
|
}
|
|
706
937
|
|
|
707
938
|
/* Upload Area */
|
|
708
939
|
.upload-area {
|
|
709
|
-
margin-top: 0.
|
|
940
|
+
margin-top: 0.25rem;
|
|
710
941
|
}
|
|
711
942
|
|
|
712
943
|
.upload-list {
|
|
@@ -718,9 +949,9 @@ onMounted(() => {
|
|
|
718
949
|
|
|
719
950
|
.upload-item {
|
|
720
951
|
position: relative;
|
|
721
|
-
width:
|
|
722
|
-
height:
|
|
723
|
-
border-radius:
|
|
952
|
+
width: 56px;
|
|
953
|
+
height: 56px;
|
|
954
|
+
border-radius: 6px;
|
|
724
955
|
overflow: hidden;
|
|
725
956
|
border: 1px solid var(--k-color-border);
|
|
726
957
|
background-color: var(--k-color-bg-2);
|
|
@@ -750,10 +981,10 @@ onMounted(() => {
|
|
|
750
981
|
}
|
|
751
982
|
|
|
752
983
|
.upload-trigger {
|
|
753
|
-
width:
|
|
754
|
-
height:
|
|
984
|
+
width: 56px;
|
|
985
|
+
height: 56px;
|
|
755
986
|
border: 2px dashed var(--k-color-border);
|
|
756
|
-
border-radius:
|
|
987
|
+
border-radius: 6px;
|
|
757
988
|
display: flex;
|
|
758
989
|
align-items: center;
|
|
759
990
|
justify-content: center;
|
|
@@ -778,9 +1009,10 @@ onMounted(() => {
|
|
|
778
1009
|
}
|
|
779
1010
|
|
|
780
1011
|
.upload-tip {
|
|
781
|
-
font-size: 0.
|
|
1012
|
+
font-size: 0.7rem;
|
|
782
1013
|
color: var(--k-color-text-description);
|
|
783
|
-
margin-top: 0.
|
|
1014
|
+
margin-top: 0.35rem;
|
|
1015
|
+
opacity: 0.8;
|
|
784
1016
|
}
|
|
785
1017
|
|
|
786
1018
|
/* Text output */
|
|
@@ -810,4 +1042,4 @@ onMounted(() => {
|
|
|
810
1042
|
.file-link:hover {
|
|
811
1043
|
background-color: var(--k-color-bg-3);
|
|
812
1044
|
}
|
|
813
|
-
</style>
|
|
1045
|
+
</style>
|