feat: restyle desktop file grid to baidu-style icon layout
This commit is contained in:
@@ -563,17 +563,31 @@ function applyNativeUploadProgress(payload: NativeUploadProgressEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fileIcon(item: FileItem) {
|
function getFileExt(item: FileItem) {
|
||||||
if (item.isDirectory || item.type === "directory") return "📁";
|
if (item.isDirectory || item.type === "directory") return "";
|
||||||
const name = String(item.name || "").toLowerCase();
|
const text = String(item.displayName || item.name || "").trim();
|
||||||
if (/\.(jpg|jpeg|png|webp|gif|bmp|svg)$/.test(name)) return "🖼️";
|
const idx = text.lastIndexOf(".");
|
||||||
if (/\.(mp4|mkv|mov|avi)$/.test(name)) return "🎬";
|
if (idx <= 0 || idx >= text.length - 1) return "";
|
||||||
if (/\.(mp3|wav|flac|aac)$/.test(name)) return "🎵";
|
return text.slice(idx + 1).toLowerCase();
|
||||||
if (/\.(zip|rar|7z|tar|gz)$/.test(name)) return "🗜️";
|
}
|
||||||
if (/\.(pdf)$/.test(name)) return "📕";
|
|
||||||
if (/\.(doc|docx)$/.test(name)) return "📘";
|
function fileVisualKind(item: FileItem) {
|
||||||
if (/\.(xls|xlsx)$/.test(name)) return "📗";
|
if (item.isDirectory || item.type === "directory") return "folder";
|
||||||
return "📄";
|
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) {
|
function matchFileTypeFilter(item: FileItem, type: string) {
|
||||||
@@ -2480,6 +2494,13 @@ onBeforeUnmount(() => {
|
|||||||
<span>{{ pathState.mode === "search" ? "搜索结果" : "当前目录" }} {{ pathState.currentPath }}</span>
|
<span>{{ pathState.mode === "search" ? "搜索结果" : "当前目录" }} {{ pathState.currentPath }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="files-summary-bar">
|
||||||
|
<span class="files-summary-main">
|
||||||
|
{{ pathState.mode === "search" ? `搜索完成,共 ${filteredFiles.length} 个结果` : `已加载完成,共 ${filteredFiles.length} 个` }}
|
||||||
|
</span>
|
||||||
|
<span class="files-summary-sub">双击打开,右键查看更多操作</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="file-drop-surface" :class="{ active: dropState.active }">
|
<div class="file-drop-surface" :class="{ active: dropState.active }">
|
||||||
<div v-if="pathState.loading" class="empty-tip">正在加载目录...</div>
|
<div v-if="pathState.loading" class="empty-tip">正在加载目录...</div>
|
||||||
<div v-else-if="pathState.error" class="empty-tip error">{{ pathState.error }}</div>
|
<div v-else-if="pathState.error" class="empty-tip error">{{ pathState.error }}</div>
|
||||||
@@ -2501,7 +2522,15 @@ onBeforeUnmount(() => {
|
|||||||
<div v-if="batchMode" class="batch-check" :class="{ active: isBatchSelected(item.name) }">
|
<div v-if="batchMode" class="batch-check" :class="{ active: isBatchSelected(item.name) }">
|
||||||
{{ isBatchSelected(item.name) ? "✓" : "" }}
|
{{ isBatchSelected(item.name) ? "✓" : "" }}
|
||||||
</div>
|
</div>
|
||||||
<div class="file-icon-glyph">{{ fileIcon(item) }}</div>
|
<div class="file-icon-shell" :class="`kind-${fileVisualKind(item)}`">
|
||||||
|
<template v-if="item.isDirectory || item.type === 'directory'">
|
||||||
|
<span class="folder-tab" />
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span class="file-corner" />
|
||||||
|
<span class="file-ext">{{ fileExtLabel(item) }}</span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
<div class="file-name" :title="item.displayName || item.name">
|
<div class="file-name" :title="item.displayName || item.name">
|
||||||
<template v-if="isInlineRenaming(item)">
|
<template v-if="isInlineRenaming(item)">
|
||||||
<input
|
<input
|
||||||
@@ -2520,8 +2549,6 @@ onBeforeUnmount(() => {
|
|||||||
{{ item.displayName || item.name }}
|
{{ item.displayName || item.name }}
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="file-meta">{{ fileTypeLabel(item) }} · {{ item.isDirectory ? "-" : (item.sizeFormatted || formatBytes(item.size)) }}</div>
|
|
||||||
<div class="file-time">{{ formatDate(item.modifiedAt) }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="dropState.active" class="drop-overlay">
|
<div v-if="dropState.active" class="drop-overlay">
|
||||||
@@ -3330,7 +3357,7 @@ select:focus {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: flex-end;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input {
|
.search-input {
|
||||||
@@ -3447,21 +3474,49 @@ select:focus {
|
|||||||
text-overflow: ellipsis;
|
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 {
|
.icon-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 12px;
|
gap: 8px 10px;
|
||||||
grid-template-columns: repeat(auto-fill, 144px);
|
grid-template-columns: repeat(auto-fill, 112px);
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-content: start;
|
align-content: start;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding: 2px 4px 2px 2px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-drop-surface {
|
.file-drop-surface {
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
border: 1px solid #e8eef7;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: #fff;
|
||||||
|
padding: 12px 10px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-drop-surface.active .icon-grid {
|
.file-drop-surface.active .icon-grid {
|
||||||
@@ -3469,19 +3524,19 @@ select:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.file-card {
|
.file-card {
|
||||||
border: 1px solid #d8e1ee;
|
border: 1px solid transparent;
|
||||||
border-radius: 14px;
|
border-radius: 10px;
|
||||||
background: #fff;
|
background: transparent;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 12px;
|
padding: 8px 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
gap: 6px;
|
gap: 4px;
|
||||||
width: 144px;
|
width: 112px;
|
||||||
height: 144px;
|
min-height: 132px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@@ -3492,66 +3547,139 @@ select:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.file-card:hover {
|
.file-card:hover {
|
||||||
border-color: #c6d7ee;
|
border-color: #d7e6fb;
|
||||||
box-shadow: 0 6px 12px rgba(34, 72, 116, 0.08);
|
background: #f3f8ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-card.selected {
|
.file-card.selected {
|
||||||
border-color: #7baaf8;
|
border-color: #a5c7f8;
|
||||||
background: #eef5ff;
|
background: #e8f2ff;
|
||||||
box-shadow: 0 0 0 2px rgba(29, 111, 255, 0.1);
|
box-shadow: 0 0 0 1px rgba(73, 128, 216, 0.14) inset;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-card.batchSelected {
|
.file-card.batchSelected {
|
||||||
border-color: #3f84ec;
|
border-color: #82b0f3;
|
||||||
box-shadow: 0 0 0 2px rgba(63, 132, 236, 0.14);
|
background: #eaf3ff;
|
||||||
|
box-shadow: 0 0 0 1px rgba(63, 132, 236, 0.2) inset;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-card.renaming {
|
.file-card.renaming {
|
||||||
border-color: #7baaf8;
|
border-color: #7baaf8;
|
||||||
box-shadow: 0 0 0 2px rgba(29, 111, 255, 0.14);
|
background: #eff6ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.batch-check {
|
.batch-check {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 8px;
|
right: 6px;
|
||||||
top: 8px;
|
top: 6px;
|
||||||
width: 18px;
|
width: 16px;
|
||||||
height: 18px;
|
height: 16px;
|
||||||
border-radius: 6px;
|
border-radius: 999px;
|
||||||
border: 1px solid #c3d3e7;
|
border: 1px solid #bad0eb;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
color: #2a5da6;
|
color: #1f67c9;
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
font-size: 12px;
|
font-size: 11px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.batch-check.active {
|
.batch-check.active {
|
||||||
border-color: #3f84ec;
|
border-color: #2e7be8;
|
||||||
background: #e6f0ff;
|
background: #2e7be8;
|
||||||
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-icon-glyph {
|
.file-icon-shell {
|
||||||
width: 48px;
|
width: 62px;
|
||||||
height: 48px;
|
height: 52px;
|
||||||
|
position: relative;
|
||||||
|
margin-top: 2px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border: 1px solid #d7e4f8;
|
border: 1px solid #cfdced;
|
||||||
background: #f2f7ff;
|
background: linear-gradient(180deg, #7ab5ff 0%, #4689dd 100%);
|
||||||
display: grid;
|
box-shadow: 0 2px 4px rgba(32, 77, 131, 0.14);
|
||||||
place-items: center;
|
}
|
||||||
font-size: 24px;
|
|
||||||
|
.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;
|
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 {
|
.file-name {
|
||||||
font-size: 13px;
|
font-size: 12px;
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
color: #1f2b3a;
|
color: #1f2b3a;
|
||||||
line-height: 1.35;
|
line-height: 1.4;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
@@ -3559,13 +3687,13 @@ select:focus {
|
|||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
min-height: 34px;
|
min-height: 33px;
|
||||||
max-height: 34px;
|
max-height: 33px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inline-rename-input {
|
.inline-rename-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 28px;
|
height: 30px;
|
||||||
border: 1px solid #7baaf8;
|
border: 1px solid #7baaf8;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@@ -3581,21 +3709,6 @@ select:focus {
|
|||||||
box-shadow: 0 0 0 2px rgba(63, 132, 236, 0.16);
|
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 {
|
.task-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
Reference in New Issue
Block a user