Files
xianyan/lib/features/mine/settings/presentation/image_cache_models.dart
Developer 9ea8d3d606 chore: 汇总批量提交的功能优化与bug修复
本次提交包含多项迭代优化和问题修复:
1. 新增缩略图图片组件、数字格式化工具类,补充多语言翻译类型与本地化支持
2. 优化底部导航栏主题色统一使用动态accent色值
3. 修复多处图表动画、路由跳转、API请求相关问题
4. 简化服务器公告文案,调整默认分屏状态为关闭
5. 新增安卓/iOS桌面快捷方式配置
6. 重构多处状态管理类使用SafeNotifierInit统一异常保护
7. 替换硬编码蓝色为主题色,更新版本号获取方式为动态读取
8. 优化缓存预加载逻辑,移除无用代码
9. 调整默认设置项,优化用户体验细节
2026-05-31 12:24:05 +08:00

264 lines
7.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// ============================================================
/// 闲言APP — 图片缓存模型与工具类
/// 创建时间: 2026-05-30
/// 更新时间: 2026-05-30
/// 作用: 图片缓存页面的数据模型、枚举、格式化工具、分类映射
/// 上次更新: 新增ImageCacheState/CacheCleanLogEntry/CacheImageExtensions扩展CacheItem字段
/// ============================================================
import 'package:flutter/cupertino.dart';
import '../../../../core/services/data/image_cache_metadata_service.dart';
import '../../../../l10n/translations.dart';
/// 视图模式:网格 / 列表
enum ViewMode { grid, list }
/// 排序模式:按时间 / 按大小 / 按类型
enum SortMode { date, size, type }
/// 缓存条目数据模型
class CacheItem {
const CacheItem({
required this.path,
required this.size,
required this.modified,
this.category,
this.sourceUrl,
this.expiresAt,
});
final String path;
final int size;
final DateTime modified;
final String? category;
final String? sourceUrl;
final DateTime? expiresAt;
}
/// 缓存格式化工具
class CacheFormatter {
CacheFormatter._();
/// 格式化文件大小
static String formatSize(int bytes) {
if (bytes <= 0) return '0 B';
if (bytes < 1024) return '$bytes B';
if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB';
if (bytes < 1024 * 1024 * 1024) {
return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
}
return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)} GB';
}
/// 格式化日期时间
static String formatDate(DateTime dt) {
return '${dt.year}-${dt.month.toString().padLeft(2, '0')}-${dt.day.toString().padLeft(2, '0')} '
'${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}';
}
/// 计算缓存占比
static double cacheRatio(int part, int total) {
if (total <= 0) return 0;
return part / total;
}
}
/// 缓存图片扩展名常量
class CacheImageExtensions {
CacheImageExtensions._();
static const List<String> image = [
'jpg',
'jpeg',
'png',
'gif',
'webp',
'bmp',
];
static bool isImage(String path) {
final ext = path.split('.').last.toLowerCase();
return image.contains(ext);
}
}
/// 缓存清理日志条目
class CacheCleanLogEntry {
const CacheCleanLogEntry({
required this.timestamp,
required this.type,
required this.count,
required this.size,
this.category,
});
final DateTime timestamp;
final String type;
final int count;
final int size;
final String? category;
Map<String, dynamic> toJson() => {
'timestamp': timestamp.toIso8601String(),
'type': type,
'count': count,
'size': size,
'category': category,
};
factory CacheCleanLogEntry.fromJson(Map<String, dynamic> json) {
return CacheCleanLogEntry(
timestamp: DateTime.parse(json['timestamp'] as String),
type: json['type'] as String,
count: json['count'] as int,
size: json['size'] as int,
category: json['category'] as String?,
);
}
String typeLabel(TSettingsCache ct) => switch (type) {
'expired' => '🗑️ ${ct.clearExpiredCache}',
'all' => '⚠️ ${ct.clearAllCache}',
'single' => '📄 ${ct.delete}',
'batch' => '📋 ${ct.batchDelete}',
'category' => '📁 ${ct.category}',
_ => '🧹 ${ct.cleanLog}',
};
}
/// 图片缓存页面状态
class ImageCacheState {
const ImageCacheState({
this.cacheItems = const [],
this.totalSize = 0,
this.imageCacheCount = 0,
this.expiredCount = 0,
this.expiredSize = 0,
this.categoryStats = const {},
this.categoryCounts = const {},
this.autoCleanPolicy = AutoCleanPolicy.days7,
this.cacheSizeLimit = 100,
this.isLoading = true,
this.isCleaning = false,
this.cleanProgress = 0,
this.cleanTotal = 0,
this.isBatchMode = false,
this.selectedPaths = const {},
this.viewMode = ViewMode.grid,
this.sortMode = SortMode.date,
this.selectedCategory,
this.error,
});
final List<CacheItem> cacheItems;
final int totalSize;
final int imageCacheCount;
final int expiredCount;
final int expiredSize;
final Map<String, int> categoryStats;
final Map<String, int> categoryCounts;
final String autoCleanPolicy;
final int cacheSizeLimit;
final bool isLoading;
final bool isCleaning;
final int cleanProgress;
final int cleanTotal;
final bool isBatchMode;
final Set<String> selectedPaths;
final ViewMode viewMode;
final SortMode sortMode;
final String? selectedCategory;
final String? error;
int get expiredDays => AutoCleanPolicy.toDays(autoCleanPolicy) ?? 7;
List<CacheItem> get filteredItems {
if (selectedCategory == null) return cacheItems;
return cacheItems.where((i) => i.category == selectedCategory).toList();
}
Map<String, List<CacheItem>> get groupedByDate {
final items = filteredItems;
final groups = <String, List<CacheItem>>{};
for (final item in items) {
final group = DateGroup.groupOf(item.modified);
groups.putIfAbsent(group, () => []).add(item);
}
return groups;
}
ImageCacheState copyWith({
List<CacheItem>? cacheItems,
int? totalSize,
int? imageCacheCount,
int? expiredCount,
int? expiredSize,
Map<String, int>? categoryStats,
Map<String, int>? categoryCounts,
String? autoCleanPolicy,
int? cacheSizeLimit,
bool? isLoading,
bool? isCleaning,
int? cleanProgress,
int? cleanTotal,
bool? isBatchMode,
Set<String>? selectedPaths,
ViewMode? viewMode,
SortMode? sortMode,
String? Function()? selectedCategory,
String? Function()? error,
}) {
return ImageCacheState(
cacheItems: cacheItems ?? this.cacheItems,
totalSize: totalSize ?? this.totalSize,
imageCacheCount: imageCacheCount ?? this.imageCacheCount,
expiredCount: expiredCount ?? this.expiredCount,
expiredSize: expiredSize ?? this.expiredSize,
categoryStats: categoryStats ?? this.categoryStats,
categoryCounts: categoryCounts ?? this.categoryCounts,
autoCleanPolicy: autoCleanPolicy ?? this.autoCleanPolicy,
cacheSizeLimit: cacheSizeLimit ?? this.cacheSizeLimit,
isLoading: isLoading ?? this.isLoading,
isCleaning: isCleaning ?? this.isCleaning,
cleanProgress: cleanProgress ?? this.cleanProgress,
cleanTotal: cleanTotal ?? this.cleanTotal,
isBatchMode: isBatchMode ?? this.isBatchMode,
selectedPaths: selectedPaths ?? this.selectedPaths,
viewMode: viewMode ?? this.viewMode,
sortMode: sortMode ?? this.sortMode,
selectedCategory: selectedCategory != null
? selectedCategory()
: this.selectedCategory,
error: error != null ? error() : this.error,
);
}
}
/// 缓存分类图标与颜色映射
class CacheCategoryStyle {
CacheCategoryStyle._();
/// 分类对应图标
static IconData icon(String category) {
return switch (category) {
CacheCategory.avatar => CupertinoIcons.person_fill,
CacheCategory.wallpaper => CupertinoIcons.photo_fill,
CacheCategory.feed => CupertinoIcons.doc_text_fill,
CacheCategory.card => CupertinoIcons.square_stack_3d_up_fill,
_ => CupertinoIcons.archivebox_fill,
};
}
/// 分类对应颜色
static Color color(String category) {
return switch (category) {
CacheCategory.avatar => CupertinoColors.systemPurple,
CacheCategory.wallpaper => CupertinoColors.systemTeal,
CacheCategory.feed => CupertinoColors.systemBlue,
CacheCategory.card => CupertinoColors.systemPink,
_ => CupertinoColors.systemGrey,
};
}
}