koishi-plugin-media-luna 0.0.3 → 0.0.5

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 +46 -11
  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 +21 -17
  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 +231 -2
  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
@@ -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
  /* 任务信息 */
@@ -1,7 +1,7 @@
1
1
  <template>
2
- <div class="view-container">
3
- <div class="view-header">
4
- <div class="header-left">
2
+ <div class="ml-view-container">
3
+ <div class="ml-view-header">
4
+ <div class="ml-header-left">
5
5
  <k-button solid type="primary" @click="openCreateDialog">
6
6
  <template #icon><k-icon name="add"></k-icon></template>
7
7
  新建预设
@@ -12,19 +12,19 @@
12
12
  :preset-tags="presetTags"
13
13
  />
14
14
  </div>
15
- <div class="header-right">
15
+ <div class="ml-header-right">
16
16
  <ViewModeSwitch v-model="viewMode" />
17
17
  </div>
18
18
  </div>
19
19
 
20
- <div class="view-content">
20
+ <div class="ml-view-content">
21
21
  <div v-if="loading" class="loading-state">
22
- <k-icon name="sync" class="loading-icon"></k-icon> 加载中...
22
+ <k-icon name="sync" class="ml-spin"></k-icon> 加载中...
23
23
  </div>
24
24
 
25
25
  <!-- 卡片视图 (瀑布流) -->
26
- <div v-else-if="viewMode === 'card'" class="masonry-grid">
27
- <div v-for="preset in filteredPresets" :key="preset.id" class="masonry-item">
26
+ <div v-else-if="viewMode === 'card'" class="ml-masonry">
27
+ <div v-for="preset in filteredPresets" :key="preset.id" class="ml-masonry-item">
28
28
  <div class="preset-card" @click="openEditDialog(preset)">
29
29
  <!-- 缩略图 -->
30
30
  <div class="card-thumbnail" v-if="preset.thumbnail">
@@ -57,14 +57,14 @@
57
57
  size="small"
58
58
  @change="toggleEnable(preset)"
59
59
  />
60
- <k-button size="mini" class="copy-btn" @click="copyPreset(preset)">
60
+ <k-button size="mini" class="ml-btn-outline-primary" @click="copyPreset(preset)">
61
61
  <template #icon><k-icon name="copy"></k-icon></template>
62
62
  复制
63
63
  </k-button>
64
64
  <k-button
65
65
  v-if="preset.source === 'user'"
66
66
  size="mini"
67
- class="delete-btn"
67
+ class="ml-btn-outline-danger"
68
68
  @click="confirmDelete(preset)"
69
69
  >
70
70
  <template #icon><k-icon name="delete"></k-icon></template>
@@ -81,8 +81,8 @@
81
81
  </div>
82
82
 
83
83
  <!-- 列表视图 -->
84
- <div v-else class="list-view">
85
- <table class="preset-table">
84
+ <div v-else class="ml-table-container">
85
+ <table class="ml-table">
86
86
  <thead>
87
87
  <tr>
88
88
  <th class="col-thumb">缩略图</th>
@@ -98,7 +98,6 @@
98
98
  <tr
99
99
  v-for="preset in filteredPresets"
100
100
  :key="preset.id"
101
- class="preset-row"
102
101
  @click="openEditDialog(preset)"
103
102
  >
104
103
  <td class="col-thumb">
@@ -132,14 +131,14 @@
132
131
  </td>
133
132
  <td class="col-actions" @click.stop>
134
133
  <div class="action-btns">
135
- <k-button size="mini" class="copy-btn" @click="copyPreset(preset)">
134
+ <k-button size="mini" class="ml-btn-outline-primary" @click="copyPreset(preset)">
136
135
  <template #icon><k-icon name="copy"></k-icon></template>
137
136
  复制
138
137
  </k-button>
139
138
  <k-button
140
139
  v-if="preset.source === 'user'"
141
140
  size="mini"
142
- class="delete-btn"
141
+ class="ml-btn-outline-danger"
143
142
  @click="confirmDelete(preset)"
144
143
  >
145
144
  <template #icon><k-icon name="delete"></k-icon></template>
