announcement_list_page.dart 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_riverpod/flutter_riverpod.dart';
  3. import 'package:go_router/go_router.dart';
  4. import 'package:tdesign_flutter/tdesign_flutter.dart';
  5. import '../../core/theme/app_colors.dart';
  6. import '../../core/utils/date_utils.dart' as du;
  7. import '../../core/utils/responsive.dart';
  8. import '../../shared/widgets/empty_state.dart';
  9. import '../../shared/widgets/loading_widget.dart';
  10. import 'announcement_list_controller.dart';
  11. import 'announcement_model.dart';
  12. class AnnouncementListPage extends ConsumerStatefulWidget {
  13. const AnnouncementListPage({super.key});
  14. @override
  15. ConsumerState<AnnouncementListPage> createState() =>
  16. _AnnouncementListPageState();
  17. }
  18. class _AnnouncementListPageState extends ConsumerState<AnnouncementListPage> {
  19. @override
  20. Widget build(BuildContext context) {
  21. final itemsAsync = ref.watch(announcementListProvider);
  22. final r = ResponsiveHelper.of(context);
  23. return Scaffold(
  24. appBar: TDNavBar(
  25. title: '公告通知',
  26. titleColor: Colors.white,
  27. backgroundColor: const Color(0xFF00ABF3),
  28. centerTitle: false,
  29. ),
  30. body: Center(
  31. child: ConstrainedBox(
  32. constraints: BoxConstraints(maxWidth: r.listMaxWidth),
  33. child: itemsAsync.when(
  34. loading: () => const LoadingWidget(),
  35. error: (_, __) => const EmptyState(message: '加载失败'),
  36. data: (items) => items.isEmpty
  37. ? const EmptyState(message: '暂无公告')
  38. : ListView.builder(
  39. padding: const EdgeInsets.symmetric(vertical: 4),
  40. itemCount: items.length,
  41. itemBuilder: (_, index) => _buildItem(items[index]),
  42. ),
  43. ),
  44. ),
  45. ),
  46. );
  47. }
  48. Widget _buildItem(AnnouncementModel item) {
  49. return Padding(
  50. padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
  51. child: TDCell(
  52. title: item.title,
  53. titleWidget: Row(
  54. children: [
  55. if (item.isTop)
  56. Padding(
  57. padding: const EdgeInsets.only(right: 6),
  58. child: TDTag(
  59. '置顶',
  60. size: TDTagSize.small,
  61. theme: TDTagTheme.warning,
  62. shape: TDTagShape.square,
  63. ),
  64. ),
  65. Expanded(
  66. child: Text(
  67. item.title,
  68. maxLines: 1,
  69. overflow: TextOverflow.ellipsis,
  70. style: const TextStyle(
  71. fontWeight: FontWeight.w600,
  72. fontSize: 14,
  73. color: AppColors.textPrimary),
  74. ),
  75. ),
  76. ],
  77. ),
  78. descriptionWidget: Text(
  79. '${item.type} · ${item.publisherName} ${du.DateUtils.formatDate(item.publishTime)}',
  80. style: const TextStyle(
  81. color: AppColors.textSecondary, fontSize: 12),
  82. ),
  83. noteWidget: Row(
  84. mainAxisSize: MainAxisSize.min,
  85. children: [
  86. Icon(Icons.visibility_outlined,
  87. size: 13, color: AppColors.textHint),
  88. const SizedBox(width: 3),
  89. Text('${item.readCount}',
  90. style: const TextStyle(
  91. color: AppColors.textHint, fontSize: 11)),
  92. ],
  93. ),
  94. arrow: true,
  95. bordered: true,
  96. onClick: (_) => context.push('/announcement/detail/${item.id}'),
  97. ),
  98. );
  99. }
  100. }