ok了老铁

This commit is contained in:
DelLevin-Home
2026-03-07 23:57:36 +08:00
parent d28c5e562e
commit 901d6e27d6
5 changed files with 361 additions and 33 deletions

View File

@@ -1,9 +1,11 @@
import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/app_provider.dart';
import '../utils/user_prefs.dart';
import '../utils/toast_util.dart';
import '../models/data_models.dart';
/// 自定义左侧弹出菜单 - 极简主义设计
class CustomDrawer extends StatelessWidget {
@@ -20,6 +22,11 @@ class CustomDrawer extends StatelessWidget {
const Divider(height: 0.5, thickness: 0.5, color: Color(0xFFE5E5E5)),
// 回顾功能区域
_buildMemorySection(context),
const Divider(height: 0.5, thickness: 0.5, color: Color(0xFFE5E5E5)),
// 菜单项列表
Expanded(
child: ListView(
@@ -44,6 +51,219 @@ class CustomDrawer extends StatelessWidget {
);
}
/// 构建回顾功能区域
Widget _buildMemorySection(BuildContext context) {
return Consumer<AppProvider>(
builder: (context, provider, child) {
final memoryItem = _getRandomMemoryItem(provider);
if (memoryItem == null) {
return const SizedBox.shrink();
}
final memoryText = _buildMemoryText(memoryItem);
final timeAgo = _getTimeAgoText(memoryItem.date);
return Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题
Row(
children: [
const Icon(
Icons.history,
size: 16,
color: Color(0xFF666666),
),
const SizedBox(width: 8),
const Text(
'回顾',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: Color(0xFF666666),
),
),
],
),
const SizedBox(height: 12),
// 内容卡片(带头图)
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 头图(影视/书籍显示,笔记不显示)
if (memoryItem.imagePath != null && memoryItem.imagePath!.isNotEmpty)
Container(
width: 60,
height: 80,
margin: const EdgeInsets.only(right: 12),
decoration: BoxDecoration(
color: const Color(0xFFF5F5F5),
borderRadius: BorderRadius.circular(4),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(4),
child: Image.file(
File(memoryItem.imagePath!),
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => const Icon(
Icons.image,
color: Color(0xFFCCCCCC),
),
),
),
)
else if (memoryItem.type != 'note')
Container(
width: 60,
height: 80,
margin: const EdgeInsets.only(right: 12),
decoration: BoxDecoration(
color: const Color(0xFFF5F5F5),
borderRadius: BorderRadius.circular(4),
),
child: Icon(
memoryItem.type == 'movie' ? Icons.movie : Icons.menu_book,
color: const Color(0xFFCCCCCC),
size: 24,
),
),
// 文字内容
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 时间标签
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: const Color(0xFFF5F5F5),
borderRadius: BorderRadius.circular(4),
),
child: Text(
timeAgo,
style: const TextStyle(
fontSize: 11,
color: Color(0xFF999999),
),
),
),
const SizedBox(height: 8),
// 内容文字
Text(
memoryText,
style: const TextStyle(
fontSize: 14,
color: Color(0xFF1A1A1A),
height: 1.5,
),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
],
),
);
},
);
}
/// 获取随机回顾项
_MemoryItem? _getRandomMemoryItem(AppProvider provider) {
final now = DateTime.now();
final candidates = <_MemoryItem>[];
// 收集所有未删除的影视、书籍(去掉笔记)
for (final movie in provider.movies.where((m) => !m.isDeleted)) {
candidates.add(_MemoryItem(
type: 'movie',
title: movie.title,
date: movie.createdAt,
imagePath: movie.posterPath,
));
}
for (final book in provider.books.where((b) => !b.isDeleted)) {
candidates.add(_MemoryItem(
type: 'book',
title: book.title,
date: book.createdAt,
imagePath: book.coverPath,
));
}
if (candidates.isEmpty) return null;
// 优先选择1个月、3个月、6个月、1年前的数据
final oneMonthAgo = now.subtract(const Duration(days: 30));
final threeMonthsAgo = now.subtract(const Duration(days: 90));
final sixMonthsAgo = now.subtract(const Duration(days: 180));
final oneYearAgo = now.subtract(const Duration(days: 365));
final memoryCandidates = candidates.where((item) {
return _isInTimeRange(item.date, oneMonthAgo, threeMonthsAgo, sixMonthsAgo, oneYearAgo);
}).toList();
// 如果有符合时间范围的,从中随机选择;否则从所有数据中随机选择
final random = Random();
final selectedList = memoryCandidates.isNotEmpty ? memoryCandidates : candidates;
return selectedList[random.nextInt(selectedList.length)];
}
/// 检查时间是否在范围内1月、3月、6月、1年前
bool _isInTimeRange(DateTime date, DateTime oneMonth, DateTime threeMonths,
DateTime sixMonths, DateTime oneYear) {
// 检查是否在1个月前左右±7天
if (_isCloseTo(date, oneMonth)) return true;
// 检查是否在3个月前左右±14天
if (_isCloseTo(date, threeMonths, days: 14)) return true;
// 检查是否在6个月前左右±30天
if (_isCloseTo(date, sixMonths, days: 30)) return true;
// 检查是否在1年前左右±30天
if (_isCloseTo(date, oneYear, days: 30)) return true;
return false;
}
/// 检查两个日期是否接近
bool _isCloseTo(DateTime date, DateTime target, {int days = 7}) {
final diff = date.difference(target).inDays.abs();
return diff <= days;
}
/// 构建回顾文本
String _buildMemoryText(_MemoryItem item) {
// 只显示标题,不添加描述前缀
return '${item.title}';
}
/// 获取时间描述文本
String _getTimeAgoText(DateTime date) {
final now = DateTime.now();
final diff = now.difference(date);
if (diff.inDays >= 365) {
return '1年前';
} else if (diff.inDays >= 180) {
return '6个月前';
} else if (diff.inDays >= 90) {
return '3个月前';
} else if (diff.inDays >= 30) {
return '1个月前';
} else {
return '${diff.inDays}天前';
}
}
/// 构建头部
Widget _buildHeader(BuildContext context) {
return Consumer<AppProvider>(
@@ -192,3 +412,18 @@ class CustomDrawer extends StatelessWidget {
ToastUtil.show(context, message);
}
}
/// 回顾项数据类
class _MemoryItem {
final String type; // 'movie', 'book', 'note'
final String title;
final DateTime date;
final String? imagePath; // 头图路径(影视/书籍有,笔记无)
_MemoryItem({
required this.type,
required this.title,
required this.date,
this.imagePath,
});
}