diff --git a/desktop-client/src/App.vue b/desktop-client/src/App.vue
index 1984062..d58c7ce 100644
--- a/desktop-client/src/App.vue
+++ b/desktop-client/src/App.vue
@@ -563,17 +563,31 @@ function applyNativeUploadProgress(payload: NativeUploadProgressEvent) {
}
}
-function fileIcon(item: FileItem) {
- if (item.isDirectory || item.type === "directory") return "📁";
- const name = String(item.name || "").toLowerCase();
- if (/\.(jpg|jpeg|png|webp|gif|bmp|svg)$/.test(name)) return "🖼️";
- if (/\.(mp4|mkv|mov|avi)$/.test(name)) return "🎬";
- if (/\.(mp3|wav|flac|aac)$/.test(name)) return "🎵";
- if (/\.(zip|rar|7z|tar|gz)$/.test(name)) return "🗜️";
- if (/\.(pdf)$/.test(name)) return "📕";
- if (/\.(doc|docx)$/.test(name)) return "📘";
- if (/\.(xls|xlsx)$/.test(name)) return "📗";
- return "📄";
+function getFileExt(item: FileItem) {
+ if (item.isDirectory || item.type === "directory") return "";
+ const text = String(item.displayName || item.name || "").trim();
+ const idx = text.lastIndexOf(".");
+ if (idx <= 0 || idx >= text.length - 1) return "";
+ return text.slice(idx + 1).toLowerCase();
+}
+
+function fileVisualKind(item: FileItem) {
+ if (item.isDirectory || item.type === "directory") return "folder";
+ const ext = getFileExt(item);
+ if (!ext) return "file";
+ if (["jpg", "jpeg", "png", "webp", "gif", "bmp", "svg", "heic"].includes(ext)) return "image";
+ if (["mp4", "mkv", "mov", "avi", "webm", "flv"].includes(ext)) return "video";
+ if (["mp3", "wav", "flac", "aac", "ogg", "m4a"].includes(ext)) return "audio";
+ if (["zip", "rar", "7z", "tar", "gz", "bz2", "xz"].includes(ext)) return "archive";
+ if (["pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "md", "csv"].includes(ext)) return "document";
+ if (["apk", "exe", "msi", "dmg", "deb", "rpm"].includes(ext)) return "app";
+ return "file";
+}
+
+function fileExtLabel(item: FileItem) {
+ const ext = getFileExt(item).toUpperCase();
+ if (!ext) return "FILE";
+ return ext.slice(0, 4);
}
function matchFileTypeFilter(item: FileItem, type: string) {
@@ -2480,6 +2494,13 @@ onBeforeUnmount(() => {
{{ pathState.mode === "search" ? "搜索结果" : "当前目录" }} {{ pathState.currentPath }}
+
+
+ {{ pathState.mode === "search" ? `搜索完成,共 ${filteredFiles.length} 个结果` : `已加载完成,共 ${filteredFiles.length} 个` }}
+
+ 双击打开,右键查看更多操作
+
+
正在加载目录...
{{ pathState.error }}
@@ -2501,7 +2522,15 @@ onBeforeUnmount(() => {
{{ isBatchSelected(item.name) ? "✓" : "" }}
-
{{ fileIcon(item) }}
+
+
+
+
+
+
+ {{ fileExtLabel(item) }}
+
+
{
{{ item.displayName || item.name }}
-
{{ fileTypeLabel(item) }} · {{ item.isDirectory ? "-" : (item.sizeFormatted || formatBytes(item.size)) }}
-
{{ formatDate(item.modifiedAt) }}
@@ -3330,7 +3357,7 @@ select:focus {
align-items: center;
gap: 8px;
flex-wrap: wrap;
- justify-content: flex-end;
+ justify-content: flex-start;
}
.search-input {
@@ -3447,21 +3474,49 @@ select:focus {
text-overflow: ellipsis;
}
+.files-summary-bar {
+ height: 34px;
+ border: 1px solid #e8eef7;
+ border-radius: 10px;
+ background: #f8fbff;
+ padding: 0 12px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+ margin-bottom: 10px;
+}
+
+.files-summary-main {
+ font-size: 12px;
+ color: #385373;
+ font-weight: 600;
+}
+
+.files-summary-sub {
+ font-size: 12px;
+ color: #6b8097;
+}
+
.icon-grid {
display: grid;
- gap: 12px;
- grid-template-columns: repeat(auto-fill, 144px);
+ gap: 8px 10px;
+ grid-template-columns: repeat(auto-fill, 112px);
justify-content: flex-start;
align-content: start;
min-height: 0;
overflow: auto;
- padding: 2px 4px 2px 2px;
+ padding: 4px;
}
.file-drop-surface {
position: relative;
min-height: 0;
flex: 1;
+ border: 1px solid #e8eef7;
+ border-radius: 12px;
+ background: #fff;
+ padding: 12px 10px 10px;
}
.file-drop-surface.active .icon-grid {
@@ -3469,19 +3524,19 @@ select:focus {
}
.file-card {
- border: 1px solid #d8e1ee;
- border-radius: 14px;
- background: #fff;
+ border: 1px solid transparent;
+ border-radius: 10px;
+ background: transparent;
text-align: left;
- padding: 12px;
+ padding: 8px 6px;
cursor: pointer;
display: flex;
flex-direction: column;
- align-items: flex-start;
+ align-items: center;
justify-content: flex-start;
- gap: 6px;
- width: 144px;
- height: 144px;
+ gap: 4px;
+ width: 112px;
+ min-height: 132px;
overflow: hidden;
position: relative;
}
@@ -3492,66 +3547,139 @@ select:focus {
}
.file-card:hover {
- border-color: #c6d7ee;
- box-shadow: 0 6px 12px rgba(34, 72, 116, 0.08);
+ border-color: #d7e6fb;
+ background: #f3f8ff;
}
.file-card.selected {
- border-color: #7baaf8;
- background: #eef5ff;
- box-shadow: 0 0 0 2px rgba(29, 111, 255, 0.1);
+ border-color: #a5c7f8;
+ background: #e8f2ff;
+ box-shadow: 0 0 0 1px rgba(73, 128, 216, 0.14) inset;
}
.file-card.batchSelected {
- border-color: #3f84ec;
- box-shadow: 0 0 0 2px rgba(63, 132, 236, 0.14);
+ border-color: #82b0f3;
+ background: #eaf3ff;
+ box-shadow: 0 0 0 1px rgba(63, 132, 236, 0.2) inset;
}
.file-card.renaming {
border-color: #7baaf8;
- box-shadow: 0 0 0 2px rgba(29, 111, 255, 0.14);
+ background: #eff6ff;
}
.batch-check {
position: absolute;
- right: 8px;
- top: 8px;
- width: 18px;
- height: 18px;
- border-radius: 6px;
- border: 1px solid #c3d3e7;
+ right: 6px;
+ top: 6px;
+ width: 16px;
+ height: 16px;
+ border-radius: 999px;
+ border: 1px solid #bad0eb;
background: #fff;
- color: #2a5da6;
+ color: #1f67c9;
display: grid;
place-items: center;
- font-size: 12px;
+ font-size: 11px;
font-weight: 700;
}
.batch-check.active {
- border-color: #3f84ec;
- background: #e6f0ff;
+ border-color: #2e7be8;
+ background: #2e7be8;
+ color: #fff;
}
-.file-icon-glyph {
- width: 48px;
- height: 48px;
+.file-icon-shell {
+ width: 62px;
+ height: 52px;
+ position: relative;
+ margin-top: 2px;
border-radius: 10px;
- border: 1px solid #d7e4f8;
- background: #f2f7ff;
- display: grid;
- place-items: center;
- font-size: 24px;
+ border: 1px solid #cfdced;
+ background: linear-gradient(180deg, #7ab5ff 0%, #4689dd 100%);
+ box-shadow: 0 2px 4px rgba(32, 77, 131, 0.14);
+}
+
+.file-icon-shell.kind-folder {
+ height: 44px;
+ margin-top: 8px;
+ border-radius: 8px;
+ border-color: #d8b860;
+ background: linear-gradient(180deg, #f2c85e 0%, #e3b145 100%);
+ box-shadow: 0 2px 4px rgba(152, 106, 14, 0.14);
+}
+
+.folder-tab {
+ position: absolute;
+ left: 9px;
+ top: -7px;
+ width: 24px;
+ height: 10px;
+ border-radius: 6px 6px 0 0;
+ background: linear-gradient(180deg, #f8d379 0%, #e8bc53 100%);
+ border: 1px solid #d8b860;
+ border-bottom: 0;
+}
+
+.file-corner {
+ position: absolute;
+ right: 0;
+ top: 0;
+ width: 16px;
+ height: 16px;
+ background: rgba(255, 255, 255, 0.9);
+ clip-path: polygon(100% 0, 0 0, 100% 100%);
+ border-top-right-radius: 8px;
+}
+
+.file-ext {
+ position: absolute;
+ left: 50%;
+ bottom: 7px;
+ transform: translateX(-50%);
+ color: #fff;
+ font-size: 10px;
+ font-weight: 700;
+ letter-spacing: 0.4px;
line-height: 1;
- margin-bottom: 2px;
+}
+
+.file-icon-shell.kind-document {
+ background: linear-gradient(180deg, #79b6ff 0%, #4c8ee2 100%);
+}
+
+.file-icon-shell.kind-image {
+ background: linear-gradient(180deg, #6fc7a1 0%, #3fa67b 100%);
+}
+
+.file-icon-shell.kind-video {
+ background: linear-gradient(180deg, #88b4ff 0%, #5b7fd9 100%);
+}
+
+.file-icon-shell.kind-audio {
+ background: linear-gradient(180deg, #ff9f7b 0%, #eb7059 100%);
+}
+
+.file-icon-shell.kind-archive {
+ background: linear-gradient(180deg, #f6bc74 0%, #e58e3a 100%);
+}
+
+.file-icon-shell.kind-app {
+ background: linear-gradient(180deg, #9cc36f 0%, #6ea649 100%);
+}
+
+.file-icon-shell.kind-file {
+ background: linear-gradient(180deg, #95b0d3 0%, #6684ad 100%);
}
.file-name {
- font-size: 13px;
- font-weight: 600;
+ font-size: 12px;
+ font-weight: 500;
color: #1f2b3a;
- line-height: 1.35;
+ line-height: 1.4;
width: 100%;
+ text-align: center;
overflow: hidden;
white-space: normal;
overflow-wrap: anywhere;
@@ -3559,13 +3687,13 @@ select:focus {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
- min-height: 34px;
- max-height: 34px;
+ min-height: 33px;
+ max-height: 33px;
}
.inline-rename-input {
width: 100%;
- height: 28px;
+ height: 30px;
border: 1px solid #7baaf8;
border-radius: 8px;
background: #fff;
@@ -3581,21 +3709,6 @@ select:focus {
box-shadow: 0 0 0 2px rgba(63, 132, 236, 0.16);
}
-.file-meta,
-.file-time {
- font-size: 11px;
- color: #60768f;
- line-height: 1.35;
- width: 100%;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.file-time {
- margin-top: auto;
-}
-
.task-list {
display: flex;
flex-direction: column;