@huyooo/file-explorer-frontend-vue 0.4.2

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 (76) hide show
  1. package/dist/components/Breadcrumb.vue.d.ts +11 -0
  2. package/dist/components/Breadcrumb.vue.d.ts.map +1 -0
  3. package/dist/components/CompressDialog.vue.d.ts +16 -0
  4. package/dist/components/CompressDialog.vue.d.ts.map +1 -0
  5. package/dist/components/ContextMenu.vue.d.ts +18 -0
  6. package/dist/components/ContextMenu.vue.d.ts.map +1 -0
  7. package/dist/components/FileGrid.vue.d.ts +40 -0
  8. package/dist/components/FileGrid.vue.d.ts.map +1 -0
  9. package/dist/components/FileIcon.vue.d.ts +13 -0
  10. package/dist/components/FileIcon.vue.d.ts.map +1 -0
  11. package/dist/components/FileInfoDialog.vue.d.ts +14 -0
  12. package/dist/components/FileInfoDialog.vue.d.ts.map +1 -0
  13. package/dist/components/FileList.vue.d.ts +37 -0
  14. package/dist/components/FileList.vue.d.ts.map +1 -0
  15. package/dist/components/FileListView.vue.d.ts +43 -0
  16. package/dist/components/FileListView.vue.d.ts.map +1 -0
  17. package/dist/components/FileSidebar.vue.d.ts +17 -0
  18. package/dist/components/FileSidebar.vue.d.ts.map +1 -0
  19. package/dist/components/ProgressDialog.vue.d.ts +28 -0
  20. package/dist/components/ProgressDialog.vue.d.ts.map +1 -0
  21. package/dist/components/SortIndicator.vue.d.ts +6 -0
  22. package/dist/components/SortIndicator.vue.d.ts.map +1 -0
  23. package/dist/components/StatusBar.vue.d.ts +27 -0
  24. package/dist/components/StatusBar.vue.d.ts.map +1 -0
  25. package/dist/components/Toolbar.vue.d.ts +60 -0
  26. package/dist/components/Toolbar.vue.d.ts.map +1 -0
  27. package/dist/components/Window.vue.d.ts +65 -0
  28. package/dist/components/Window.vue.d.ts.map +1 -0
  29. package/dist/composables/useApplicationIcon.d.ts +16 -0
  30. package/dist/composables/useApplicationIcon.d.ts.map +1 -0
  31. package/dist/composables/useDragAndDrop.d.ts +14 -0
  32. package/dist/composables/useDragAndDrop.d.ts.map +1 -0
  33. package/dist/composables/useMediaPlayer.d.ts +24 -0
  34. package/dist/composables/useMediaPlayer.d.ts.map +1 -0
  35. package/dist/composables/useSelection.d.ts +15 -0
  36. package/dist/composables/useSelection.d.ts.map +1 -0
  37. package/dist/composables/useWindowDrag.d.ts +18 -0
  38. package/dist/composables/useWindowDrag.d.ts.map +1 -0
  39. package/dist/composables/useWindowResize.d.ts +12 -0
  40. package/dist/composables/useWindowResize.d.ts.map +1 -0
  41. package/dist/index.css +1 -0
  42. package/dist/index.d.ts +22 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +4051 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/types/index.d.ts +268 -0
  47. package/dist/types/index.d.ts.map +1 -0
  48. package/dist/utils/fileTypeIcon.d.ts +6 -0
  49. package/dist/utils/fileTypeIcon.d.ts.map +1 -0
  50. package/dist/utils/folderTypeIcon.d.ts +14 -0
  51. package/dist/utils/folderTypeIcon.d.ts.map +1 -0
  52. package/package.json +55 -0
  53. package/src/components/Breadcrumb.vue +111 -0
  54. package/src/components/CompressDialog.vue +478 -0
  55. package/src/components/ContextMenu.vue +550 -0
  56. package/src/components/FileGrid.vue +504 -0
  57. package/src/components/FileIcon.vue +132 -0
  58. package/src/components/FileInfoDialog.vue +465 -0
  59. package/src/components/FileList.vue +421 -0
  60. package/src/components/FileListView.vue +321 -0
  61. package/src/components/FileSidebar.vue +158 -0
  62. package/src/components/ProgressDialog.vue +368 -0
  63. package/src/components/SortIndicator.vue +22 -0
  64. package/src/components/StatusBar.vue +43 -0
  65. package/src/components/Toolbar.vue +271 -0
  66. package/src/components/Window.vue +561 -0
  67. package/src/composables/useApplicationIcon.ts +79 -0
  68. package/src/composables/useDragAndDrop.ts +103 -0
  69. package/src/composables/useMediaPlayer.ts +174 -0
  70. package/src/composables/useSelection.ts +107 -0
  71. package/src/composables/useWindowDrag.ts +66 -0
  72. package/src/composables/useWindowResize.ts +134 -0
  73. package/src/index.ts +32 -0
  74. package/src/types/index.ts +273 -0
  75. package/src/utils/fileTypeIcon.ts +309 -0
  76. package/src/utils/folderTypeIcon.ts +132 -0
