519 lines
15 KiB
Dart
519 lines
15 KiB
Dart
/// 时间: 2025-03-21
|
|
/// 功能: 历史记录页面
|
|
/// 介绍: 独立的历史记录管理页面,支持查看、搜索、删除和导出功能
|
|
/// 最新变化: 新创建文件,实现历史记录的独立页面功能
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import '../../constants/app_constants.dart';
|
|
import '../../controllers/history_controller.dart';
|
|
|
|
class HistoryPage extends StatefulWidget {
|
|
const HistoryPage({super.key});
|
|
|
|
@override
|
|
State<HistoryPage> createState() => _HistoryPageState();
|
|
}
|
|
|
|
class _HistoryPageState extends State<HistoryPage> {
|
|
List<Map<String, dynamic>> _historyList = [];
|
|
List<Map<String, dynamic>> _filteredHistoryList = [];
|
|
bool _isLoading = true;
|
|
String _searchKeyword = '';
|
|
int _selectedSortType = 0; // 0: 时间倒序, 1: 时间正序, 2: 按名称排序
|
|
final List<String> _sortTypes = ['时间倒序', '时间正序', '按名称排序'];
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_loadHistory();
|
|
}
|
|
|
|
// === 加载历史记录 ===
|
|
Future<void> _loadHistory() async {
|
|
setState(() {
|
|
_isLoading = true;
|
|
});
|
|
|
|
try {
|
|
final history = await HistoryController.getHistory();
|
|
setState(() {
|
|
_historyList = history;
|
|
_filteredHistoryList = history;
|
|
_isLoading = false;
|
|
});
|
|
} catch (e) {
|
|
// print('加载历史记录失败: $e');
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
// === 搜索历史记录 ===
|
|
void _searchHistory(String keyword) {
|
|
setState(() {
|
|
_searchKeyword = keyword;
|
|
});
|
|
|
|
if (keyword.isEmpty) {
|
|
setState(() {
|
|
_filteredHistoryList = _historyList;
|
|
});
|
|
return;
|
|
}
|
|
|
|
_performSearch(keyword);
|
|
}
|
|
|
|
Future<void> _performSearch(String keyword) async {
|
|
try {
|
|
final searchResults = await HistoryController.searchHistory(keyword);
|
|
setState(() {
|
|
_filteredHistoryList = searchResults;
|
|
});
|
|
} catch (e) {
|
|
// print('搜索历史记录失败: $e');
|
|
}
|
|
}
|
|
|
|
// === 排序历史记录 ===
|
|
void _sortHistory(int sortType) {
|
|
setState(() {
|
|
_selectedSortType = sortType;
|
|
});
|
|
|
|
final sortedList = List<Map<String, dynamic>>.from(_filteredHistoryList);
|
|
|
|
switch (sortType) {
|
|
case 0: // 时间倒序
|
|
sortedList.sort(
|
|
(a, b) => (b['timestamp'] ?? 0).compareTo(a['timestamp'] ?? 0),
|
|
);
|
|
break;
|
|
case 1: // 时间正序
|
|
sortedList.sort(
|
|
(a, b) => (a['timestamp'] ?? 0).compareTo(b['timestamp'] ?? 0),
|
|
);
|
|
break;
|
|
case 2: // 按名称排序
|
|
sortedList.sort((a, b) => (a['name'] ?? '').compareTo(b['name'] ?? ''));
|
|
break;
|
|
}
|
|
|
|
setState(() {
|
|
_filteredHistoryList = sortedList;
|
|
});
|
|
}
|
|
|
|
// === 清空历史记录 ===
|
|
Future<void> _clearHistory() async {
|
|
final confirmed = await _showConfirmDialog(
|
|
'清空历史记录',
|
|
'确定要清空所有历史记录吗?此操作不可撤销。',
|
|
);
|
|
|
|
if (confirmed == null || !confirmed) return;
|
|
|
|
try {
|
|
final success = await HistoryController.clearHistory();
|
|
|
|
if (success) {
|
|
setState(() {
|
|
_historyList.clear();
|
|
_filteredHistoryList.clear();
|
|
});
|
|
|
|
_showSnackBar('历史记录已清空');
|
|
} else {
|
|
_showSnackBar('清空失败');
|
|
}
|
|
} catch (e) {
|
|
//('清空历史记录失败: $e');
|
|
_showSnackBar('清空失败');
|
|
}
|
|
}
|
|
|
|
// === 删除单条记录 ===
|
|
Future<void> _deleteHistoryItem(int index, Map<String, dynamic> item) async {
|
|
final confirmed = await _showConfirmDialog('删除记录', '确定要删除这条历史记录吗?');
|
|
|
|
if (confirmed == null || !confirmed) return;
|
|
|
|
try {
|
|
final poetryId = item['id'] as int;
|
|
final success = await HistoryController.removeFromHistory(poetryId);
|
|
|
|
if (success) {
|
|
// 重新加载历史记录,避免索引不匹配问题
|
|
await _loadHistory();
|
|
_showSnackBar('删除成功');
|
|
} else {
|
|
_showSnackBar('删除失败');
|
|
}
|
|
} catch (e) {
|
|
_showSnackBar('删除失败');
|
|
}
|
|
}
|
|
|
|
// === 导出历史记录 ===
|
|
Future<void> _exportHistory() async {
|
|
try {
|
|
final exportData = await HistoryController.exportHistory(format: 'json');
|
|
|
|
if (exportData.isNotEmpty) {
|
|
// 这里可以实现文件保存功能
|
|
_showSnackBar('导出功能开发中...');
|
|
} else {
|
|
_showSnackBar('无数据可导出');
|
|
}
|
|
} catch (e) {
|
|
_showSnackBar('导出失败');
|
|
}
|
|
}
|
|
|
|
// === 显示确认对话框 ===
|
|
Future<bool?> _showConfirmDialog(String title, String content) async {
|
|
return await showDialog<bool?>(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: Text(title),
|
|
content: Text(content),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context, false),
|
|
child: const Text('取消'),
|
|
),
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context, true),
|
|
child: const Text('确定'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// === 显示历史记录统计 ===
|
|
void _showStatsDialog() async {
|
|
try {
|
|
final stats = await HistoryController.getHistoryStats();
|
|
|
|
if (stats.isEmpty) {
|
|
_showSnackBar('暂无统计数据');
|
|
return;
|
|
}
|
|
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: const Text('历史记录统计'),
|
|
content: SingleChildScrollView(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text('总数: ${stats['totalCount']}'),
|
|
Text('今日: ${stats['todayCount']}'),
|
|
Text('本周: ${stats['thisWeekCount']}'),
|
|
Text('本月: ${stats['thisMonthCount']}'),
|
|
const SizedBox(height: 16),
|
|
const Text('热门朝代:'),
|
|
if (stats['topDynasties'] != null &&
|
|
stats['topDynasties'] is Map) ...[
|
|
...(stats['topDynasties'] as Map<String, int>).entries
|
|
.map((entry) => Text('${entry.key}: ${entry.value}'))
|
|
.toList(),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: const Text('关闭'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
} catch (e) {
|
|
_showSnackBar('获取统计失败');
|
|
}
|
|
}
|
|
|
|
void _showSnackBar(String message) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(message),
|
|
backgroundColor: AppConstants.primaryColor,
|
|
duration: const Duration(seconds: 2),
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFFF5F5F5),
|
|
appBar: _buildAppBar(),
|
|
body: Column(
|
|
children: [
|
|
_buildSearchBar(),
|
|
_buildSortOptions(),
|
|
Expanded(
|
|
child: _isLoading
|
|
? _buildLoadingWidget()
|
|
: _filteredHistoryList.isEmpty
|
|
? _buildEmptyWidget()
|
|
: _buildHistoryList(),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// === 顶部导航栏 ===
|
|
PreferredSizeWidget _buildAppBar() {
|
|
return AppBar(
|
|
title: Text(
|
|
'历史记录',
|
|
style: TextStyle(
|
|
color: AppConstants.primaryColor,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
backgroundColor: Colors.white,
|
|
elevation: 0,
|
|
centerTitle: true,
|
|
actions: [
|
|
IconButton(
|
|
icon: Icon(Icons.delete_sweep, color: AppConstants.primaryColor),
|
|
onPressed: _historyList.isEmpty ? null : _clearHistory,
|
|
),
|
|
IconButton(
|
|
icon: Icon(Icons.bar_chart, color: AppConstants.primaryColor),
|
|
onPressed: _showStatsDialog,
|
|
),
|
|
IconButton(
|
|
icon: Icon(Icons.file_download, color: AppConstants.primaryColor),
|
|
onPressed: _exportHistory,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// === 搜索栏 ===
|
|
Widget _buildSearchBar() {
|
|
return Container(
|
|
padding: const EdgeInsets.all(16),
|
|
child: TextField(
|
|
onChanged: _searchHistory,
|
|
decoration: InputDecoration(
|
|
hintText: '搜索历史记录...',
|
|
prefixIcon: const Icon(Icons.search),
|
|
suffixIcon: _searchKeyword.isNotEmpty
|
|
? IconButton(
|
|
icon: const Icon(Icons.clear),
|
|
onPressed: () => _searchHistory(''),
|
|
)
|
|
: null,
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(25),
|
|
borderSide: BorderSide(color: Colors.grey[300]!),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(25),
|
|
borderSide: BorderSide(color: AppConstants.primaryColor),
|
|
),
|
|
filled: true,
|
|
fillColor: Colors.white,
|
|
contentPadding: const EdgeInsets.symmetric(
|
|
horizontal: 16,
|
|
vertical: 12,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// === 排序选项 ===
|
|
Widget _buildSortOptions() {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
child: Row(
|
|
children: [
|
|
const Text('排序方式:'),
|
|
const SizedBox(width: 16),
|
|
Expanded(
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: Colors.grey[300]!),
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: DropdownButton<String>(
|
|
value: _sortTypes[_selectedSortType],
|
|
isExpanded: true,
|
|
underline: Container(),
|
|
items: _sortTypes.map((type) {
|
|
return DropdownMenuItem<String>(
|
|
value: type,
|
|
child: Text(type),
|
|
);
|
|
}).toList(),
|
|
onChanged: (value) {
|
|
if (value != null) {
|
|
_sortHistory(_sortTypes.indexOf(value));
|
|
}
|
|
},
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// === 加载状态 ===
|
|
Widget _buildLoadingWidget() {
|
|
return Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
CircularProgressIndicator(),
|
|
SizedBox(height: 16),
|
|
Text(
|
|
'加载历史记录...',
|
|
style: TextStyle(fontSize: 16, color: Colors.grey[600]!),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// === 空状态 ===
|
|
Widget _buildEmptyWidget() {
|
|
return Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Icon(Icons.history, size: 64, color: Colors.grey[400]!),
|
|
SizedBox(height: 16),
|
|
Text(
|
|
'暂无历史记录',
|
|
style: TextStyle(fontSize: 16, color: Colors.grey[600]!),
|
|
),
|
|
SizedBox(height: 16),
|
|
ElevatedButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: AppConstants.primaryColor,
|
|
),
|
|
child: Text('返回'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// === 历史记录列表 ===
|
|
Widget _buildHistoryList() {
|
|
return ListView.builder(
|
|
padding: const EdgeInsets.all(16),
|
|
itemCount: _filteredHistoryList.length,
|
|
itemBuilder: (context, index) {
|
|
final item = _filteredHistoryList[index];
|
|
return Card(
|
|
margin: const EdgeInsets.only(bottom: 8),
|
|
child: ListTile(
|
|
leading: CircleAvatar(
|
|
radius: 20,
|
|
backgroundColor: AppConstants.primaryColor.withOpacity(0.1),
|
|
child: Text(
|
|
'${index + 1}',
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: AppConstants.primaryColor,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
title: Text(
|
|
item['name'] ?? '未知诗词',
|
|
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
subtitle: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'${item['alias'] ?? '未知朝代'} • ${item['date'] ?? ''}',
|
|
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
|
),
|
|
if (item['introduce']?.toString().isNotEmpty == true)
|
|
Text(
|
|
item['introduce']?.toString() ?? '',
|
|
style: TextStyle(fontSize: 11, color: Colors.grey[600]!),
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
],
|
|
),
|
|
trailing: PopupMenuButton<String>(
|
|
onSelected: (value) {
|
|
switch (value) {
|
|
case 'delete':
|
|
_deleteHistoryItem(index, item);
|
|
break;
|
|
case 'share':
|
|
_showSnackBar('分享功能开发中...');
|
|
break;
|
|
case 'view':
|
|
_showSnackBar('查看详情功能开发中...');
|
|
break;
|
|
}
|
|
},
|
|
itemBuilder: (context) => [
|
|
const PopupMenuItem<String>(
|
|
value: 'delete',
|
|
child: Row(
|
|
children: [
|
|
Icon(Icons.delete, color: Colors.red),
|
|
SizedBox(width: 8),
|
|
Text('删除'),
|
|
],
|
|
),
|
|
),
|
|
const PopupMenuItem<String>(
|
|
value: 'share',
|
|
child: Row(
|
|
children: [
|
|
Icon(Icons.share, color: AppConstants.primaryColor),
|
|
SizedBox(width: 8),
|
|
Text('分享'),
|
|
],
|
|
),
|
|
),
|
|
const PopupMenuItem<String>(
|
|
value: 'view',
|
|
child: Row(
|
|
children: [
|
|
Icon(Icons.visibility, color: AppConstants.primaryColor),
|
|
SizedBox(width: 8),
|
|
Text('查看'),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
onTap: () {
|
|
// 可以添加点击查看详情的功能
|
|
HapticFeedback.lightImpact();
|
|
},
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|