generated from dellevin/template
搜索优化,首页云备份
This commit is contained in:
@@ -2,10 +2,13 @@ import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../providers/app_provider.dart';
|
||||
import '../utils/user_prefs.dart';
|
||||
import '../utils/sync/webdav_service.dart';
|
||||
import '../utils/toast_util.dart';
|
||||
import 'movies/movie_tab_page.dart';
|
||||
import 'book/book_tab_page.dart';
|
||||
import 'note/note_tab_page.dart';
|
||||
import 'search_page.dart';
|
||||
import 'sync/webdav_sync_page.dart';
|
||||
|
||||
/// 主内容页 - 观影/阅读/笔记标签页
|
||||
class MainContentPage extends StatefulWidget {
|
||||
@@ -87,6 +90,8 @@ class _MainContentPageState extends State<MainContentPage> {
|
||||
return AppBar(
|
||||
title: Text(_getAppBarTitle(provider)),
|
||||
actions: [
|
||||
// 云备份按钮
|
||||
_buildCloudSyncButton(context),
|
||||
// 搜索按钮
|
||||
IconButton(
|
||||
icon: const Icon(Icons.search),
|
||||
@@ -105,6 +110,301 @@ class _MainContentPageState extends State<MainContentPage> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建云备份按钮
|
||||
Widget _buildCloudSyncButton(BuildContext context) {
|
||||
return PopupMenuButton<String>(
|
||||
icon: const Icon(Icons.cloud_sync_outlined),
|
||||
tooltip: '云备份',
|
||||
offset: const Offset(0, 40),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: 'upload',
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFAFAFA),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.cloud_upload_outlined,
|
||||
size: 18,
|
||||
color: Color(0xFF666666),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Text(
|
||||
'上传数据',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xFF1A1A1A),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'download',
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFAFAFA),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.cloud_download_outlined,
|
||||
size: 18,
|
||||
color: Color(0xFF666666),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Text(
|
||||
'下载数据',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xFF1A1A1A),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const PopupMenuDivider(),
|
||||
PopupMenuItem(
|
||||
value: 'settings',
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFAFAFA),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.settings_outlined,
|
||||
size: 18,
|
||||
color: Color(0xFF666666),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Text(
|
||||
'WebDAV设置',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xFF1A1A1A),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
onSelected: (value) async {
|
||||
switch (value) {
|
||||
case 'upload':
|
||||
await _performSync(context, SyncDirection.upload);
|
||||
break;
|
||||
case 'download':
|
||||
await _performSync(context, SyncDirection.download);
|
||||
break;
|
||||
case 'settings':
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const WebDAVSyncPage(),
|
||||
),
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 执行同步操作
|
||||
Future<void> _performSync(BuildContext context, SyncDirection direction) async {
|
||||
// 检查是否已配置
|
||||
final config = await WebDAVService.instance.getConfig();
|
||||
if (config == null) {
|
||||
if (context.mounted) {
|
||||
_showResultDialog(
|
||||
context,
|
||||
title: '同步失败',
|
||||
message: '请先配置 WebDAV 服务器',
|
||||
isSuccess: false,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示加载对话框
|
||||
if (context.mounted) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => const Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: Color(0xFF1A1A1A),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 执行同步
|
||||
final result = await WebDAVService.instance.syncData(direction: direction);
|
||||
|
||||
// 关闭加载对话框
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
// 显示结果
|
||||
if (context.mounted) {
|
||||
final isSuccess = result.success;
|
||||
final message = result.message.isNotEmpty ? result.message : (isSuccess ? '同步成功' : '同步失败');
|
||||
|
||||
_showResultDialog(
|
||||
context,
|
||||
title: isSuccess ? '同步成功' : '同步失败',
|
||||
message: message,
|
||||
isSuccess: isSuccess,
|
||||
details: {
|
||||
'uploaded': result.uploadedFiles + result.uploadedImages,
|
||||
'downloaded': result.downloadedFiles + result.downloadedImages,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 显示同步结果对话框
|
||||
void _showResultDialog(
|
||||
BuildContext context, {
|
||||
required String title,
|
||||
required String message,
|
||||
required bool isSuccess,
|
||||
Map<String, dynamic>? details,
|
||||
}) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
title: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: isSuccess ? const Color(0xFFE8F5E9) : const Color(0xFFFFEBEE),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Icon(
|
||||
isSuccess ? Icons.check_circle : Icons.error,
|
||||
color: isSuccess ? const Color(0xFF4CAF50) : const Color(0xFFE57373),
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
message,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xFF666666),
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
if (details != null) ...[
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFAFAFA),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (details['uploaded'] != null)
|
||||
_buildDetailRow('上传文件', '${details['uploaded']} 个'),
|
||||
if (details['downloaded'] != null)
|
||||
_buildDetailRow('下载文件', '${details['downloaded']} 个'),
|
||||
if (details['conflicts'] != null)
|
||||
_buildDetailRow('冲突处理', '${details['conflicts']} 个'),
|
||||
if (details['errors'] != null && details['errors'] > 0)
|
||||
_buildDetailRow('错误', '${details['errors']} 个', isError: true),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF1A1A1A),
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
),
|
||||
child: const Text('确定'),
|
||||
),
|
||||
],
|
||||
actionsPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建详情行
|
||||
Widget _buildDetailRow(String label, String value, {bool isError = false}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
color: Color(0xFF999999),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
value,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isError ? const Color(0xFFE57373) : const Color(0xFF1A1A1A),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 获取 AppBar 标题
|
||||
String _getAppBarTitle(AppProvider provider) {
|
||||
switch (provider.mainTabIndex) {
|
||||
|
||||
@@ -87,16 +87,20 @@ class _SearchPageState extends State<SearchPage> {
|
||||
}
|
||||
|
||||
bool _matchNote(Note note, String keyword) {
|
||||
// 搜索框为空且不选标签时,不显示任何笔记
|
||||
if (keyword.isEmpty && _selectedTag == null) {
|
||||
return false;
|
||||
}
|
||||
// 如果有选中的标签,先按标签筛选
|
||||
if (_selectedTag != null) {
|
||||
if (!note.tags.contains(_selectedTag)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 关键词为空时,只按标签筛选
|
||||
// 有标签但关键词为空时,只按标签筛选
|
||||
if (keyword.isEmpty) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 按内容搜索
|
||||
return note.content.toLowerCase().contains(keyword.toLowerCase());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user