二次开发源码提交

This commit is contained in:
2026-05-15 08:52:48 +08:00
parent 4131f7321a
commit eb35a1d067
191 changed files with 34566 additions and 0 deletions
+232
View File
@@ -0,0 +1,232 @@
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
import '../../data/models/file_model.dart';
import 'file_utils.dart';
/// 文件图标、颜色、类型标签统一管理
class FileIconUtils {
// ---- 文件图标 ----
// 后缀分类映射:安装包/可执行/字体/数据库/镜像等
static const _installerExtensions = {'apk', 'exe', 'msi', 'dmg', 'deb', 'rpm', 'appimage', 'snap'};
static const _executableExtensions = {'sh', 'bat', 'cmd', 'ps1', 'bin', 'run'};
static const _fontExtensions = {'ttf', 'otf', 'woff', 'woff2', 'eot'};
static const _databaseExtensions = {'db', 'sqlite', 'sqlite3', 'mdb', 'sql'};
static const _imageDiskExtensions = {'iso', 'img', 'vmdk', 'vdi', 'qcow2'};
static const _torrentExtensions = {'torrent'};
static IconData getFileIcon(String fileName) {
if (FileUtils.isImageFile(fileName)) return LucideIcons.image;
if (FileUtils.isVideoFile(fileName)) return LucideIcons.video;
if (FileUtils.isAudioFile(fileName)) return LucideIcons.music;
if (FileUtils.isPdfFile(fileName)) return LucideIcons.fileText;
if (FileUtils.isTextFile(fileName)) return LucideIcons.fileText;
if (FileUtils.isCodeFile(fileName)) return LucideIcons.code;
if (FileUtils.isArchiveFile(fileName)) return LucideIcons.archive;
if (FileUtils.isDocumentFile(fileName)) return LucideIcons.file;
// fallback: 按后缀分类
final ext = FileUtils.getFileExtension(fileName);
if (_installerExtensions.contains(ext)) return LucideIcons.package;
if (_executableExtensions.contains(ext)) return LucideIcons.terminal;
if (_fontExtensions.contains(ext)) return LucideIcons.type;
if (_databaseExtensions.contains(ext)) return LucideIcons.database;
if (_imageDiskExtensions.contains(ext)) return LucideIcons.hardDrive;
if (_torrentExtensions.contains(ext)) return LucideIcons.download;
return LucideIcons.file;
}
static Color getFileIconColor(String fileName) {
if (FileUtils.isImageFile(fileName)) return const Color(0xFFA855F7);
if (FileUtils.isVideoFile(fileName)) return const Color(0xFFF97316);
if (FileUtils.isAudioFile(fileName)) return const Color(0xFF3B82F6);
if (FileUtils.isPdfFile(fileName)) return const Color(0xFFEF4444);
if (FileUtils.isTextFile(fileName)) return const Color(0xFF14B8A6);
if (FileUtils.isCodeFile(fileName)) return const Color(0xFF06B6D4);
if (FileUtils.isArchiveFile(fileName)) return const Color(0xFFF59E0B);
if (FileUtils.isDocumentFile(fileName)) return const Color(0xFF6366F1);
// fallback: 按后缀分类
final ext = FileUtils.getFileExtension(fileName);
if (_installerExtensions.contains(ext)) return const Color(0xFF22C55E);
if (_executableExtensions.contains(ext)) return const Color(0xFF10B981);
if (_fontExtensions.contains(ext)) return const Color(0xFF8B5CF6);
if (_databaseExtensions.contains(ext)) return const Color(0xFF6366F1);
if (_imageDiskExtensions.contains(ext)) return const Color(0xFF64748B);
if (_torrentExtensions.contains(ext)) return const Color(0xFF06B6D4);
// 真正未知:基于后缀名 hash 生成稳定颜色
if (ext.isNotEmpty) return _stableColor(ext);
return const Color(0xFF64748B);
}
/// 基于字符串生成稳定的颜色(避免同类型不同后缀撞色)
static Color _stableColor(String key) {
final hash = key.hashCode.abs();
const palette = [
Color(0xFF64748B), Color(0xFF6366F1), Color(0xFF8B5CF6),
Color(0xFFEC4899), Color(0xFF14B8A6), Color(0xFFF59E0B),
Color(0xFF22C55E), Color(0xFF0EA5E9), Color(0xFFEF4444),
];
return palette[hash % palette.length];
}
/// 文件类型中文标签
static String getFileTypeLabel(String fileName, {bool isFolder = false}) {
if (isFolder) return '文件夹';
if (FileUtils.isImageFile(fileName)) return '图片';
if (FileUtils.isVideoFile(fileName)) return '视频';
if (FileUtils.isAudioFile(fileName)) return '音频';
if (FileUtils.isPdfFile(fileName)) return 'PDF';
if (FileUtils.isTextFile(fileName)) {
final ext = FileUtils.getFileExtension(fileName);
return ext.isNotEmpty ? '${ext.toUpperCase()}文件' : '文本';
}
if (FileUtils.isCodeFile(fileName)) {
final ext = FileUtils.getFileExtension(fileName);
return ext.isNotEmpty ? '${ext.toUpperCase()}文件' : '代码';
}
if (FileUtils.isArchiveFile(fileName)) return '压缩包';
if (FileUtils.isDocumentFile(fileName)) return '文档';
// fallback: 未知后缀直接用大写后缀名
final ext = FileUtils.getFileExtension(fileName);
if (ext.isNotEmpty) return '${ext.toUpperCase()}文件';
return '文件';
}
// ---- 语义化文件夹图标 ----
static final _folderIconMap = <String, IconData>{
'DOCUMENTS': LucideIcons.fileText,
'DOCS': LucideIcons.fileText,
'PICTURES': LucideIcons.image,
'PHOTOS': LucideIcons.image,
'IMAGES': LucideIcons.image,
'VIDEOS': LucideIcons.video,
'MOVIES': LucideIcons.video,
'MUSIC': LucideIcons.music,
'AUDIO': LucideIcons.music,
'DOWNLOADS': LucideIcons.download,
'GAMES': LucideIcons.gamepad2,
'OS_IMAGE': LucideIcons.hardDrive,
'DOCKER': LucideIcons.container,
'CONTAINERS': LucideIcons.container,
'ARIA2': LucideIcons.download,
'BACKUP': LucideIcons.archive,
'DESKTOP': LucideIcons.monitor,
'FAVORITES': LucideIcons.star,
'SHARE': LucideIcons.share2,
'SHARED': LucideIcons.share2,
'TRASH': LucideIcons.trash2,
'RECYCLE': LucideIcons.trash2,
};
static final _folderColorMap = <String, Color>{
'DOCUMENTS': const Color(0xFF3B82F6),
'DOCS': const Color(0xFF3B82F6),
'PICTURES': const Color(0xFFA855F7),
'PHOTOS': const Color(0xFFA855F7),
'IMAGES': const Color(0xFFA855F7),
'VIDEOS': const Color(0xFFF97316),
'MOVIES': const Color(0xFFF97316),
'MUSIC': const Color(0xFF3B82F6),
'AUDIO': const Color(0xFF3B82F6),
'DOWNLOADS': const Color(0xFF22C55E),
'GAMES': const Color(0xFF8B5CF6),
'OS_IMAGE': const Color(0xFF64748B),
'DOCKER': const Color(0xFF0EA5E9),
'CONTAINERS': const Color(0xFF0EA5E9),
'ARIA2': const Color(0xFF06B6D4),
'BACKUP': const Color(0xFFF59E0B),
'DESKTOP': const Color(0xFF6366F1),
'FAVORITES': const Color(0xFFEAB308),
'SHARE': const Color(0xFF14B8A6),
'SHARED': const Color(0xFF14B8A6),
'TRASH': const Color(0xFFEF4444),
'RECYCLE': const Color(0xFFEF4444),
};
static final _folderKeywords = <String, IconData>{
'文档': LucideIcons.fileText,
'图片': LucideIcons.image,
'相册': LucideIcons.image,
'视频': LucideIcons.video,
'音乐': LucideIcons.music,
'下载': LucideIcons.download,
'游戏': LucideIcons.gamepad2,
'备份': LucideIcons.archive,
'桌面': LucideIcons.monitor,
'收藏': LucideIcons.star,
'分享': LucideIcons.share2,
'回收站': LucideIcons.trash2,
};
static final _folderKeywordColors = <String, Color>{
'文档': const Color(0xFF3B82F6),
'图片': const Color(0xFFA855F7),
'相册': const Color(0xFFA855F7),
'视频': const Color(0xFFF97316),
'音乐': const Color(0xFF3B82F6),
'下载': const Color(0xFF22C55E),
'游戏': const Color(0xFF8B5CF6),
'备份': const Color(0xFFF59E0B),
'桌面': const Color(0xFF6366F1),
'收藏': const Color(0xFFEAB308),
'分享': const Color(0xFF14B8A6),
'回收站': const Color(0xFFEF4444),
};
static IconData getFolderIcon(String folderName) {
final upper = folderName.toUpperCase();
if (_folderIconMap.containsKey(upper)) return _folderIconMap[upper]!;
for (final entry in _folderKeywords.entries) {
if (folderName.contains(entry.key)) return entry.value;
}
return LucideIcons.folder;
}
static Color? getFolderIconColor(String folderName) {
final upper = folderName.toUpperCase();
if (_folderColorMap.containsKey(upper)) return _folderColorMap[upper]!;
for (final entry in _folderKeywordColors.entries) {
if (folderName.contains(entry.key)) return entry.value;
}
return null; // 使用 colorScheme.primary 作为默认
}
// ---- 统一图标容器组件 ----
static Widget buildIconWidget({
required BuildContext context,
required FileModel file,
double size = 36,
double iconSize = 20,
double borderRadius = 8,
}) {
final colorScheme = Theme.of(context).colorScheme;
final isDark = Theme.of(context).brightness == Brightness.dark;
if (file.isFolder) {
final folderColor = getFolderIconColor(file.name) ?? colorScheme.primary;
final folderIcon = getFolderIcon(file.name);
return Container(
width: size,
height: size,
decoration: BoxDecoration(
color: folderColor.withValues(alpha: isDark ? 0.15 : 0.1),
borderRadius: BorderRadius.circular(borderRadius),
),
child: Icon(folderIcon, color: folderColor, size: iconSize),
);
}
final icon = getFileIcon(file.name);
final iconColor = getFileIconColor(file.name);
return Container(
width: size,
height: size,
decoration: BoxDecoration(
color: iconColor.withValues(alpha: isDark ? 0.15 : 0.1),
borderRadius: BorderRadius.circular(borderRadius),
),
child: Icon(icon, color: iconColor, size: iconSize),
);
}
}