@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,421 @@
1
+ <template>
2
+ <div class="file-list" @contextmenu.prevent="handleEmptyContextMenu">
3
+ <table class="file-list-table">
4
+ <thead class="file-list-header">
5
+ <tr>
6
+ <th
7
+ class="file-list-header-cell file-list-header-cell--name"
8
+ @click="$emit('sort', 'name')"
9
+ >
10
+ 名称
11
+ <SortIndicator v-if="sortConfig?.field === 'name'" :direction="sortConfig.direction" />
12
+ </th>
13
+ <th
14
+ class="file-list-header-cell"
15
+ @click="$emit('sort', 'dateModified')"
16
+ >
17
+ 修改日期
18
+ <SortIndicator v-if="sortConfig?.field === 'dateModified'" :direction="sortConfig.direction" />
19
+ </th>
20
+ <th
21
+ class="file-list-header-cell"
22
+ @click="$emit('sort', 'size')"
23
+ >
24
+ 大小
25
+ <SortIndicator v-if="sortConfig?.field === 'size'" :direction="sortConfig.direction" />
26
+ </th>
27
+ <th
28
+ class="file-list-header-cell"
29
+ @click="$emit('sort', 'type')"
30
+ >
31
+ 类型
32
+ <SortIndicator v-if="sortConfig?.field === 'type'" :direction="sortConfig.direction" />
33
+ </th>
34
+ </tr>
35
+ </thead>
36
+ <tbody class="file-list-body">
37
+ <tr
38
+ v-for="(item, index) in items"
39
+ :key="item.id"
40
+ :draggable="editingId !== item.id"
41
+ @dragstart="$emit('dragStart', $event, item)"
42
+ @dragover.prevent="$emit('dragOver', $event, item)"
43
+ @dragleave="$emit('dragLeave', $event)"
44
+ @drop.prevent="$emit('drop', $event, item)"
45
+ @click.stop="$emit('select', item, $event)"
46
+ @dblclick.stop="$emit('open', item)"
47
+ @contextmenu.prevent.stop="$emit('contextMenu', item, $event)"
48
+ :class="[
49
+ 'file-list-row',
50
+ selectedIds.has(item.id)
51
+ ? 'file-list-row--selected'
52
+ : dragOverId === item.id
53
+ ? 'file-list-row--drag-over'
54
+ : index % 2 === 0
55
+ ? 'file-list-row--even'
56
+ : 'file-list-row--odd'
57
+ ]"
58
+ >
59
+ <td class="file-list-cell file-list-cell--name">
60
+ <FileIcon :type="item.type" :name="item.name" :size="16" />
61
+ <input
62
+ v-if="editingId === item.id"
63
+ type="text"
64
+ class="file-list-rename-input"
65
+ :value="item.name"
66
+ @blur="handleRename(item, $event)"
67
+ @keydown.enter="handleEnterKey"
68
+ @keydown.escape="handleEscapeKey"
69
+ autofocus
70
+ />
71
+ <span
72
+ v-else
73
+ @click.stop="$emit('nameClick', item, $event)"
74
+ :class="[
75
+ 'file-list-name',
76
+ selectedIds.has(item.id) ? 'file-list-name--selected' : ''
77
+ ]"
78
+ >
79
+ {{ item.name }}
80
+ </span>
81
+ </td>
82
+ <td :class="['file-list-cell', selectedIds.has(item.id) ? 'file-list-cell--selected' : '']">
83
+ {{ item.dateModified || '--' }}
84
+ </td>
85
+ <td :class="['file-list-cell file-list-cell--size', selectedIds.has(item.id) ? 'file-list-cell--selected' : '']">
86
+ {{ item.size || '--' }}
87
+ </td>
88
+ <td :class="['file-list-cell', selectedIds.has(item.id) ? 'file-list-cell--selected' : '']">
89
+ {{ getTypeLabel(item.type) }}
90
+ </td>
91
+ </tr>
92
+ </tbody>
93
+ </table>
94
+ </div>
95
+ </template>
96
+
97
+ <script setup lang="ts">
98
+ import type { FileItem, SortConfig } from '../types';
99
+ import { FileType } from '../types';
100
+ import FileIcon from './FileIcon.vue';
101
+ import SortIndicator from './SortIndicator.vue';
102
+
103
+ interface Props {
104
+ items: FileItem[];
105
+ selectedIds: Set<string>;
106
+ sortConfig?: SortConfig;
107
+ editingId?: string | null;
108
+ dragOverId?: string | null;
109
+ }
110
+
111
+ const props = defineProps<Props>();
112
+
113
+ const emit = defineEmits<{
114
+ select: [item: FileItem, e: MouseEvent];
115
+ open: [item: FileItem];
116
+ contextMenu: [item: FileItem, e: MouseEvent];
117
+ contextMenuEmpty: [e: MouseEvent];
118
+ nameClick: [item: FileItem, e: MouseEvent];
119
+ rename: [item: FileItem, newName: string];
120
+ renameCancel: [item: FileItem];
121
+ sort: [field: string];
122
+ dragStart: [e: DragEvent, item: FileItem];
123
+ dragOver: [e: DragEvent, item: FileItem];
124
+ dragLeave: [e: DragEvent];
125
+ drop: [e: DragEvent, item: FileItem];
126
+ }>();
127
+
128
+ /**
129
+ * 空白处右键菜单
130
+ */
131
+ const handleEmptyContextMenu = (e: MouseEvent) => {
132
+ // 只有点击容器本身(不是表格行)时才触发
133
+ const target = e.target as HTMLElement;
134
+ if (!target.closest('tr')) {
135
+ emit('contextMenuEmpty', e);
136
+ }
137
+ };
138
+
139
+ /**
140
+ * 获取类型标签
141
+ */
142
+ const getTypeLabel = (type: FileType): string => {
143
+ const labels: Record<FileType, string> = {
144
+ [FileType.FOLDER]: '文件夹',
145
+ [FileType.FILE]: '文件',
146
+ [FileType.IMAGE]: '图片',
147
+ [FileType.VIDEO]: '视频',
148
+ [FileType.MUSIC]: '音频',
149
+ [FileType.DOCUMENT]: '文档',
150
+ [FileType.CODE]: '代码',
151
+ [FileType.TEXT]: '文本',
152
+ [FileType.PDF]: 'PDF',
153
+ [FileType.ARCHIVE]: '压缩包',
154
+ [FileType.APPLICATION]: '应用程序',
155
+ [FileType.UNKNOWN]: '未知'
156
+ };
157
+ return labels[type] || type;
158
+ };
159
+
160
+ /**
161
+ * 处理重命名(blur 事件)
162
+ */
163
+ const handleRename = (item: FileItem, e: Event) => {
164
+ const input = e.target as HTMLInputElement;
165
+ const newName = input.value.trim();
166
+ if (newName && newName !== item.name) {
167
+ emit('rename', item, newName);
168
+ } else {
169
+ emit('renameCancel', item);
170
+ }
171
+ };
172
+
173
+ /**
174
+ * Enter 键保存
175
+ */
176
+ const handleEnterKey = (e: KeyboardEvent) => {
177
+ (e.target as HTMLInputElement).blur();
178
+ };
179
+
180
+ /**
181
+ * Escape 键取消
182
+ */
183
+ const handleEscapeKey = (e: KeyboardEvent) => {
184
+ const input = e.target as HTMLInputElement;
185
+ // 恢复原始值,这样 blur 时会触发 renameCancel
186
+ const item = props.items.find(i => i.id === props.editingId);
187
+ if (item) {
188
+ input.value = item.name;
189
+ }
190
+ input.blur();
191
+ };
192
+ </script>
193
+
194
+ <style scoped>
195
+ /**
196
+ * FileList - 列表视图样式
197
+ * 参考 macOS Finder 和 Windows Explorer 的设计
198
+ */
199
+
200
+ .file-list {
201
+ width: 100%;
202
+ overflow-y: auto;
203
+ overflow-x: hidden;
204
+ }
205
+
206
+ .file-list-table {
207
+ width: 100%;
208
+ font-size: 13px;
209
+ text-align: left;
210
+ border-collapse: collapse;
211
+ }
212
+
213
+ /* 表头 - 更紧凑 */
214
+ .file-list-header {
215
+ font-size: 11px;
216
+ color: #86868b;
217
+ font-weight: 500;
218
+ text-transform: uppercase;
219
+ letter-spacing: 0.3px;
220
+ position: sticky;
221
+ top: 0;
222
+ background: rgba(255, 255, 255, 0.92);
223
+ backdrop-filter: blur(8px);
224
+ -webkit-backdrop-filter: blur(8px);
225
+ z-index: 10;
226
+ user-select: none;
227
+ }
228
+
229
+ .file-list-header-cell {
230
+ padding: 6px 12px;
231
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
232
+ cursor: pointer;
233
+ text-align: left;
234
+ font-weight: 500;
235
+ transition: background-color 100ms;
236
+ }
237
+
238
+ .file-list-header-cell:hover {
239
+ background: rgba(0, 0, 0, 0.04);
240
+ }
241
+
242
+ .file-list-header-cell--name {
243
+ padding-left: 12px;
244
+ padding-right: 12px;
245
+ width: 45%;
246
+ }
247
+
248
+ .file-list-body {
249
+ /* 无需额外边框 */
250
+ }
251
+
252
+ /* 行样式 - 更紧凑 */
253
+ .file-list-row {
254
+ cursor: pointer;
255
+ user-select: none;
256
+ transition: background-color 80ms ease;
257
+ border-bottom: 1px solid transparent;
258
+ }
259
+
260
+ /* 可拖拽时的鼠标样式 */
261
+ .file-list-row[draggable="true"] {
262
+ cursor: grab;
263
+ }
264
+
265
+ .file-list-row:active {
266
+ cursor: grabbing;
267
+ }
268
+
269
+ /* 偶数行 */
270
+ .file-list-row--even {
271
+ background: transparent;
272
+ }
273
+
274
+ /* 奇数行 - 轻微斑马纹 */
275
+ .file-list-row--odd {
276
+ background: rgba(0, 0, 0, 0.015);
277
+ }
278
+
279
+ /* 悬停效果 */
280
+ .file-list-row--even:hover,
281
+ .file-list-row--odd:hover {
282
+ background: rgba(0, 0, 0, 0.04);
283
+ }
284
+
285
+ /* 选中状态 - macOS 风格整行高亮 */
286
+ .file-list-row--selected {
287
+ background: #007aff;
288
+ color: white;
289
+ }
290
+
291
+ .file-list-row--selected:hover {
292
+ background: #0066d6;
293
+ }
294
+
295
+ /* 拖拽悬停 */
296
+ .file-list-row--drag-over {
297
+ background: rgba(0, 122, 255, 0.15);
298
+ border-bottom-color: rgba(0, 122, 255, 0.3);
299
+ }
300
+
301
+ /* 单元格 - 更紧凑的行高 */
302
+ .file-list-cell {
303
+ padding: 4px 12px;
304
+ white-space: nowrap;
305
+ color: #6e6e73;
306
+ vertical-align: middle;
307
+ height: 28px;
308
+ }
309
+
310
+ /* 名称列 - 图标和文字对齐 */
311
+ .file-list-cell--name {
312
+ padding-left: 12px;
313
+ padding-right: 12px;
314
+ display: flex;
315
+ align-items: center;
316
+ gap: 8px;
317
+ }
318
+
319
+ /* 选中时单元格颜色 */
320
+ .file-list-cell--selected {
321
+ color: rgba(255, 255, 255, 0.75);
322
+ }
323
+
324
+ /* 大小列 - 等宽字体 */
325
+ .file-list-cell--size {
326
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
327
+ font-size: 11px;
328
+ color: #8e8e93;
329
+ font-variant-numeric: tabular-nums;
330
+ }
331
+
332
+ /* 文件名 */
333
+ .file-list-name {
334
+ overflow: hidden;
335
+ text-overflow: ellipsis;
336
+ flex: 1;
337
+ color: #1d1d1f;
338
+ cursor: default;
339
+ font-weight: 400;
340
+ }
341
+
342
+ /* 选中后点击文件名可编辑 */
343
+ .file-list-name--selected {
344
+ color: white;
345
+ font-weight: 500;
346
+ cursor: text;
347
+ }
348
+
349
+ /* 重命名输入框 */
350
+ .file-list-rename-input {
351
+ flex: 1;
352
+ font-size: 13px;
353
+ padding: 2px 6px;
354
+ border: 1px solid #007aff;
355
+ border-radius: 4px;
356
+ outline: none;
357
+ box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.2);
358
+ background: white;
359
+ cursor: text;
360
+ }
361
+
362
+ /* 深色模式支持 */
363
+ @media (prefers-color-scheme: dark) {
364
+ .file-list-header {
365
+ background: rgba(28, 28, 30, 0.92);
366
+ color: #98989d;
367
+ }
368
+
369
+ .file-list-header-cell {
370
+ border-bottom-color: rgba(255, 255, 255, 0.08);
371
+ }
372
+
373
+ .file-list-header-cell:hover {
374
+ background: rgba(255, 255, 255, 0.04);
375
+ }
376
+
377
+ .file-list-row--odd {
378
+ background: rgba(255, 255, 255, 0.02);
379
+ }
380
+
381
+ .file-list-row--even:hover,
382
+ .file-list-row--odd:hover {
383
+ background: rgba(255, 255, 255, 0.06);
384
+ }
385
+
386
+ .file-list-row--selected {
387
+ background: #0a84ff;
388
+ }
389
+
390
+ .file-list-row--selected:hover {
391
+ background: #0077ed;
392
+ }
393
+
394
+ .file-list-row--drag-over {
395
+ background: rgba(10, 132, 255, 0.2);
396
+ border-bottom-color: rgba(10, 132, 255, 0.4);
397
+ }
398
+
399
+ .file-list-cell {
400
+ color: #98989d;
401
+ }
402
+
403
+ .file-list-cell--size {
404
+ color: #6e6e73;
405
+ }
406
+
407
+ .file-list-name {
408
+ color: #f5f5f7;
409
+ }
410
+
411
+ .file-list-name--selected {
412
+ color: white;
413
+ }
414
+
415
+ .file-list-rename-input {
416
+ background: #1c1c1e;
417
+ border-color: #0a84ff;
418
+ color: #f5f5f7;
419
+ }
420
+ }
421
+ </style>