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

dispatch_page.dart 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. import 'package:flutter/material.dart';
  2. /// 今日调度页面
  3. class DispatchPage extends StatefulWidget {
  4. const DispatchPage({super.key});
  5. @override
  6. State<DispatchPage> createState() => _DispatchPageState();
  7. }
  8. class _DispatchPageState extends State<DispatchPage>
  9. with AutomaticKeepAliveClientMixin {
  10. @override
  11. bool get wantKeepAlive => true;
  12. @override
  13. Widget build(BuildContext context) {
  14. super.build(context);
  15. return Scaffold(
  16. appBar: AppBar(
  17. title: const Text('今日调度'),
  18. actions: [
  19. IconButton(
  20. icon: const Icon(Icons.calendar_today),
  21. onPressed: () {},
  22. ),
  23. ],
  24. ),
  25. body: const Column(
  26. children: [
  27. Expanded(
  28. child: SingleChildScrollView(
  29. padding: EdgeInsets.all(16),
  30. child: Column(
  31. children: [
  32. _ShiftOverviewCard(),
  33. SizedBox(height: 16),
  34. _DutyPersonnelList(),
  35. SizedBox(height: 16),
  36. _DispatchCommandsCard(),
  37. SizedBox(height: 16),
  38. _EmergencyContactsCard(),
  39. ],
  40. ),
  41. ),
  42. ),
  43. ],
  44. ),
  45. );
  46. }
  47. }
  48. /// 班次概览卡片
  49. class _ShiftOverviewCard extends StatelessWidget {
  50. const _ShiftOverviewCard();
  51. @override
  52. Widget build(BuildContext context) {
  53. return Card(
  54. elevation: 2,
  55. child: Padding(
  56. padding: const EdgeInsets.all(16),
  57. child: Column(
  58. crossAxisAlignment: CrossAxisAlignment.start,
  59. children: [
  60. Row(
  61. children: [
  62. const Icon(Icons.access_time, color: Color(0xFF1976D2)),
  63. const SizedBox(width: 8),
  64. const Text(
  65. '班次概览',
  66. style: TextStyle(
  67. fontSize: 16,
  68. fontWeight: FontWeight.bold,
  69. ),
  70. ),
  71. const Spacer(),
  72. Text(
  73. '2026-06-17',
  74. style: TextStyle(
  75. fontSize: 14,
  76. color: Colors.grey[600],
  77. ),
  78. ),
  79. ],
  80. ),
  81. const SizedBox(height: 16),
  82. _buildShiftRow('早班 (06:00-14:00)', '3/4 在岗', Colors.green),
  83. const SizedBox(height: 8),
  84. _buildShiftRow('中班 (14:00-22:00)', '2/3 在岗', Colors.orange),
  85. const SizedBox(height: 8),
  86. _buildShiftRow('夜班 (22:00-06:00)', '2/3 在岗', Colors.blue),
  87. ],
  88. ),
  89. ),
  90. );
  91. }
  92. Widget _buildShiftRow(String shift, String status, Color color) {
  93. return Row(
  94. children: [
  95. Container(
  96. padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
  97. decoration: BoxDecoration(
  98. color: color.withOpacity(0.1),
  99. borderRadius: BorderRadius.circular(16),
  100. border: Border.all(color: color),
  101. ),
  102. child: Text(
  103. shift,
  104. style: TextStyle(
  105. fontSize: 13,
  106. color: color,
  107. fontWeight: FontWeight.w500,
  108. ),
  109. ),
  110. ),
  111. const SizedBox(width: 12),
  112. Expanded(
  113. child: Text(
  114. status,
  115. style: const TextStyle(fontSize: 14),
  116. ),
  117. ),
  118. ],
  119. );
  120. }
  121. }
  122. /// 值班人员列表
  123. class _DutyPersonnelList extends StatelessWidget {
  124. const _DutyPersonnelList();
  125. @override
  126. Widget build(BuildContext context) {
  127. final personnel = [
  128. {'name': '张明', 'role': '值班长', 'station': '城东加压站', 'status': '在岗', 'phone': '138****1234'},
  129. {'name': '李强', 'role': '技术员', 'station': '城西水厂', 'status': '在岗', 'phone': '139****5678'},
  130. {'name': '王芳', 'role': '操作员', 'station': '南区配水站', 'status': '休息', 'phone': '137****9012'},
  131. {'name': '赵刚', 'role': '安全员', 'station': '北区调蓄池', 'status': '在岗', 'phone': '136****3456'},
  132. {'name': '刘洋', 'role': '技术员', 'station': '中心泵站', 'status': '在岗', 'phone': '135****7890'},
  133. ];
  134. return Card(
  135. elevation: 2,
  136. child: Padding(
  137. padding: const EdgeInsets.all(16),
  138. child: Column(
  139. crossAxisAlignment: CrossAxisAlignment.start,
  140. children: [
  141. Row(
  142. children: [
  143. const Icon(Icons.people, color: Color(0xFF1976D2)),
  144. const SizedBox(width: 8),
  145. const Text(
  146. '值班人员',
  147. style: TextStyle(
  148. fontSize: 16,
  149. fontWeight: FontWeight.bold,
  150. ),
  151. ),
  152. ],
  153. ),
  154. const SizedBox(height: 12),
  155. ListView.builder(
  156. shrinkWrap: true,
  157. physics: const NeverScrollableScrollPhysics(),
  158. itemCount: personnel.length,
  159. itemBuilder: (context, index) {
  160. final person = personnel[index];
  161. return _buildPersonnelItem(person);
  162. },
  163. ),
  164. ],
  165. ),
  166. ),
  167. );
  168. }
  169. Widget _buildPersonnelItem(Map<String, dynamic> person) {
  170. final isWorking = person['status'] == '在岗';
  171. return Padding(
  172. padding: const EdgeInsets.only(bottom: 12),
  173. child: Row(
  174. children: [
  175. Container(
  176. width: 40,
  177. height: 40,
  178. decoration: BoxDecoration(
  179. color: isWorking ? Colors.green.withOpacity(0.1) : Colors.grey.withOpacity(0.1),
  180. borderRadius: BorderRadius.circular(20),
  181. border: Border.all(
  182. color: isWorking ? Colors.green : Colors.grey,
  183. ),
  184. ),
  185. child: Icon(
  186. isWorking ? Icons.person : Icons.person_off,
  187. color: isWorking ? Colors.green : Colors.grey,
  188. ),
  189. ),
  190. const SizedBox(width: 12),
  191. Expanded(
  192. child: Column(
  193. crossAxisAlignment: CrossAxisAlignment.start,
  194. children: [
  195. Row(
  196. children: [
  197. Text(
  198. person['name'],
  199. style: const TextStyle(
  200. fontSize: 16,
  201. fontWeight: FontWeight.bold,
  202. ),
  203. ),
  204. const SizedBox(width: 8),
  205. Container(
  206. padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
  207. decoration: BoxDecoration(
  208. color: isWorking ? Colors.green.withOpacity(0.1) : Colors.grey.withOpacity(0.1),
  209. borderRadius: BorderRadius.circular(10),
  210. ),
  211. child: Text(
  212. person['status'],
  213. style: TextStyle(
  214. fontSize: 12,
  215. color: isWorking ? Colors.green : Colors.grey,
  216. ),
  217. ),
  218. ),
  219. ],
  220. ),
  221. const SizedBox(height: 4),
  222. Text(
  223. '${person['role']} · ${person['station']}',
  224. style: TextStyle(
  225. fontSize: 13,
  226. color: Colors.grey[600],
  227. ),
  228. ),
  229. ],
  230. ),
  231. ),
  232. if (isWorking)
  233. IconButton(
  234. icon: const Icon(Icons.phone, color: Color(0xFF1976D2)),
  235. onPressed: () {
  236. // 拨打电话逻辑
  237. },
  238. ),
  239. ],
  240. ),
  241. );
  242. }
  243. }
  244. /// 调度指令卡片
  245. class _DispatchCommandsCard extends StatelessWidget {
  246. const _DispatchCommandsCard();
  247. @override
  248. Widget build(BuildContext context) {
  249. final commands = [
  250. {
  251. 'id': 'CMD-001',
  252. 'type': '设备启停',
  253. 'target': '城东加压站 #2泵',
  254. 'status': '执行中',
  255. 'operator': '张明',
  256. 'time': '08:30',
  257. },
  258. {
  259. 'id': 'CMD-002',
  260. 'type': '参数调整',
  261. 'target': '城西水厂出水压力',
  262. 'status': '已完成',
  263. 'operator': '李强',
  264. 'time': '09:15',
  265. },
  266. {
  267. 'id': 'CMD-003',
  268. 'type': '故障处理',
  269. 'target': '北区调蓄池传感器',
  270. 'status': '待处理',
  271. 'operator': '赵刚',
  272. 'time': '10:20',
  273. },
  274. ];
  275. return Card(
  276. elevation: 2,
  277. child: Padding(
  278. padding: const EdgeInsets.all(16),
  279. child: Column(
  280. crossAxisAlignment: CrossAxisAlignment.start,
  281. children: [
  282. Row(
  283. children: [
  284. const Icon(Icons.assignment, color: Color(0xFF1976D2)),
  285. const SizedBox(width: 8),
  286. const Text(
  287. '调度指令',
  288. style: TextStyle(
  289. fontSize: 16,
  290. fontWeight: FontWeight.bold,
  291. ),
  292. ),
  293. ],
  294. ),
  295. const SizedBox(height: 12),
  296. ListView.builder(
  297. shrinkWrap: true,
  298. physics: const NeverScrollableScrollPhysics(),
  299. itemCount: commands.length,
  300. itemBuilder: (context, index) {
  301. final command = commands[index];
  302. return _buildCommandItem(command);
  303. },
  304. ),
  305. ],
  306. ),
  307. ),
  308. );
  309. }
  310. Widget _buildCommandItem(Map<String, dynamic> command) {
  311. Color statusColor;
  312. switch (command['status']) {
  313. case '执行中':
  314. statusColor = Colors.orange;
  315. break;
  316. case '已完成':
  317. statusColor = Colors.green;
  318. break;
  319. case '待处理':
  320. statusColor = Colors.red;
  321. break;
  322. default:
  323. statusColor = Colors.grey;
  324. }
  325. return Padding(
  326. padding: const EdgeInsets.only(bottom: 12),
  327. child: Container(
  328. padding: const EdgeInsets.all(12),
  329. decoration: BoxDecoration(
  330. border: Border.all(color: Colors.grey[300]!),
  331. borderRadius: BorderRadius.circular(8),
  332. ),
  333. child: Row(
  334. children: [
  335. Container(
  336. padding: const EdgeInsets.all(4),
  337. decoration: BoxDecoration(
  338. color: statusColor.withOpacity(0.1),
  339. borderRadius: BorderRadius.circular(4),
  340. ),
  341. child: Icon(
  342. _getCommandIcon(command['type']),
  343. color: statusColor,
  344. size: 16,
  345. ),
  346. ),
  347. const SizedBox(width: 12),
  348. Expanded(
  349. child: Column(
  350. crossAxisAlignment: CrossAxisAlignment.start,
  351. children: [
  352. Text(
  353. command['type'],
  354. style: const TextStyle(
  355. fontSize: 14,
  356. fontWeight: FontWeight.w500,
  357. ),
  358. ),
  359. Text(
  360. command['target'],
  361. style: TextStyle(
  362. fontSize: 13,
  363. color: Colors.grey[600],
  364. ),
  365. ),
  366. ],
  367. ),
  368. ),
  369. Column(
  370. crossAxisAlignment: CrossAxisAlignment.end,
  371. children: [
  372. Container(
  373. padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
  374. decoration: BoxDecoration(
  375. color: statusColor.withOpacity(0.1),
  376. borderRadius: BorderRadius.circular(12),
  377. ),
  378. child: Text(
  379. command['status'],
  380. style: TextStyle(
  381. fontSize: 11,
  382. color: statusColor,
  383. fontWeight: FontWeight.bold,
  384. ),
  385. ),
  386. ),
  387. const SizedBox(height: 4),
  388. Text(
  389. '${command['time']} · ${command['operator']}',
  390. style: TextStyle(
  391. fontSize: 11,
  392. color: Colors.grey[500],
  393. ),
  394. ),
  395. ],
  396. ),
  397. ],
  398. ),
  399. ),
  400. );
  401. }
  402. IconData _getCommandIcon(String type) {
  403. switch (type) {
  404. case '设备启停':
  405. return Icons.power_settings_new;
  406. case '参数调整':
  407. return Icons.tune;
  408. case '故障处理':
  409. return build;
  410. default:
  411. return Icons.assignment;
  412. }
  413. }
  414. }
  415. /// 紧急联系人卡片
  416. class _EmergencyContactsCard extends StatelessWidget {
  417. const _EmergencyContactsCard();
  418. @override
  419. Widget build(BuildContext context) {
  420. final contacts = [
  421. {'name': '应急指挥中心', 'phone': '119', 'type': '消防'},
  422. {'name': '抢修热线', 'phone': '96116', 'type': '抢修'},
  423. {'name': '技术支持', 'phone': '400-123-4567', 'type': '技术'},
  424. {'name': '环保举报', 'phone': '12369', 'type': '环保'},
  425. ];
  426. return Card(
  427. elevation: 2,
  428. child: Padding(
  429. padding: const EdgeInsets.all(16),
  430. child: Column(
  431. crossAxisAlignment: CrossAxisAlignment.start,
  432. children: [
  433. Row(
  434. children: [
  435. const Icon(Icons.phone_in_talk, color: Color(0xFF1976D2)),
  436. const SizedBox(width: 8),
  437. const Text(
  438. '紧急联系人',
  439. style: TextStyle(
  440. fontSize: 16,
  441. fontWeight: FontWeight.bold,
  442. ),
  443. ),
  444. ],
  445. ),
  446. const SizedBox(height: 12),
  447. ListView.builder(
  448. shrinkWrap: true,
  449. physics: const NeverScrollableScrollPhysics(),
  450. itemCount: contacts.length,
  451. itemBuilder: (context, index) {
  452. final contact = contacts[index];
  453. return _buildContactItem(contact);
  454. },
  455. ),
  456. ],
  457. ),
  458. ),
  459. );
  460. }
  461. Widget _buildContactItem(Map<String, dynamic> contact) {
  462. return Padding(
  463. padding: const EdgeInsets.only(bottom: 8),
  464. child: Row(
  465. children: [
  466. Container(
  467. width: 32,
  468. height: 32,
  469. decoration: BoxDecoration(
  470. color: Colors.red.withOpacity(0.1),
  471. borderRadius: BorderRadius.circular(16),
  472. ),
  473. child: Icon(
  474. Icons.phone,
  475. color: Colors.red,
  476. size: 16,
  477. ),
  478. ),
  479. const SizedBox(width: 12),
  480. Expanded(
  481. child: Column(
  482. crossAxisAlignment: CrossAxisAlignment.start,
  483. children: [
  484. Text(
  485. contact['name'],
  486. style: const TextStyle(
  487. fontSize: 14,
  488. fontWeight: FontWeight.w500,
  489. ),
  490. ),
  491. Text(
  492. '${contact['type']} · ${contact['phone']}',
  493. style: TextStyle(
  494. fontSize: 13,
  495. color: Colors.grey[600],
  496. ),
  497. ),
  498. ],
  499. ),
  500. ),
  501. IconButton(
  502. icon: const Icon(Icons.call),
  503. color: Colors.red,
  504. onPressed: () {
  505. // 拨打电话逻辑
  506. },
  507. ),
  508. ],
  509. ),
  510. );
  511. }
  512. }