Files
wushu/lib/views/footprint/footprint_page.dart
2026-03-30 02:35:31 +08:00

693 lines
22 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// 时间: 2025-03-22
/// 功能: 点赞足迹页面
/// 介绍: 显示用户点赞的诗词列表,支持删除操作
/// 最新变化: 新增点赞足迹管理功能
import 'dart:async' show StreamSubscription;
import 'package:flutter/material.dart';
import 'package:flutter_application_2/controllers/history_controller.dart';
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 {
const FootprintPage({super.key});
@override
State<FootprintPage> createState() => _FootprintPageState();
}
class _FootprintPageState extends State<FootprintPage>
with NetworkListenerMixin {
List<PoetryData> _likedPoetryList = [];
bool _isLoading = false;
String _errorMessage = '';
StreamSubscription<NetworkEvent>? _networkSubscription;
@override
void initState() {
super.initState();
_loadLikedPoetry();
_setupNetworkListener();
}
void _setupNetworkListener() {
// 监听网络事件
_networkSubscription = networkEvents.listen((event) {
if (mounted) {
switch (event.type) {
case NetworkEventType.like:
case NetworkEventType.unlike:
// 当有点赞或取消点赞事件时,刷新列表
_loadLikedPoetry();
break;
case NetworkEventType.refresh:
// 刷新事件
_loadLikedPoetry();
break;
default:
break;
}
}
});
}
@override
void dispose() {
_networkSubscription?.cancel();
super.dispose();
}
Future<void> _loadLikedPoetry() async {
setState(() {
_isLoading = true;
_errorMessage = '';
});
try {
// 从SQLite加载点赞列表
final likedListJson = await HistoryController.getLikedHistory();
final likedList = likedListJson
.map((item) => PoetryData.fromJson(item))
.toList();
if (mounted) {
setState(() {
_likedPoetryList = likedList;
_isLoading = false;
});
}
} catch (e) {
if (mounted) {
setState(() {
_errorMessage = '加载点赞列表失败';
_isLoading = false;
});
}
}
}
Future<void> _showPoetryDetails(PoetryData poetry) async {
try {
print('DEBUG: 点击查看详情 - Poetry ID: ${poetry.id}');
// 从SQLite获取完整数据
final likedList = await HistoryController.getLikedHistory();
print('DEBUG: 获取到 ${likedList.length} 条点赞记录');
// 查找对应的诗词数据,优先使用存储的完整数据
Map<String, dynamic> poetryData;
try {
poetryData = likedList.firstWhere(
(item) => item['id'].toString() == poetry.id.toString(),
orElse: () => poetry.toJson(),
);
print('DEBUG: 找到匹配的诗词数据');
} catch (e) {
print('DEBUG: 未找到匹配数据使用当前poetry数据');
poetryData = poetry.toJson();
}
print('DEBUG: 诗词数据字段: ${poetryData.keys.toList()}');
if (mounted) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => Container(
height: MediaQuery.of(context).size.height * 0.8,
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
child: Column(
children: [
// 顶部拖拽指示器
Container(
margin: const EdgeInsets.only(top: 8),
width: 40,
height: 4,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(2),
),
),
// 标题栏
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Icon(
Icons.article,
color: AppConstants.primaryColor,
size: 24,
),
const SizedBox(width: 8),
const Text(
'诗词详情',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const Spacer(),
// 加载图标(不可点击)
GestureDetector(
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('开发中'),
duration: Duration(seconds: 1),
),
);
},
child: Container(
padding: const EdgeInsets.all(8),
child: Icon(
Icons.more_horiz,
color: Colors.grey[600],
size: 20,
),
),
),
const SizedBox(width: 8),
// 关闭按钮
IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: const Icon(Icons.close),
color: Colors.grey[600],
),
],
),
),
// 内容区域
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 诗词名称 - 单独一行
_buildDetailCard(
'精选诗句',
poetryData['name'] ?? poetry.name,
Icons.format_quote,
AppConstants.primaryColor,
),
// 时间和标签 - 同一行
Row(
children: [
// 点赞时间
if (poetryData['liked_time'] != null) ...[
Expanded(
child: _buildDetailCard(
'点赞时间',
'${poetryData['liked_date'] ?? ''} ${poetryData['liked_time'] ?? ''}',
Icons.favorite,
Colors.orange,
),
),
],
// 标签
if (poetryData['keywords'] != null &&
poetryData['keywords']
.toString()
.isNotEmpty) ...[
const SizedBox(width: 8),
Expanded(
child: _buildDetailCard(
'标签',
poetryData['keywords'],
Icons.local_offer,
Colors.green,
),
),
],
],
),
// 朝代和出处 - 同一行
Row(
children: [
// 朝代
if (poetryData['alias'] != null &&
poetryData['alias'].toString().isNotEmpty) ...[
Expanded(
child: _buildDetailCard(
'朝代',
poetryData['alias'],
Icons.history,
Colors.blue,
),
),
],
// 出处
if (poetryData['url'] != null &&
poetryData['url'].toString().isNotEmpty) ...[
const SizedBox(width: 8),
Expanded(
child: _buildDetailCard(
'出处',
poetryData['url'],
Icons.link,
Colors.teal,
),
),
],
],
),
// 译文/介绍 - 单独一行
if (poetryData['introduce'] != null &&
poetryData['introduce'].toString().isNotEmpty)
_buildDetailCard(
'译文/介绍',
poetryData['introduce'],
Icons.translate,
AppConstants.primaryColor,
),
// 原文 - 单独一行
if (poetryData['drtime'] != null &&
poetryData['drtime'].toString().isNotEmpty)
_buildDetailCard(
'原文',
poetryData['drtime'],
Icons.menu_book,
Colors.purple,
),
const SizedBox(height: 20),
],
),
),
),
],
),
),
);
}
} catch (e) {
print('DEBUG: 显示详情失败 - $e');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('加载详情失败: $e'),
backgroundColor: AppConstants.errorColor,
duration: const Duration(seconds: 2),
),
);
}
}
}
Widget _buildDetailCard(
String title,
String content,
IconData icon,
Color accentColor,
) {
return Container(
width: double.infinity,
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: accentColor.withValues(alpha: 0.05),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: accentColor.withValues(alpha: 0.2), width: 1),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题栏
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: accentColor.withValues(alpha: 0.1),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
),
),
child: Row(
children: [
Icon(icon, size: 18, color: accentColor),
const SizedBox(width: 8),
Text(
title,
style: TextStyle(
fontSize: 14,
color: accentColor,
fontWeight: FontWeight.w600,
),
),
],
),
),
// 内容区域
Padding(
padding: const EdgeInsets.all(12),
child: Text(
content,
style: const TextStyle(
fontSize: 15,
color: Colors.black87,
height: 1.5,
),
),
),
],
),
);
}
Future<void> _removeLikedPoetry(String poetryId) async {
try {
// 从SQLite中删除
await HistoryController.removeLikedPoetry(poetryId);
// 重新加载列表
await _loadLikedPoetry();
if (mounted) {
PoetryStateManager.showSnackBar(
context,
'已取消点赞',
backgroundColor: AppConstants.successColor,
duration: const Duration(milliseconds: 200),
);
}
} catch (e) {
if (mounted) {
PoetryStateManager.showSnackBar(
context,
'操作失败,请重试',
backgroundColor: AppConstants.errorColor,
duration: const Duration(milliseconds: 200),
);
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[50],
appBar: AppBar(
title: const Text('点赞足迹'),
backgroundColor: AppConstants.primaryColor,
foregroundColor: Colors.white,
elevation: 0,
),
body: _buildBody(),
);
}
Widget _buildBody() {
if (_isLoading) {
return const Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(AppConstants.primaryColor),
),
);
}
if (_errorMessage.isNotEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, size: 64, color: AppConstants.errorColor),
const SizedBox(height: 16),
Text(
_errorMessage,
style: TextStyle(fontSize: 16, color: AppConstants.errorColor),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _loadLikedPoetry,
style: ElevatedButton.styleFrom(
backgroundColor: AppConstants.primaryColor,
foregroundColor: Colors.white,
),
child: const Text('重试'),
),
],
),
);
}
if (_likedPoetryList.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.favorite_border, size: 64, color: Colors.grey[400]),
const SizedBox(height: 16),
Text(
'暂无点赞记录',
style: TextStyle(fontSize: 16, color: Colors.grey[600]),
),
const SizedBox(height: 8),
Text(
'去主页点赞喜欢的诗词吧',
style: TextStyle(fontSize: 14, color: Colors.grey[500]),
),
],
),
);
}
return RefreshIndicator(
onRefresh: _loadLikedPoetry,
child: Column(
children: [
// 添加不可点击的刷新按钮
if (_likedPoetryList.isNotEmpty) ...[
Container(
margin: const EdgeInsets.fromLTRB(16, 8, 16, 8),
child: Row(
children: [
Icon(
Icons.refresh,
size: 20,
color: AppConstants.primaryColor,
),
const SizedBox(width: 8),
Text(
'下拉刷新列表',
style: TextStyle(
fontSize: 14,
color: AppConstants.primaryColor,
fontWeight: FontWeight.w500,
),
),
const Spacer(),
Text(
'${_likedPoetryList.length} 条记录',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
],
),
),
],
Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: _likedPoetryList.length,
itemBuilder: (context, index) {
final poetry = _likedPoetryList[index];
return _buildLikedPoetryCard(poetry);
},
),
),
],
),
);
}
Widget _buildLikedPoetryCard(PoetryData poetry) {
return Container(
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// URL字段 (出处)
Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Text(
"出处: ${poetry.url}",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
fontStyle: FontStyle.italic,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
// Name字段 (精选诗句) - 与主页样式一致
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
margin: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppConstants.primaryColor.withValues(alpha: 0.1),
AppConstants.primaryColor.withValues(alpha: 0.05),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: AppConstants.primaryColor.withValues(alpha: 0.2),
width: 1,
),
boxShadow: [
BoxShadow(
color: AppConstants.primaryColor.withValues(alpha: 0.1),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.format_quote,
color: AppConstants.primaryColor,
size: 20,
),
const SizedBox(width: 8),
Expanded(
child: Text(
'精选诗句',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: AppConstants.primaryColor,
),
),
),
],
),
const SizedBox(height: 8),
Text(
poetry.name,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.black87,
height: 1.4,
),
textAlign: TextAlign.center,
),
],
),
),
// drtime字段 (原文)
if (poetry.drtime.isNotEmpty) ...[
const SizedBox(height: 12),
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFF8F8F8),
borderRadius: BorderRadius.circular(8),
),
child: Text(
poetry.drtime,
style: const TextStyle(
fontSize: 14,
height: 1.6,
color: Colors.black87,
),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
),
),
],
// 操作按钮
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: Row(
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: () => _removeLikedPoetry(poetry.id.toString()),
icon: const Icon(Icons.favorite_border, size: 18),
label: const Text('取消点赞'),
style: OutlinedButton.styleFrom(
foregroundColor: AppConstants.errorColor,
side: BorderSide(color: AppConstants.errorColor),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton.icon(
onPressed: () {
print('DEBUG: 查看详情按钮被点击 - Poetry ID: ${poetry.id}');
_showPoetryDetails(poetry);
},
icon: const Icon(Icons.visibility, size: 18),
label: const Text('查看详情'),
style: ElevatedButton.styleFrom(
backgroundColor: AppConstants.primaryColor,
foregroundColor: Colors.white,
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.symmetric(vertical: 12),
),
),
),
],
),
),
],
),
);
}
}