@@ -381,68 +380,9 @@ onMounted(() => {
381
380
  </script>
382
381
 
383
382
  <style scoped>
384
- .view-container {
385
- height: 100%;
386
- display: flex;
387
- flex-direction: column;
388
- min-height: 0;
389
- }
390
-
391
- .view-header {
392
- flex-shrink: 0;
393
- display: flex;
394
- justify-content: space-between;
395
- align-items: center;
396
- margin-bottom: 1rem;
397
- flex-wrap: wrap;
398
- gap: 1rem;
399
- }
400
-
401
- .header-left {
402
- display: flex;
403
- align-items: center;
404
- gap: 1rem;
405
- flex-wrap: wrap;
406
- }
407
-
408
- .header-right {
409
- display: flex;
410
- align-items: center;
411
- gap: 1rem;
412
- }
413
-
414
- /* 内容区域 */
415
- .view-content {
416
- flex: 1 1 0;
417
- min-height: 0;
418
- overflow-y: auto;
419
- overflow-x: hidden;
420
- padding: 0.5rem 0;
421
- }
422
-
423
- /* 瀑布流布局 */
424
- .masonry-grid {
425
- columns: 5;
426
- column-gap: 1rem;
427
- }
428
-
429
- @media (max-width: 1400px) {
430
- .masonry-grid { columns: 4; }
431
- }
432
- @media (max-width: 1100px) {
433
- .masonry-grid { columns: 3; }
434
- }
435
- @media (max-width: 800px) {
436
- .masonry-grid { columns: 2; }
437
- }
438
- @media (max-width: 500px) {
439
- .masonry-grid { columns: 1; }
440
- }
383
+ @import '../styles/shared.css';
441
384
 
442
- .masonry-item {
443
- break-inside: avoid;
444
- margin-bottom: 1rem;
445
- }
385
+ /* ========== 预设卡片特有样式 ========== */
446
386
 
447
387
  .preset-card {
448
388
  background-color: var(--k-color-bg-1);
@@ -571,92 +511,18 @@ onMounted(() => {
571
511
  color: white;
572
512
  }
573
513
 
574
- /* 列表视图 - 表格样式 */
575
- .list-view {
576
- background-color: var(--k-card-bg);
577
- border: 1px solid var(--k-color-border);
578
- border-radius: 8px;
579
- overflow: hidden;
580
- }
581
-
582
- .preset-table {
583
- width: 100%;
584
- border-collapse: collapse;
585
- table-layout: fixed;
586
- }
587
-
588
- .preset-table thead {
589
- background-color: var(--k-color-bg-2);
590
- }
591
-
592
- .preset-table th {
593
- padding: 0.875rem 1rem;
594
- font-size: 0.8rem;
595
- font-weight: 600;
596
- color: var(--k-color-text-description);
597
- text-align: left;
598
- border-bottom: 1px solid var(--k-color-border);
599
- white-space: nowrap;
600
- overflow: hidden;
601
- text-overflow: ellipsis;
602
- }
603
-
604
- .preset-table .preset-row {
605
- cursor: pointer;
606
- transition: background-color 0.15s ease;
607
- }
608
-
609
- .preset-table .preset-row:hover {
610
- background-color: var(--k-color-bg-1);
611
- }
612
-
613
- .preset-table .preset-row td {
614
- padding: 0.75rem 1rem;
615
- border-bottom: 1px solid var(--k-color-border);
616
- vertical-align: middle;
617
- text-align: left;
618
- overflow: hidden;
619
- text-overflow: ellipsis;
620
- white-space: nowrap;
621
- }
622
-
623
- .preset-table .preset-row:last-child td {
624
- border-bottom: none;
625
- }
514
+ /* ========== 列表视图特有样式 ========== */
626
515
 
627
516
  /* 表格列宽定义 */
628
- .preset-table .col-thumb {
629
- width: 60px;
630
- }
631
-
632
- .preset-table .col-name {
633
- width: 15%;
634
- }
517
+ .col-thumb { width: 60px; }
518
+ .col-name { width: 15%; }
519
+ .col-template { width: auto; }
520
+ .col-refs { width: 10%; text-align: center !important; }
521
+ .col-source { width: 10%; }
522
+ .col-status { width: 8%; }
523
+ .col-actions { width: 15%; }
635
524
 
636
- .preset-table .col-template {
637
- width: auto;
638
- }
639
-
640
- .preset-table .col-refs {
641
- width: 10%;
642
- text-align: center !important;
643
- }
644
-
645
- .preset-table th.col-refs {
646
- text-align: center !important;
647
- }
648
-
649
- .preset-table .col-source {
650
- width: 10%;
651
- }
652
-
653
- .preset-table .col-status {
654
- width: 8%;
655
- }
656
-
657
- .preset-table .col-actions {
658
- width: 15%;
659
- }
525
+ th.col-refs { text-align: center !important; }
660
526
 
661
527
  .thumb-wrapper {
662
528
  width: 40px;
@@ -672,7 +538,7 @@ onMounted(() => {
672
538
  object-fit: cover;
673
539
  }
674
540
 
675
- .preset-table .name-text {
541
+ .name-text {
676
542
  font-weight: 600;
677
543
  color: var(--k-color-text);
678
544
  display: block;
@@ -706,15 +572,6 @@ onMounted(() => {
706
572
  gap: 0.5rem;
707
573
  }
708
574
 
709
- .loading-icon {
710
- animation: spin 1s linear infinite;
711
- }
712
-
713
- @keyframes spin {
714
- from { transform: rotate(0deg); }
715
- to { transform: rotate(360deg); }
716
- }
717
-
718
575
  /* 表单样式 */
719
576
  .form-tip {
720
577
  font-size: 0.8rem;
@@ -722,32 +579,6 @@ onMounted(() => {
722
579
  margin-top: 0.25rem;
723
580
  }
724
581
 
725
- .ref-image-input {
726
- margin-bottom: 8px;
727
- }
728
-
729
- /* 复制按钮样式 */
730
- .copy-btn {
731
- color: var(--k-color-active) !important;
732
- border-color: var(--k-color-active) !important;
733
- }
734
-
735
- .copy-btn:hover {
736
- background-color: var(--k-color-active) !important;
737
- color: white !important;
738
- }
739
-
740
- /* 删除按钮样式 */
741
- .delete-btn {
742
- color: var(--k-color-error, #f56c6c) !important;
743
- border-color: var(--k-color-error, #f56c6c) !important;
744
- }
745
-
746
- .delete-btn:hover {
747
- background-color: var(--k-color-error, #f56c6c) !important;
748
- color: white !important;
749
- }
750
-
751
582
  /* 表格内元素样式 */
752
583
  .thumb-placeholder {
753
584
  width: 44px;
@@ -760,11 +591,6 @@ onMounted(() => {
760
591
  color: var(--k-color-text-description);
761
592
  }
762
593
 
763
- .name-text {
764
- font-weight: 500;
765
- color: var(--k-color-text);
766
- }
767
-
768
594
  .source-badge {
769
595
  display: inline-block;
770
596
  font-size: 0.75rem;
@@ -180,6 +180,32 @@ onMounted(async () => {
180
180
  min-height: 0;
181
181
  overflow-y: auto;
182
182
  overflow-x: hidden;
183
+ /* 隐藏式滚动条 */
184
+ scrollbar-width: thin;
185
+ scrollbar-color: transparent transparent;
186
+ }
187
+
188
+ .main-content:hover {
189
+ scrollbar-color: var(--k-color-border) transparent;
190
+ }
191
+
192
+ /* Webkit 隐藏式滚动条 */
193
+ .main-content::-webkit-scrollbar {
194
+ width: 6px;
195
+ }
196
+
197
+ .main-content::-webkit-scrollbar-track {
198
+ background: transparent;
199
+ }
200
+
201
+ .main-content::-webkit-scrollbar-thumb {
202
+ background-color: transparent;
203
+ border-radius: 3px;
204
+ transition: background-color 0.2s;
205
+ }
206
+
207
+ .main-content:hover::-webkit-scrollbar-thumb {
208
+ background-color: var(--k-color-border);
183
209
  }
184
210
 
185
211
  .loading {
@@ -1,8 +1,8 @@
1
1
  <template>
2
- <div class="view-container">
2
+ <div class="ml-view-container">
3
3
  <!-- 固定头部区域 -->
4
- <div class="view-header">
5
- <div class="header-left">
4
+ <div class="ml-view-header">
5
+ <div class="ml-header-left">
6
6
  <!-- 视图切换 -->
7
7
  <div class="view-mode-switch">
8
8
  <button
@@ -32,7 +32,7 @@
32
32
  </button>
33
33
  </div>
34
34
  </div>
35
- <div class="header-actions">
35
+ <div class="ml-header-right">
36
36
  <k-button @click="openCleanupDialog">
37
37
  <template #icon><k-icon name="delete"></k-icon></template>
38
38
  清理记录
@@ -101,7 +101,7 @@
101
101
  </div>
102
102
 
103
103
  <!-- 可滚动的内容区域 -->
104
- <div class="view-content">
104
+ <div class="ml-view-content">
105
105
  <!-- 列表视图 -->
106
106
  <template v-if="viewMode === 'list'">
107
107
  <el-table :data="tasks" style="width: 100%" class="task-table" @row-click="openDetailDialog">
@@ -155,11 +155,11 @@
155
155
  <k-icon name="image" class="empty-icon"></k-icon>
156
156
  <p>暂无成功生成的图片</p>
157
157
  </div>
158
- <div v-else class="masonry-grid">
158
+ <div v-else class="ml-masonry">
159
159
  <div
160
160
  v-for="item in galleryItems"
161
161
  :key="item.id + '-' + item.assetIndex"
162
- class="masonry-item"
162
+ class="ml-masonry-item"
163
163
  >
164
164
  <div class="gallery-item" @click="openGalleryDetail(item)">
165
165
  <div class="gallery-image-wrapper">
@@ -500,34 +500,11 @@ onMounted(() => {
500
500
  </script>
501
501
 
502
502
  <style scoped>
503
- .view-container {
504
- height: 100%;
505
- display: flex;
506
- flex-direction: column;
507
- min-height: 0;
508
- }
509
-
510
- .view-header {
511
- flex-shrink: 0;
512
- display: flex;
513
- justify-content: space-between;
514
- align-items: center;
515
- margin-bottom: 1rem;
516
- padding: 0 0.5rem;
517
- }
518
-
519
- .header-left {
520
- display: flex;
521
- align-items: center;
522
- gap: 1rem;
523
- }
503
+ @import '../styles/shared.css';
524
504
 
525
- .header-actions {
526
- display: flex;
527
- gap: 10px;
528
- align-items: center;
529
- }
505
+ /* ========== 任务视图特有样式 ========== */
530
506
 
507
+ /* 视图模式切换 */
531
508
  .view-mode-switch {
532
509
  display: flex;
533
510
  gap: 2px;
@@ -563,13 +540,6 @@ onMounted(() => {
563
540
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
564
541
  }
565
542
 
566
- .view-content {
567
- flex: 1 1 0;
568
- min-height: 0;
569
- overflow-y: auto;
570
- overflow-x: hidden;
571
- }
572
-
573
543
  /* Stats Grid */
574
544
  .stats-grid {
575
545
  flex-shrink: 0;
@@ -585,7 +555,7 @@ onMounted(() => {
585
555
  border-radius: 16px;
586
556
  padding: 1.5rem;
587
557
  display: flex;
588
- flex-direction: row; /* Layout change: row */
558
+ flex-direction: row;
589
559
  align-items: center;
590
560
  justify-content: space-between;
591
561
  transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
@@ -626,7 +596,7 @@ onMounted(() => {
626
596
  .stat-content {
627
597
  display: flex;
628
598
  flex-direction: column-reverse;
629
- align-items: flex-end; /* Align right */
599
+ align-items: flex-end;
630
600
  flex-grow: 1;
631
601
  margin-left: 1rem;
632
602
  }
@@ -682,7 +652,7 @@ onMounted(() => {
682
652
  border-radius: 12px;
683
653
  cursor: pointer;
684
654
  overflow: hidden;
685
- --el-table-header-bg-color: var(--k-color-bg-1); /* Blend with page bg */
655
+ --el-table-header-bg-color: var(--k-color-bg-1);
686
656
  --el-table-row-hover-bg-color: var(--k-color-bg-2);
687
657
  --el-table-border-color: var(--k-color-border);
688
658
  }
@@ -720,30 +690,7 @@ onMounted(() => {
720
690
  color: var(--k-color-text-description);
721
691
  }
722
692
 
723
- /* Masonry Gallery */
724
- .masonry-grid {
725
- columns: 5;
726
- column-gap: 1rem;
727
- }
728
-
729
- @media (max-width: 1400px) {
730
- .masonry-grid { columns: 4; }
731
- }
732
- @media (max-width: 1100px) {
733
- .masonry-grid { columns: 3; }
734
- }
735
- @media (max-width: 800px) {
736
- .masonry-grid { columns: 2; }
737
- }
738
- @media (max-width: 500px) {
739
- .masonry-grid { columns: 1; }
740
- }
741
-
742
- .masonry-item {
743
- break-inside: avoid;
744
- margin-bottom: 1rem;
745
- }
746
-
693
+ /* Gallery Item */
747
694
  .gallery-item {
748
695
  background: var(--k-card-bg);
749
696
  border: 1px solid var(--k-color-border);
@@ -796,7 +743,7 @@ onMounted(() => {
796
743
  .zoom-icon {
797
744
  font-size: 2rem;
798
745
  color: #fff;
799
- margin-bottom: auto; /* Push to center/top if needed, or remove to stay bottom */
746
+ margin-bottom: auto;
800
747
  align-self: center;
801
748
  opacity: 0;
802
749
  transform: scale(0.8);
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Composables 统一导出
3
+ */
4
+ export { useLoading } from './useLoading'
5
+ export type { UseLoadingOptions, UseLoadingReturn } from './useLoading'
6
+
7
+ export { usePagination } from './usePagination'
8
+ export type { UsePaginationOptions, UsePaginationReturn } from './usePagination'
9
+
10
+ export { useDataFetch } from './useDataFetch'
11
+ export type { UseDataFetchOptions, UseDataFetchReturn } from './useDataFetch'
12
+
13
+ export { useDialog } from './useDialog'
14
+ export type { UseDialogReturn } from './useDialog'