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

quality_page.dart 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098
  1. import 'package:flutter/material.dart';
  2. /// 水质查看页面
  3. class QualityPage extends StatefulWidget {
  4. const QualityPage({super.key});
  5. @override
  6. State<QualityPage> createState() => _QualityPageState();
  7. }
  8. class _QualityPageState extends State<QualityPage>
  9. with AutomaticKeepAliveClientMixin {
  10. int _selectedTabIndex = 0;
  11. @override
  12. bool get wantKeepAlive => true;
  13. @override
  14. Widget build(BuildContext context) {
  15. super.build(context);
  16. return Scaffold(
  17. appBar: AppBar(
  18. title: const Text('水质监测'),
  19. actions: [
  20. IconButton(
  21. icon: const Icon(Icons.refresh),
  22. onPressed: () {
  23. setState(() {});
  24. },
  25. ),
  26. ],
  27. ),
  28. body: Column(
  29. children: [
  30. _buildTabBar(),
  31. const SizedBox(height: 16),
  32. Expanded(
  33. child: _buildTabContent(),
  34. ),
  35. ],
  36. ),
  37. );
  38. }
  39. Widget _buildTabBar() {
  40. final tabs = ['原水', '出厂水', '末梢水'];
  41. return Container(
  42. padding: const EdgeInsets.symmetric(horizontal: 16),
  43. child: Row(
  44. children: List.generate(tabs.length, (index) {
  45. final isSelected = index == _selectedTabIndex;
  46. return Expanded(
  47. child: GestureDetector(
  48. onTap: () {
  49. setState(() {
  50. _selectedTabIndex = index;
  51. });
  52. },
  53. child: Container(
  54. margin: const EdgeInsets.symmetric(horizontal: 4),
  55. padding: const EdgeInsets.symmetric(vertical: 12),
  56. decoration: BoxDecoration(
  57. color: isSelected
  58. ? const Color(0xFF1976D2)
  59. : Colors.grey[200],
  60. borderRadius: BorderRadius.circular(20),
  61. ),
  62. child: Text(
  63. tabs[index],
  64. textAlign: TextAlign.center,
  65. style: TextStyle(
  66. color: isSelected ? Colors.white : Colors.grey[700],
  67. fontSize: 14,
  68. fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
  69. ),
  70. ),
  71. ),
  72. ),
  73. );
  74. }),
  75. ),
  76. );
  77. }
  78. Widget _buildTabContent() {
  79. switch (_selectedTabIndex) {
  80. case 0:
  81. return const _RawWaterQualityTab();
  82. case 1:
  83. return const _TreatedWaterQualityTab();
  84. case 2:
  85. return const _TapWaterQualityTab();
  86. default:
  87. return const _RawWaterQualityTab();
  88. }
  89. }
  90. }
  91. /// 原水水质标签页
  92. class _RawWaterQualityTab extends StatelessWidget {
  93. const _RawWaterQualityTab();
  94. @override
  95. Widget build(BuildContext context) {
  96. return SingleChildScrollView(
  97. padding: const EdgeInsets.all(16),
  98. child: Column(
  99. crossAxisAlignment: CrossAxisAlignment.start,
  100. children: [
  101. _buildQualityOverview(),
  102. const SizedBox(height: 16),
  103. _buildWaterSources(),
  104. const SizedBox(height: 16),
  105. _buildIndicatorsTable(),
  106. ],
  107. ),
  108. );
  109. }
  110. Widget _buildQualityOverview() {
  111. return Card(
  112. child: Padding(
  113. padding: const EdgeInsets.all(16),
  114. child: Column(
  115. crossAxisAlignment: CrossAxisAlignment.start,
  116. children: [
  117. const Text(
  118. '原水水质总体情况',
  119. style: TextStyle(
  120. fontSize: 16,
  121. fontWeight: FontWeight.bold,
  122. ),
  123. ),
  124. const SizedBox(height: 12),
  125. Row(
  126. children: [
  127. Expanded(
  128. child: _buildOverviewItem(
  129. '监测点数',
  130. '8个',
  131. Icons.location_on,
  132. Colors.blue,
  133. ),
  134. ),
  135. const SizedBox(width: 12),
  136. Expanded(
  137. child: _buildOverviewItem(
  138. '达标率',
  139. '87.5%',
  140. Icons.check_circle,
  141. Colors.green,
  142. ),
  143. ),
  144. const SizedBox(width: 12),
  145. Expanded(
  146. child: _buildOverviewItem(
  147. '异常点数',
  148. '1个',
  149. Icons.warning,
  150. Colors.orange,
  151. ),
  152. ),
  153. ],
  154. ),
  155. ],
  156. ),
  157. ),
  158. );
  159. }
  160. Widget _buildOverviewItem(String label, String value, IconData icon, Color color) {
  161. return Container(
  162. padding: const EdgeInsets.all(12),
  163. decoration: BoxDecoration(
  164. color: color.withOpacity(0.1),
  165. borderRadius: BorderRadius.circular(8),
  166. ),
  167. child: Column(
  168. children: [
  169. Icon(icon, color: color, size: 20),
  170. const SizedBox(height: 8),
  171. Text(
  172. label,
  173. style: TextStyle(
  174. fontSize: 12,
  175. color: Colors.grey[600],
  176. ),
  177. ),
  178. const SizedBox(height: 4),
  179. Text(
  180. value,
  181. style: TextStyle(
  182. fontSize: 16,
  183. fontWeight: FontWeight.bold,
  184. color: color,
  185. ),
  186. ),
  187. ],
  188. ),
  189. );
  190. }
  191. Widget _buildWaterSources() {
  192. final sources = [
  193. {
  194. 'name': '长江取水口',
  195. 'location': '城东',
  196. 'quality': '良好',
  197. 'updateTime': '2026-06-17 08:30',
  198. 'indicators': {
  199. '浊度': '2.3 NTU',
  200. 'pH值': '7.2',
  201. '溶解氧': '6.8 mg/L',
  202. '氨氮': '0.15 mg/L',
  203. },
  204. },
  205. {
  206. 'name': '汉江取水口',
  207. 'location': '城西',
  208. 'quality': '合格',
  209. 'updateTime': '2026-06-17 08:45',
  210. 'indicators': {
  211. '浊度': '5.1 NTU',
  212. 'pH值': '7.1',
  213. '溶解氧': '6.5 mg/L',
  214. '氨氮': '0.22 mg/L',
  215. },
  216. },
  217. {
  218. 'name': '东湖取水口',
  219. 'location': '城南',
  220. 'quality': '异常',
  221. 'updateTime': '2026-06-17 09:15',
  222. 'indicators': {
  223. '浊度': '15.2 NTU',
  224. 'pH值': '6.8',
  225. '溶解氧': '5.2 mg/L',
  226. '氨氮': '0.45 mg/L',
  227. },
  228. },
  229. ];
  230. return Column(
  231. crossAxisAlignment: CrossAxisAlignment.start,
  232. children: [
  233. const Text(
  234. '水源监测点',
  235. style: TextStyle(
  236. fontSize: 16,
  237. fontWeight: FontWeight.bold,
  238. ),
  239. ),
  240. const SizedBox(height: 12),
  241. ListView.builder(
  242. shrinkWrap: true,
  243. physics: const NeverScrollableScrollPhysics(),
  244. itemCount: sources.length,
  245. itemBuilder: (context, index) {
  246. final source = sources[index];
  247. return _buildSourceCard(source);
  248. },
  249. ),
  250. ],
  251. );
  252. }
  253. Widget _buildSourceCard(Map<String, dynamic> source) {
  254. Color qualityColor;
  255. switch (source['quality']) {
  256. case '良好':
  257. qualityColor = Colors.green;
  258. break;
  259. case '合格':
  260. qualityColor = Colors.orange;
  261. break;
  262. case '异常':
  263. qualityColor = Colors.red;
  264. break;
  265. default:
  266. qualityColor = Colors.grey;
  267. }
  268. return Card(
  269. margin: const EdgeInsets.only(bottom: 12),
  270. child: Padding(
  271. padding: const EdgeInsets.all(16),
  272. child: Column(
  273. crossAxisAlignment: CrossAxisAlignment.start,
  274. children: [
  275. Row(
  276. children: [
  277. Expanded(
  278. child: Text(
  279. source['name'],
  280. style: const TextStyle(
  281. fontSize: 16,
  282. fontWeight: FontWeight.bold,
  283. ),
  284. ),
  285. ),
  286. Container(
  287. padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
  288. decoration: BoxDecoration(
  289. color: qualityColor.withOpacity(0.1),
  290. borderRadius: BorderRadius.circular(12),
  291. border: Border.all(color: qualityColor),
  292. ),
  293. child: Text(
  294. source['quality'],
  295. style: TextStyle(
  296. fontSize: 12,
  297. color: qualityColor,
  298. fontWeight: FontWeight.bold,
  299. ),
  300. ),
  301. ),
  302. ],
  303. ),
  304. const SizedBox(height: 8),
  305. Text(
  306. '位置:${source['location']}',
  307. style: TextStyle(
  308. fontSize: 14,
  309. color: Colors.grey[600],
  310. ),
  311. ),
  312. const SizedBox(height: 8),
  313. Text(
  314. '更新时间:${source['updateTime']}',
  315. style: TextStyle(
  316. fontSize: 12,
  317. color: Colors.grey[500],
  318. ),
  319. ),
  320. const SizedBox(height: 12),
  321. ...source['indicators'].entries.map((entry) {
  322. return _buildIndicatorRow(entry.key, entry.value);
  323. }),
  324. ],
  325. ),
  326. ),
  327. );
  328. }
  329. Widget _buildIndicatorRow(String name, String value) {
  330. return Padding(
  331. padding: const EdgeInsets.symmetric(vertical: 2),
  332. child: Row(
  333. children: [
  334. SizedBox(
  335. width: 60,
  336. child: Text(
  337. name,
  338. style: TextStyle(
  339. fontSize: 13,
  340. color: Colors.grey[600],
  341. ),
  342. ),
  343. ),
  344. Expanded(
  345. child: Text(
  346. value,
  347. style: const TextStyle(fontSize: 13),
  348. ),
  349. ),
  350. ],
  351. ),
  352. );
  353. }
  354. Widget _buildIndicatorsTable() {
  355. return Card(
  356. child: Padding(
  357. padding: const EdgeInsets.all(16),
  358. child: Column(
  359. crossAxisAlignment: CrossAxisAlignment.start,
  360. children: [
  361. const Text(
  362. '水质指标详情',
  363. style: TextStyle(
  364. fontSize: 16,
  365. fontWeight: FontWeight.bold,
  366. ),
  367. ),
  368. const SizedBox(height: 16),
  369. _buildIndicatorTable(),
  370. ],
  371. ),
  372. ),
  373. );
  374. }
  375. Widget _buildIndicatorTable() {
  376. final indicators = [
  377. {
  378. 'name': '浊度',
  379. 'unit': 'NTU',
  380. 'standard': '≤5',
  381. 'maxValue': '10',
  382. 'currentValue': '2.3-15.2',
  383. 'status': '部分超标',
  384. },
  385. {
  386. 'name': 'pH值',
  387. 'unit': '',
  388. 'standard': '6.5-8.5',
  389. 'maxValue': '9',
  390. 'currentValue': '6.8-7.2',
  391. 'status': '正常',
  392. },
  393. {
  394. 'name': '溶解氧',
  395. 'unit': 'mg/L',
  396. 'standard': '≥6',
  397. 'maxValue': '8',
  398. 'currentValue': '5.2-6.8',
  399. 'status': '正常',
  400. },
  401. {
  402. 'name': '氨氮',
  403. 'unit': 'mg/L',
  404. 'standard': '≤0.5',
  405. 'maxValue': '1.0',
  406. 'currentValue': '0.15-0.45',
  407. 'status': '正常',
  408. },
  409. {
  410. 'name': '总硬度',
  411. 'unit': 'mg/L',
  412. 'standard': '≤450',
  413. 'maxValue': '500',
  414. 'currentValue': '120-280',
  415. 'status': '正常',
  416. },
  417. {
  418. 'name': '总大肠菌群',
  419. 'unit': '个/L',
  420. 'standard': '≤3',
  421. 'maxValue': '100',
  422. 'currentValue': '0-2',
  423. 'status': '正常',
  424. },
  425. {
  426. 'name': '菌落总数',
  427. 'unit': 'CFU/mL',
  428. 'standard': '≤100',
  429. 'maxValue': '200',
  430. 'currentValue': '10-50',
  431. 'status': '正常',
  432. },
  433. ];
  434. return DataTable(
  435. columnSpacing: 16,
  436. dataRowHeight: 40,
  437. headingRowHeight: 40,
  438. columns: const [
  439. DataColumn(label: Text('指标', style: TextStyle(fontWeight: FontWeight.bold))),
  440. DataColumn(label: Text('标准', style: TextStyle(fontWeight: FontWeight.bold))),
  441. DataColumn(label: Text('当前值', style: TextStyle(fontWeight: FontWeight.bold))),
  442. DataColumn(label: Text('状态', style: TextStyle(fontWeight: FontWeight.bold))),
  443. ],
  444. rows: indicators.map((indicator) {
  445. Color statusColor;
  446. switch (indicator['status']) {
  447. case '正常':
  448. statusColor = Colors.green;
  449. break;
  450. case '部分超标':
  451. statusColor = Colors.orange;
  452. break;
  453. case '超标':
  454. statusColor = Colors.red;
  455. break;
  456. default:
  457. statusColor = Colors.grey;
  458. }
  459. return DataRow(
  460. cells: [
  461. DataCell(Text('${indicator['name']} (${indicator['unit']})')),
  462. DataCell(Text(indicator['standard'])),
  463. DataCell(Text(indicator['currentValue'])),
  464. DataCell(
  465. Container(
  466. padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
  467. decoration: BoxDecoration(
  468. color: statusColor.withOpacity(0.1),
  469. borderRadius: BorderRadius.circular(8),
  470. ),
  471. child: Text(
  472. indicator['status'],
  473. style: TextStyle(
  474. fontSize: 12,
  475. color: statusColor,
  476. fontWeight: FontWeight.bold,
  477. ),
  478. ),
  479. ),
  480. ),
  481. ],
  482. );
  483. }).toList(),
  484. );
  485. }
  486. }
  487. /// 出厂水水质标签页
  488. class _TreatedWaterQualityTab extends StatelessWidget {
  489. const _TreatedWaterQualityTab();
  490. @override
  491. Widget build(BuildContext context) {
  492. return const SingleChildScrollView(
  493. padding: EdgeInsets.all(16),
  494. child: Column(
  495. crossAxisAlignment: CrossAxisAlignment.start,
  496. children: [
  497. _QualityOverviewCard(),
  498. SizedBox(height: 16),
  499. _TreatmentPlantList(),
  500. SizedBox(height: 16),
  501. _TreatedIndicatorsTable(),
  502. ],
  503. ),
  504. );
  505. }
  506. }
  507. /// 末梢水水质标签页
  508. class _TapWaterQualityTab extends StatelessWidget {
  509. const _TapWaterQualityTab();
  510. @override
  511. Widget build(BuildContext context) {
  512. return const SingleChildScrollView(
  513. padding: EdgeInsets.all(16),
  514. child: Column(
  515. crossAxisAlignment: CrossAxisAlignment.start,
  516. children: [
  517. _QualityOverviewCard(),
  518. SizedBox(height: 16),
  519. _DistributionPointsList(),
  520. SizedBox(height: 16),
  521. _TapIndicatorsTable(),
  522. ],
  523. ),
  524. );
  525. }
  526. }
  527. /// 出厂水总体情况卡片
  528. class _QualityOverviewCard extends StatelessWidget {
  529. const _QualityOverviewCard();
  530. @override
  531. Widget build(BuildContext context) {
  532. return Card(
  533. child: Padding(
  534. padding: const EdgeInsets.all(16),
  535. child: Column(
  536. crossAxisAlignment: CrossAxisAlignment.start,
  537. children: [
  538. const Text(
  539. '水质总体评价',
  540. style: TextStyle(
  541. fontSize: 16,
  542. fontWeight: FontWeight.bold,
  543. ),
  544. ),
  545. const SizedBox(height: 16),
  546. Row(
  547. children: [
  548. Expanded(
  549. child: _buildOverviewItem(
  550. '合格率',
  551. '98.5%',
  552. Icons.check_circle,
  553. Colors.green,
  554. ),
  555. ),
  556. const SizedBox(width: 12),
  557. Expanded(
  558. child: _buildOverviewItem(
  559. '监测点数',
  560. '24个',
  561. Icons.location_on,
  562. Colors.blue,
  563. ),
  564. ),
  565. const SizedBox(width: 12),
  566. Expanded(
  567. child: _buildOverviewItem(
  568. '优良率',
  569. '85.2%',
  570. StarsIcon(),
  571. Colors.amber,
  572. ),
  573. ),
  574. ],
  575. ),
  576. const SizedBox(height: 16),
  577. const Text(
  578. '📋 综合评价:出厂水水质稳定达标,各项指标均在控制范围内。',
  579. style: TextStyle(
  580. fontSize: 14,
  581. color: Colors.green,
  582. ),
  583. ),
  584. ],
  585. ),
  586. ),
  587. );
  588. }
  589. Widget _buildOverviewItem(String label, String value, IconData icon, Color color) {
  590. return Container(
  591. padding: const EdgeInsets.all(12),
  592. decoration: BoxDecoration(
  593. color: color.withOpacity(0.1),
  594. borderRadius: BorderRadius.circular(8),
  595. ),
  596. child: Column(
  597. children: [
  598. Icon(icon, color: color, size: 20),
  599. const SizedBox(height: 8),
  600. Text(
  601. label,
  602. style: TextStyle(
  603. fontSize: 12,
  604. color: Colors.grey[600],
  605. ),
  606. ),
  607. const SizedBox(height: 4),
  608. Text(
  609. value,
  610. style: TextStyle(
  611. fontSize: 16,
  612. fontWeight: FontWeight.bold,
  613. color: color,
  614. ),
  615. ),
  616. ],
  617. ),
  618. );
  619. }
  620. }
  621. /// 水厂列表
  622. class _TreatmentPlantList extends StatelessWidget {
  623. const _TreatmentPlantList();
  624. @override
  625. Widget build(BuildContext context) {
  626. final plants = [
  627. {
  628. 'name': '城东水厂',
  629. 'capacity': '50万吨/日',
  630. 'efficiency': '95%',
  631. 'quality': '优良',
  632. },
  633. {
  634. 'name': '城西水厂',
  635. 'capacity': '30万吨/日',
  636. 'efficiency': '92%',
  637. 'quality': '良好',
  638. },
  639. {
  640. 'name': '南区水厂',
  641. 'capacity': '20万吨/日',
  642. 'efficiency': '88%',
  643. 'quality': '良好',
  644. },
  645. ];
  646. return Column(
  647. crossAxisAlignment: CrossAxisAlignment.start,
  648. children: [
  649. const Text(
  650. '水厂运行情况',
  651. style: TextStyle(
  652. fontSize: 16,
  653. fontWeight: FontWeight.bold,
  654. ),
  655. ),
  656. const SizedBox(height: 12),
  657. ListView.builder(
  658. shrinkWrap: true,
  659. physics: const NeverScrollableScrollPhysics(),
  660. itemCount: plants.length,
  661. itemBuilder: (context, index) {
  662. final plant = plants[index];
  663. return _buildPlantCard(plant);
  664. },
  665. ),
  666. ],
  667. );
  668. }
  669. Widget _buildPlantCard(Map<String, dynamic> plant) {
  670. Color qualityColor;
  671. switch (plant['quality']) {
  672. case '优良':
  673. qualityColor = Colors.green;
  674. break;
  675. case '良好':
  676. qualityColor = Colors.orange;
  677. break;
  678. default:
  679. qualityColor = Colors.grey;
  680. }
  681. return Card(
  682. margin: const EdgeInsets.only(bottom: 12),
  683. child: Padding(
  684. padding: const EdgeInsets.all(16),
  685. child: Row(
  686. children: [
  687. Expanded(
  688. child: Column(
  689. crossAxisAlignment: CrossAxisAlignment.start,
  690. children: [
  691. Text(
  692. plant['name'],
  693. style: const TextStyle(
  694. fontSize: 16,
  695. fontWeight: FontWeight.bold,
  696. ),
  697. ),
  698. const SizedBox(height: 8),
  699. Row(
  700. children: [
  701. _buildMiniStat('产能', plant['capacity']),
  702. const SizedBox(width: 16),
  703. _buildMiniStat('效率', plant['efficiency']),
  704. ],
  705. ),
  706. ],
  707. ),
  708. ),
  709. Container(
  710. padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
  711. decoration: BoxDecoration(
  712. color: qualityColor.withOpacity(0.1),
  713. borderRadius: BorderRadius.circular(12),
  714. border: Border.all(color: qualityColor),
  715. ),
  716. child: Text(
  717. plant['quality'],
  718. style: TextStyle(
  719. fontSize: 12,
  720. color: qualityColor,
  721. fontWeight: FontWeight.bold,
  722. ),
  723. ),
  724. ),
  725. ],
  726. ),
  727. ),
  728. );
  729. }
  730. Widget _buildMiniStat(String label, String value) {
  731. return Column(
  732. crossAxisAlignment: CrossAxisAlignment.start,
  733. children: [
  734. Text(
  735. label,
  736. style: TextStyle(
  737. fontSize: 12,
  738. color: Colors.grey[600],
  739. ),
  740. ),
  741. Text(
  742. value,
  743. style: const TextStyle(
  744. fontSize: 14,
  745. fontWeight: FontWeight.bold,
  746. ),
  747. ),
  748. ],
  749. );
  750. }
  751. }
  752. /// 水质指标表格
  753. class _TreatedIndicatorsTable extends StatelessWidget {
  754. const _TreatedIndicatorsTable();
  755. @override
  756. Widget build(BuildContext context) {
  757. return Card(
  758. child: Padding(
  759. padding: const EdgeInsets.all(16),
  760. child: Column(
  761. crossAxisAlignment: CrossAxisAlignment.start,
  762. children: [
  763. const Text(
  764. '出厂水水质指标',
  765. style: TextStyle(
  766. fontSize: 16,
  767. fontWeight: FontWeight.bold,
  768. ),
  769. ),
  770. const SizedBox(height: 16),
  771. _buildDataTable(
  772. [
  773. '浊度 (NTU)', 'pH值', '余氯 (mg/L)', '菌落总数 (CFU/mL)',
  774. '总大肠菌群 (个/L)', '总硬度 (mg/L)', '溶解氧 (mg/L)'
  775. ],
  776. [
  777. ['0.1', '0.2', '0.3'], ['7.1', '7.2', '7.3'],
  778. ['0.3', '0.4', '0.5'], ['<10', '<15', '<20'],
  779. ['0', '0', '0'], ['120', '150', '180'], ['6.5', '7.0', '7.5']
  780. ],
  781. ['≤1', '6.5-8.5', '≥0.3', '≤100', '不得检出', '≤450', '≥6.0']
  782. ),
  783. ],
  784. ),
  785. ),
  786. );
  787. }
  788. Widget _buildDataTable(List<String> headers, List<List<String>> data, List<String> standards) {
  789. return DataTable(
  790. columnSpacing: 16,
  791. dataRowHeight: 40,
  792. headingRowHeight: 40,
  793. columns: [
  794. const DataColumn(label: Text('检测点', style: TextStyle(fontWeight: FontWeight.bold))),
  795. ...headers.map((header) => DataColumn(label: Text(header, style: TextStyle(fontWeight: FontWeight.bold)))),
  796. const DataColumn(label: Text('标准', style: TextStyle(fontWeight: FontWeight.bold))),
  797. ],
  798. rows: data.asMap().entries.entries.map((entry) {
  799. return DataRow(
  800. cells: [
  801. DataCell(Text('点${entry.key + 1}')),
  802. ...entry.value.map((value) => DataCell(Text(value))),
  803. DataCell(Text(standards[entry.key])),
  804. ],
  805. );
  806. }).toList(),
  807. );
  808. }
  809. }
  810. /// 供水点列表
  811. class _DistributionPointsList extends StatelessWidget {
  812. const _DistributionPointsList();
  813. @override
  814. Widget build(BuildContext context) {
  815. final points = [
  816. {
  817. 'name': '中心广场',
  818. 'address': '中山路123号',
  819. 'quality': '优良',
  820. 'pressure': '0.35 MPa',
  821. 'updateTime': '2026-06-17 10:30',
  822. },
  823. {
  824. 'name': '东区居民区',
  825. 'address': '幸福路456号',
  826. 'quality': '良好',
  827. 'pressure': '0.28 MPa',
  828. 'updateTime': '2026-06-17 10:25',
  829. },
  830. {
  831. 'name': '西区商业区',
  832. 'address': '解放路789号',
  833. 'quality': '良好',
  834. 'pressure': '0.32 MPa',
  835. 'updateTime': '2026-06-17 10:20',
  836. },
  837. ];
  838. return Column(
  839. crossAxisAlignment: CrossAxisAlignment.start,
  840. children: [
  841. const Text(
  842. '末梢水监测点',
  843. style: TextStyle(
  844. fontSize: 16,
  845. fontWeight: FontWeight.bold,
  846. ),
  847. ),
  848. const SizedBox(height: 12),
  849. ListView.builder(
  850. shrinkWrap: true,
  851. physics: const NeverScrollableScrollPhysics(),
  852. itemCount: points.length,
  853. itemBuilder: (context, index) {
  854. final point = points[index];
  855. return _buildDistributionCard(point);
  856. },
  857. ),
  858. ],
  859. );
  860. }
  861. Widget _buildDistributionCard(Map<String, dynamic> point) {
  862. Color qualityColor;
  863. switch (point['quality']) {
  864. case '优良':
  865. qualityColor = Colors.green;
  866. break;
  867. case '良好':
  868. qualityColor = Colors.orange;
  869. break;
  870. default:
  871. qualityColor = Colors.grey;
  872. }
  873. return Card(
  874. margin: const EdgeInsets.only(bottom: 12),
  875. child: Padding(
  876. padding: const EdgeInsets.all(16),
  877. child: Column(
  878. crossAxisAlignment: CrossAxisAlignment.start,
  879. children: [
  880. Row(
  881. children: [
  882. Expanded(
  883. child: Text(
  884. point['name'],
  885. style: const TextStyle(
  886. fontSize: 16,
  887. fontWeight: FontWeight.bold,
  888. ),
  889. ),
  890. ),
  891. Container(
  892. padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
  893. decoration: BoxDecoration(
  894. color: qualityColor.withOpacity(0.1),
  895. borderRadius: BorderRadius.circular(12),
  896. border: Border.all(color: qualityColor),
  897. ),
  898. child: Text(
  899. point['quality'],
  900. style: TextStyle(
  901. fontSize: 12,
  902. color: qualityColor,
  903. fontWeight: FontWeight.bold,
  904. ),
  905. ),
  906. ),
  907. ],
  908. ),
  909. const SizedBox(height: 8),
  910. Text(
  911. '地址:${point['address']}',
  912. style: TextStyle(
  913. fontSize: 14,
  914. color: Colors.grey[600],
  915. ),
  916. ),
  917. const SizedBox(height: 8),
  918. Row(
  919. children: [
  920. _buildMiniStat('水压', point['pressure']),
  921. const SizedBox(width: 16),
  922. _buildMiniStat('更新', point['updateTime']),
  923. ],
  924. ),
  925. ],
  926. ),
  927. ),
  928. );
  929. }
  930. Widget _buildMiniStat(String label, String value) {
  931. return Column(
  932. crossAxisAlignment: CrossAxisAlignment.start,
  933. children: [
  934. Text(
  935. label,
  936. style: TextStyle(
  937. fontSize: 12,
  938. color: Colors.grey[600],
  939. ),
  940. ),
  941. Text(
  942. value,
  943. style: const TextStyle(
  944. fontSize: 14,
  945. fontWeight: FontWeight.bold,
  946. ),
  947. ),
  948. ],
  949. );
  950. }
  951. }
  952. /// 末梢水指标表格
  953. class _TapIndicatorsTable extends StatelessWidget {
  954. const _TapIndicatorsTable();
  955. @override
  956. Widget build(BuildContext context) {
  957. return Card(
  958. child: Padding(
  959. padding: const EdgeInsets.all(16),
  960. child: Column(
  961. crossAxisAlignment: CrossAxisAlignment.start,
  962. children: [
  963. const Text(
  964. '末梢水水质指标',
  965. style: TextStyle(
  966. fontSize: 16,
  967. fontWeight: FontWeight.bold,
  968. ),
  969. ),
  970. const SizedBox(height: 16),
  971. _buildIndicatorTable(),
  972. ],
  973. ),
  974. ),
  975. );
  976. }
  977. Widget _buildIndicatorTable() {
  978. final indicators = [
  979. {'name': '浊度', 'unit': 'NTU', 'standard': '≤1', 'current': '0.3-0.8', 'status': '正常'},
  980. {'name': '余氯', 'unit': 'mg/L', 'standard': '≥0.05', 'current': '0.2-0.5', 'status': '正常'},
  981. {'name': 'pH值', 'unit': '', 'standard': '6.5-8.5', 'current': '7.0-7.5', 'status': '正常'},
  982. {'name': '菌落总数', 'unit': 'CFU/mL', 'standard': '≤100', 'current': '10-50', 'status': '正常'},
  983. {'name': '总大肠菌群', 'unit': '个/L', 'standard': '不得检出', 'current': '0', 'status': '正常'},
  984. ];
  985. return DataTable(
  986. columnSpacing: 16,
  987. dataRowHeight: 40,
  988. headingRowHeight: 40,
  989. columns: const [
  990. DataColumn(label: Text('指标', style: TextStyle(fontWeight: FontWeight.bold))),
  991. DataColumn(label: Text('单位', style: TextStyle(fontWeight: FontWeight.bold))),
  992. DataColumn(label: Text('标准', style: TextStyle(fontWeight: FontWeight.bold))),
  993. DataColumn(label: Text('当前值', style: TextStyle(fontWeight: FontWeight.bold))),
  994. DataColumn(label: Text('状态', style: TextStyle(fontWeight: FontWeight.bold))),
  995. ],
  996. rows: indicators.map((indicator) {
  997. Color statusColor = Colors.green;
  998. if (indicator['status'] == '部分超标') {
  999. statusColor = Colors.orange;
  1000. } else if (indicator['status'] == '超标') {
  1001. statusColor = Colors.red;
  1002. }
  1003. return DataRow(
  1004. cells: [
  1005. DataCell(Text(indicator['name'])),
  1006. DataCell(Text(indicator['unit'])),
  1007. DataCell(Text(indicator['standard'])),
  1008. DataCell(Text(indicator['current'])),
  1009. DataCell(
  1010. Container(
  1011. padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
  1012. decoration: BoxDecoration(
  1013. color: statusColor.withOpacity(0.1),
  1014. borderRadius: BorderRadius.circular(8),
  1015. ),
  1016. child: Text(
  1017. indicator['status'],
  1018. style: TextStyle(
  1019. fontSize: 12,
  1020. color: statusColor,
  1021. fontWeight: FontWeight.bold,
  1022. ),
  1023. ),
  1024. ),
  1025. ),
  1026. ],
  1027. );
  1028. }).toList(),
  1029. );
  1030. }
  1031. }
  1032. /// 星星图标组件
  1033. class StarsIcon extends StatelessWidget {
  1034. const StarsIcon({super.key});
  1035. @override
  1036. Widget build(BuildContext context) {
  1037. return Row(
  1038. mainAxisSize: MainAxisSize.min,
  1039. children: List.generate(5, (index) {
  1040. return Icon(
  1041. index < 4 ? Icons.star : Icons.star_border,
  1042. color: Colors.amber,
  1043. size: 16,
  1044. );
  1045. }),
  1046. );
  1047. }
  1048. }