重构
This commit is contained in:
67
CHANGELOG.md
67
CHANGELOG.md
@@ -4,61 +4,20 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
---
|
||||
|
||||
## [1.3.7] - 2026-03-30
|
||||
## [1.3.6] - 2026-03-31
|
||||
|
||||
### 新增
|
||||
- 🎵 **点击音效功能**
|
||||
- 新增音频管理类 `lib/utils/audio_manager.dart`,使用audioplayers库管理音效播放
|
||||
- 在首页点击诗词卡片时播放音效 `assets/audios/deep.mp3`
|
||||
- 在点击"下一条"按钮时播放音效
|
||||
- 在点击"点赞"按钮时播放音效
|
||||
- 修改 `lib/views/home/home_page.dart`,初始化音频管理器并在点赞、下一条事件中添加音效
|
||||
- 修改 `lib/views/home/home_part.dart`,在诗词卡片点击事件中添加音效
|
||||
- 支持静音控制,可通过AudioManager设置静音状态
|
||||
|
||||
### 修复
|
||||
- 📁 **音频文件声明**
|
||||
- 在 `pubspec.yaml` 中添加 `assets/audios/deep.mp3` 音频文件的声明,确保应用能正确加载音频资源
|
||||
- 🔊 **音频播放阻塞UI修复**
|
||||
- 修复声音开关打开时主页刷新点不动的问题
|
||||
- 修复点击按钮时没有声音的问题
|
||||
- 将音频播放改为非阻塞方式,避免等待播放完成
|
||||
- 修改 `lib/utils/audio_manager.dart`,移除 `_playSound` 方法中的 `await`
|
||||
- 修改 `lib/views/home/home_page.dart` 和 `lib/views/home/home_part.dart`,移除音频播放调用的 `await`
|
||||
- 确保音频播放不影响UI响应速度
|
||||
- 🔊 **音频重复播放修复**
|
||||
- 修复只有第一次播放声音,后续播放无声音的问题
|
||||
- 简化音频播放逻辑,移除复杂的音频上下文设置
|
||||
- 使用单个 AudioPlayer 实例,避免重复创建
|
||||
- 添加播放状态标志,防止重复播放
|
||||
- 播放前停止当前播放,确保音频可以重复播放
|
||||
|
||||
### 功能优化
|
||||
- 🔊 **声音反馈开关**
|
||||
- 在 `lib/views/profile/settings/app_fun.dart` 中添加声音反馈开关
|
||||
- 默认关闭声音反馈,可在设置中开启
|
||||
- 状态持久化到 SharedPreferences
|
||||
- 与 AudioManager 集成,控制首页音效播放
|
||||
- 🔊 **悬浮按钮音频播放**
|
||||
- 修改 `lib/views/home/home_part.dart` 中的悬浮按钮组件
|
||||
- 为上一条、下一条、点赞按钮添加音频播放功能
|
||||
- 确保操作时的声音反馈与设置中的开关状态同步
|
||||
|
||||
---
|
||||
|
||||
## [1.3.6] - 2026-03-30
|
||||
|
||||
### 新增
|
||||
- 🐛 **已知bug列表功能**
|
||||
- 新增bug列表页面 `lib/views/profile/components/bug_list_page.dart`
|
||||
- 从下到上弹出页面显示已知bug、解决方法和解决时间
|
||||
- 支持下拉刷新和滚动查看,列表可下滑
|
||||
- 显示bug优先级(高/中/低)、状态(已解决/解决中/待解决)
|
||||
- 显示影响用户范围、报告时间和预计解决时间
|
||||
- 提供详细的解决方案描述
|
||||
- 新增复现步骤功能,支持查看和收起复现步骤
|
||||
- 修改个人页面"已知bug"按钮点击事件,从显示SnackBar改为弹出bug列表页面
|
||||
- 文件:`lib/views/profile/profile_page.dart`
|
||||
### 优化
|
||||
- 🏷️ **文件重命名与统一**
|
||||
- 将 `sqlite_storage_controller.dart` 重命名为 `shared_preferences_storage_controller.dart`
|
||||
- 将类名 `SQLiteStorageController` 重命名为 `SharedPreferencesStorageController`
|
||||
- 更新所有引用该文件的导入语句和类名调用
|
||||
- 涉及文件:
|
||||
- `lib/main.dart`
|
||||
- `lib/controllers/history_controller.dart`
|
||||
- `lib/views/profile/profile_page.dart`
|
||||
- `lib/views/profile/level/poetry.dart`
|
||||
- `lib/views/profile/level/distinguish.dart`
|
||||
- 保持功能不变,仅统一命名规范
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/// 最新变化: 添加笔记管理功能
|
||||
|
||||
import 'dart:convert';
|
||||
import 'sqlite_storage_controller.dart';
|
||||
import 'shared_preferences_storage_controller.dart';
|
||||
|
||||
/// 历史记录控制器类
|
||||
/// 负责管理诗词浏览历史记录和点赞记录的本地存储和读取
|
||||
@@ -19,7 +19,7 @@ class HistoryController {
|
||||
/// 返回按时间倒序排列的诗词历史记录
|
||||
static Future<List<Map<String, dynamic>>> getHistory() async {
|
||||
try {
|
||||
final historyJson = await SQLiteStorageController.getString(
|
||||
final historyJson = await SharedPreferencesStorageController.getString(
|
||||
_historyKey,
|
||||
defaultValue: '[]',
|
||||
);
|
||||
@@ -48,7 +48,7 @@ class HistoryController {
|
||||
_isAdding = true;
|
||||
|
||||
try {
|
||||
final historyJson = await SQLiteStorageController.getString(
|
||||
final historyJson = await SharedPreferencesStorageController.getString(
|
||||
_historyKey,
|
||||
defaultValue: '[]',
|
||||
);
|
||||
@@ -73,7 +73,10 @@ class HistoryController {
|
||||
}
|
||||
|
||||
final updatedHistoryJson = json.encode(historyList);
|
||||
await SQLiteStorageController.setString(_historyKey, updatedHistoryJson);
|
||||
await SharedPreferencesStorageController.setString(
|
||||
_historyKey,
|
||||
updatedHistoryJson,
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
@@ -88,7 +91,7 @@ class HistoryController {
|
||||
/// 返回是否移除成功
|
||||
static Future<bool> removeFromHistory(int poetryId) async {
|
||||
try {
|
||||
final historyJson = await SQLiteStorageController.getString(
|
||||
final historyJson = await SharedPreferencesStorageController.getString(
|
||||
_historyKey,
|
||||
defaultValue: '[]',
|
||||
);
|
||||
@@ -100,7 +103,7 @@ class HistoryController {
|
||||
|
||||
if (historyList.length < originalLength) {
|
||||
final updatedHistoryJson = json.encode(historyList);
|
||||
await SQLiteStorageController.setString(
|
||||
await SharedPreferencesStorageController.setString(
|
||||
_historyKey,
|
||||
updatedHistoryJson,
|
||||
);
|
||||
@@ -118,7 +121,7 @@ class HistoryController {
|
||||
/// 返回是否清空成功
|
||||
static Future<bool> clearHistory() async {
|
||||
try {
|
||||
await SQLiteStorageController.remove(_historyKey);
|
||||
await SharedPreferencesStorageController.remove(_historyKey);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
@@ -266,7 +269,7 @@ class HistoryController {
|
||||
/// 返回点赞的诗词列表
|
||||
static Future<List<Map<String, dynamic>>> getLikedHistory() async {
|
||||
try {
|
||||
final likedJson = await SQLiteStorageController.getString(
|
||||
final likedJson = await SharedPreferencesStorageController.getString(
|
||||
_likedKey,
|
||||
defaultValue: '[]',
|
||||
);
|
||||
@@ -283,7 +286,7 @@ class HistoryController {
|
||||
|
||||
static Future<bool> addToLiked(Map<String, dynamic> poetryData) async {
|
||||
try {
|
||||
final likedJson = await SQLiteStorageController.getString(
|
||||
final likedJson = await SharedPreferencesStorageController.getString(
|
||||
_likedKey,
|
||||
defaultValue: '[]',
|
||||
);
|
||||
@@ -307,7 +310,10 @@ class HistoryController {
|
||||
likedList.insert(0, enrichedPoetryData);
|
||||
|
||||
final updatedLikedJson = json.encode(likedList);
|
||||
await SQLiteStorageController.setString(_likedKey, updatedLikedJson);
|
||||
await SharedPreferencesStorageController.setString(
|
||||
_likedKey,
|
||||
updatedLikedJson,
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
@@ -317,7 +323,7 @@ class HistoryController {
|
||||
|
||||
static Future<bool> removeLikedPoetry(String poetryId) async {
|
||||
try {
|
||||
final likedJson = await SQLiteStorageController.getString(
|
||||
final likedJson = await SharedPreferencesStorageController.getString(
|
||||
_likedKey,
|
||||
defaultValue: '[]',
|
||||
);
|
||||
@@ -329,7 +335,10 @@ class HistoryController {
|
||||
|
||||
if (likedList.length < originalLength) {
|
||||
final updatedLikedJson = json.encode(likedList);
|
||||
await SQLiteStorageController.setString(_likedKey, updatedLikedJson);
|
||||
await SharedPreferencesStorageController.setString(
|
||||
_likedKey,
|
||||
updatedLikedJson,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -351,7 +360,7 @@ class HistoryController {
|
||||
|
||||
static Future<bool> clearLikedHistory() async {
|
||||
try {
|
||||
await SQLiteStorageController.remove(_likedKey);
|
||||
await SharedPreferencesStorageController.remove(_likedKey);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
@@ -385,7 +394,9 @@ class HistoryController {
|
||||
/// 返回笔记列表(置顶的排在前面)
|
||||
static Future<List<Map<String, dynamic>>> getNotes() async {
|
||||
try {
|
||||
final notesJson = await SQLiteStorageController.getString(_notesKey);
|
||||
final notesJson = await SharedPreferencesStorageController.getString(
|
||||
_notesKey,
|
||||
);
|
||||
if (notesJson.isEmpty) {
|
||||
// 返回默认笔记示例
|
||||
return _getDefaultNotes();
|
||||
@@ -529,7 +540,7 @@ class HistoryController {
|
||||
}
|
||||
|
||||
final notesJson = json.encode(notes);
|
||||
await SQLiteStorageController.setString(_notesKey, notesJson);
|
||||
await SharedPreferencesStorageController.setString(_notesKey, notesJson);
|
||||
|
||||
return id;
|
||||
} catch (e) {
|
||||
@@ -557,7 +568,7 @@ class HistoryController {
|
||||
notes.removeWhere((n) => n['id'] == noteId);
|
||||
|
||||
final notesJson = json.encode(notes);
|
||||
await SQLiteStorageController.setString(_notesKey, notesJson);
|
||||
await SharedPreferencesStorageController.setString(_notesKey, notesJson);
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
@@ -578,7 +589,7 @@ class HistoryController {
|
||||
notes[index]['isPinned'] = !currentPinned;
|
||||
|
||||
final notesJson = json.encode(notes);
|
||||
await SQLiteStorageController.setString(_notesKey, notesJson);
|
||||
await SharedPreferencesStorageController.setString(_notesKey, notesJson);
|
||||
|
||||
return !currentPinned;
|
||||
} catch (e) {
|
||||
@@ -604,7 +615,7 @@ class HistoryController {
|
||||
}
|
||||
|
||||
final notesJson = json.encode(notes);
|
||||
await SQLiteStorageController.setString(_notesKey, notesJson);
|
||||
await SharedPreferencesStorageController.setString(_notesKey, notesJson);
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/// 时间: 2025-03-25
|
||||
/// 功能: SharedPreferences存储控制器
|
||||
/// 介绍: 使用SharedPreferences实现本地存储功能,替代SQLite
|
||||
/// 最新变化: 将SQLite替换为SharedPreferences实现
|
||||
/// 介绍: 使用SharedPreferences实现本地存储功能
|
||||
/// 最新变化: 重命名为SharedPreferencesStorageController
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// SharedPreferences存储控制器类
|
||||
/// 负责管理本地键值对存储,基于SharedPreferences实现
|
||||
class SQLiteStorageController {
|
||||
class SharedPreferencesStorageController {
|
||||
static SharedPreferences? _prefs;
|
||||
|
||||
/// 初始化SharedPreferences
|
||||
@@ -4,12 +4,12 @@ import 'utils/app_initializer.dart';
|
||||
import 'utils/force_guide_checker.dart';
|
||||
import 'routes/app_routes.dart';
|
||||
import 'constants/app_constants.dart';
|
||||
import 'controllers/sqlite_storage_controller.dart';
|
||||
import 'controllers/shared_preferences_storage_controller.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await SQLiteStorageController.init();
|
||||
await SharedPreferencesStorageController.init();
|
||||
|
||||
final result = await AppInitializer.initialize();
|
||||
|
||||
|
||||
@@ -4,23 +4,22 @@
|
||||
/// 最新变化: 新增 NetworkEventType.search 与 sendSearchEvent,供诗词搜索页通知
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// 网络状态枚举
|
||||
enum NetworkStatus {
|
||||
idle, // 空闲
|
||||
loading, // 加载中
|
||||
success, // 成功
|
||||
error, // 错误
|
||||
idle, // 空闲
|
||||
loading, // 加载中
|
||||
success, // 成功
|
||||
error, // 错误
|
||||
}
|
||||
|
||||
/// 网络事件类型
|
||||
enum NetworkEventType {
|
||||
like, // 点赞操作
|
||||
unlike, // 取消点赞
|
||||
refresh, // 刷新操作
|
||||
load, // 加载操作
|
||||
search, // 诗词搜索完成
|
||||
like, // 点赞操作
|
||||
unlike, // 取消点赞
|
||||
refresh, // 刷新操作
|
||||
load, // 加载操作
|
||||
search, // 诗词搜索完成
|
||||
noteUpdate, // 笔记更新(新建/编辑/删除)
|
||||
}
|
||||
|
||||
@@ -41,20 +40,21 @@ class NetworkEvent {
|
||||
|
||||
/// 网络监听服务
|
||||
class NetworkListenerService {
|
||||
static final NetworkListenerService _instance = NetworkListenerService._internal();
|
||||
static final NetworkListenerService _instance =
|
||||
NetworkListenerService._internal();
|
||||
factory NetworkListenerService() => _instance;
|
||||
NetworkListenerService._internal();
|
||||
|
||||
// 状态管理
|
||||
NetworkStatus _status = NetworkStatus.idle;
|
||||
final Map<String, bool> _loadingStates = {};
|
||||
|
||||
|
||||
// 事件流控制器
|
||||
final StreamController<NetworkEvent> _eventController =
|
||||
final StreamController<NetworkEvent> _eventController =
|
||||
StreamController<NetworkEvent>.broadcast();
|
||||
|
||||
|
||||
// 状态流控制器
|
||||
final StreamController<NetworkStatus> _statusController =
|
||||
final StreamController<NetworkStatus> _statusController =
|
||||
StreamController<NetworkStatus>.broadcast();
|
||||
|
||||
// 公开流
|
||||
@@ -89,10 +89,7 @@ class NetworkListenerService {
|
||||
/// 发送错误事件
|
||||
void sendErrorEvent(NetworkEventType type, {String? errorMessage}) {
|
||||
_updateStatus(NetworkStatus.error);
|
||||
_eventController.add(NetworkEvent(
|
||||
type: type,
|
||||
errorMessage: errorMessage,
|
||||
));
|
||||
_eventController.add(NetworkEvent(type: type, errorMessage: errorMessage));
|
||||
}
|
||||
|
||||
/// 发送点赞事件
|
||||
@@ -131,7 +128,7 @@ class NetworkListenerService {
|
||||
/// 方便其他类使用网络监听功能
|
||||
mixin NetworkListenerMixin {
|
||||
final NetworkListenerService _networkService = NetworkListenerService();
|
||||
|
||||
|
||||
Stream<NetworkEvent> get networkEvents => _networkService.eventStream;
|
||||
Stream<NetworkStatus> get networkStatus => _networkService.statusStream;
|
||||
NetworkStatus get currentNetworkStatus => _networkService.currentStatus;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:audioplayers/audioplayers.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// 时间: 2026-03-30
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class HttpClient {
|
||||
static const String _baseUrl = 'https://yy.vogov.cn/api/';
|
||||
@@ -89,7 +88,7 @@ class HttpClient {
|
||||
final options = Options(
|
||||
method: method,
|
||||
headers: headers != null
|
||||
? {..._options.headers!, ...headers}
|
||||
? {..._options.headers, ...headers}
|
||||
: _options.headers,
|
||||
);
|
||||
|
||||
@@ -165,7 +164,7 @@ class HttpClient {
|
||||
final options = Options(
|
||||
method: method,
|
||||
headers: headers != null
|
||||
? {..._options.headers!, ...headers}
|
||||
? {..._options.headers, ...headers}
|
||||
: _options.headers,
|
||||
);
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
/// 最新变化: 新增 search.php 搜索接口(与 API_DOCUMENTATION.md 一致)
|
||||
|
||||
import 'http_client.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class PoetryApi {
|
||||
static const String _endpoint = 'pms.php';
|
||||
|
||||
/// 全文搜索(见 lib/services/API_DOCUMENTATION.md 第二节)
|
||||
static const String _searchEndpoint = 'searchs.php';
|
||||
|
||||
@@ -17,26 +17,29 @@ class PoetryApi {
|
||||
String? tag,
|
||||
}) async {
|
||||
final queryParams = <String, dynamic>{};
|
||||
|
||||
|
||||
if (dynasty != null && dynasty.isNotEmpty) {
|
||||
queryParams['dyn'] = dynasty;
|
||||
}
|
||||
|
||||
|
||||
if (tag != null && tag.isNotEmpty) {
|
||||
queryParams['tag'] = tag;
|
||||
}
|
||||
|
||||
final response = await HttpClient.get(_endpoint, queryParameters: queryParams);
|
||||
|
||||
final response = await HttpClient.get(
|
||||
_endpoint,
|
||||
queryParameters: queryParams,
|
||||
);
|
||||
|
||||
if (!response.isSuccess) {
|
||||
throw HttpException('获取诗词失败1: ${response.message}');
|
||||
}
|
||||
|
||||
|
||||
final jsonData = response.jsonData;
|
||||
if (jsonData['code'] != 0) {
|
||||
throw HttpException(jsonData['msg'] ?? '获取诗词失败2');
|
||||
}
|
||||
|
||||
|
||||
return PoetryResponse.fromJson(jsonData);
|
||||
}
|
||||
|
||||
@@ -46,16 +49,16 @@ class PoetryApi {
|
||||
_endpoint,
|
||||
queryParameters: {'id': id.toString()},
|
||||
);
|
||||
|
||||
|
||||
if (!response.isSuccess) {
|
||||
throw HttpException('获取诗词失败3: ${response.message}');
|
||||
}
|
||||
|
||||
|
||||
final jsonData = response.jsonData;
|
||||
if (jsonData['code'] != 0) {
|
||||
throw HttpException(jsonData['msg'] ?? '获取诗词失败4');
|
||||
}
|
||||
|
||||
|
||||
return PoetryResponse.fromJson(jsonData);
|
||||
}
|
||||
|
||||
@@ -68,16 +71,16 @@ class PoetryApi {
|
||||
'like': '', // 无值参数
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
if (!response.isSuccess) {
|
||||
throw HttpException('点赞失败: ${response.message}');
|
||||
}
|
||||
|
||||
|
||||
final jsonData = response.jsonData;
|
||||
if (jsonData['code'] != 0) {
|
||||
throw HttpException(jsonData['msg'] ?? '点赞失败');
|
||||
}
|
||||
|
||||
|
||||
return PoetryResponse.fromJson(jsonData);
|
||||
}
|
||||
|
||||
@@ -87,16 +90,16 @@ class PoetryApi {
|
||||
_endpoint,
|
||||
queryParameters: {'lid': lid.toString()},
|
||||
);
|
||||
|
||||
|
||||
if (!response.isSuccess) {
|
||||
throw HttpException('点赞失败: ${response.message}');
|
||||
}
|
||||
|
||||
|
||||
final jsonData = response.jsonData;
|
||||
if (jsonData['code'] != 0) {
|
||||
throw HttpException(jsonData['msg'] ?? '点赞失败');
|
||||
}
|
||||
|
||||
|
||||
return PoetryResponse.fromJson(jsonData);
|
||||
}
|
||||
|
||||
@@ -111,7 +114,10 @@ class PoetryApi {
|
||||
}
|
||||
|
||||
/// 按朝代和标签获取诗词
|
||||
static Future<PoetryResponse> getPoetryByDynastyAndTag(String dynasty, String tag) async {
|
||||
static Future<PoetryResponse> getPoetryByDynastyAndTag(
|
||||
String dynasty,
|
||||
String tag,
|
||||
) async {
|
||||
return getRandomPoetry(dynasty: dynasty, tag: tag);
|
||||
}
|
||||
|
||||
@@ -167,9 +173,12 @@ class PoetryApi {
|
||||
|
||||
// 从 pagination 中获取分页信息
|
||||
final pagination = raw['pagination'] as Map<String, dynamic>? ?? {};
|
||||
final totalCount = int.tryParse(pagination['total_count']?.toString() ?? '0') ?? 0;
|
||||
final currentPage = int.tryParse(pagination['current_page']?.toString() ?? '$page') ?? page;
|
||||
final pageSize = int.tryParse(pagination['page_size']?.toString() ?? '$limit') ?? limit;
|
||||
final totalCount =
|
||||
int.tryParse(pagination['total_count']?.toString() ?? '0') ?? 0;
|
||||
final currentPage =
|
||||
int.tryParse(pagination['current_page']?.toString() ?? '$page') ?? page;
|
||||
final pageSize =
|
||||
int.tryParse(pagination['page_size']?.toString() ?? '$limit') ?? limit;
|
||||
|
||||
return SearchPoetryResult(
|
||||
total: totalCount,
|
||||
@@ -203,11 +212,7 @@ class PoetryResponse {
|
||||
final String message;
|
||||
final PoetryData? data;
|
||||
|
||||
PoetryResponse({
|
||||
required this.code,
|
||||
required this.message,
|
||||
this.data,
|
||||
});
|
||||
PoetryResponse({required this.code, required this.message, this.data});
|
||||
|
||||
factory PoetryResponse.fromJson(Map<String, dynamic> json) {
|
||||
return PoetryResponse(
|
||||
@@ -218,18 +223,14 @@ class PoetryResponse {
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'code': code,
|
||||
'msg': message,
|
||||
'data': data?.toJson(),
|
||||
};
|
||||
return {'code': code, 'msg': message, 'data': data?.toJson()};
|
||||
}
|
||||
}
|
||||
|
||||
/// 诗词数据模型
|
||||
class PoetryData {
|
||||
final int id;
|
||||
final String name;// 精选诗句
|
||||
final String name; // 精选诗句
|
||||
final String alias; // 朝代
|
||||
final String keywords; // 标签
|
||||
final String introduce; // 译文/介绍
|
||||
@@ -323,7 +324,11 @@ class PoetryData {
|
||||
/// 获取标签列表
|
||||
List<String> get keywordList {
|
||||
if (keywords.isEmpty) return [];
|
||||
return keywords.split(',').map((k) => k.trim()).where((k) => k.isNotEmpty).toList();
|
||||
return keywords
|
||||
.split(',')
|
||||
.map((k) => k.trim())
|
||||
.where((k) => k.isNotEmpty)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// 生成星级显示
|
||||
|
||||
@@ -8,7 +8,6 @@ import 'dart:io' as io show Platform;
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
|
||||
import 'package:cookie_jar/cookie_jar.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:platform_info/platform_info.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../constants/app_constants.dart';
|
||||
import '../../utils/responsive_layout.dart';
|
||||
|
||||
/// 时间: 2026-03-25
|
||||
/// 功能: 分类页面
|
||||
@@ -14,27 +13,67 @@ class CategoryPage extends StatefulWidget {
|
||||
State<CategoryPage> createState() => _CategoryPageState();
|
||||
}
|
||||
|
||||
class _CategoryPageState extends State<CategoryPage> with SingleTickerProviderStateMixin {
|
||||
class _CategoryPageState extends State<CategoryPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late TabController _tabController;
|
||||
final List<String> _tabCategories = ['场景分类', '朝代分类'];
|
||||
|
||||
static const sceneData = {
|
||||
"节日": ["七夕节", "中秋节", "元宵节", "寒食节", "清明节", "端午节", "重阳节", "春节", "节日"],
|
||||
"季节": ["三月", "二月", "冬天", "夏天", "春天", "春季", "秋天"],
|
||||
"古籍": ["三国志", "三国演义", "三字经", "中庸", "列子", "史记", "后汉书", "吕氏春秋", "商君书", "围炉夜话", "增广贤文", "墨子", "孙子兵法", "孟子", "小窗幽记", "尚书", "左传", "幼学琼林", "庄子", "战国策", "文心雕龙", "易传", "晋书", "汉书", "淮南子", "礼记", "管子", "红楼梦", "老子", "荀子", "菜根谭", "警世通言", "论语", "资治通鉴", "韩非子", "鬼谷子", "古籍", "格言联璧"],
|
||||
"古籍": [
|
||||
"三国志",
|
||||
"三国演义",
|
||||
"三字经",
|
||||
"中庸",
|
||||
"列子",
|
||||
"史记",
|
||||
"后汉书",
|
||||
"吕氏春秋",
|
||||
"商君书",
|
||||
"围炉夜话",
|
||||
"增广贤文",
|
||||
"墨子",
|
||||
"孙子兵法",
|
||||
"孟子",
|
||||
"小窗幽记",
|
||||
"尚书",
|
||||
"左传",
|
||||
"幼学琼林",
|
||||
"庄子",
|
||||
"战国策",
|
||||
"文心雕龙",
|
||||
"易传",
|
||||
"晋书",
|
||||
"汉书",
|
||||
"淮南子",
|
||||
"礼记",
|
||||
"管子",
|
||||
"红楼梦",
|
||||
"老子",
|
||||
"荀子",
|
||||
"菜根谭",
|
||||
"警世通言",
|
||||
"论语",
|
||||
"资治通鉴",
|
||||
"韩非子",
|
||||
"鬼谷子",
|
||||
"古籍",
|
||||
"格言联璧",
|
||||
],
|
||||
"情感": ["伤感", "励志", "友情", "思乡", "思念", "感恩", "爱国", "爱情", "离别"],
|
||||
"景物": ["庐山", "泰山", "西湖", "长江", "黄河", "边塞", "田园", "山水", "夜景"],
|
||||
"天文气象": ["写云", "写雨", "写雪", "写风", "星星", "月亮", "流星"],
|
||||
"动植物": ["写鸟", "柳树", "桃花", "梅花", "竹子", "荷花", "菊花"],
|
||||
"语言文学": ["对联", "谚语", "一言", "读书", "哲理"],
|
||||
"其他": ["母亲", "老师", "户外", "礼物", "酒"]
|
||||
"其他": ["母亲", "老师", "户外", "礼物", "酒"],
|
||||
};
|
||||
|
||||
static const dynastyData = {
|
||||
"主要朝代": ["唐代", "宋代", "元代", "明代", "清代"],
|
||||
"古代朝代": ["南北朝", "五代", "隋代"],
|
||||
"近现代": ["近现代", "用户投稿", "管理员测试"],
|
||||
"其他": ["暂无朝代"]
|
||||
"其他": ["暂无朝代"],
|
||||
};
|
||||
|
||||
@override
|
||||
@@ -51,21 +90,20 @@ class _CategoryPageState extends State<CategoryPage> with SingleTickerProviderSt
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDesktop = ResponsiveLayout.isDesktop(context);
|
||||
final isTablet = ResponsiveLayout.isTablet(context);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
body: Column(
|
||||
children: [
|
||||
// 自定义标题栏
|
||||
|
||||
|
||||
// Tab栏
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: TabBar(
|
||||
controller: _tabController,
|
||||
tabs: _tabCategories.map((category) => Tab(text: category)).toList(),
|
||||
tabs: _tabCategories
|
||||
.map((category) => Tab(text: category))
|
||||
.toList(),
|
||||
labelColor: AppConstants.primaryColor,
|
||||
unselectedLabelColor: Colors.grey[600],
|
||||
indicatorColor: AppConstants.primaryColor,
|
||||
@@ -88,7 +126,10 @@ class _CategoryPageState extends State<CategoryPage> with SingleTickerProviderSt
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCategoryGrid(Map<String, List<String>> data, String categoryType) {
|
||||
Widget _buildCategoryGrid(
|
||||
Map<String, List<String>> data,
|
||||
String categoryType,
|
||||
) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: ListView.builder(
|
||||
@@ -96,7 +137,7 @@ class _CategoryPageState extends State<CategoryPage> with SingleTickerProviderSt
|
||||
itemBuilder: (context, index) {
|
||||
final category = data.keys.elementAt(index);
|
||||
final items = data[category]!;
|
||||
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 16.0),
|
||||
elevation: 2,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../constants/app_constants.dart';
|
||||
import '../../utils/responsive_layout.dart';
|
||||
import '../../utils/http/http_client.dart';
|
||||
import '../../models/poetry_model.dart';
|
||||
import '../../controllers/load/locally.dart';
|
||||
|
||||
@@ -23,14 +23,6 @@ class _RatePageState extends State<RatePage>
|
||||
int _debugBarCount = 7;
|
||||
bool _showDebugPanel = false;
|
||||
|
||||
// 活跃度阈值配置
|
||||
final Map<String, int> _activityThresholds = {
|
||||
'low': 1, // 1-5
|
||||
'medium': 6, // 6-20
|
||||
'high': 21, // 21-100
|
||||
'veryHigh': 100, // 100+
|
||||
};
|
||||
|
||||
// 模拟活跃度数据 (实际条数)
|
||||
List<int> _weekData = [];
|
||||
List<List<int>> _monthData = [];
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../constants/app_constants.dart';
|
||||
import '../utils/responsive_layout.dart';
|
||||
import '../utils/flutter_compatibility_fix.dart';
|
||||
import '../widgets/tabbed_nav_app_bar.dart';
|
||||
import 'active/active_search_page.dart';
|
||||
import 'active/category_page.dart';
|
||||
@@ -80,7 +79,6 @@ class _DiscoverPageState extends State<DiscoverPage>
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
children: _categories.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final category = entry.value;
|
||||
// 搜索标签显示 ActiveSearchPage
|
||||
if (category == '搜索') {
|
||||
|
||||
@@ -234,73 +234,4 @@ class _FavoritesPageState extends State<FavoritesPage>
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showFavoriteDetails(BuildContext context, int index, String category) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('$category详情'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('分类: $category'),
|
||||
const SizedBox(height: 8),
|
||||
Text('收藏时间: ${(index + 1) * 2}天前'),
|
||||
const SizedBox(height: 8),
|
||||
const Text('这是收藏内容的详细信息。'),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('关闭'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleFavoriteAction(String action, int index) {
|
||||
switch (action) {
|
||||
case 'share':
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('分享了收藏${index + 1}')));
|
||||
break;
|
||||
case 'edit':
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('编辑了收藏${index + 1}')));
|
||||
break;
|
||||
case 'delete':
|
||||
_showDeleteConfirm(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _showDeleteConfirm(int index) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('确认删除'),
|
||||
content: Text('确定要删除收藏${index + 1}吗?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('取消'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('已删除收藏${index + 1}')));
|
||||
},
|
||||
child: const Text('删除', style: TextStyle(color: Colors.red)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import '../../../constants/app_constants.dart';
|
||||
import '../../../utils/http/poetry_api.dart';
|
||||
import '../../../services/network_listener_service.dart';
|
||||
import '../home/home_components.dart';
|
||||
import '../home/home_part.dart';
|
||||
|
||||
/// 点赞足迹页面
|
||||
class FootprintPage extends StatefulWidget {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
/// 最新变化: 新建文件
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../../utils/http/poetry_api.dart';
|
||||
|
||||
@@ -31,7 +31,6 @@ class _HomePageState extends State<HomePage>
|
||||
bool _isLoadingLike = false;
|
||||
String _errorMessage = '';
|
||||
String _starDisplay = '';
|
||||
final String _historyKey = 'poetry_history';
|
||||
List<Map<String, dynamic>> _historyList = [];
|
||||
int _currentHistoryIndex = -1;
|
||||
late AnimationController _fadeController;
|
||||
@@ -41,7 +40,6 @@ class _HomePageState extends State<HomePage>
|
||||
|
||||
// 动态加载状态
|
||||
bool _isLoadingNext = false;
|
||||
bool _isLoadingPrevious = false;
|
||||
Map<String, bool> _sectionLoadingStates = {
|
||||
'title': false,
|
||||
'content': false,
|
||||
|
||||
@@ -524,34 +524,26 @@ class _PoetryCardState extends State<PoetryCard> {
|
||||
),
|
||||
),
|
||||
// 右边:星星和点赞
|
||||
if (!isLoading &&
|
||||
(widget.poetryData.star != null ||
|
||||
widget.poetryData.like != null ||
|
||||
widget.poetryData.hitsTotal != null))
|
||||
if (!isLoading)
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (widget.poetryData.star != null) ...[
|
||||
Text(
|
||||
PoetryDataUtils.generateStars(widget.poetryData.star),
|
||||
style: const TextStyle(fontSize: 14),
|
||||
Text(
|
||||
PoetryDataUtils.generateStars(widget.poetryData.star),
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
PoetryDataUtils.generateLikeText(widget.poetryData.like),
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
PoetryDataUtils.generateViewText(
|
||||
widget.poetryData.hitsTotal,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
if (widget.poetryData.like != null) ...[
|
||||
Text(
|
||||
PoetryDataUtils.generateLikeText(widget.poetryData.like),
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
if (widget.poetryData.hitsTotal != null) ...[
|
||||
Text(
|
||||
PoetryDataUtils.generateViewText(
|
||||
widget.poetryData.hitsTotal,
|
||||
),
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../constants/app_constants.dart';
|
||||
import '../routes/app_routes.dart';
|
||||
import './home/home_page.dart';
|
||||
import './discover_page.dart';
|
||||
import './favorites_page.dart';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../../constants/app_constants.dart';
|
||||
import '../../../config/app_config.dart';
|
||||
|
||||
/// 时间: 2026-03-27
|
||||
/// 功能: 应用数据管理页面
|
||||
@@ -222,39 +221,6 @@ class _AppDataPageState extends State<AppDataPage> {
|
||||
);
|
||||
}
|
||||
|
||||
void _showRestartDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => AlertDialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
title: Row(
|
||||
children: [
|
||||
Icon(Icons.refresh, color: AppConstants.primaryColor),
|
||||
const SizedBox(width: 8),
|
||||
const Text('需要重启'),
|
||||
],
|
||||
),
|
||||
content: const Text('数据已清空,需要重启应用才能生效。'),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppConstants.primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: const Text('知道了'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showSnackBar(String message) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
|
||||
@@ -161,30 +161,6 @@ class _SpGuidePageState extends State<SpGuidePage>
|
||||
}
|
||||
}
|
||||
|
||||
void _skipToEnd() {
|
||||
if (_currentPage == 1) {
|
||||
if (!_agreementAccepted) {
|
||||
_showNeedAcceptAgreementDialog();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_currentPage == 2) {
|
||||
if (!_agreementAccepted) {
|
||||
_showNeedAcceptAgreementDialog();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_pageController.hasClients) {
|
||||
_pageController.animateToPage(
|
||||
_totalPages - 1,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _showNeedAcceptAgreementDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
|
||||
@@ -7,7 +7,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import '../../constants/app_constants.dart';
|
||||
import '../../controllers/history_controller.dart';
|
||||
import '../../utils/flutter_compatibility_fix.dart';
|
||||
|
||||
class HistoryPage extends StatefulWidget {
|
||||
const HistoryPage({super.key});
|
||||
@@ -363,7 +362,7 @@ class _HistoryPageState extends State<HistoryPage> {
|
||||
}).toList(),
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
_sortHistory(_sortTypes.indexOf(value!));
|
||||
_sortHistory(_sortTypes.indexOf(value));
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../constants/app_constants.dart';
|
||||
import '../../../controllers/sqlite_storage_controller.dart';
|
||||
import '../../../controllers/shared_preferences_storage_controller.dart';
|
||||
import '../../../controllers/history_controller.dart';
|
||||
import '../../../services/network_listener_service.dart';
|
||||
|
||||
@@ -47,10 +47,11 @@ class _DistinguishPageState extends State<DistinguishPage> {
|
||||
Future<void> _loadAnswerRecords() async {
|
||||
try {
|
||||
// 获取答题记录列表
|
||||
List<String> records = await SQLiteStorageController.getStringList(
|
||||
'poetryAnswerRecords',
|
||||
defaultValue: [],
|
||||
);
|
||||
List<String> records =
|
||||
await SharedPreferencesStorageController.getStringList(
|
||||
'poetryAnswerRecords',
|
||||
defaultValue: [],
|
||||
);
|
||||
|
||||
// 解析记录
|
||||
_answerRecords = records
|
||||
@@ -85,27 +86,27 @@ class _DistinguishPageState extends State<DistinguishPage> {
|
||||
/// 加载统计数据
|
||||
Future<void> _loadStatistics() async {
|
||||
try {
|
||||
_totalQuestions = await SQLiteStorageController.getInt(
|
||||
_totalQuestions = await SharedPreferencesStorageController.getInt(
|
||||
'totalQuestions',
|
||||
defaultValue: 0,
|
||||
);
|
||||
_correctAnswers = await SQLiteStorageController.getInt(
|
||||
_correctAnswers = await SharedPreferencesStorageController.getInt(
|
||||
'correctAnswers',
|
||||
defaultValue: 0,
|
||||
);
|
||||
_wrongAnswers = await SQLiteStorageController.getInt(
|
||||
_wrongAnswers = await SharedPreferencesStorageController.getInt(
|
||||
'wrongAnswers',
|
||||
defaultValue: 0,
|
||||
);
|
||||
int totalTime = await SQLiteStorageController.getInt(
|
||||
int totalTime = await SharedPreferencesStorageController.getInt(
|
||||
'totalTime',
|
||||
defaultValue: 0,
|
||||
);
|
||||
_hintCount = await SQLiteStorageController.getInt(
|
||||
_hintCount = await SharedPreferencesStorageController.getInt(
|
||||
'hintCount',
|
||||
defaultValue: 0,
|
||||
);
|
||||
_skipCount = await SQLiteStorageController.getInt(
|
||||
_skipCount = await SharedPreferencesStorageController.getInt(
|
||||
'skipCount',
|
||||
defaultValue: 0,
|
||||
);
|
||||
@@ -453,7 +454,10 @@ $_poetryLevel
|
||||
|
||||
if (confirmed == true) {
|
||||
try {
|
||||
await SQLiteStorageController.setStringList('poetryAnswerRecords', []);
|
||||
await SharedPreferencesStorageController.setStringList(
|
||||
'poetryAnswerRecords',
|
||||
[],
|
||||
);
|
||||
setState(() {
|
||||
_answerRecords = [];
|
||||
});
|
||||
@@ -810,87 +814,4 @@ $_poetryLevel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 从答题记录创建笔记
|
||||
Future<void> _createNoteFromRecords() async {
|
||||
try {
|
||||
// 生成答题记录的详细内容
|
||||
final StringBuffer content = StringBuffer();
|
||||
content.writeln('## 答题记录汇总');
|
||||
content.writeln(
|
||||
'生成时间: ${DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now())}',
|
||||
);
|
||||
content.writeln('总记录数: ${_answerRecords.length}');
|
||||
content.writeln('');
|
||||
|
||||
// 统计信息
|
||||
int correctCount = 0;
|
||||
int totalCount = _answerRecords.length;
|
||||
|
||||
for (final record in _answerRecords) {
|
||||
if (record['isCorrect'] == true) {
|
||||
correctCount++;
|
||||
}
|
||||
}
|
||||
|
||||
content.writeln('### 统计信息');
|
||||
content.writeln('- 总题数: $totalCount');
|
||||
content.writeln('- 答对数: $correctCount');
|
||||
content.writeln('- 答错数: ${totalCount - correctCount}');
|
||||
content.writeln(
|
||||
'- 正确率: ${totalCount > 0 ? (correctCount / totalCount * 100).toStringAsFixed(1) : 0}%',
|
||||
);
|
||||
content.writeln('');
|
||||
|
||||
content.writeln('### 详细记录');
|
||||
content.writeln('');
|
||||
|
||||
for (int i = 0; i < _answerRecords.length; i++) {
|
||||
final record = _answerRecords[i];
|
||||
final question = record['question'] ?? '未知题目';
|
||||
final userAnswer = record['userAnswer'] ?? '未知答案';
|
||||
final correctAnswer = record['correctAnswer'] ?? '未知答案';
|
||||
final isCorrect = record['isCorrect'] == true;
|
||||
final answerTime = record['answerTime'] ?? '未知时间';
|
||||
final tags = record['tags'] as List<dynamic>? ?? [];
|
||||
|
||||
content.writeln('#### ${i + 1}. $question');
|
||||
content.writeln('- **你的答案**: $userAnswer');
|
||||
content.writeln('- **正确答案**: $correctAnswer');
|
||||
content.writeln('- **答题结果**: ${isCorrect ? '✅ 正确' : '❌ 错误'}');
|
||||
content.writeln('- **答题时间**: ${_formatTime(answerTime)}');
|
||||
|
||||
if (tags.isNotEmpty) {
|
||||
content.writeln('- **标签**: ${tags.join(', ')}');
|
||||
}
|
||||
|
||||
content.writeln('');
|
||||
}
|
||||
|
||||
final noteId = await HistoryController.saveNote(
|
||||
title: '答题记录_${DateFormat('yyyyMMdd_HHmmss').format(DateTime.now())}',
|
||||
content: content.toString(),
|
||||
category: '答题记录',
|
||||
);
|
||||
|
||||
if (noteId != null) {
|
||||
NetworkListenerService().sendSuccessEvent(
|
||||
NetworkEventType.noteUpdate,
|
||||
data: noteId,
|
||||
);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(const SnackBar(content: Text('答题记录已保存到笔记')));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('创建笔记失败: $e');
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('创建笔记失败: $e')));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../constants/app_constants.dart';
|
||||
import '../../../controllers/sqlite_storage_controller.dart';
|
||||
import '../../../controllers/shared_preferences_storage_controller.dart';
|
||||
import 'level-jilu.dart';
|
||||
import 'flow-anim.dart';
|
||||
import 'distinguish.dart';
|
||||
@@ -329,12 +329,21 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
Future<void> _saveAnswerRecord({bool isCorrect = false}) async {
|
||||
try {
|
||||
// 保存统计数据
|
||||
await SQLiteStorageController.setInt('totalQuestions', _totalQuestions);
|
||||
await SQLiteStorageController.setInt('correctAnswers', _correctAnswers);
|
||||
await SQLiteStorageController.setInt('wrongAnswers', _wrongAnswers);
|
||||
await SQLiteStorageController.setInt('totalTime', _totalTime);
|
||||
await SQLiteStorageController.setInt('hintCount', _hintCount);
|
||||
await SQLiteStorageController.setInt('skipCount', _skipCount);
|
||||
await SharedPreferencesStorageController.setInt(
|
||||
'totalQuestions',
|
||||
_totalQuestions,
|
||||
);
|
||||
await SharedPreferencesStorageController.setInt(
|
||||
'correctAnswers',
|
||||
_correctAnswers,
|
||||
);
|
||||
await SharedPreferencesStorageController.setInt(
|
||||
'wrongAnswers',
|
||||
_wrongAnswers,
|
||||
);
|
||||
await SharedPreferencesStorageController.setInt('totalTime', _totalTime);
|
||||
await SharedPreferencesStorageController.setInt('hintCount', _hintCount);
|
||||
await SharedPreferencesStorageController.setInt('skipCount', _skipCount);
|
||||
|
||||
// 保存当前题目的详细记录
|
||||
if (_currentQuestion != null) {
|
||||
@@ -360,16 +369,17 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
};
|
||||
|
||||
// 获取已有的记录列表
|
||||
List<String> records = await SQLiteStorageController.getStringList(
|
||||
'poetryAnswerRecords',
|
||||
defaultValue: [],
|
||||
);
|
||||
List<String> records =
|
||||
await SharedPreferencesStorageController.getStringList(
|
||||
'poetryAnswerRecords',
|
||||
defaultValue: [],
|
||||
);
|
||||
|
||||
// 添加新记录(JSON格式)
|
||||
records.add(jsonEncode(record));
|
||||
|
||||
// 保存更新后的列表
|
||||
await SQLiteStorageController.setStringList(
|
||||
await SharedPreferencesStorageController.setStringList(
|
||||
'poetryAnswerRecords',
|
||||
records,
|
||||
);
|
||||
|
||||
@@ -4,16 +4,13 @@
|
||||
/// 最新变化: 重新设计布局,实现朋友圈风格的个人页面
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../constants/app_constants.dart';
|
||||
import '../../controllers/history_controller.dart';
|
||||
import '../../controllers/sqlite_storage_controller.dart';
|
||||
import '../../utils/flutter_compatibility_fix.dart';
|
||||
import '../../controllers/shared_preferences_storage_controller.dart';
|
||||
import 'history_page.dart';
|
||||
import 'per_card.dart';
|
||||
import 'settings/app_fun.dart';
|
||||
@@ -23,7 +20,6 @@ import 'settings/privacy.dart';
|
||||
import 'settings/learn-us.dart';
|
||||
import 'app-info.dart';
|
||||
import 'level/poetry.dart';
|
||||
import 'guide/sp-guide.dart';
|
||||
import 'guide/permission.dart';
|
||||
import 'guide/app-data.dart';
|
||||
import 'theme/app-diy.dart';
|
||||
@@ -47,10 +43,8 @@ class _ProfilePageState extends State<ProfilePage>
|
||||
double _startY = 0.0;
|
||||
// 历史记录相关
|
||||
List<Map<String, dynamic>> _poetryHistory = [];
|
||||
final String _historyKey = 'poetry_history';
|
||||
|
||||
// 答题统计数据
|
||||
int _totalQuestions = 0;
|
||||
int _correctAnswers = 0;
|
||||
int _todayQuestions = 0;
|
||||
int _weekQuestions = 0;
|
||||
@@ -86,10 +80,6 @@ class _ProfilePageState extends State<ProfilePage>
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _handleScroll() {
|
||||
// 这个方法现在由手势检测处理
|
||||
}
|
||||
|
||||
void _onPageChanged(int page) {
|
||||
setState(() {
|
||||
_currentPage = page;
|
||||
@@ -107,55 +97,21 @@ class _ProfilePageState extends State<ProfilePage>
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
Future<void> _savePoetryToHistory(Map<String, dynamic> poetryData) async {
|
||||
try {
|
||||
final success = await HistoryController.addToHistory(poetryData);
|
||||
|
||||
if (success) {
|
||||
_showSnackBar('已添加到历史记录');
|
||||
} else {
|
||||
_showSnackBar('该诗词已在历史记录中');
|
||||
}
|
||||
} catch (e) {
|
||||
_showSnackBar('保存失败');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _clearPoetryHistory() async {
|
||||
try {
|
||||
final success = await HistoryController.clearHistory();
|
||||
|
||||
if (success) {
|
||||
setState(() {
|
||||
_poetryHistory.clear();
|
||||
});
|
||||
_showSnackBar('历史记录已清空');
|
||||
} else {
|
||||
_showSnackBar('清空失败');
|
||||
}
|
||||
} catch (e) {
|
||||
_showSnackBar('清空失败');
|
||||
}
|
||||
}
|
||||
|
||||
// === 答题统计相关方法 ===
|
||||
Future<void> _loadPoetryStatistics() async {
|
||||
try {
|
||||
// 加载总体统计
|
||||
_totalQuestions = await SQLiteStorageController.getInt(
|
||||
'totalQuestions',
|
||||
defaultValue: 0,
|
||||
);
|
||||
_correctAnswers = await SQLiteStorageController.getInt(
|
||||
_correctAnswers = await SharedPreferencesStorageController.getInt(
|
||||
'correctAnswers',
|
||||
defaultValue: 0,
|
||||
);
|
||||
|
||||
// 加载答题记录列表来计算今日和本周答题数
|
||||
List<String> records = await SQLiteStorageController.getStringList(
|
||||
'poetryAnswerRecords',
|
||||
defaultValue: [],
|
||||
);
|
||||
List<String> records =
|
||||
await SharedPreferencesStorageController.getStringList(
|
||||
'poetryAnswerRecords',
|
||||
defaultValue: [],
|
||||
);
|
||||
|
||||
final now = DateTime.now();
|
||||
final todayStart = DateTime(now.year, now.month, now.day);
|
||||
@@ -287,158 +243,6 @@ class _ProfilePageState extends State<ProfilePage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatCard(String label, String value, IconData icon) {
|
||||
// === 单个统计卡片:显示统计数据和图标 ===
|
||||
return Expanded(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
blurRadius: 5,
|
||||
offset: const Offset(0, 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(icon, color: AppConstants.primaryColor, size: 24),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTabBar() {
|
||||
// === 页面切换标签栏:显示当前页面指示器 ===
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onVerticalDragStart: (_) {},
|
||||
onVerticalDragUpdate: (_) {},
|
||||
onVerticalDragEnd: (_) {},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppConstants.primaryColor.withValues(alpha: 0.85),
|
||||
AppConstants.primaryColor.withValues(alpha: 0.8),
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
// 页面指示器
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 3,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.3),
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
// 背景条
|
||||
Container(
|
||||
height: 3,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.3),
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
// 滑动指示器
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
width: MediaQuery.of(context).size.width / 3 - 40,
|
||||
height: 3,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
margin: EdgeInsets.only(
|
||||
left:
|
||||
(MediaQuery.of(context).size.width / 3 - 40) *
|
||||
_currentPage,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// 页面标签
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildTabLabel(0, '卡片'),
|
||||
_buildTabLabel(1, '设置'),
|
||||
_buildTabLabel(2, '更多'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTabLabel(int index, String label) {
|
||||
// === 单个页面标签:可点击切换到对应页面 ===
|
||||
final isSelected = _currentPage == index;
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
// 添加 mounted 和 hasClients 检查,避免 dispose 后调用导致黑屏
|
||||
if (mounted && _pageController.hasClients) {
|
||||
_pageController.animateToPage(
|
||||
index,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: isSelected ? Colors.white : Colors.white70,
|
||||
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPage1() {
|
||||
// === 第1页:个人信息卡片展示 ===
|
||||
return SingleChildScrollView(
|
||||
@@ -914,15 +718,6 @@ class _ProfilePageState extends State<ProfilePage>
|
||||
);
|
||||
}
|
||||
|
||||
void _navigateToSpGuidePage() {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const SpGuidePage(fromSettings: true),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _navigateToPermissionPage() {
|
||||
Navigator.push(
|
||||
context,
|
||||
@@ -972,129 +767,7 @@ class _ProfilePageState extends State<ProfilePage>
|
||||
);
|
||||
}
|
||||
|
||||
// === 历史记录对话框 ===
|
||||
void _showHistoryDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => Dialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width * 0.9,
|
||||
height: MediaQuery.of(context).size.height * 0.7,
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
children: [
|
||||
// 对话框标题
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'历史记录',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppConstants.primaryColor,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: _poetryHistory.isEmpty
|
||||
? null
|
||||
: () {
|
||||
Navigator.pop(context);
|
||||
_clearPoetryHistory();
|
||||
},
|
||||
child: const Text('清空'),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
icon: const Icon(Icons.close),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// 历史记录列表
|
||||
Expanded(
|
||||
child: _poetryHistory.isEmpty
|
||||
? Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.history,
|
||||
size: 64,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'暂无历史记录',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
itemCount: _poetryHistory.length,
|
||||
itemBuilder: (context, index) {
|
||||
final poetry = _poetryHistory[index];
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 8),
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
radius: 20,
|
||||
backgroundColor: AppConstants.primaryColor
|
||||
.withValues(alpha: 0.1),
|
||||
child: Text(
|
||||
'${index + 1}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppConstants.primaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
poetry['name'] ?? '未知诗词',
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
subtitle: Text(
|
||||
'${poetry['alias'] ?? '未知朝代'} • ${poetry['date'] ?? ''}',
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.favorite_border),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
_showSnackBar('已添加到收藏');
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget? _showMoreOptions() {
|
||||
void _showMoreOptions() {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: Colors.transparent,
|
||||
|
||||
@@ -25,9 +25,7 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
|
||||
bool _vibrationEnabled = true;
|
||||
bool _darkModeEnabled = false;
|
||||
bool _preloadEnabled = true;
|
||||
bool _notificationEnabled = true;
|
||||
bool _globalTipsEnabled = true; // 添加全局Tips开关状态
|
||||
int _cacheSize = 128;
|
||||
|
||||
static const String _autoRefreshKey = 'auto_refresh_enabled';
|
||||
static const String _debugInfoKey = 'debug_info_enabled';
|
||||
@@ -311,28 +309,6 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCacheItem() {
|
||||
return ListTile(
|
||||
leading: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppConstants.primaryColor.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(Icons.storage, color: AppConstants.primaryColor, size: 20),
|
||||
),
|
||||
title: const Text(
|
||||
'缓存大小',
|
||||
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
|
||||
),
|
||||
subtitle: Text(
|
||||
'$_cacheSize MB',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
|
||||
),
|
||||
trailing: Icon(Icons.chevron_right, color: Colors.grey[400]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionItem(
|
||||
String title,
|
||||
String subtitle,
|
||||
@@ -403,32 +379,6 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
|
||||
).showSnackBar(SnackBar(content: Text(message)));
|
||||
}
|
||||
|
||||
void _showClearCacheDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('清除缓存'),
|
||||
content: const Text('确定要清除所有缓存数据吗?这不会影响您的笔记和收藏。'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('取消'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
setState(() {
|
||||
_cacheSize = 0;
|
||||
});
|
||||
_showSnackBar('缓存已清除');
|
||||
},
|
||||
child: Text('确定', style: TextStyle(color: Colors.red[400])),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showResetDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
@@ -450,7 +400,6 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
|
||||
_vibrationEnabled = true;
|
||||
_globalTipsEnabled = true; // 重置全局Tips开关为开启
|
||||
_darkModeEnabled = false;
|
||||
_notificationEnabled = true;
|
||||
});
|
||||
_showSnackBar('已恢复默认设置');
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user