import 'dart:async'; import 'package:flutter/material.dart'; import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; import 'package:qhd_prevention/pages/KeyProjects/keyProjects_tab_list.dart'; import 'package:qhd_prevention/pages/badge_manager.dart'; import 'package:qhd_prevention/pages/home/NFC/home_nfc_list_page.dart'; import 'package:qhd_prevention/pages/home/SafeCheck/safeCheck_tab_list.dart'; import 'package:qhd_prevention/pages/home/home_danger_page.dart'; import 'package:qhd_prevention/pages/home/low_page.dart'; import 'package:qhd_prevention/pages/home/risk/riskControl_page.dart'; import 'package:qhd_prevention/pages/home/study/study_garden_page.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/work_tab_list_page.dart'; import 'package:qhd_prevention/pages/home/userInfo_page.dart'; import 'package:qhd_prevention/pages/home/work/danger_page.dart'; import 'package:qhd_prevention/pages/app/danger_wait_list_page.dart'; import 'package:qhd_prevention/pages/home/work/laws_regulations_page.dart'; import 'package:qhd_prevention/pages/home/workSet_page.dart'; import 'Safetymeeting/safety_meeting_list_page.dart'; import 'hidden_roll_widget.dart'; import '../../http/ApiService.dart'; import '../../tools/tools.dart'; class HomePage extends StatefulWidget { const HomePage({Key? key}) : super(key: key); @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State { int _eight_work_count = 0; int _safetyEnvironmentalInspection = 0; String _workKey(int idx) { switch (idx) { case 1: return 'dpc'; // 待排查 case 2: return 'dzg'; // 待整改 case 3: return 'ycq'; // 已超期 case 4: return 'dys'; // 待验收 case 5: return 'yys'; // 已验收 default: return ''; } } /// 按钮信息 List> buttonInfos = [ {"icon": "assets/icon-apps/home-base.png", "title": "人员信息", "unreadCount": 0}, {"icon": "assets/icon-apps/home-rili.png", "title": "工作安排", "unreadCount": 0}, {"icon": "assets/icon-apps/home-risk.png", "title": "风险布控", "unreadCount": 0}, {"icon": "assets/icon-apps/home-fl.png", "title": "法律法规", "unreadCount": 0}, {"icon": "assets/icon-apps/home-gw.png", "title": "特殊作业", "unreadCount": 0}, {"icon": "assets/icon-apps/home-zdgcgl.jpg", "title": "重点工程管理", "unreadCount": 0}, {"icon": "assets/icon-apps/home-cns.png", "title": "安全承诺", "unreadCount": 0}, {"icon": "assets/icon-apps/home-study.png", "title": "学习园地", "unreadCount": 0}, {"icon": "assets/icon-apps/home-base.png", "title": "安全检查", "unreadCount": 0}, {"icon": "assets/icon-apps/home-speEquip.jpg", "title": "设备巡检", "unreadCount": 0}, {"icon": "assets/icon-apps/safetymeeting.png", "title": "安全例会", "unreadCount": 0}, {"icon": "assets/icon-apps/home-xj.png", "title": "燃气巡检", "unreadCount": 0}, ]; /// 我的工作 List> workInfos = [ {"icon": "assets/icon-apps/jobico1.png", "index": 1, "detail": "待排查", "num": '0'}, {"icon": "assets/icon-apps/jobico2.png", "index": 2, "detail": "待整改", "num": '0'}, {"icon": "assets/icon-apps/jobico3.png", "index": 3, "detail": "已超期", "num": '0'}, {"icon": "assets/icon-apps/jobico4.png", "index": 4, "detail": "待验收", "num": '0'}, {"icon": "assets/icon-apps/jobico5.png", "index": 5, "detail": "已验收", "num": '0'}, ]; /// 排查数据 List> pcData = [ {"index": 1, "detail": {"jiancha": '0', "yinhuan": '0', "yanshou": '0'}}, {"index": 2, "detail": {"jiancha": '0', "yinhuan": '0', "yanshou": '0'}}, {"index": 3, "detail": {"jiancha": '0', "yinhuan": '0', "yanshou": '0'}}, ]; /// 隐患播报列表及状态 List> hiddenList = []; bool _initialLoadingHidden = true; final List> tagLabels = [ ['我', '的'], ['部', '门'], ['监', '管'], ]; @override void initState() { super.initState(); _onRefresh(); } Future _onRefresh() async { await BadgeManager().initAllModules(); await Future.wait([ _fetchData(), _fetchHiddenList(showLoading: false), ]); } Future _fetchHiddenList({bool showLoading = true}) async { // 保留已有列表,仅首次显示loading if (showLoading && hiddenList.isEmpty) setState(() {}); try { final res = await ApiService.getHiddenRoll().timeout(Duration(seconds: 10)); final list = (res['hiddenList'] as List).cast>(); setState(() { hiddenList = list; _initialLoadingHidden = false; }); } catch (e) { // 超时或失败且已有数据,则不处理 if (hiddenList.isEmpty) { setState(() { _initialLoadingHidden = false; }); } } } Future _fetchData() async { try { // “我的工作”数据 final data = await ApiService.getWork(); final hidCount = data['hidCount'] as Map? ?? {}; // 告知BadgeManager去更新“安全巡检”和“八项作业”角标 BadgeManager().updateEnvInspectCount(); BadgeManager().updateEightWorkCount(); // 拉取其他数据 final results = await Future.wait([ ApiService.getUserData(), ApiService.getDeptData(), ApiService.getSuperviseDeptData(), ]); final myData = results[0]; final deptData = results[1]; final superDeptData= results[2]; //更新页面状态 setState(() { // ———— 更新 workInfos ———— workInfos = workInfos.map((info) { final idx = info['index'] as int; final key = _workKey(idx); final num = (hidCount[key] ?? 0).toString(); return {...info, 'num': num}; }).toList(); // ———— 从 BadgeManager 拿最新的本页角标 ———— _safetyEnvironmentalInspection = BadgeManager().envInspectCount; _eight_work_count = BadgeManager().eightWorkCount; // ———— 更新 buttonInfos 中两个角标位置 ———— buttonInfos = buttonInfos.map((info) { switch (info['title'] as String) { case '特殊作业': info['unreadCount'] = _eight_work_count; break; case '安全检查': info['unreadCount'] = _safetyEnvironmentalInspection; break; default: // 其它保持原样 break; } return info; }).toList(); // ———— 更新排查数据 pcData ———— pcData = [ myData, deptData, superDeptData, ].asMap().entries.map((entry) { final idx = entry.key; final pdMap = (entry.value['pd'] as Map? ) ?? {}; return { 'index': idx + 1, 'detail': { 'jiancha': pdMap['check_count']?.toString() ?? '0', 'yinhuan': pdMap['hidden_count']?.toString() ?? '0', 'yanshou': pdMap['rectify_count']?.toString() ?? '0', }, }; }).toList(); }); } catch (e) { debugPrint('加载首页数据失败:$e'); } } @override Widget build(BuildContext context) { return Scaffold( body: RefreshIndicator( onRefresh: _onRefresh, child: ListView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.all(10), children: [ ClipRRect( borderRadius: BorderRadius.circular(5), child: Image.asset('assets/images/banner.jpg', fit: BoxFit.cover), ), const SizedBox(height: 10), _buildIconSection(context), const SizedBox(height: 10), _buildWorkSection(context), const SizedBox(height: 10), Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(5), color: Colors.white, ), child: Column( children: [ ListItemFactory.createBuildSimpleSection("隐患播报"), _initialLoadingHidden && hiddenList.isEmpty ? SizedBox( height: 150, child: Center(child: CircularProgressIndicator()), ) : HiddenRollWidget(hiddenList: hiddenList), ], ) ), const SizedBox(height: 10), _buildPCDataSection(), SizedBox(height: 20), ], ), ), ); } Widget _buildWorkSection(BuildContext context) { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ ListItemFactory.createBuildSimpleSection("我的工作"), Padding( padding: const EdgeInsets.fromLTRB(10, 0, 10, 10), child: LayoutBuilder( builder: (context, constraints) { const spacing = 10.0; final totalWidth = constraints.maxWidth; final itemWidth = (totalWidth - spacing * 2) / 3; return Wrap( spacing: spacing, runSpacing: spacing, children: workInfos.map((info) { return SizedBox( width: itemWidth, child: _buildGridItem( context, icon: info["icon"]!, title: info["num"]!, subtitle: info["detail"]!, index: info["index"]!, ), ); }).toList(), ); }, ), ), ], ), ); } Widget _buildPCDataSection() { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ ListItemFactory.createBuildSimpleSection("排查数据"), ...pcData.map(_widgetPCDataItem), ], ), ); } double _itemWidth(BuildContext context) => (MediaQuery.of(context).size.width - 20) / 4; Widget _buildIconSection(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(vertical: 10), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), ), child: Wrap( runSpacing: 16, children: buttonInfos.asMap().entries.map((entry) { final idx = entry.key; final info = entry.value; return _buildIconButton( context: context, iconPath: info["icon"] as String, label: info["title"] as String, unreadCount: info["unreadCount"] as int, onPressed: () async { switch (idx) { case 0:// 人员信息 pushPage(UserinfoPage(), context); break; case 1:// 法律法规 pushPage(WorkSetPage(), context); break; case 2:// 风险布控 pushPage(RiskControlPage(), context); break; case 3:// 法律法规 pushPage(LawsRegulationsPage(), context); break; case 4:// 八项作业 await pushPage(WorkTabListPage(), context); break; case 5: // 重点工程管理 await pushPage(KeyprojectsTabList(), context); break; case 7: pushPage(StudyGardenPage(), context); break; case 8: // 安全检查 await pushPage(SafecheckTabList(), context); break; case 10: pushPage(SafetyMeetingListPage(), context); break; case 11: pushPage(HomeNfcListPage(), context); break; } _onRefresh(); }, ); }).toList(), ), ); } Widget _buildIconButton({ required BuildContext context, required String iconPath, required String label, required VoidCallback onPressed, int unreadCount = 0, }) { final w = _itemWidth(context); return InkWell( onTap: onPressed, borderRadius: BorderRadius.circular(8), child: SizedBox( width: w, child: Stack( clipBehavior: Clip.none, children: [ Align( alignment: Alignment.topCenter, child: Column( mainAxisSize: MainAxisSize.min, children: [ Image.asset(iconPath, width: 40, height: 40), const SizedBox(height: 5), Text(label, style: const TextStyle(fontSize: 14)), ], ), ), if (unreadCount > 0) Positioned( right: 20, top: -5, child: Container( padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), decoration: BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(10)), constraints: const BoxConstraints(minWidth: 16, minHeight: 16), child: Center( child: Text( unreadCount > 99 ? '99+' : '$unreadCount', style: const TextStyle(color: Colors.white, fontSize: 11, height: 1), textAlign: TextAlign.center, ), ) ), ), ], ), ), ); } Widget _widgetPCDataItem(Map item) { final detail = item["detail"] as Map; final values = [detail["jiancha"]!, detail["yinhuan"]!, detail["yanshou"]!]; const labels = ["检查数", "发现隐患数", "已验收隐患数"]; const colors = [Color(0xFF3283FF), Color(0xFFF37B1D), Color(0xFF41B852)]; final idx = int.tryParse(item["index"].toString())! - 1; return Padding( padding: const EdgeInsets.fromLTRB(15, 0, 10, 15), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ for (var i = 0; i < 3; i++) Column( children: [ Text(values[i], style: TextStyle(color: colors[i], fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 4), Text(labels[i], style: TextStyle(color: Colors.black38, fontSize: 15)), ], ), Container( color: idx == 0 ? Colors.red : Colors.blue, width: 20, height: 40, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(tagLabels[idx][0], style: TextStyle(color: Colors.white, fontSize: 12)), Text(tagLabels[idx][1], style: TextStyle(color: Colors.white, fontSize: 12)), ], ), ), ], ), SizedBox(height: 10), Divider(height: 1, color: Colors.black12), ], ), ); } Widget _buildGridItem(BuildContext context, {required String icon, required String title, required String subtitle, required int index}) { return GestureDetector( onTap: () { if (index == 1) { pushPage(HomeDangerPage(), context); } else if (index == 2) { pushPage(DangerWaitListPage(DangerType.wait, 2), context); } else if (index == 3) { pushPage(DangerWaitListPage(DangerType.expired, 3), context); } else if (index == 4) { pushPage(DangerWaitListPage(DangerType.waitAcceptance, 4), context); } else if (index == 5) { pushPage(DangerWaitListPage(DangerType.acceptance, 5), context); } }, child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration(color: const Color(0xfbf9f9ff), borderRadius: BorderRadius.circular(6)), child: Row( children: [ Image.asset(icon, width: 35, height: 35), const SizedBox(width: 5), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), maxLines: 1, overflow: TextOverflow.ellipsis), const SizedBox(height: 4), Text(subtitle, style: const TextStyle(fontSize: 14, color: Colors.black), maxLines: 1, overflow: TextOverflow.ellipsis), ], ), ), ], ), ), ); } }