import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../core/theme/app_colors.dart'; import '../shell/nav_bar_config.dart'; import '../../core/utils/date_utils.dart' as du; import '../../core/utils/responsive.dart'; import '../../shared/widgets/filter_tabs.dart'; import '../../shared/widgets/empty_state.dart'; import '../../shared/widgets/loading_widget.dart'; import '../../core/i18n/app_localizations.dart'; import 'announcement_list_controller.dart'; import 'announcement_model.dart'; class AnnouncementListPage extends ConsumerStatefulWidget { const AnnouncementListPage({super.key}); @override ConsumerState createState() => _AnnouncementListPageState(); } class _AnnouncementListPageState extends ConsumerState { int _tabIndex = 0; final _tabs = ['通知公告', '人事与制度', '放假与活动', '草稿']; final _searchCtrl = TextEditingController(); @override void dispose() { _searchCtrl.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final itemsAsync = ref.watch(announcementListProvider); final r = ResponsiveHelper.of(context); final l10n = AppLocalizations.of(context); ref .read(navBarConfigProvider.notifier) .update( NavBarConfig( title: l10n.get('announcementList'), showBack: true, showRight: true, rightWidget: IconButton( icon: const Icon( Icons.add, size: 22, color: AppColors.textSecondary, ), onPressed: () => context.push('/announcement/create'), ), onBack: () => context.pop(), ), ); return Center( child: ConstrainedBox( constraints: BoxConstraints(maxWidth: r.listMaxWidth), child: Column( children: [ FilterTabs( tabs: _tabs, selectedIndex: _tabIndex, onChanged: (i) => setState(() => _tabIndex = i), ), Expanded( child: itemsAsync.when( loading: () => const LoadingWidget(), error: (_, __) => const EmptyState(message: '加载失败'), data: (items) => items.isEmpty ? const EmptyState(message: '暂无公告') : RefreshIndicator( onRefresh: () async { ref.invalidate(announcementListProvider); }, child: ListView.builder( padding: const EdgeInsets.symmetric(vertical: 8), itemCount: items.length, itemBuilder: (_, i) => _buildAnnouncementCard(items[i]), ), ), ), ), ], ), ), ); } Widget _buildAnnouncementCard(AnnouncementModel item) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: GestureDetector( onTap: () => context.push('/announcement/detail/${item.id}'), child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: AppColors.bgCard, borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 标题行 Row( children: [ if (item.isTop) Container( margin: const EdgeInsets.only(right: 8), padding: const EdgeInsets.symmetric( horizontal: 4, vertical: 1, ), decoration: BoxDecoration( color: AppColors.danger, borderRadius: BorderRadius.circular(2), ), child: const Text( '置顶', style: TextStyle(fontSize: 10, color: Colors.white), ), ), Expanded( child: Text( item.title, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: AppColors.textPrimary, ), ), ), if (item.readCount > 0) Container( width: 8, height: 8, decoration: const BoxDecoration( color: AppColors.danger, shape: BoxShape.circle, ), ), ], ), const SizedBox(height: 8), // 元信息行 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ _buildTypeTag(item.type), const SizedBox(width: 8), Text( item.publisherName, style: const TextStyle( fontSize: 12, color: AppColors.textSecondary, ), ), ], ), Text( du.DateUtils.formatDateTime(item.publishTime), style: const TextStyle( fontSize: 12, color: AppColors.textPlaceholder, ), ), ], ), ], ), ), ), ); } Widget _buildTypeTag(String type) { Color bgColor; Color textColor; switch (type) { case '人事与制度': bgColor = AppColors.successBg; textColor = AppColors.success; case '放假与活动': bgColor = AppColors.warningBg; textColor = AppColors.warning; case '系统公告': bgColor = AppColors.warningBg; textColor = AppColors.warning; default: bgColor = AppColors.primaryLight; textColor = AppColors.primary; } return Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: bgColor, borderRadius: BorderRadius.circular(3), ), child: Text(type, style: TextStyle(fontSize: 11, color: textColor)), ); } }