announcement_detail_page.dart 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. import 'package:flutter/material.dart';
  2. import 'package:go_router/go_router.dart';
  3. import 'package:flutter_riverpod/flutter_riverpod.dart';
  4. import '../../core/theme/app_colors.dart';
  5. import '../shell/nav_bar_config.dart';
  6. import '../../core/utils/date_utils.dart' as du;
  7. import '../../core/i18n/app_localizations.dart';
  8. import 'announcement_list_controller.dart';
  9. class AnnouncementDetailPage extends ConsumerWidget {
  10. final String id;
  11. const AnnouncementDetailPage({super.key, required this.id});
  12. @override
  13. Widget build(BuildContext context, WidgetRef ref) {
  14. final item = mockAnnouncements.firstWhere(
  15. (e) => e.id == id,
  16. orElse: () => mockAnnouncements.first,
  17. );
  18. final l10n = AppLocalizations.of(context);
  19. ref
  20. .read(navBarConfigProvider.notifier)
  21. .update(
  22. NavBarConfig(
  23. title: l10n.get('announcementDetail'),
  24. showBack: true,
  25. onBack: () => context.pop(),
  26. ),
  27. );
  28. return SingleChildScrollView(
  29. child: Column(
  30. crossAxisAlignment: CrossAxisAlignment.start,
  31. children: [
  32. // 红色横幅区
  33. Container(
  34. width: double.infinity,
  35. padding: const EdgeInsets.all(24),
  36. color: AppColors.bgCard,
  37. child: Column(
  38. crossAxisAlignment: CrossAxisAlignment.start,
  39. children: [
  40. Text(
  41. item.title,
  42. style: const TextStyle(
  43. fontSize: 20,
  44. fontWeight: FontWeight.w700,
  45. color: AppColors.textPrimary,
  46. height: 1.4,
  47. ),
  48. ),
  49. const SizedBox(height: 12),
  50. Row(
  51. children: [
  52. Text(
  53. item.publisherName,
  54. style: const TextStyle(
  55. fontSize: 13,
  56. color: AppColors.textSecondary,
  57. ),
  58. ),
  59. const SizedBox(width: 12),
  60. Text(
  61. du.DateUtils.formatDateTime(item.publishTime),
  62. style: const TextStyle(
  63. fontSize: 13,
  64. color: AppColors.textPlaceholder,
  65. ),
  66. ),
  67. ],
  68. ),
  69. ],
  70. ),
  71. ),
  72. // 红色分隔线
  73. Container(width: double.infinity, height: 2, color: AppColors.danger),
  74. // 正文内容
  75. Padding(
  76. padding: const EdgeInsets.all(16),
  77. child: Container(
  78. width: double.infinity,
  79. padding: const EdgeInsets.all(20),
  80. decoration: BoxDecoration(
  81. color: AppColors.bgCard,
  82. borderRadius: BorderRadius.circular(8),
  83. ),
  84. child: Column(
  85. crossAxisAlignment: CrossAxisAlignment.start,
  86. children: [
  87. Text(
  88. '各部门、各位同事:',
  89. style: const TextStyle(
  90. fontSize: 14,
  91. color: AppColors.textPrimary,
  92. height: 1.7,
  93. ),
  94. ),
  95. const SizedBox(height: 12),
  96. Text(
  97. item.content,
  98. style: const TextStyle(
  99. fontSize: 14,
  100. color: AppColors.textSecondary,
  101. height: 1.7,
  102. ),
  103. ),
  104. ],
  105. ),
  106. ),
  107. ),
  108. // 附件
  109. if (item.attachments.isNotEmpty)
  110. Padding(
  111. padding: const EdgeInsets.symmetric(horizontal: 16),
  112. child: Column(
  113. crossAxisAlignment: CrossAxisAlignment.start,
  114. children: [
  115. const Text(
  116. '附件下载',
  117. style: TextStyle(
  118. fontSize: 14,
  119. fontWeight: FontWeight.w600,
  120. color: AppColors.textPrimary,
  121. ),
  122. ),
  123. const SizedBox(height: 8),
  124. ...item.attachments.map(
  125. (att) => Container(
  126. width: double.infinity,
  127. margin: const EdgeInsets.only(bottom: 8),
  128. padding: const EdgeInsets.all(12),
  129. decoration: BoxDecoration(
  130. color: AppColors.bgCard,
  131. borderRadius: BorderRadius.circular(8),
  132. ),
  133. child: Row(
  134. children: [
  135. const Icon(
  136. Icons.description_outlined,
  137. size: 20,
  138. color: AppColors.primary,
  139. ),
  140. const SizedBox(width: 8),
  141. Expanded(
  142. child: Text(
  143. att,
  144. style: const TextStyle(
  145. fontSize: 14,
  146. color: AppColors.textPrimary,
  147. ),
  148. ),
  149. ),
  150. Text(
  151. '${(att.length * 100) ~/ 1000}KB',
  152. style: const TextStyle(
  153. fontSize: 12,
  154. color: AppColors.textPlaceholder,
  155. ),
  156. ),
  157. const SizedBox(width: 8),
  158. const Icon(
  159. Icons.download_outlined,
  160. size: 16,
  161. color: AppColors.textPlaceholder,
  162. ),
  163. ],
  164. ),
  165. ),
  166. ),
  167. ],
  168. ),
  169. ),
  170. // 审计区
  171. Padding(
  172. padding: const EdgeInsets.all(16),
  173. child: Container(
  174. width: double.infinity,
  175. padding: const EdgeInsets.all(16),
  176. decoration: BoxDecoration(
  177. color: AppColors.bgCard,
  178. borderRadius: BorderRadius.circular(8),
  179. ),
  180. child: Column(
  181. crossAxisAlignment: CrossAxisAlignment.start,
  182. children: [
  183. const Text(
  184. '全员触达率审计追踪',
  185. style: TextStyle(
  186. fontSize: 14,
  187. fontWeight: FontWeight.w600,
  188. color: AppColors.textPrimary,
  189. ),
  190. ),
  191. const SizedBox(height: 12),
  192. Row(
  193. children: [
  194. Container(
  195. padding: const EdgeInsets.symmetric(
  196. horizontal: 12,
  197. vertical: 6,
  198. ),
  199. decoration: BoxDecoration(
  200. color: AppColors.successBg,
  201. borderRadius: BorderRadius.circular(16),
  202. ),
  203. child: Text(
  204. '${item.readCount} 人已读',
  205. style: const TextStyle(
  206. fontSize: 12,
  207. color: AppColors.success,
  208. ),
  209. ),
  210. ),
  211. const SizedBox(width: 12),
  212. Container(
  213. padding: const EdgeInsets.symmetric(
  214. horizontal: 12,
  215. vertical: 6,
  216. ),
  217. decoration: BoxDecoration(
  218. color: AppColors.bgPage,
  219. borderRadius: BorderRadius.circular(16),
  220. ),
  221. child: Text(
  222. '${item.unreadCount} 人未读',
  223. style: const TextStyle(
  224. fontSize: 12,
  225. color: AppColors.statusGray,
  226. ),
  227. ),
  228. ),
  229. ],
  230. ),
  231. const SizedBox(height: 12),
  232. Container(
  233. width: double.infinity,
  234. padding: const EdgeInsets.symmetric(
  235. horizontal: 16,
  236. vertical: 10,
  237. ),
  238. decoration: BoxDecoration(
  239. color: AppColors.danger,
  240. borderRadius: BorderRadius.circular(20),
  241. ),
  242. child: const Text(
  243. '一键强力 DING 催办',
  244. textAlign: TextAlign.center,
  245. style: TextStyle(
  246. fontSize: 14,
  247. fontWeight: FontWeight.w600,
  248. color: Colors.white,
  249. ),
  250. ),
  251. ),
  252. ],
  253. ),
  254. ),
  255. ),
  256. const SizedBox(height: 24),
  257. ],
  258. ),
  259. );
  260. }
  261. }