智慧水务管理系统 - 精河县供水工程综合管理平台

alert_page.dart 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. import 'package:flutter/material.dart';
  2. /// 报警推送列表页面
  3. class AlertPage extends StatefulWidget {
  4. const AlertPage({super.key});
  5. @override
  6. State<AlertPage> createState() => _AlertPageState();
  7. }
  8. class _AlertPageState extends State<AlertPage>
  9. with AutomaticKeepAliveClientMixin {
  10. List<Map<String, dynamic>> _alerts = [];
  11. bool _isLoading = true;
  12. @override
  13. bool get wantKeepAlive => true;
  14. @override
  15. void initState() {
  16. super.initState();
  17. _loadData();
  18. }
  19. void _loadData() {
  20. // 模拟数据加载
  21. Future.delayed(const Duration(milliseconds: 500), () {
  22. setState(() {
  23. _alerts = _generateMockAlerts();
  24. _isLoading = false;
  25. });
  26. });
  27. }
  28. List<Map<String, dynamic>> _generateMockAlerts() {
  29. return [
  30. {
  31. 'id': 'AL-001',
  32. 'title': '城东加压站压力异常',
  33. 'level': 'critical',
  34. 'station': '城东加压站',
  35. 'content': '水压达到 3.2 MPa,超过警戒值',
  36. 'time': '2026-06-17 08:30',
  37. 'isRead': false,
  38. },
  39. {
  40. 'id': 'AL-002',
  41. 'title': '城西水厂流量下降',
  42. 'level': 'warning',
  43. 'station': '城西水厂',
  44. 'content': '流量从 450 m³/h 降至 280 m³/h',
  45. 'time': '2026-06-17 09:15',
  46. 'isRead': false,
  47. },
  48. {
  49. 'id': 'AL-003',
  50. 'title': '南区配水站水质异常',
  51. 'level': 'error',
  52. 'station': '南区配水站',
  53. 'content': '浊度指标超标,需立即处理',
  54. 'time': '2026-06-17 10:20',
  55. 'isRead': true,
  56. },
  57. {
  58. 'id': 'AL-004',
  59. 'title': '中心泵站设备维护',
  60. 'level': 'info',
  61. 'station': '中心泵站',
  62. 'content': '计划性维护,预计停机 2 小时',
  63. 'time': '2026-06-17 11:00',
  64. 'isRead': true,
  65. },
  66. {
  67. 'id': 'AL-005',
  68. 'title': '高新区水厂水质恢复',
  69. 'level': 'normal',
  70. 'station': '高新区水厂',
  71. 'content': '水质指标恢复正常,告警已解除',
  72. 'time': '2026-06-16 15:45',
  73. 'isRead': true,
  74. },
  75. {
  76. 'id': 'AL-006',
  77. 'title': '工业园加压站供电异常',
  78. 'level': 'critical',
  79. 'station': '工业园加压站',
  80. 'content': '备用电源已启动,需尽快恢复主供电',
  81. 'time': '2026-06-17 12:10',
  82. 'isRead': false,
  83. },
  84. ];
  85. }
  86. Color _getLevelColor(String level) {
  87. switch (level) {
  88. case 'critical':
  89. return Colors.red;
  90. case 'error':
  91. return Colors.orange;
  92. case 'warning':
  93. return Colors.yellow;
  94. case 'info':
  95. return Colors.blue;
  96. default:
  97. return Colors.green;
  98. }
  99. }
  100. String _getLevelText(String level) {
  101. switch (level) {
  102. case 'critical':
  103. return '严重';
  104. case 'error':
  105. return '错误';
  106. case 'warning':
  107. return '警告';
  108. case 'info':
  109. return '信息';
  110. default:
  111. return '正常';
  112. }
  113. }
  114. @override
  115. Widget build(BuildContext context) {
  116. super.build(context);
  117. return Scaffold(
  118. appBar: AppBar(
  119. title: const Text('报警信息'),
  120. actions: [
  121. IconButton(
  122. icon: const Icon(Icons.filter_list),
  123. onPressed: () {
  124. _showFilterDialog();
  125. },
  126. ),
  127. ],
  128. ),
  129. body: _isLoading
  130. ? const Center(child: CircularProgressIndicator())
  131. : RefreshIndicator(
  132. onRefresh: () async {
  133. setState(() {
  134. _isLoading = true;
  135. });
  136. _loadData();
  137. },
  138. child: ListView.builder(
  139. padding: const EdgeInsets.all(12),
  140. itemCount: _alerts.length,
  141. itemBuilder: (context, index) {
  142. final alert = _alerts[index];
  143. return _buildAlertCard(alert);
  144. },
  145. ),
  146. ),
  147. floatingActionButton: FloatingActionButton(
  148. onPressed: () {
  149. _showAlertDetail();
  150. },
  151. child: const Icon(Icons.info),
  152. ),
  153. );
  154. }
  155. Widget _buildAlertCard(Map<String, dynamic> alert) {
  156. final isUnread = !alert['isRead'];
  157. return Card(
  158. margin: const EdgeInsets.only(bottom: 12),
  159. child: Padding(
  160. padding: const EdgeInsets.all(16),
  161. child: Column(
  162. crossAxisAlignment: CrossAxisAlignment.start,
  163. children: [
  164. Row(
  165. children: [
  166. Container(
  167. padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
  168. decoration: BoxDecoration(
  169. color: _getLevelColor(alert['level']).withOpacity(0.1),
  170. borderRadius: BorderRadius.circular(12),
  171. border: Border.all(
  172. color: _getLevelColor(alert['level']),
  173. ),
  174. ),
  175. child: Text(
  176. _getLevelText(alert['level']),
  177. style: TextStyle(
  178. fontSize: 12,
  179. color: _getLevelColor(alert['level']),
  180. fontWeight: FontWeight.bold,
  181. ),
  182. ),
  183. ),
  184. if (isUnread)
  185. Container(
  186. margin: const EdgeInsets.only(left: 8),
  187. width: 8,
  188. height: 8,
  189. decoration: BoxDecoration(
  190. color: Colors.red,
  191. shape: BoxShape.circle,
  192. ),
  193. ),
  194. const Spacer(),
  195. Text(
  196. alert['time'],
  197. style: TextStyle(fontSize: 12, color: Colors.grey[500]),
  198. ),
  199. ],
  200. ),
  201. const SizedBox(height: 12),
  202. Text(
  203. alert['title'],
  204. style: const TextStyle(
  205. fontSize: 16,
  206. fontWeight: FontWeight.bold,
  207. ),
  208. ),
  209. const SizedBox(height: 8),
  210. Row(
  211. children: [
  212. const Icon(Icons.location_on, size: 16, color: Colors.grey),
  213. const SizedBox(width: 4),
  214. Text(
  215. alert['station'],
  216. style: TextStyle(fontSize: 14, color: Colors.grey[600]),
  217. ),
  218. ],
  219. ),
  220. const SizedBox(height: 8),
  221. Text(
  222. alert['content'],
  223. style: TextStyle(fontSize: 14, color: Colors.grey[700]),
  224. maxLines: 2,
  225. overflow: TextOverflow.ellipsis,
  226. ),
  227. ],
  228. ),
  229. ),
  230. );
  231. }
  232. void _showFilterDialog() {
  233. showDialog(
  234. context: context,
  235. builder: (context) => AlertDialog(
  236. title: const Text('筛选报警'),
  237. content: Column(
  238. mainAxisSize: MainAxisSize.min,
  239. children: [
  240. _buildFilterItem('严重', 'critical'),
  241. _buildFilterItem('错误', 'error'),
  242. _buildFilterItem('警告', 'warning'),
  243. _buildFilterItem('信息', 'info'),
  244. ],
  245. ),
  246. actions: [
  247. TextButton(
  248. onPressed: () => Navigator.pop(context),
  249. child: const Text('取消'),
  250. ),
  251. TextButton(
  252. onPressed: () {
  253. setState(() {
  254. _loadData();
  255. });
  256. Navigator.pop(context);
  257. },
  258. child: const Text('确定'),
  259. ),
  260. ],
  261. ),
  262. );
  263. }
  264. Widget _buildFilterItem(String text, String level) {
  265. return CheckboxListTile(
  266. title: Text(text),
  267. value: true,
  268. onChanged: (value) {},
  269. );
  270. }
  271. void _showAlertDetail() {
  272. // 显示最后一个未读告警详情
  273. final unreadAlerts = _alerts.where((alert) => !alert['isRead']).toList();
  274. if (unreadAlerts.isNotEmpty) {
  275. showDialog(
  276. context: context,
  277. builder: (context) => AlertDialog(
  278. title: Text(unreadAlerts.first['title']),
  279. content: Column(
  280. mainAxisSize: MainAxisSize.min,
  281. crossAxisAlignment: CrossAxisAlignment.start,
  282. children: [
  283. _buildDetailRow('地点', unreadAlerts.first['station']),
  284. _buildDetailRow('时间', unreadAlerts.first['time']),
  285. _buildDetailRow('级别', _getLevelText(unreadAlerts.first['level'])),
  286. const SizedBox(height: 16),
  287. Text(unreadAlerts.first['content']),
  288. ],
  289. ),
  290. actions: [
  291. TextButton(
  292. onPressed: () => Navigator.pop(context),
  293. child: const Text('关闭'),
  294. ),
  295. TextButton(
  296. onPressed: () {
  297. setState(() {
  298. unreadAlerts.first['isRead'] = true;
  299. });
  300. Navigator.pop(context);
  301. },
  302. child: const Text('标记已读'),
  303. ),
  304. ],
  305. ),
  306. );
  307. }
  308. }
  309. Widget _buildDetailRow(String label, String value) {
  310. return Padding(
  311. padding: const EdgeInsets.symmetric(vertical: 4),
  312. child: Row(
  313. crossAxisAlignment: CrossAxisAlignment.start,
  314. children: [
  315. Text(
  316. '$label: ',
  317. style: const TextStyle(
  318. fontSize: 14,
  319. fontWeight: FontWeight.bold,
  320. ),
  321. ),
  322. Expanded(
  323. child: Text(value),
  324. ),
  325. ],
  326. ),
  327. );
  328. }
  329. }