generated from dellevin/template
新增本地自动备份
This commit is contained in:
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../providers/app_provider.dart';
|
||||
import '../utils/backup_service.dart';
|
||||
import '../utils/auto_backup_service.dart';
|
||||
import '../utils/toast_util.dart';
|
||||
|
||||
/// 本地备份页面
|
||||
@@ -16,6 +17,30 @@ class BackupPage extends StatefulWidget {
|
||||
class _BackupPageState extends State<BackupPage> {
|
||||
bool _isExporting = false;
|
||||
bool _isImporting = false;
|
||||
bool _autoBackupEnabled = false;
|
||||
bool _isLoading = true;
|
||||
List<File> _autoBackupFiles = [];
|
||||
String? _backupDirPath;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadAutoBackupStatus();
|
||||
}
|
||||
|
||||
Future<void> _loadAutoBackupStatus() async {
|
||||
final enabled = await AutoBackupService.instance.getEnabled();
|
||||
final files = await AutoBackupService.instance.getBackupFiles();
|
||||
final dirPath = await AutoBackupService.instance.getBackupDirectoryPath();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_autoBackupEnabled = enabled;
|
||||
_autoBackupFiles = files;
|
||||
_backupDirPath = dirPath;
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -24,9 +49,22 @@ class _BackupPageState extends State<BackupPage> {
|
||||
appBar: AppBar(
|
||||
title: const Text('本地备份'),
|
||||
),
|
||||
body: ListView(
|
||||
body: _isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: ListView(
|
||||
padding: const EdgeInsets.all(24),
|
||||
children: [
|
||||
// 自动备份开关
|
||||
_buildAutoBackupSection(),
|
||||
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// 自动备份文件列表
|
||||
if (_autoBackupFiles.isNotEmpty) ...[
|
||||
_buildBackupFilesSection(),
|
||||
const SizedBox(height: 32),
|
||||
],
|
||||
|
||||
// 导出数据
|
||||
_buildSection(
|
||||
title: '导出数据',
|
||||
@@ -298,4 +336,189 @@ class _BackupPageState extends State<BackupPage> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 构建自动备份区域
|
||||
Widget _buildAutoBackupSection() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: const Color(0xFFE5E5E5)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.schedule,
|
||||
size: 24,
|
||||
color: Color(0xFF1A1A1A),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Expanded(
|
||||
child: Text(
|
||||
'自动本地备份',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF1A1A1A),
|
||||
),
|
||||
),
|
||||
),
|
||||
Switch(
|
||||
value: _autoBackupEnabled,
|
||||
onChanged: (value) async {
|
||||
setState(() => _autoBackupEnabled = value);
|
||||
await AutoBackupService.instance.setEnabled(value);
|
||||
if (value) {
|
||||
ToastUtil.show(context, '自动备份已开启,每2分钟备份一次');
|
||||
} else {
|
||||
ToastUtil.show(context, '自动备份已关闭');
|
||||
}
|
||||
// 刷新文件列表
|
||||
await _loadAutoBackupStatus();
|
||||
},
|
||||
activeColor: const Color(0xFF1A1A1A),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'每隔2分钟自动备份到下载目录/mooknote文件夹,最多保留10个备份文件',
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
color: Color(0xFF666666),
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
if (_backupDirPath != null) ...[
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'备份位置: $_backupDirPath',
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
color: Color(0xFF999999),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建备份文件列表区域
|
||||
Widget _buildBackupFilesSection() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: const Color(0xFFE5E5E5)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.folder_outlined,
|
||||
size: 24,
|
||||
color: Color(0xFF1A1A1A),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'自动备份文件 (${_autoBackupFiles.length}/10)',
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF1A1A1A),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
..._autoBackupFiles.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final file = entry.value;
|
||||
final fileName = file.path.split('/').last;
|
||||
final stat = file.statSync();
|
||||
final size = _formatFileSize(stat.size);
|
||||
final modified = _formatDateTime(stat.modified);
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: index < _autoBackupFiles.length - 1
|
||||
? const BorderSide(color: Color(0xFFE5E5E5))
|
||||
: BorderSide.none,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.backup,
|
||||
size: 18,
|
||||
color: Color(0xFF666666),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
fileName,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
color: Color(0xFF1A1A1A),
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'$modified · $size',
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
color: Color(0xFF999999),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (index == 0)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF5F5F5),
|
||||
border: Border.all(color: const Color(0xFFE5E5E5)),
|
||||
),
|
||||
child: const Text(
|
||||
'最新',
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Color(0xFF666666),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 格式化文件大小
|
||||
String _formatFileSize(int bytes) {
|
||||
if (bytes < 1024) return '$bytes B';
|
||||
if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB';
|
||||
return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
|
||||
}
|
||||
|
||||
/// 格式化日期时间
|
||||
String _formatDateTime(DateTime dateTime) {
|
||||
return '${dateTime.month}/${dateTime.day} ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user