feat: add file type categorization and size formatting in FileListView

This commit is contained in:
shiyu
2026-05-08 21:39:49 +08:00
parent deddbdf585
commit ee4de697fc
3 changed files with 69 additions and 5 deletions

View File

@@ -239,6 +239,20 @@
"Type": "Type",
"Folder": "Folder",
"File": "File",
"Image": "Image",
"Video": "Video",
"Audio": "Audio",
"PDF": "PDF",
"Word": "Word",
"Spreadsheet": "Spreadsheet",
"Presentation": "Presentation",
"Archive": "Archive",
"Code": "Code",
"Markdown": "Markdown",
"Text": "Text",
"Font": "Font",
"Database": "Database",
"Config": "Config",
"Path": "Path",
"Path copied to clipboard": "Path copied to clipboard",
"Copy failed": "Copy failed",

View File

@@ -258,6 +258,20 @@
"Type": "类型",
"Folder": "文件夹",
"File": "文件",
"Image": "图片",
"Video": "视频",
"Audio": "音频",
"PDF": "PDF",
"Word": "Word 文档",
"Spreadsheet": "表格",
"Presentation": "演示文稿",
"Archive": "压缩包",
"Code": "代码",
"Markdown": "Markdown",
"Text": "文本",
"Font": "字体",
"Database": "数据库",
"Config": "配置",
"Path": "路径",
"Path copied to clipboard": "路径已复制到剪贴板",
"Copy failed": "复制失败",
@@ -775,7 +789,6 @@
"Users": "用户",
"Create User": "创建用户",
"Create Role": "创建角色",
"Edit": "编辑",
"Submit": "提交",
"Super Admin": "超级管理员",
"Disabled": "已禁用",
@@ -796,14 +809,11 @@
"Is Regex": "正则表达式",
"Priority": "优先级",
"Higher value = higher priority": "数值越大优先级越高",
"Permissions": "权限",
"System Permissions": "系统权限",
"Download and preview files": "下载和预览文件",
"Upload and modify files": "上传和修改文件",
"Delete files and folders": "删除文件和目录",
"Create share links": "创建分享链接",
"Share": "分享",
"Delete": "删除",
"permission.category.system": "系统",
"permission.category.adapter": "存储适配器"
}

View File

@@ -19,6 +19,45 @@ interface FileListViewProps {
onContextMenu: (e: React.MouseEvent, entry: VfsEntry) => void;
}
const fileTypeGroups: Array<{ key: string; exts: string[] }> = [
{ key: 'Image', exts: ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'bmp', 'ico', 'tiff'] },
{ key: 'Video', exts: ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm', 'm4v', '3gp'] },
{ key: 'Audio', exts: ['mp3', 'wav', 'flac', 'aac', 'ogg', 'wma', 'm4a'] },
{ key: 'PDF', exts: ['pdf'] },
{ key: 'Word', exts: ['doc', 'docx'] },
{ key: 'Spreadsheet', exts: ['xls', 'xlsx', 'csv'] },
{ key: 'Presentation', exts: ['ppt', 'pptx'] },
{ key: 'Archive', exts: ['zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz'] },
{ key: 'Code', exts: ['js', 'jsx', 'ts', 'tsx', 'vue', 'html', 'htm', 'css', 'scss', 'sass', 'less', 'json', 'xml', 'yaml', 'yml', 'py', 'java', 'cpp', 'cc', 'cxx', 'c', 'h', 'hpp', 'hxx', 'php', 'rb', 'go', 'rs', 'rust', 'swift', 'kt', 'scala', 'clj', 'cljs', 'cs', 'vb', 'fs', 'pl', 'pm', 'r', 'lua', 'dart', 'elm'] },
{ key: 'Markdown', exts: ['md', 'markdown'] },
{ key: 'Text', exts: ['txt', 'log', 'ini', 'cfg', 'conf', 'sh', 'bash', 'zsh', 'fish', 'ps1', 'bat', 'cmd', 'dockerfile', 'makefile', 'gradle', 'cmake', 'gitignore', 'gitattributes', 'editorconfig', 'prettierrc'] },
{ key: 'Font', exts: ['ttf', 'otf', 'woff', 'woff2', 'eot'] },
{ key: 'Database', exts: ['db', 'sqlite', 'sql'] },
{ key: 'Config', exts: ['env', 'config', 'properties', 'toml'] },
];
const formatFileSize = (size: number) => {
if (!Number.isFinite(size) || size < 0) return '-';
const units = ['B', 'KB', 'MB', 'GB'];
let value = size;
let unitIndex = 0;
while (value >= 1024 && unitIndex < units.length - 1) {
value /= 1024;
unitIndex += 1;
}
if (unitIndex === 0) return `${value} ${units[unitIndex]}`;
return `${value.toFixed(2)} ${units[unitIndex]}`;
};
const getFileTypeLabel = (entry: VfsEntry, t: (key: string) => string) => {
if (entry.type === 'mount') return t('Mount Point');
if (entry.is_dir) return t('Folder');
const ext = entry.name.split('.').pop()?.toLowerCase() || '';
const group = fileTypeGroups.find(item => item.exts.includes(ext));
return t(group?.key || 'File');
};
export const FileListView: React.FC<FileListViewProps> = ({
entries,
selectedEntries,
@@ -63,7 +102,8 @@ export const FileListView: React.FC<FileListViewProps> = ({
</span>
)
},
{ title: t('Size'), dataIndex: 'size', width: 100, render: (v: number, r: VfsEntry) => r.is_dir ? '-' : v },
{ title: t('Type'), key: 'fileType', width: 110, render: (_: any, r: VfsEntry) => getFileTypeLabel(r, t) },
{ title: t('Size'), dataIndex: 'size', width: 120, render: (v: number, r: VfsEntry) => r.is_dir ? '-' : formatFileSize(v) },
{ title: t('Modified Time'), dataIndex: 'mtime', width: 160, render: (v: number) => v ? new Date(v * 1000).toLocaleString() : '-' },
{
title: t('Actions'),