背景
在缩略图文件夹中点击缩略图预览时,发现显示的是源文件而非缩略图本身,查看详情时信息也不准确。这是因为缩略图和普通文件共用同一个 FileMetadata 模型,UI 层无法区分。
此外,缩略图和源文件同名,先下载源文件再下载缩略图会导致覆盖。
设计决策
方案对比
| 方案 | 描述 | 优点 | 缺点 |
|---|---|---|---|
| A: 标志位 | FileMetadata 添加 isThumbnail 字段 | 信息随文件走,健壮性高 | 每个文件多 1 bool |
| B: 参数传递 | 预览页面传 isThumbnail 参数 | 不改模型 | 需要在多处传参,易漏 |
| C: 检查路径 | 根据 isInThumbsFolder 判断 | 改动小 | 耦合路径逻辑 |
选择方案 A,原因:
- 内存代价可忽略(1 bool/文件)
- 代码自解释,不怕漏传参数
- 未来新功能自动感知
命名规范后端化
缩略图命名加 _ 前缀的逻辑放在后端实现,原因:
- 前端换 UI 不需要重新实现
- 符合”核心逻辑后端化”的设计原则
实现
1. FileMetadata 添加 isThumbnail 字段
class FileMetadata {
// ... 现有字段
final bool isThumbnail; // 默认 false
}2. 后端 listThumbnails 返回 name 字段
type ThumbInfo struct {
ID string `json:"id"`
Name string `json:"name"` // 新增
Size int64 `json:"size"`
Time time.Time `json:"time"`
}
// 加载 metadata 获取源文件名,加 _ 前缀
name := "_" + id
if fileMeta, ok := meta.Files[id]; ok {
name = "_" + fileMeta.Name
}3. 前端根据 isThumbnail 选择 API
// ImagePreviewPage
final result = widget.file.isThumbnail
? await appState.api.getThumbnail(widget.file.id)
: await appState.api.downloadFile(widget.file.id);
// 下载逻辑同理
final result = job.file.isThumbnail
? await api.getThumbnail(job.file.id)
: await api.downloadFile(job.file.id);4. 详情对话框区分显示
- 图标:缩略图用
Icons.photo_size_select_actual(青色) - 类型:显示”缩略图”而非 mimeType
- ID 标签:显示”源文件ID”而非”文件ID”
- 隐藏”加密后大小”(缩略图没有此信息)
附带优化
标题栏只显示当前文件夹名
// 之前:显示完整路径 /文档/工作/
// 现在:只显示 工作
String path = state.currentPath;
if (path.endsWith('/')) {
path = path.substring(0, path.length - 1);
}
final folderName = path.substring(path.lastIndexOf('/') + 1);
return Text(folderName);行为变化总结
| 场景 | 之前 | 现在 |
|---|---|---|
| 点击缩略图预览 | 显示源文件 | 显示缩略图本身 |
| 下载缩略图 | 下载源文件 | 下载缩略图本身 |
| 缩略图文件名 | 与源文件同名 | _ + 源文件名 |
| 缩略图详情 | 类型显示 image/jpeg | 类型显示”缩略图” |
| 文件夹标题 | 完整路径 | 只显示文件夹名 |
经验总结
- 标志位 vs 参数传递:当信息需要在多个组件间流转时,标志位更健壮
- 后端化命名规则:减少前端逻辑,方便换 UI
- 模型变更注意事项:新增字段时 fromJson 需要安全回退(如
json['name'] ?? json['id'])