优化图标显示
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 332 KiB After Width: | Height: | Size: 746 KiB |
|
Before Width: | Height: | Size: 543 KiB After Width: | Height: | Size: 883 KiB |
@@ -993,6 +993,18 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
onTap: () =>
|
onTap: () =>
|
||||||
launchUrl(Uri.parse('https://mooknote.iletter.top/#/')),
|
launchUrl(Uri.parse('https://mooknote.iletter.top/#/')),
|
||||||
),
|
),
|
||||||
|
Divider(
|
||||||
|
height: 0.5,
|
||||||
|
indent: 24,
|
||||||
|
endIndent: 24,
|
||||||
|
color: colors.outlineVariant),
|
||||||
|
_buildActionItem(
|
||||||
|
icon: Icons.open_in_new_outlined,
|
||||||
|
title: '项目源码',
|
||||||
|
subtitle: '查看 GitHub 项目仓库',
|
||||||
|
onTap: () =>
|
||||||
|
launchUrl(Uri.parse('https://github.com/dellevin/mooknote')),
|
||||||
|
),
|
||||||
Divider(
|
Divider(
|
||||||
height: 0.5,
|
height: 0.5,
|
||||||
indent: 24,
|
indent: 24,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:sqflite/sqflite.dart';
|
import 'package:sqflite/sqflite.dart';
|
||||||
import '../database_helper.dart';
|
import '../database_helper.dart';
|
||||||
import '../user_prefs.dart';
|
import '../user_prefs.dart';
|
||||||
@@ -75,6 +76,7 @@ class BackupService {
|
|||||||
'appName': 'MookNote',
|
'appName': 'MookNote',
|
||||||
'hasImages': true,
|
'hasImages': true,
|
||||||
'userInfo': userInfo,
|
'userInfo': userInfo,
|
||||||
|
'sharedPrefs': await _exportSharedPrefs(),
|
||||||
'data': {
|
'data': {
|
||||||
'movies': movies,
|
'movies': movies,
|
||||||
'books': books,
|
'books': books,
|
||||||
@@ -455,6 +457,44 @@ class BackupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 恢复完整 SharedPreferences
|
||||||
|
if (backupData.containsKey('sharedPrefs')) {
|
||||||
|
await _restoreSharedPrefs(backupData['sharedPrefs'] as Map<String, dynamic>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 导出完整 SharedPreferences
|
||||||
|
Future<Map<String, dynamic>> _exportSharedPrefs() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final keys = prefs.getKeys();
|
||||||
|
final map = <String, dynamic>{};
|
||||||
|
for (final key in keys) {
|
||||||
|
map[key] = prefs.get(key);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 恢复 SharedPreferences(保留当前 avatarPath,因为已在上面用 imagePathMap 更新过)
|
||||||
|
Future<void> _restoreSharedPrefs(Map<String, dynamic> data) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
for (final entry in data.entries) {
|
||||||
|
final key = entry.key;
|
||||||
|
final value = entry.value;
|
||||||
|
// avatarPath 已单独处理,跳过
|
||||||
|
if (key == 'avatarPath') continue;
|
||||||
|
if (value is String) {
|
||||||
|
await prefs.setString(key, value);
|
||||||
|
} else if (value is int) {
|
||||||
|
await prefs.setInt(key, value);
|
||||||
|
} else if (value is double) {
|
||||||
|
await prefs.setDouble(key, value);
|
||||||
|
} else if (value is bool) {
|
||||||
|
await prefs.setBool(key, value);
|
||||||
|
} else if (value is List) {
|
||||||
|
await prefs.setStringList(key, value.cast<String>());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, int> _buildStats(Map<String, dynamic> data, int imageCount) {
|
Map<String, int> _buildStats(Map<String, dynamic> data, int imageCount) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
@@ -227,7 +227,7 @@ class WebDAVService {
|
|||||||
if (success) {
|
if (success) {
|
||||||
uploadedFiles = 1;
|
uploadedFiles = 1;
|
||||||
uploadedImages = exportResult.imageCount;
|
uploadedImages = exportResult.imageCount;
|
||||||
print('[WebDAV] 备份上传成功 (影视${exportResult.movieCount} 书籍${exportResult.bookCount} 笔记${exportResult.noteCount} 图片${exportResult.imageCount})');
|
debugPrint('[WebDAV] 备份上传成功 (影视${exportResult.movieCount} 书籍${exportResult.bookCount} 笔记${exportResult.noteCount} 图片${exportResult.imageCount})');
|
||||||
} else {
|
} else {
|
||||||
return SyncResult(success: false, message: '上传备份文件失败');
|
return SyncResult(success: false, message: '上传备份文件失败');
|
||||||
}
|
}
|
||||||
@@ -246,7 +246,7 @@ class WebDAVService {
|
|||||||
downloadedFiles = 1;
|
downloadedFiles = 1;
|
||||||
downloadedImages = importResult.stats?['图片'] ?? 0;
|
downloadedImages = importResult.stats?['图片'] ?? 0;
|
||||||
needReload = true;
|
needReload = true;
|
||||||
print('[WebDAV] 备份恢复成功: ${importResult.statsText}');
|
debugPrint('[WebDAV] 备份恢复成功: ${importResult.statsText}');
|
||||||
} else {
|
} else {
|
||||||
return SyncResult(success: false, message: importResult.errorMessage ?? '恢复备份失败');
|
return SyncResult(success: false, message: importResult.errorMessage ?? '恢复备份失败');
|
||||||
}
|
}
|
||||||
@@ -268,11 +268,11 @@ class WebDAVService {
|
|||||||
downloadedFiles = 1;
|
downloadedFiles = 1;
|
||||||
downloadedImages = importResult.stats?['图片'] ?? 0;
|
downloadedImages = importResult.stats?['图片'] ?? 0;
|
||||||
needReload = true;
|
needReload = true;
|
||||||
print('[WebDAV] 远程备份已恢复: ${importResult.statsText}');
|
debugPrint('[WebDAV] 远程备份已恢复: ${importResult.statsText}');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try { await tempZip.delete(); } catch (_) {}
|
try { await tempZip.delete(); } catch (_) {}
|
||||||
print('[WebDAV] 服务器无备份,仅上传本地数据');
|
debugPrint('[WebDAV] 服务器无备份,仅上传本地数据');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 始终上传本地,确保远程是最新的
|
// 始终上传本地,确保远程是最新的
|
||||||
@@ -282,7 +282,7 @@ class WebDAVService {
|
|||||||
if (uploadSuccess) {
|
if (uploadSuccess) {
|
||||||
uploadedFiles = 1;
|
uploadedFiles = 1;
|
||||||
uploadedImages = exportResult.imageCount;
|
uploadedImages = exportResult.imageCount;
|
||||||
print('[WebDAV] 本地备份已上传 (影视${exportResult.movieCount} 书籍${exportResult.bookCount} 笔记${exportResult.noteCount} 图片${exportResult.imageCount})');
|
debugPrint('[WebDAV] 本地备份已上传 (影视${exportResult.movieCount} 书籍${exportResult.bookCount} 笔记${exportResult.noteCount} 图片${exportResult.imageCount})');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,7 +338,7 @@ class WebDAVService {
|
|||||||
await prefs.setBool(_autoSyncKey, true);
|
await prefs.setBool(_autoSyncKey, true);
|
||||||
|
|
||||||
// 立即执行一次同步
|
// 立即执行一次同步
|
||||||
print('[WebDAV] 自动同步已启动,间隔 $_autoSyncIntervalMinutes 分钟');
|
debugPrint('[WebDAV] 自动同步已启动,间隔 $_autoSyncIntervalMinutes 分钟');
|
||||||
await syncData(direction: SyncDirection.bidirectional);
|
await syncData(direction: SyncDirection.bidirectional);
|
||||||
|
|
||||||
// 设置定时器
|
// 设置定时器
|
||||||
@@ -407,10 +407,10 @@ class WebDAVService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print('[WebDAV] PUT $url -> ${response.statusCode}');
|
debugPrint('[WebDAV] PUT $url -> ${response.statusCode}');
|
||||||
return response.statusCode == 200 || response.statusCode == 201 || response.statusCode == 204;
|
return response.statusCode == 200 || response.statusCode == 201 || response.statusCode == 204;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('[WebDAV] _uploadBytes error: $e');
|
debugPrint('[WebDAV] _uploadBytes error: $e');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -440,13 +440,13 @@ class WebDAVService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print('[WebDAV] GET $url -> ${response.statusCode}');
|
debugPrint('[WebDAV] GET $url -> ${response.statusCode}');
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
await localFile.parent.create(recursive: true);
|
await localFile.parent.create(recursive: true);
|
||||||
final bytes = await response.stream.toBytes();
|
final bytes = await response.stream.toBytes();
|
||||||
await localFile.writeAsBytes(bytes);
|
await localFile.writeAsBytes(bytes);
|
||||||
print('[WebDAV] Downloaded ${bytes.length} bytes');
|
debugPrint('[WebDAV] Downloaded ${bytes.length} bytes');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||