深色模式、首页设置页面和功能优化
This commit is contained in:
344
lib/services/get/history_controller.dart
Normal file
344
lib/services/get/history_controller.dart
Normal file
@@ -0,0 +1,344 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import '../../controllers/history_controller.dart' as history_controller;
|
||||
|
||||
class HistoryController extends GetxController {
|
||||
var historyList = <Map<String, dynamic>>[].obs;
|
||||
var filteredHistoryList = <Map<String, dynamic>>[].obs;
|
||||
var isLoading = true.obs;
|
||||
var searchKeyword = ''.obs;
|
||||
var selectedSortType = 0.obs; // 0: 时间倒序, 1: 时间正序, 2: 按名称排序
|
||||
var currentPage = 1.obs;
|
||||
var totalPages = 1.obs;
|
||||
var pageSize = 20;
|
||||
var totalCount = 0.obs;
|
||||
|
||||
final List<String> sortTypes = ['时间倒序', '时间正序', '按名称排序'];
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
loadHistory();
|
||||
}
|
||||
|
||||
// === 加载历史记录 ===
|
||||
Future<void> loadHistory() async {
|
||||
isLoading.value = true;
|
||||
|
||||
try {
|
||||
final history = await history_controller.HistoryController.getHistory();
|
||||
historyList.value = history;
|
||||
totalCount.value = history.length;
|
||||
totalPages.value = (totalCount.value / pageSize).ceil();
|
||||
filterAndSortHistory();
|
||||
} catch (e) {
|
||||
print('加载历史记录失败: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// === 搜索历史记录 ===
|
||||
void searchHistory(String keyword) {
|
||||
searchKeyword.value = keyword;
|
||||
currentPage.value = 1;
|
||||
filterAndSortHistory();
|
||||
}
|
||||
|
||||
// === 排序历史记录 ===
|
||||
void sortHistory(int sortType) {
|
||||
selectedSortType.value = sortType;
|
||||
currentPage.value = 1;
|
||||
filterAndSortHistory();
|
||||
}
|
||||
|
||||
// === 过滤和排序历史记录 ===
|
||||
void filterAndSortHistory() {
|
||||
var filtered = historyList;
|
||||
|
||||
// 搜索过滤
|
||||
if (searchKeyword.value.isNotEmpty) {
|
||||
filtered = historyList
|
||||
.where((item) {
|
||||
final name = item['name']?.toString().toLowerCase() ?? '';
|
||||
final alias = item['alias']?.toString().toLowerCase() ?? '';
|
||||
final introduce = item['introduce']?.toString().toLowerCase() ?? '';
|
||||
final keyword = searchKeyword.value.toLowerCase();
|
||||
return name.contains(keyword) ||
|
||||
alias.contains(keyword) ||
|
||||
introduce.contains(keyword);
|
||||
})
|
||||
.toList()
|
||||
.obs;
|
||||
}
|
||||
|
||||
// 排序
|
||||
final sortedList = List<Map<String, dynamic>>.from(filtered);
|
||||
|
||||
switch (selectedSortType.value) {
|
||||
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;
|
||||
}
|
||||
|
||||
totalCount.value = sortedList.length;
|
||||
totalPages.value = (totalCount.value / pageSize).ceil();
|
||||
filteredHistoryList.value = sortedList;
|
||||
}
|
||||
|
||||
// === 获取当前页的数据 ===
|
||||
List<Map<String, dynamic>> getCurrentPageData() {
|
||||
final startIndex = (currentPage.value - 1) * pageSize;
|
||||
final endIndex = startIndex + pageSize;
|
||||
if (startIndex >= filteredHistoryList.length) {
|
||||
return [];
|
||||
}
|
||||
return filteredHistoryList.sublist(
|
||||
startIndex,
|
||||
endIndex > filteredHistoryList.length
|
||||
? filteredHistoryList.length
|
||||
: endIndex,
|
||||
);
|
||||
}
|
||||
|
||||
// === 切换到下一页 ===
|
||||
void nextPage() {
|
||||
if (currentPage.value < totalPages.value) {
|
||||
currentPage.value++;
|
||||
}
|
||||
}
|
||||
|
||||
// === 切换到上一页 ===
|
||||
void previousPage() {
|
||||
if (currentPage.value > 1) {
|
||||
currentPage.value--;
|
||||
}
|
||||
}
|
||||
|
||||
// === 跳转到指定页 ===
|
||||
void goToPage(int page) {
|
||||
if (page >= 1 && page <= totalPages.value) {
|
||||
currentPage.value = page;
|
||||
}
|
||||
}
|
||||
|
||||
// === 清空历史记录 ===
|
||||
Future<void> clearHistory() async {
|
||||
try {
|
||||
final success = await history_controller.HistoryController.clearHistory();
|
||||
|
||||
if (success) {
|
||||
historyList.clear();
|
||||
filteredHistoryList.clear();
|
||||
totalCount.value = 0;
|
||||
totalPages.value = 1;
|
||||
currentPage.value = 1;
|
||||
Get.snackbar('提示', '历史记录已清空');
|
||||
} else {
|
||||
Get.snackbar('提示', '清空失败');
|
||||
}
|
||||
} catch (e) {
|
||||
print('清空历史记录失败: $e');
|
||||
Get.snackbar('提示', '清空失败');
|
||||
}
|
||||
}
|
||||
|
||||
// === 删除单条记录 ===
|
||||
Future<void> deleteHistoryItem(int index, Map<String, dynamic> item) async {
|
||||
try {
|
||||
final poetryId = item['id'] as int;
|
||||
final success = await history_controller
|
||||
.HistoryController.removeFromHistory(poetryId);
|
||||
|
||||
if (success) {
|
||||
await loadHistory();
|
||||
Get.snackbar('提示', '删除成功');
|
||||
} else {
|
||||
Get.snackbar('提示', '删除失败');
|
||||
}
|
||||
} catch (e) {
|
||||
Get.snackbar('提示', '删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
// === 导出历史记录 ===
|
||||
Future<void> exportHistory() async {
|
||||
try {
|
||||
if (filteredHistoryList.isEmpty) {
|
||||
Get.snackbar('提示', '无数据可导出');
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示格式选择对话框
|
||||
final selectedFormat = await Get.dialog<String>(
|
||||
CupertinoAlertDialog(
|
||||
title: const Text('选择导出格式'),
|
||||
content: const Text('请选择要导出的文件格式'),
|
||||
actions: [
|
||||
CupertinoDialogAction(
|
||||
child: const Text('JSON'),
|
||||
onPressed: () => Get.back(result: 'json'),
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
child: const Text('TXT'),
|
||||
onPressed: () => Get.back(result: 'txt'),
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
isDestructiveAction: true,
|
||||
child: const Text('取消'),
|
||||
onPressed: () => Get.back(result: null),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (selectedFormat == null) {
|
||||
return; // 用户取消
|
||||
}
|
||||
|
||||
// 生成导出内容
|
||||
String content;
|
||||
String fileName;
|
||||
|
||||
if (selectedFormat == 'json') {
|
||||
content = _generateJsonContent();
|
||||
fileName =
|
||||
'history_export_${DateTime.now().millisecondsSinceEpoch}.json';
|
||||
} else {
|
||||
content = _generateTxtContent();
|
||||
fileName =
|
||||
'history_export_${DateTime.now().millisecondsSinceEpoch}.txt';
|
||||
}
|
||||
|
||||
// 保存到临时文件
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final file = File('${tempDir.path}/$fileName');
|
||||
await file.writeAsString(content);
|
||||
|
||||
// 使用 share_plus 分享文件
|
||||
final result = await Share.shareXFiles(
|
||||
[XFile(file.path)],
|
||||
subject: '历史记录导出',
|
||||
text: '诗词历史记录导出文件',
|
||||
);
|
||||
|
||||
if (result.status == ShareResultStatus.success) {
|
||||
Get.snackbar('成功', '历史记录已导出并分享');
|
||||
} else if (result.status == ShareResultStatus.dismissed) {
|
||||
Get.snackbar('提示', '分享已取消');
|
||||
}
|
||||
|
||||
// 清理临时文件
|
||||
if (await file.exists()) {
|
||||
await file.delete();
|
||||
}
|
||||
} catch (e) {
|
||||
print('导出历史记录失败: $e');
|
||||
Get.snackbar('错误', '导出失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// === 生成 JSON 格式内容 ===
|
||||
String _generateJsonContent() {
|
||||
final exportData = {
|
||||
'exportTime': DateTime.now().toIso8601String(),
|
||||
'totalCount': filteredHistoryList.length,
|
||||
'records': filteredHistoryList
|
||||
.map(
|
||||
(item) => {
|
||||
'id': item['id'],
|
||||
'name': item['name'],
|
||||
'alias': item['alias'],
|
||||
'date': item['date'],
|
||||
'introduce': item['introduce'],
|
||||
'timestamp': item['timestamp'],
|
||||
},
|
||||
)
|
||||
.toList(),
|
||||
};
|
||||
return const JsonEncoder.withIndent(' ').convert(exportData);
|
||||
}
|
||||
|
||||
// === 生成 TXT 格式内容 ===
|
||||
String _generateTxtContent() {
|
||||
final buffer = StringBuffer();
|
||||
buffer.writeln('诗词历史记录导出');
|
||||
buffer.writeln('导出时间: ${DateTime.now()}');
|
||||
buffer.writeln('记录总数: ${filteredHistoryList.length}');
|
||||
buffer.writeln('=' * 50);
|
||||
buffer.writeln();
|
||||
|
||||
for (int i = 0; i < filteredHistoryList.length; i++) {
|
||||
final item = filteredHistoryList[i];
|
||||
buffer.writeln('【${i + 1}】${item['name'] ?? '未知诗词'}');
|
||||
buffer.writeln('朝代: ${item['alias'] ?? '未知朝代'}');
|
||||
buffer.writeln('日期: ${item['date'] ?? ''}');
|
||||
if (item['introduce']?.toString().isNotEmpty == true) {
|
||||
buffer.writeln('简介: ${item['introduce']}');
|
||||
}
|
||||
buffer.writeln('-' * 50);
|
||||
buffer.writeln();
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
// === 获取历史记录统计 ===
|
||||
Future<void> showStatsDialog() async {
|
||||
try {
|
||||
final stats =
|
||||
await history_controller.HistoryController.getHistoryStats();
|
||||
|
||||
if (stats.isEmpty) {
|
||||
Get.snackbar('提示', '暂无统计数据');
|
||||
return;
|
||||
}
|
||||
|
||||
Get.dialog(
|
||||
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: () => Get.back(), child: const Text('关闭')),
|
||||
],
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
Get.snackbar('提示', '获取统计失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user