generated from dellevin/template
ok了老铁
This commit is contained in:
@@ -9,22 +9,36 @@ extension MovieExtension on Movie {
|
|||||||
Movie copyWith({
|
Movie copyWith({
|
||||||
String? id,
|
String? id,
|
||||||
String? title,
|
String? title,
|
||||||
String? poster,
|
String? posterPath,
|
||||||
|
DateTime? releaseDate,
|
||||||
|
List<String>? directors,
|
||||||
|
List<String>? writers,
|
||||||
|
List<String>? actors,
|
||||||
|
List<String>? genres,
|
||||||
|
List<String>? alternateTitles,
|
||||||
|
String? summary,
|
||||||
double? rating,
|
double? rating,
|
||||||
int? year,
|
|
||||||
String? status,
|
String? status,
|
||||||
DateTime? watchDate,
|
DateTime? createdAt,
|
||||||
String? note,
|
DateTime? updatedAt,
|
||||||
|
bool? isDeleted,
|
||||||
}) {
|
}) {
|
||||||
return Movie(
|
return Movie(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
title: title ?? this.title,
|
title: title ?? this.title,
|
||||||
poster: poster ?? this.poster,
|
posterPath: posterPath ?? this.posterPath,
|
||||||
|
releaseDate: releaseDate ?? this.releaseDate,
|
||||||
|
directors: directors ?? this.directors,
|
||||||
|
writers: writers ?? this.writers,
|
||||||
|
actors: actors ?? this.actors,
|
||||||
|
genres: genres ?? this.genres,
|
||||||
|
alternateTitles: alternateTitles ?? this.alternateTitles,
|
||||||
|
summary: summary ?? this.summary,
|
||||||
rating: rating ?? this.rating,
|
rating: rating ?? this.rating,
|
||||||
year: year ?? this.year,
|
|
||||||
status: status ?? this.status,
|
status: status ?? this.status,
|
||||||
watchDate: watchDate ?? this.watchDate,
|
createdAt: createdAt ?? this.createdAt,
|
||||||
note: note ?? this.note,
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
|
isDeleted: isDeleted ?? this.isDeleted,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -35,22 +49,32 @@ extension BookExtension on Book {
|
|||||||
Book copyWith({
|
Book copyWith({
|
||||||
String? id,
|
String? id,
|
||||||
String? title,
|
String? title,
|
||||||
String? author,
|
String? coverPath,
|
||||||
String? cover,
|
List<String>? authors,
|
||||||
|
List<String>? alternateTitles,
|
||||||
|
String? publisher,
|
||||||
|
List<String>? genres,
|
||||||
|
String? summary,
|
||||||
double? rating,
|
double? rating,
|
||||||
String? status,
|
String? status,
|
||||||
DateTime? readDate,
|
DateTime? createdAt,
|
||||||
String? note,
|
DateTime? updatedAt,
|
||||||
|
bool? isDeleted,
|
||||||
}) {
|
}) {
|
||||||
return Book(
|
return Book(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
title: title ?? this.title,
|
title: title ?? this.title,
|
||||||
author: author ?? this.author,
|
coverPath: coverPath ?? this.coverPath,
|
||||||
cover: cover ?? this.cover,
|
authors: authors ?? this.authors,
|
||||||
|
alternateTitles: alternateTitles ?? this.alternateTitles,
|
||||||
|
publisher: publisher ?? this.publisher,
|
||||||
|
genres: genres ?? this.genres,
|
||||||
|
summary: summary ?? this.summary,
|
||||||
rating: rating ?? this.rating,
|
rating: rating ?? this.rating,
|
||||||
status: status ?? this.status,
|
status: status ?? this.status,
|
||||||
readDate: readDate ?? this.readDate,
|
createdAt: createdAt ?? this.createdAt,
|
||||||
note: note ?? this.note,
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
|
isDeleted: isDeleted ?? this.isDeleted,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,19 +84,23 @@ extension NoteExtension on Note {
|
|||||||
/// 创建副本并允许修改部分属性
|
/// 创建副本并允许修改部分属性
|
||||||
Note copyWith({
|
Note copyWith({
|
||||||
String? id,
|
String? id,
|
||||||
String? title,
|
|
||||||
String? content,
|
String? content,
|
||||||
|
String? contentType,
|
||||||
List<String>? tags,
|
List<String>? tags,
|
||||||
|
List<String>? images,
|
||||||
DateTime? createdAt,
|
DateTime? createdAt,
|
||||||
DateTime? updatedAt,
|
DateTime? updatedAt,
|
||||||
|
bool? isDeleted,
|
||||||
}) {
|
}) {
|
||||||
return Note(
|
return Note(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
title: title ?? this.title,
|
|
||||||
content: content ?? this.content,
|
content: content ?? this.content,
|
||||||
|
contentType: contentType ?? this.contentType,
|
||||||
tags: tags ?? this.tags,
|
tags: tags ?? this.tags,
|
||||||
|
images: images ?? this.images,
|
||||||
createdAt: createdAt ?? this.createdAt,
|
createdAt: createdAt ?? this.createdAt,
|
||||||
updatedAt: updatedAt ?? this.updatedAt,
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
|
isDeleted: isDeleted ?? this.isDeleted,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -410,6 +410,12 @@ class _NoteFormPageState extends State<NoteFormPage> {
|
|||||||
void _showAddTagDialog() {
|
void _showAddTagDialog() {
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
|
|
||||||
|
// 获取所有已有标签(从所有笔记中收集)
|
||||||
|
final provider = context.read<AppProvider>();
|
||||||
|
final allTags = _getAllExistingTags(provider);
|
||||||
|
// 过滤掉已添加的标签
|
||||||
|
final availableTags = allTags.where((tag) => !_tags.contains(tag)).toList();
|
||||||
|
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
@@ -423,11 +429,18 @@ class _NoteFormPageState extends State<NoteFormPage> {
|
|||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
content: TextField(
|
content: SizedBox(
|
||||||
|
width: double.maxFinite,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// 输入框
|
||||||
|
TextField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
hintText: '输入标签名称',
|
hintText: '输入新标签名称',
|
||||||
border: UnderlineInputBorder(),
|
border: UnderlineInputBorder(),
|
||||||
),
|
),
|
||||||
onSubmitted: (value) {
|
onSubmitted: (value) {
|
||||||
@@ -435,6 +448,49 @@ class _NoteFormPageState extends State<NoteFormPage> {
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// 已有标签列表
|
||||||
|
if (availableTags.isNotEmpty) ...[
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Text(
|
||||||
|
'或选择已有标签:',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Color(0xFF999999),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Wrap(
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 8,
|
||||||
|
children: availableTags.map((tag) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
_addTag(tag);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFFF5F5F5),
|
||||||
|
border: Border.all(color: const Color(0xFFE5E5E5)),
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
tag,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: Color(0xFF666666),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context),
|
||||||
@@ -452,6 +508,15 @@ class _NoteFormPageState extends State<NoteFormPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 获取所有已有标签(从所有笔记中收集)
|
||||||
|
List<String> _getAllExistingTags(AppProvider provider) {
|
||||||
|
final allTags = <String>{};
|
||||||
|
for (final note in provider.notes) {
|
||||||
|
allTags.addAll(note.tags);
|
||||||
|
}
|
||||||
|
return allTags.toList()..sort();
|
||||||
|
}
|
||||||
|
|
||||||
/// 添加标签
|
/// 添加标签
|
||||||
void _addTag(String tag) {
|
void _addTag(String tag) {
|
||||||
final trimmed = tag.trim();
|
final trimmed = tag.trim();
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class _WebDAVSyncPageState extends State<WebDAVSyncPage> {
|
|||||||
try {
|
try {
|
||||||
if (value) {
|
if (value) {
|
||||||
await WebDAVService.instance.startAutoSync();
|
await WebDAVService.instance.startAutoSync();
|
||||||
ToastUtil.show(context, '自动备份已开启,每2分钟执行一次');
|
ToastUtil.show(context, '自动备份已开启,每5分钟执行一次');
|
||||||
} else {
|
} else {
|
||||||
await WebDAVService.instance.stopAutoSync();
|
await WebDAVService.instance.stopAutoSync();
|
||||||
ToastUtil.show(context, '自动备份已关闭');
|
ToastUtil.show(context, '自动备份已关闭');
|
||||||
@@ -386,7 +386,7 @@ class _WebDAVSyncPageState extends State<WebDAVSyncPage> {
|
|||||||
),
|
),
|
||||||
SizedBox(height: 2),
|
SizedBox(height: 2),
|
||||||
Text(
|
Text(
|
||||||
'每2分钟自动备份一次,保留最近10个备份',
|
'每5分钟自动备份一次,保留最近10个备份',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: Color(0xFF999999),
|
color: Color(0xFF999999),
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class WebDAVService {
|
|||||||
static const String _backupListKey = 'webdav_backup_list';
|
static const String _backupListKey = 'webdav_backup_list';
|
||||||
|
|
||||||
static const int _maxBackupCount = 10; // 保留最近10条备份
|
static const int _maxBackupCount = 10; // 保留最近10条备份
|
||||||
static const Duration _autoSyncInterval = Duration(minutes: 2); // 每2分钟自动备份
|
static const Duration _autoSyncInterval = Duration(minutes: 5); // 每5分钟自动备份
|
||||||
|
|
||||||
Map<String, String>? _cachedConfig;
|
Map<String, String>? _cachedConfig;
|
||||||
Timer? _autoSyncTimer;
|
Timer? _autoSyncTimer;
|
||||||
@@ -403,7 +403,7 @@ class WebDAVService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
print('WebDAV: 自动备份已启动,每2分钟执行一次');
|
print('WebDAV: 自动备份已启动,每5分钟执行一次');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 停止自动同步
|
/// 停止自动同步
|
||||||
@@ -551,7 +551,7 @@ class WebDAVService {
|
|||||||
final bytes = await entity.readAsBytes();
|
final bytes = await entity.readAsBytes();
|
||||||
final archivePath = '$relativePath/$fileName';
|
final archivePath = '$relativePath/$fileName';
|
||||||
archive.addFile(ArchiveFile(archivePath, bytes.length, bytes));
|
archive.addFile(ArchiveFile(archivePath, bytes.length, bytes));
|
||||||
print('WebDAV: 添加文件到备份 - $archivePath');
|
// print('WebDAV: 添加文件到备份 - $archivePath');
|
||||||
} else if (entity is Directory) {
|
} else if (entity is Directory) {
|
||||||
final dirName = p.basename(entity.path);
|
final dirName = p.basename(entity.path);
|
||||||
await _addImagesToArchive(archive, entity, '$relativePath/$dirName');
|
await _addImagesToArchive(archive, entity, '$relativePath/$dirName');
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../providers/app_provider.dart';
|
import '../providers/app_provider.dart';
|
||||||
import '../utils/user_prefs.dart';
|
import '../utils/user_prefs.dart';
|
||||||
import '../utils/toast_util.dart';
|
import '../utils/toast_util.dart';
|
||||||
|
import '../models/data_models.dart';
|
||||||
|
|
||||||
/// 自定义左侧弹出菜单 - 极简主义设计
|
/// 自定义左侧弹出菜单 - 极简主义设计
|
||||||
class CustomDrawer extends StatelessWidget {
|
class CustomDrawer extends StatelessWidget {
|
||||||
@@ -20,6 +22,11 @@ class CustomDrawer extends StatelessWidget {
|
|||||||
|
|
||||||
const Divider(height: 0.5, thickness: 0.5, color: Color(0xFFE5E5E5)),
|
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(
|
Expanded(
|
||||||
child: ListView(
|
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) {
|
Widget _buildHeader(BuildContext context) {
|
||||||
return Consumer<AppProvider>(
|
return Consumer<AppProvider>(
|
||||||
@@ -192,3 +412,18 @@ class CustomDrawer extends StatelessWidget {
|
|||||||
ToastUtil.show(context, message);
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user