From 370f1c6787a3201ea6c2d325ae1fd176ccb75bc0 Mon Sep 17 00:00:00 2001 From: DelLevin-Home Date: Sun, 28 Jun 2026 07:43:57 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B8=85=E7=90=86=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- lib/main.dart | 3 ++ lib/pages/profile_page.dart | 55 +++++++++++++++++++++++++----- lib/utils/sync/backup_service.dart | 52 ++++++++++++++++++++++++---- 4 files changed, 96 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 33863bb..39f9239 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ http://docmost.iletter.top/share/ropwljpyvn/p/mook-note-lHmPTswdDC | :--------------------------------------------: | :--------------------------------------------: | :--------------------------------------------: | | ![1782484732881](image/README/1782484732881.png) | ![1782484753882](image/README/1782484753882.png) | ![1782484769014](image/README/1782484769014.png) | -更多预览图片请到**应用功能****详情预览**里面查看 +更多预览图片请到**应用功能**详情预览里面查看 ## 功能特性 diff --git a/lib/main.dart b/lib/main.dart index 04fb973..7e631ef 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -17,6 +17,8 @@ import 'utils/usage_stats_service.dart'; import 'providers/app_provider.dart'; import 'providers/note_plus_provider.dart'; +final RouteObserver> routeObserver = RouteObserver>(); + void main() async { WidgetsFlutterBinding.ensureInitialized(); await UserPrefs.init(); @@ -217,6 +219,7 @@ class _MyAppState extends State with WidgetsBindingObserver { ], home: const HomePage(), navigatorKey: _navigatorKey, + navigatorObservers: [routeObserver], onGenerateRoute: AppRouter.generateRoute, builder: (context, child) { return _AppIconWrapper(iconName: iconName, child: child!); diff --git a/lib/pages/profile_page.dart b/lib/pages/profile_page.dart index c646def..ed8a857 100644 --- a/lib/pages/profile_page.dart +++ b/lib/pages/profile_page.dart @@ -6,6 +6,7 @@ import 'package:path/path.dart' as path; import 'package:provider/provider.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:url_launcher/url_launcher.dart'; +import '../main.dart' show routeObserver; import '../models/data_models.dart'; import '../providers/app_provider.dart'; import '../utils/user_prefs.dart'; @@ -32,7 +33,7 @@ class ProfilePage extends StatefulWidget { State createState() => _ProfilePageState(); } -class _ProfilePageState extends State { +class _ProfilePageState extends State with RouteAware { final ImagePicker _picker = ImagePicker(); final UserPrefs _userPrefs = UserPrefs(); @@ -46,6 +47,24 @@ class _ProfilePageState extends State { _loadUserData(); } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + routeObserver.subscribe(this, ModalRoute.of(context)!); + } + + @override + void dispose() { + routeObserver.unsubscribe(this); + super.dispose(); + } + + @override + void didPopNext() { + // 从其他页面返回时刷新用户数据(头像、昵称等) + _loadUserData(); + } + Future _loadUserData() async { try { await UserPrefs.init(); @@ -1821,7 +1840,7 @@ class _SettingsPageState extends State { fontSize: 18, fontWeight: FontWeight.w600, color: colors.onSurface)), - content: Text('这将删除所有未在数据库中引用的图片文件。确定要继续吗?', + content: Text('这将删除所有未在数据库中引用的文件。确定要继续吗?', style: TextStyle( fontSize: 14, color: colors.onSurface.withValues(alpha: 0.6), @@ -1877,12 +1896,13 @@ class _SettingsPageState extends State { Navigator.pop(context); if (context.mounted) { - final total = deletedImages + deletedEpubs + deletedTemp + deletedEmptyDirs; + final total = + deletedImages + deletedEpubs + deletedTemp + deletedEmptyDirs; if (total == 0) { ToastUtil.show(context, '没有需要清理的缓存'); } else { - ToastUtil.show( - context, '已清理 $deletedImages 个孤立图片,$deletedEpubs 个孤立电子书,$deletedTemp 个临时文件,$deletedEmptyDirs 个空文件夹'); + ToastUtil.show(context, + '已清理 $deletedImages 个孤立图片,$deletedEpubs 个孤立电子书,$deletedTemp 个临时文件,$deletedEmptyDirs 个空文件夹'); } } } catch (e) { @@ -1939,10 +1959,16 @@ class _SettingsPageState extends State { Future _cleanOrphanedEpubBooks(AppProvider provider) async { int deletedCount = 0; try { - // 收集数据库中所有 reader_books 的 bookId final db = await DatabaseHelper.instance.database; - final rows = await db.query('reader_books', columns: ['id']); - final dbIds = rows.map((r) => r['id'] as String).toSet(); + // 收集数据库中所有引用的 epub_books 子目录名 + final rows = await db.query('reader_books', columns: ['id', 'file_path', 'cover_path']); + final usedDirs = {}; + for (final r in rows) { + final id = r['id'] as String?; + if (id != null && id.isNotEmpty) usedDirs.add(id); + _collectEpubDirName(r['file_path'] as String?, usedDirs); + _collectEpubDirName(r['cover_path'] as String?, usedDirs); + } final appDir = await getApplicationDocumentsDirectory(); final epubDir = Directory('${appDir.path}/epub_books'); @@ -1951,7 +1977,7 @@ class _SettingsPageState extends State { await for (final entity in epubDir.list(followLinks: false)) { if (entity is Directory) { final dirName = path.basename(entity.path); - if (!dbIds.contains(dirName)) { + if (!usedDirs.contains(dirName)) { try { await entity.delete(recursive: true); deletedCount++; @@ -1965,6 +1991,17 @@ class _SettingsPageState extends State { return deletedCount; } + /// 从绝对路径中提取 epub_books/ 下的目录名 + void _collectEpubDirName(String? pathStr, Set dirs) { + if (pathStr == null || pathStr.isEmpty) return; + final marker = '/epub_books/'; + final idx = pathStr.indexOf(marker); + if (idx < 0) return; + final rest = pathStr.substring(idx + marker.length); + final slashIdx = rest.indexOf('/'); + dirs.add(slashIdx >= 0 ? rest.substring(0, slashIdx) : rest); + } + Future _cleanTempDirectory() async { int deletedCount = 0; final now = DateTime.now(); diff --git a/lib/utils/sync/backup_service.dart b/lib/utils/sync/backup_service.dart index 84fb2ba..399d127 100644 --- a/lib/utils/sync/backup_service.dart +++ b/lib/utils/sync/backup_service.dart @@ -61,10 +61,8 @@ class BackupService { } } } - for (final rb in readerBooks) { - final p = rb['cover_path'] as String?; - if (p != null && p.isNotEmpty) imagePaths.add(p); - } + // reader_books 的封面在 epub_books/ 目录下,由 epub_books 归档处理 + // 不加入 imagePaths,避免 basename 碰撞导致所有封面变成同一个路径 final userPrefs = UserPrefs(); final userInfo = { @@ -258,6 +256,8 @@ class BackupService { int imageCount = 0; // 完整相对路径 → 新绝对路径 的映射(避免同名文件碰撞) final imagePathMap = {}; + // epub_books/ 内相对路径 → 新绝对路径 的映射 + final epubFileMap = {}; if (extension == '.zip') { final bytes = await file.readAsBytes(); @@ -288,6 +288,7 @@ class BackupService { final outputFile = File(path.join(epubDir.path, relativePath)); if (!await outputFile.parent.exists()) await outputFile.parent.create(recursive: true); await outputFile.writeAsBytes(archiveFile.content as List); + epubFileMap[relativePath] = outputFile.path; } } } else { @@ -368,7 +369,9 @@ class BackupService { } if (data.containsKey('reader_books')) { for (final rb in data['reader_books'] as List) { - await txn.insert('reader_books', _updateImagePath(_convertToDbMapSafe(rb, readerBooksCols), 'cover_path', imagePathMap)); + var row = _updateImagePath(_convertToDbMapSafe(rb, readerBooksCols), 'cover_path', imagePathMap); + row = _updateEpubPaths(row, epubFileMap); + await txn.insert('reader_books', row); } } if (data.containsKey('book_annotations')) { @@ -410,6 +413,7 @@ class BackupService { final backupData = jsonDecode(utf8.decode(dataFile.content as List)) as Map; final imagePathMap = {}; + final epubFileMap = {}; int imageCount = 0; final appDir = await getApplicationDocumentsDirectory(); @@ -431,6 +435,7 @@ class BackupService { final outputFile = File(path.join(epubDir.path, relativePath)); if (!await outputFile.parent.exists()) await outputFile.parent.create(recursive: true); await outputFile.writeAsBytes(archiveFile.content as List); + epubFileMap[relativePath] = outputFile.path; } } @@ -506,7 +511,9 @@ class BackupService { } if (data.containsKey('reader_books')) { for (final rb in data['reader_books'] as List) { - await txn.insert('reader_books', _updateImagePath(_convertToDbMapSafe(rb, readerBooksCols), 'cover_path', imagePathMap)); + var row = _updateImagePath(_convertToDbMapSafe(rb, readerBooksCols), 'cover_path', imagePathMap); + row = _updateEpubPaths(row, epubFileMap); + await txn.insert('reader_books', row); } } if (data.containsKey('book_annotations')) { @@ -651,6 +658,39 @@ class BackupService { return {}; } + /// 更新 epub 阅读器的 file_path 和 cover_path + Map _updateEpubPaths(Map item, Map epubFileMap) { + if (epubFileMap.isEmpty) return item; + final newItem = Map.from(item); + + final oldFilePath = item['file_path'] as String?; + if (oldFilePath != null && oldFilePath.isNotEmpty) { + final oldRel = _toEpubRelativePath(oldFilePath); + if (oldRel != null && epubFileMap.containsKey(oldRel)) { + newItem['file_path'] = epubFileMap[oldRel]; + } + } + + final oldCoverPath = item['cover_path'] as String?; + if (oldCoverPath != null && oldCoverPath.isNotEmpty) { + final oldRel = _toEpubRelativePath(oldCoverPath); + if (oldRel != null && epubFileMap.containsKey(oldRel)) { + newItem['cover_path'] = epubFileMap[oldRel]; + } + } + + return newItem; + } + + /// 从绝对路径中提取 epub_books/ 下的相对路径 + String? _toEpubRelativePath(String absolutePath) { + final idx = absolutePath.indexOf('/epub_books/'); + if (idx >= 0) return absolutePath.substring(idx + 13); // skip '/epub_books/' + final winIdx = absolutePath.indexOf('\\epub_books\\'); + if (winIdx >= 0) return absolutePath.substring(winIdx + 13); + return null; + } + /// 更新单值图片路径(poster_path / cover_path) Map _updateImagePath(Map item, String pathField, Map imagePathMap) { final newItem = Map.from(item);