345 lines
10 KiB
Dart
345 lines
10 KiB
Dart
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('提示', '获取统计失败');
|
|
}
|
|
}
|
|
}
|