import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import '../../data/api/report_data_source.dart'; import '../../data/content_providers.dart'; import '../../data/models/models.dart'; import '../../routing/app_routes.dart'; import '../../theme/wise_tokens.dart'; import '../../widgets/app_buttons.dart'; import '../../widgets/badges.dart'; import '../../widgets/mini_player.dart'; import '../../widgets/states.dart'; import '../shared/report_card_widget.dart'; class ReportsPage extends HookConsumerWidget { const ReportsPage({ required this.dataSource, required this.onPlay, this.player = const PlayerStateModel(), this.onStartModuleAudio, this.onToggleAudio, this.onSeekAudio, this.onSpeed, super.key, }); final ReportDataSource dataSource; final void Function(AudioItem item) onPlay; final PlayerStateModel player; final void Function(String audioId, String reportId, String title, int durationSec)? onStartModuleAudio; final VoidCallback? onToggleAudio; final void Function(int delta)? onSeekAudio; final VoidCallback? onSpeed; @override Widget build(BuildContext context, WidgetRef ref) { final query = useState(''); final topic = useState(''); final hasAudio = useState(false); final snapshot = ref.watch(reportsProvider); return snapshot.when( loading: () => const LoadingState(label: '正在搜索研报'), error: (error, _) => ErrorState( message: error.toString(), onRetry: () => ref.invalidate(reportsProvider), ), data: (items) { final currentQuery = query.value; final currentTopic = topic.value; final currentHasAudio = hasAudio.value; final filtered = _applyFilters( items, query: currentQuery, topic: currentTopic, hasAudio: currentHasAudio, ); return ListView( padding: const EdgeInsets.all(WiseSpacing.x4), children: [ TextField( decoration: InputDecoration( hintText: '搜索标题、机构或主题', prefixIcon: const Icon(Icons.search), suffixIcon: currentQuery.isEmpty ? null : IconButton( onPressed: () => query.value = '', icon: const Icon(Icons.close), ), filled: true, fillColor: WiseColors.surface, border: OutlineInputBorder( borderRadius: BorderRadius.circular(WiseRadius.pill), borderSide: BorderSide.none, ), ), onChanged: (value) => query.value = value.trim(), ), const SizedBox(height: WiseSpacing.x3), Row( children: [ AppButton( label: '筛选', icon: Icons.tune, kind: AppButtonKind.ghost, onPressed: items.isEmpty ? null : () => _openFilterSheet( context, items: items, topic: topic, ), ), const SizedBox(width: WiseSpacing.x2), AppChip( label: '有音频', selected: currentHasAudio, onTap: () => hasAudio.value = !currentHasAudio, ), ], ), const SizedBox(height: WiseSpacing.x3), Text( '共 ${filtered.length} 篇研报解读${currentQuery.isNotEmpty || currentTopic.isNotEmpty || currentHasAudio ? '(已筛选)' : ''}', style: Theme.of(context).textTheme.bodySmall, ), const SizedBox(height: WiseSpacing.x3), if (filtered.isEmpty) EmptyState( title: currentQuery.isNotEmpty ? '未找到相关研报' : '当前筛选下暂无研报', message: currentQuery.isNotEmpty ? '换个关键词试试' : '调整筛选条件后再试', actionLabel: '清除筛选', onAction: () { query.value = ''; topic.value = ''; hasAudio.value = false; }, ) else for (final report in filtered) ...[ ReportCardWidget( report: report, onTap: () => openReportDetail( context, dataSource, report, player: player, onStartAudio: onStartModuleAudio, onToggleAudio: onToggleAudio, onSeekAudio: onSeekAudio, onSpeed: onSpeed, ), onPlayTap: () => onPlay( AudioItem( audioId: 'local_${report.id}', reportId: report.id, titleCn: report.titleCn, reportTitleCn: report.titleCn, durationSec: 180, institution: report.institution, ), ), ), const SizedBox(height: WiseSpacing.x3), ], ], ); }, ); } } List _applyFilters( List items, { required String query, required String topic, required bool hasAudio, }) { return items.where((item) { final hay = '${item.titleCn} ${item.institution.nameCn} ${item.topics.join(' ')}' .toLowerCase(); if (query.isNotEmpty && !hay.contains(query.toLowerCase())) return false; if (topic.isNotEmpty && !item.topics.contains(topic)) return false; if (hasAudio && !item.hasAudio) return false; return true; }).toList(); } void _openFilterSheet( BuildContext context, { required List items, required ValueNotifier topic, }) { final topics = {for (final item in items) ...item.topics}.toList(); showModalBottomSheet( context: context, showDragHandle: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(WiseRadius.lg)), ), builder: (context) => Padding( padding: const EdgeInsets.fromLTRB(20, 4, 20, 28), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('筛选研报', style: Theme.of(context).textTheme.titleLarge), const SizedBox(height: WiseSpacing.x3), Wrap( spacing: WiseSpacing.x2, runSpacing: WiseSpacing.x2, children: [ AppChip( label: '全部主题', selected: topic.value.isEmpty, onTap: () => topic.value = '', ), for (final t in topics) AppChip( label: t, selected: topic.value == t, onTap: () => topic.value = t, ), ], ), const SizedBox(height: WiseSpacing.x4), AppButton( label: '完成', expand: true, onPressed: () => Navigator.pop(context), ), ], ), ), ); }