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

628 lines
21 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
/// 功能: 点赞诗词管理器
/// 介绍: 管理点赞诗词的加载、显示、删除等操作
/// 最新变化: 从FavoritesPage分离出来的点赞管理逻辑
library liked_poetry_manager;
import 'dart:async' show StreamSubscription;
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import '../../../constants/app_constants.dart';
import '../../../controllers/history_controller.dart';
import '../../../utils/http/poetry_api.dart';
import '../../../services/network_listener_service.dart';
import '../home/home_components.dart';
/// 点赞诗词管理器
class LikedPoetryManager extends StatefulWidget {
const LikedPoetryManager({super.key});
@override
State<LikedPoetryManager> createState() => _LikedPoetryManagerState();
/// 公共方法:显示诗词详情弹窗
/// 可被其他页面调用,无需传递额外参数
static Future<void> showPoetryDetails(
BuildContext context,
PoetryData poetry,
) async {
try {
debugPrint(
'DEBUG: LikedPoetryManager - 点击查看详情 - Poetry ID: ${poetry.id}',
);
final likedList = await HistoryController.getLikedHistory();
debugPrint('DEBUG: LikedPoetryManager - 获取到 ${likedList.length} 条点赞记录');
Map<String, dynamic> poetryData;
try {
poetryData = likedList.firstWhere(
(item) => item['id'].toString() == poetry.id.toString(),
orElse: () => poetry.toJson(),
);
debugPrint('DEBUG: LikedPoetryManager - 找到匹配的诗词数据');
} catch (e) {
debugPrint('DEBUG: LikedPoetryManager - 未找到匹配数据使用当前poetry数据');
poetryData = poetry.toJson();
}
debugPrint(
'DEBUG: LikedPoetryManager - 诗词数据字段: ${poetryData.keys.toList()}',
);
if (context.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.refresh,
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['url'],
Icons.link,
Colors.teal,
),
),
],
if (poetryData['url'] != null &&
poetryData['url'].toString().isNotEmpty) ...[
const SizedBox(width: 8),
Expanded(
child: _buildDetailCard(
'朝代',
poetryData['alias'],
Icons.history,
Colors.blue,
),
),
],
],
),
if (poetryData['introduce'] != null &&
poetryData['introduce'].toString().isNotEmpty)
_buildDetailCard(
'译文/介绍',
poetryData['introduce'],
Icons.translate,
AppConstants.primaryColor,
),
if (poetryData['drtime'] != null &&
poetryData['drtime'].toString().isNotEmpty)
_buildDetailCard(
'原文or赏析',
poetryData['drtime'],
Icons.menu_book,
Colors.purple,
),
const SizedBox(height: 20),
],
),
),
),
],
),
),
);
}
} catch (e) {
debugPrint('DEBUG: LikedPoetryManager - 显示详情失败 - $e');
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('加载详情失败: $e'),
backgroundColor: AppConstants.errorColor,
duration: const Duration(seconds: 2),
),
);
}
}
}
/// 公共方法:构建详情卡片
static 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,
),
),
),
],
),
);
}
}
class _LikedPoetryManagerState extends State<LikedPoetryManager>
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 {
await LikedPoetryManager.showPoetryDetails(context, poetry);
}
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 _buildLikedPoetryList();
}
Widget _buildLikedPoetryList() {
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: ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: _likedPoetryList.length + 1,
itemBuilder: (context, index) {
if (index == _likedPoetryList.length) {
return _buildBottomIndicator();
}
final poetry = _likedPoetryList[index];
return _buildLikedPoetryCard(poetry);
},
),
);
}
Widget _buildBottomIndicator() {
return Container(
padding: const EdgeInsets.symmetric(vertical: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(width: 40, height: 1, color: Colors.grey[300]),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
'到底了',
style: TextStyle(fontSize: 12, color: Colors.grey[400]),
),
),
Container(width: 40, height: 1, color: Colors.grey[300]),
],
),
);
}
Widget _buildLikedPoetryCard(PoetryData poetry) {
return Container(
margin: const EdgeInsets.only(bottom: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.03),
blurRadius: 4,
offset: const Offset(0, 1),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
// Name字段 (精选诗句) - 超紧凑样式
Container(
width: double.infinity,
padding: const EdgeInsets.all(8),
margin: const EdgeInsets.fromLTRB(8, 8, 8, 0),
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(6),
border: Border.all(
color: AppConstants.primaryColor.withValues(alpha: 0.2),
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
Icon(
Icons.format_quote,
color: AppConstants.primaryColor,
size: 14,
),
const SizedBox(width: 4),
Text(
'精选诗句',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w500,
color: AppConstants.primaryColor,
),
),
],
),
const SizedBox(height: 4),
Text(
poetry.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black87,
height: 1.2,
),
),
],
),
),
// URL字段 (出处) - 超紧凑样式
if (poetry.url.isNotEmpty)
Padding(
padding: const EdgeInsets.fromLTRB(8, 4, 8, 2),
child: Text(
"出处:${poetry.url}",
style: TextStyle(fontSize: 11, color: Colors.grey[600]),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
// 操作按钮 - 超紧凑样式
Padding(
padding: const EdgeInsets.fromLTRB(8, 2, 8, 8),
child: Row(
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: () => _removeLikedPoetry(poetry.id.toString()),
icon: const Icon(Icons.favorite_border, size: 14),
label: const Text('取消点赞', style: TextStyle(fontSize: 12)),
style: OutlinedButton.styleFrom(
foregroundColor: AppConstants.errorColor,
side: BorderSide(color: AppConstants.errorColor),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(vertical: 4),
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
),
),
const SizedBox(width: 6),
Expanded(
child: ElevatedButton.icon(
onPressed: () {
debugPrint(
'DEBUG: LikedPoetryManager - 查看详情按钮被点击 - Poetry ID: ${poetry.id}',
);
_showPoetryDetails(poetry);
},
icon: const Icon(Icons.visibility, size: 14),
label: const Text('查看详情', style: TextStyle(fontSize: 12)),
style: ElevatedButton.styleFrom(
backgroundColor: AppConstants.primaryColor,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(vertical: 4),
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
),
),
],
),
),
],
),
);
}
}