@@ -0,0 +1,465 @@
1
+ <!--
2
+ FileInfoDialog - 文件信息弹窗组件
3
+ 显示文件/文件夹的详细信息
4
+ -->
5
+ <template>
6
+ <Teleport to="body">
7
+ <div v-if="visible && item" class="file-info-dialog-overlay" @click="handleBackdropClick">
8
+ <div class="file-info-dialog">
9
+ <!-- 头部 -->
10
+ <div class="file-info-dialog-header">
11
+ <div class="file-info-dialog-title">
12
+ <Icon :icon="getFileIcon(item.type, item.name)" width="48" height="48" class="file-info-icon" :class="getIconClass(item.type)" />
13
+ <span class="file-info-dialog-name" :title="item.name">
14
+ {{ item.name }}
15
+ </span>
16
+ </div>
17
+ <button class="file-info-dialog-close" @click="emit('close')">
18
+ <X :size="18" />
19
+ </button>
20
+ </div>
21
+
22
+ <!-- 内容 -->
23
+ <div class="file-info-dialog-content">
24
+ <!-- 类型 -->
25
+ <div class="file-info-row">
26
+ <div class="file-info-label">
27
+ <File :size="14" />
28
+ <span>类型</span>
29
+ </div>
30
+ <div class="file-info-value">
31
+ {{ getFileTypeName(item.type, extension) }}
32
+ </div>
33
+ </div>
34
+
35
+ <!-- 大小 -->
36
+ <div v-if="item.type !== FileType.FOLDER && item.size" class="file-info-row">
37
+ <div class="file-info-label">
38
+ <HardDrive :size="14" />
39
+ <span>大小</span>
40
+ </div>
41
+ <div class="file-info-value">
42
+ {{ item.size }}
43
+ </div>
44
+ </div>
45
+
46
+ <!-- 位置 -->
47
+ <div class="file-info-row">
48
+ <div class="file-info-label">
49
+ <MapPin :size="14" />
50
+ <span>位置</span>
51
+ </div>
52
+ <div class="file-info-value file-info-value--path" :title="directory">
53
+ {{ directory }}
54
+ </div>
55
+ </div>
56
+
57
+ <!-- 完整路径 -->
58
+ <div class="file-info-row">
59
+ <div class="file-info-label">
60
+ <MapPin :size="14" />
61
+ <span>完整路径</span>
62
+ </div>
63
+ <div class="file-info-value file-info-value--path" :title="item.id">
64
+ {{ item.id }}
65
+ </div>
66
+ </div>
67
+
68
+ <!-- 修改时间 -->
69
+ <div v-if="item.dateModified" class="file-info-row">
70
+ <div class="file-info-label">
71
+ <Clock :size="14" />
72
+ <span>修改时间</span>
73
+ </div>
74
+ <div class="file-info-value">
75
+ {{ item.dateModified }}
76
+ </div>
77
+ </div>
78
+ </div>
79
+
80
+ <!-- 底部按钮 -->
81
+ <div class="file-info-dialog-footer">
82
+ <button class="file-info-dialog-btn" @click="emit('close')">
83
+ 关闭
84
+ </button>
85
+ </div>
86
+ </div>
87
+ </div>
88
+ </Teleport>
89
+ </template>
90
+
91
+ <script setup lang="ts">
92
+ import { computed, onMounted, onUnmounted } from 'vue';
93
+ import { Icon } from '@iconify/vue';
94
+ import {
95
+ File,
96
+ Image,
97
+ Video,
98
+ Music,
99
+ FileText,
100
+ Code,
101
+ Archive,
102
+ X,
103
+ MapPin,
104
+ HardDrive,
105
+ Clock
106
+ } from 'lucide-vue-next';
107
+ import { FileType, type FileItem } from '../types';
108
+ import { getFileTypeIcon } from '../utils/fileTypeIcon';
109
+
110
+ const props = defineProps<{
111
+ /** 是否显示 */
112
+ visible: boolean;
113
+ /** 文件项 */
114
+ item: FileItem | null;
115
+ }>();
116
+
117
+ const emit = defineEmits<{
118
+ close: [];
119
+ }>();
120
+
121
+ /** 获取文件类型图标 */
122
+ function getFileIcon(type: FileType, name?: string): string {
123
+ // 文件夹使用 material-icon-theme:folder
124
+ if (type === FileType.FOLDER) {
125
+ return 'flat-color-icons:folder';
126
+ }
127
+
128
+ // 如果有文件名,使用 material-icon-theme 图标映射(传递 type 作为兜底)
129
+ if (name) {
130
+ return getFileTypeIcon(name, type);
131
+ }
132
+
133
+ // 回退逻辑(没有文件名时)
134
+ switch (type) {
135
+ case FileType.IMAGE: return 'material-icon-theme:image';
136
+ case FileType.VIDEO: return 'material-icon-theme:video';
137
+ case FileType.MUSIC: return 'material-icon-theme:audio';
138
+ case FileType.DOCUMENT: return 'material-icon-theme:word';
139
+ case FileType.CODE: return 'material-icon-theme:javascript';
140
+ case FileType.ARCHIVE: return 'material-icon-theme:zip';
141
+ default: return 'material-icon-theme:document';
142
+ }
143
+ }
144
+
145
+ /** 获取图标样式类 */
146
+ function getIconClass(type: FileType): string {
147
+ switch (type) {
148
+ case FileType.FOLDER: return 'file-info-icon--folder';
149
+ case FileType.IMAGE: return 'file-info-icon--image';
150
+ case FileType.VIDEO: return 'file-info-icon--video';
151
+ case FileType.MUSIC: return 'file-info-icon--music';
152
+ case FileType.DOCUMENT: return 'file-info-icon--document';
153
+ case FileType.CODE: return 'file-info-icon--code';
154
+ case FileType.ARCHIVE: return 'file-info-icon--archive';
155
+ default: return 'file-info-icon--file';
156
+ }
157
+ }
158
+
159
+ /** 获取文件类型名称 */
160
+ function getFileTypeName(type: FileType, ext?: string): string {
161
+ switch (type) {
162
+ case FileType.FOLDER: return '文件夹';
163
+ case FileType.IMAGE: return `图片${ext ? ` (${ext.toUpperCase()})` : ''}`;
164
+ case FileType.VIDEO: return `视频${ext ? ` (${ext.toUpperCase()})` : ''}`;
165
+ case FileType.MUSIC: return `音频${ext ? ` (${ext.toUpperCase()})` : ''}`;
166
+ case FileType.DOCUMENT: return `文档${ext ? ` (${ext.toUpperCase()})` : ''}`;
167
+ case FileType.CODE: return `代码文件${ext ? ` (${ext.toUpperCase()})` : ''}`;
168
+ case FileType.ARCHIVE: return `压缩包${ext ? ` (${ext.toUpperCase()})` : ''}`;
169
+ default: return ext ? `${ext.toUpperCase()} 文件` : '文件';
170
+ }
171
+ }
172
+
173
+ /** 获取文件扩展名 */
174
+ const extension = computed(() => {
175
+ if (!props.item) return undefined;
176
+ const lastDot = props.item.name.lastIndexOf('.');
177
+ if (lastDot === -1 || lastDot === 0) return undefined;
178
+ return props.item.name.substring(lastDot + 1).toLowerCase();
179
+ });
180
+
181
+ /** 获取文件所在目录 */
182
+ const directory = computed(() => {
183
+ if (!props.item) return '';
184
+ const lastSlash = props.item.id.lastIndexOf('/');
185
+ if (lastSlash === -1) return props.item.id;
186
+ return props.item.id.substring(0, lastSlash) || '/';
187
+ });
188
+
189
+ /** 点击背景关闭 */
190
+ function handleBackdropClick(e: MouseEvent) {
191
+ if (e.target === e.currentTarget) {
192
+ emit('close');
193
+ }
194
+ }
195
+
196
+ /** ESC 关闭 */
197
+ function handleKeyDown(e: KeyboardEvent) {
198
+ if (e.key === 'Escape' && props.visible) {
199
+ emit('close');
200
+ }
201
+ }
202
+
203
+ onMounted(() => {
204
+ document.addEventListener('keydown', handleKeyDown);
205
+ });
206
+
207
+ onUnmounted(() => {
208
+ document.removeEventListener('keydown', handleKeyDown);
209
+ });
210
+ </script>
211
+
212
+ <style scoped>
213
+ /* FileInfoDialog 样式 */
214
+
215
+ .file-info-dialog-overlay {
216
+ position: fixed;
217
+ inset: 0;
218
+ background: rgba(0, 0, 0, 0.4);
219
+ display: flex;
220
+ align-items: center;
221
+ justify-content: center;
222
+ z-index: 10000;
223
+ animation: file-info-fadeIn 0.15s ease-out;
224
+ }
225
+
226
+ @keyframes file-info-fadeIn {
227
+ from { opacity: 0; }
228
+ to { opacity: 1; }
229
+ }
230
+
231
+ .file-info-dialog {
232
+ background: #ffffff;
233
+ border: 1px solid #e0e0e0;
234
+ border-radius: 12px;
235
+ width: 420px;
236
+ max-width: 90vw;
237
+ max-height: 80vh;
238
+ display: flex;
239
+ flex-direction: column;
240
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15), 0 2px 8px rgba(0, 0, 0, 0.1);
241
+ animation: file-info-slideIn 0.2s ease-out;
242
+ }
243
+
244
+ @keyframes file-info-slideIn {
245
+ from {
246
+ opacity: 0;
247
+ transform: scale(0.95) translateY(-10px);
248
+ }
249
+ to {
250
+ opacity: 1;
251
+ transform: scale(1) translateY(0);
252
+ }
253
+ }
254
+
255
+ /* 深色模式 */
256
+ @media (prefers-color-scheme: dark) {
257
+ .file-info-dialog {
258
+ background: #2d2d2d;
259
+ border-color: #404040;
260
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
261
+ }
262
+ }
263
+
264
+ /* 头部 */
265
+ .file-info-dialog-header {
266
+ display: flex;
267
+ align-items: center;
268
+ justify-content: space-between;
269
+ padding: 16px 20px;
270
+ border-bottom: 1px solid #e0e0e0;
271
+ gap: 12px;
272
+ }
273
+
274
+ @media (prefers-color-scheme: dark) {
275
+ .file-info-dialog-header {
276
+ border-bottom-color: #404040;
277
+ }
278
+ }
279
+
280
+ .file-info-dialog-title {
281
+ display: flex;
282
+ align-items: center;
283
+ gap: 10px;
284
+ min-width: 0;
285
+ flex: 1;
286
+ }
287
+
288
+ .file-info-dialog-name {
289
+ font-size: 16px;
290
+ font-weight: 600;
291
+ color: #1a1a1a;
292
+ white-space: nowrap;
293
+ overflow: hidden;
294
+ text-overflow: ellipsis;
295
+ }
296
+
297
+ @media (prefers-color-scheme: dark) {
298
+ .file-info-dialog-name {
299
+ color: #e0e0e0;
300
+ }
301
+ }
302
+
303
+ .file-info-dialog-close {
304
+ display: flex;
305
+ align-items: center;
306
+ justify-content: center;
307
+ width: 28px;
308
+ height: 28px;
309
+ border: none;
310
+ background: transparent;
311
+ color: #666;
312
+ border-radius: 6px;
313
+ cursor: pointer;
314
+ flex-shrink: 0;
315
+ transition: all 0.15s ease;
316
+ }
317
+
318
+ .file-info-dialog-close:hover {
319
+ background: #f0f0f0;
320
+ color: #333;
321
+ }
322
+
323
+ @media (prefers-color-scheme: dark) {
324
+ .file-info-dialog-close {
325
+ color: #888;
326
+ }
327
+ .file-info-dialog-close:hover {
328
+ background: #404040;
329
+ color: #e0e0e0;
330
+ }
331
+ }
332
+
333
+ /* 内容 */
334
+ .file-info-dialog-content {
335
+ padding: 16px 20px;
336
+ display: flex;
337
+ flex-direction: column;
338
+ gap: 12px;
339
+ overflow-y: auto;
340
+ }
341
+
342
+ .file-info-row {
343
+ display: flex;
344
+ align-items: flex-start;
345
+ gap: 16px;
346
+ }
347
+
348
+ .file-info-label {
349
+ display: flex;
350
+ align-items: center;
351
+ gap: 6px;
352
+ width: 90px;
353
+ flex-shrink: 0;
354
+ color: #666;
355
+ font-size: 13px;
356
+ }
357
+
358
+ @media (prefers-color-scheme: dark) {
359
+ .file-info-label {
360
+ color: #888;
361
+ }
362
+ }
363
+
364
+ .file-info-label svg {
365
+ flex-shrink: 0;
366
+ }
367
+
368
+ .file-info-value {
369
+ flex: 1;
370
+ color: #1a1a1a;
371
+ font-size: 13px;
372
+ word-break: break-all;
373
+ line-height: 1.4;
374
+ }
375
+
376
+ @media (prefers-color-scheme: dark) {
377
+ .file-info-value {
378
+ color: #e0e0e0;
379
+ }
380
+ }
381
+
382
+ .file-info-value--path {
383
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', monospace;
384
+ font-size: 12px;
385
+ background: #f5f5f5;
386
+ padding: 4px 8px;
387
+ border-radius: 4px;
388
+ user-select: all;
389
+ }
390
+
391
+ @media (prefers-color-scheme: dark) {
392
+ .file-info-value--path {
393
+ background: #383838;
394
+ }
395
+ }
396
+
397
+ /* 图标样式 */
398
+ .file-info-icon {
399
+ width: 32px;
400
+ height: 32px;
401
+ flex-shrink: 0;
402
+ }
403
+
404
+ .file-info-icon--folder {
405
+ color: #dcb67a;
406
+ }
407
+
408
+ .file-info-icon--image {
409
+ color: #7ec699;
410
+ }
411
+
412
+ .file-info-icon--video {
413
+ color: #c678dd;
414
+ }
415
+
416
+ .file-info-icon--music {
417
+ color: #e06c75;
418
+ }
419
+
420
+ .file-info-icon--document {
421
+ color: #61afef;
422
+ }
423
+
424
+ .file-info-icon--code {
425
+ color: #98c379;
426
+ }
427
+
428
+ .file-info-icon--archive {
429
+ color: #d19a66;
430
+ }
431
+
432
+ .file-info-icon--file {
433
+ color: #abb2bf;
434
+ }
435
+
436
+ /* 底部 */
437
+ .file-info-dialog-footer {
438
+ display: flex;
439
+ justify-content: flex-end;
440
+ padding: 12px 20px;
441
+ border-top: 1px solid #e0e0e0;
442
+ }
443
+
444
+ @media (prefers-color-scheme: dark) {
445
+ .file-info-dialog-footer {
446
+ border-top-color: #404040;
447
+ }
448
+ }
449
+
450
+ .file-info-dialog-btn {
451
+ padding: 6px 16px;
452
+ border: none;
453
+ border-radius: 6px;
454
+ font-size: 13px;
455
+ cursor: pointer;
456
+ transition: all 0.15s ease;
457
+ background: #007aff;
458
+ color: white;
459
+ }
460
+
461
+ .file-info-dialog-btn:hover {
462
+ background: #0066d6;
463
+ }
464
+ </style>
465
+