2025-07-11 11:03:21 +08:00
|
|
|
|
import 'dart:async';
|
2025-08-19 11:06:16 +08:00
|
|
|
|
import 'dart:convert';
|
2025-07-24 09:38:06 +08:00
|
|
|
|
|
2025-07-11 11:03:21 +08:00
|
|
|
|
import 'package:flutter/material.dart';
|
2025-08-19 11:06:16 +08:00
|
|
|
|
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
|
|
|
|
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
|
|
|
|
import 'package:qhd_prevention/tools/auth_service.dart';
|
|
|
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
2025-07-11 11:03:21 +08:00
|
|
|
|
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
|
2025-08-12 10:57:07 +08:00
|
|
|
|
import 'package:qhd_prevention/pages/KeyProjects/keyProjects_tab_list.dart';
|
2025-08-07 17:33:16 +08:00
|
|
|
|
import 'package:qhd_prevention/pages/badge_manager.dart';
|
2025-07-30 10:51:07 +08:00
|
|
|
|
import 'package:qhd_prevention/pages/home/NFC/home_nfc_list_page.dart';
|
2025-08-14 15:05:48 +08:00
|
|
|
|
import 'package:qhd_prevention/pages/home/SafeCheck/safeCheck_tab_list.dart';
|
2025-08-06 09:19:51 +08:00
|
|
|
|
import 'package:qhd_prevention/pages/home/home_danger_page.dart';
|
2025-07-11 11:03:21 +08:00
|
|
|
|
import 'package:qhd_prevention/pages/home/risk/riskControl_page.dart';
|
2025-07-16 08:37:08 +08:00
|
|
|
|
import 'package:qhd_prevention/pages/home/study/study_garden_page.dart';
|
2025-07-24 09:38:06 +08:00
|
|
|
|
import 'package:qhd_prevention/pages/home/tap/tabList/work_tab_list_page.dart';
|
2025-07-11 11:03:21 +08:00
|
|
|
|
import 'package:qhd_prevention/pages/home/userInfo_page.dart';
|
2025-07-24 20:01:23 +08:00
|
|
|
|
import 'package:qhd_prevention/pages/app/danger_wait_list_page.dart';
|
2025-07-23 20:14:38 +08:00
|
|
|
|
import 'package:qhd_prevention/pages/home/work/laws_regulations_page.dart';
|
2025-07-11 11:03:21 +08:00
|
|
|
|
import 'package:qhd_prevention/pages/home/workSet_page.dart';
|
|
|
|
|
|
2025-08-15 17:11:26 +08:00
|
|
|
|
import 'EquipmentInspection/equipment_inspection_list_page.dart';
|
|
|
|
|
import 'SafetyCommitment/safety_commitment_page.dart';
|
2025-08-08 10:51:48 +08:00
|
|
|
|
import 'Safetymeeting/safety_meeting_list_page.dart';
|
2025-07-28 14:22:07 +08:00
|
|
|
|
import 'hidden_roll_widget.dart';
|
2025-07-15 08:32:50 +08:00
|
|
|
|
import '../../http/ApiService.dart';
|
2025-07-11 11:03:21 +08:00
|
|
|
|
import '../../tools/tools.dart';
|
|
|
|
|
|
|
|
|
|
class HomePage extends StatefulWidget {
|
|
|
|
|
const HomePage({Key? key}) : super(key: key);
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
_HomePageState createState() => _HomePageState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _HomePageState extends State<HomePage> {
|
2025-08-06 13:43:22 +08:00
|
|
|
|
int _eight_work_count = 0;
|
|
|
|
|
int _safetyEnvironmentalInspection = 0;
|
2025-08-19 11:06:16 +08:00
|
|
|
|
|
|
|
|
|
// 缓存 key
|
|
|
|
|
static const String _hiddenCacheKey = 'hidden_roll_cache';
|
|
|
|
|
|
2025-08-07 17:33:16 +08:00
|
|
|
|
String _workKey(int idx) {
|
|
|
|
|
switch (idx) {
|
|
|
|
|
case 1:
|
2025-08-19 11:06:16 +08:00
|
|
|
|
return 'dpc'; // 待排查
|
2025-08-07 17:33:16 +08:00
|
|
|
|
case 2:
|
2025-08-19 11:06:16 +08:00
|
|
|
|
return 'dzg'; // 待整改
|
2025-08-07 17:33:16 +08:00
|
|
|
|
case 3:
|
2025-08-19 11:06:16 +08:00
|
|
|
|
return 'ycq'; // 已超期
|
2025-08-07 17:33:16 +08:00
|
|
|
|
case 4:
|
2025-08-19 11:06:16 +08:00
|
|
|
|
return 'dys'; // 待验收
|
2025-08-07 17:33:16 +08:00
|
|
|
|
case 5:
|
2025-08-19 11:06:16 +08:00
|
|
|
|
return 'yys'; // 已验收
|
2025-08-07 17:33:16 +08:00
|
|
|
|
default:
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-19 11:06:16 +08:00
|
|
|
|
|
2025-08-06 13:43:22 +08:00
|
|
|
|
/// 按钮信息
|
|
|
|
|
List<Map<String, dynamic>> buttonInfos = [
|
2025-08-19 11:06:16 +08:00
|
|
|
|
{
|
|
|
|
|
"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,
|
|
|
|
|
},
|
2025-08-06 13:43:22 +08:00
|
|
|
|
{"icon": "assets/icon-apps/home-fl.png", "title": "法律法规", "unreadCount": 0},
|
|
|
|
|
{"icon": "assets/icon-apps/home-gw.png", "title": "特殊作业", "unreadCount": 0},
|
2025-08-19 11:06:16 +08:00
|
|
|
|
{
|
|
|
|
|
"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},
|
2025-08-06 13:43:22 +08:00
|
|
|
|
];
|
2025-08-19 11:06:16 +08:00
|
|
|
|
|
2025-07-15 08:32:50 +08:00
|
|
|
|
/// 我的工作
|
2025-08-06 13:43:22 +08:00
|
|
|
|
List<Map<String, dynamic>> workInfos = [
|
2025-08-19 11:06:16 +08:00
|
|
|
|
{
|
|
|
|
|
"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',
|
|
|
|
|
},
|
2025-08-06 13:43:22 +08:00
|
|
|
|
];
|
|
|
|
|
|
2025-07-24 09:38:06 +08:00
|
|
|
|
/// 排查数据
|
2025-08-06 13:43:22 +08:00
|
|
|
|
List<Map<String, dynamic>> pcData = [
|
2025-08-19 11:06:16 +08:00
|
|
|
|
{
|
|
|
|
|
"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'},
|
|
|
|
|
},
|
2025-08-06 13:43:22 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
/// 隐患播报列表及状态
|
|
|
|
|
List<Map<String, dynamic>> hiddenList = [];
|
|
|
|
|
bool _initialLoadingHidden = true;
|
|
|
|
|
|
2025-07-11 11:03:21 +08:00
|
|
|
|
final List<List<String>> tagLabels = [
|
|
|
|
|
['我', '的'],
|
|
|
|
|
['部', '门'],
|
|
|
|
|
['监', '管'],
|
|
|
|
|
];
|
|
|
|
|
|
2025-08-06 13:43:22 +08:00
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
super.initState();
|
2025-08-19 11:06:16 +08:00
|
|
|
|
// 使用初始化加载:先恢复缓存(若存在则直接显示),然后再发起网络请求(成功则覆盖缓存)
|
|
|
|
|
_initialLoad();
|
|
|
|
|
BadgeManager().initAllModules();
|
|
|
|
|
}
|
2025-08-07 17:33:16 +08:00
|
|
|
|
|
2025-08-19 11:06:16 +08:00
|
|
|
|
/// 首次加载:先恢复缓存(如果有),然后在后台去刷新(只有当无缓存时才显示 loading)
|
|
|
|
|
Future<void> _initialLoad() async {
|
|
|
|
|
final result = await AuthService.checkUpdate();
|
|
|
|
|
if (FormUtils.hasValue(result, 'pd')) {
|
|
|
|
|
// 有更新 提示更新
|
|
|
|
|
Map pd = result['pd'];
|
|
|
|
|
CustomAlertDialog.showAlert(
|
|
|
|
|
context,
|
|
|
|
|
title: '更新通知',
|
|
|
|
|
content: pd['UPLOAD_CONTENT'] ?? '',
|
|
|
|
|
onConfirm: () {
|
|
|
|
|
ToastUtil.showNormal(context, '更新去吧!');
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
await _loadHiddenCache();
|
|
|
|
|
// 拉取其他数据 + 隐患列表(当 hiddenList 为空时显示 loading,否则不显示)
|
|
|
|
|
await _fetchData();
|
|
|
|
|
await _fetchHiddenList(showLoading: hiddenList.isEmpty);
|
2025-08-06 13:43:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-07-11 11:03:21 +08:00
|
|
|
|
Future<void> _onRefresh() async {
|
2025-08-07 17:33:16 +08:00
|
|
|
|
await BadgeManager().initAllModules();
|
2025-08-06 13:43:22 +08:00
|
|
|
|
await Future.wait([
|
|
|
|
|
_fetchData(),
|
2025-08-19 11:06:16 +08:00
|
|
|
|
// 下拉刷新时不展示 loading(因为用户主动触发),但仍会更新缓存(成功时)
|
2025-08-06 13:43:22 +08:00
|
|
|
|
_fetchHiddenList(showLoading: false),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-19 11:06:16 +08:00
|
|
|
|
/// 从 SharedPreferences 读取缓存并设置 hiddenList(如果存在)
|
|
|
|
|
Future<void> _loadHiddenCache() async {
|
|
|
|
|
try {
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
final jsonStr = prefs.getString(_hiddenCacheKey);
|
|
|
|
|
if (jsonStr != null && jsonStr.isNotEmpty) {
|
|
|
|
|
final parsed = jsonDecode(jsonStr) as List<dynamic>;
|
|
|
|
|
final list =
|
|
|
|
|
parsed.map((e) => Map<String, dynamic>.from(e as Map)).toList();
|
|
|
|
|
setState(() {
|
|
|
|
|
hiddenList = list;
|
|
|
|
|
_initialLoadingHidden = false; // 有缓存则不显示首次loading
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
// 无缓存:保持 _initialLoadingHidden = true(只在需要显示 loading 时 setState)
|
|
|
|
|
setState(() {
|
|
|
|
|
_initialLoadingHidden = true;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
debugPrint('加载 hidden cache 失败: $e');
|
|
|
|
|
// 出错时认为无缓存
|
|
|
|
|
setState(() {
|
|
|
|
|
_initialLoadingHidden = true;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 保存 hiddenList 到 SharedPreferences(调用时确保传入最新 list)
|
|
|
|
|
Future<void> _saveHiddenCache(List<Map<String, dynamic>> list) async {
|
|
|
|
|
try {
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
final jsonStr = jsonEncode(list);
|
|
|
|
|
await prefs.setString(_hiddenCacheKey, jsonStr);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
debugPrint('保存 hidden cache 失败: $e');
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-07 17:33:16 +08:00
|
|
|
|
|
2025-08-19 11:06:16 +08:00
|
|
|
|
/// 获取隐患播报列表(带缓存/加载控制逻辑)
|
2025-08-06 13:43:22 +08:00
|
|
|
|
Future<void> _fetchHiddenList({bool showLoading = true}) async {
|
2025-08-19 11:06:16 +08:00
|
|
|
|
// 仅在需要展示 loading 且当前没有数据时 setState 显示 loading indicator
|
|
|
|
|
if (showLoading && hiddenList.isEmpty) {
|
|
|
|
|
setState(() {
|
|
|
|
|
_initialLoadingHidden = true;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-06 13:43:22 +08:00
|
|
|
|
try {
|
2025-08-19 11:06:16 +08:00
|
|
|
|
final res = await ApiService.getHiddenRoll().timeout(
|
|
|
|
|
const Duration(seconds: 10),
|
|
|
|
|
);
|
2025-08-06 13:43:22 +08:00
|
|
|
|
final list = (res['hiddenList'] as List).cast<Map<String, dynamic>>();
|
2025-08-19 11:06:16 +08:00
|
|
|
|
|
|
|
|
|
// 请求成功:覆盖 UI 与缓存(始终覆盖)
|
2025-08-06 13:43:22 +08:00
|
|
|
|
setState(() {
|
|
|
|
|
hiddenList = list;
|
|
|
|
|
_initialLoadingHidden = false;
|
|
|
|
|
});
|
2025-08-19 11:06:16 +08:00
|
|
|
|
|
|
|
|
|
// 保存缓存(覆盖)
|
|
|
|
|
await _saveHiddenCache(list);
|
2025-08-06 13:43:22 +08:00
|
|
|
|
} catch (e) {
|
2025-08-19 11:06:16 +08:00
|
|
|
|
debugPrint('fetchHiddenList error: $e');
|
|
|
|
|
|
|
|
|
|
// 请求失败:如果已有缓存(hiddenList 非空),就保留缓存(什么也不做)
|
|
|
|
|
// 如果没有缓存(首次且失败),需要把 loading 关掉(避免一直转圈)
|
2025-08-06 13:43:22 +08:00
|
|
|
|
if (hiddenList.isEmpty) {
|
|
|
|
|
setState(() {
|
|
|
|
|
_initialLoadingHidden = false;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<void> _fetchData() async {
|
|
|
|
|
try {
|
2025-08-07 17:33:16 +08:00
|
|
|
|
// “我的工作”数据
|
2025-08-06 13:43:22 +08:00
|
|
|
|
final data = await ApiService.getWork();
|
|
|
|
|
final hidCount = data['hidCount'] as Map<String, dynamic>? ?? {};
|
|
|
|
|
|
2025-08-07 17:33:16 +08:00
|
|
|
|
// 告知BadgeManager去更新“安全巡检”和“八项作业”角标
|
|
|
|
|
BadgeManager().updateEnvInspectCount();
|
|
|
|
|
BadgeManager().updateEightWorkCount();
|
|
|
|
|
|
|
|
|
|
// 拉取其他数据
|
2025-08-06 13:43:22 +08:00
|
|
|
|
final results = await Future.wait([
|
|
|
|
|
ApiService.getUserData(),
|
|
|
|
|
ApiService.getDeptData(),
|
|
|
|
|
ApiService.getSuperviseDeptData(),
|
|
|
|
|
]);
|
2025-08-19 11:06:16 +08:00
|
|
|
|
final myData = results[0];
|
|
|
|
|
final deptData = results[1];
|
|
|
|
|
final superDeptData = results[2];
|
2025-08-06 13:43:22 +08:00
|
|
|
|
|
2025-08-07 17:33:16 +08:00
|
|
|
|
//更新页面状态
|
2025-08-06 13:43:22 +08:00
|
|
|
|
setState(() {
|
2025-08-07 17:33:16 +08:00
|
|
|
|
// ———— 更新 workInfos ————
|
2025-08-19 11:06:16 +08:00
|
|
|
|
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();
|
2025-08-06 13:43:22 +08:00
|
|
|
|
|
2025-08-07 17:33:16 +08:00
|
|
|
|
// ———— 从 BadgeManager 拿最新的本页角标 ————
|
|
|
|
|
_safetyEnvironmentalInspection = BadgeManager().envInspectCount;
|
2025-08-19 11:06:16 +08:00
|
|
|
|
_eight_work_count = BadgeManager().eightWorkCount;
|
2025-08-06 13:43:22 +08:00
|
|
|
|
|
2025-08-07 17:33:16 +08:00
|
|
|
|
// ———— 更新 buttonInfos 中两个角标位置 ————
|
2025-08-19 11:06:16 +08:00
|
|
|
|
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();
|
2025-08-06 13:43:22 +08:00
|
|
|
|
|
2025-08-07 17:33:16 +08:00
|
|
|
|
// ———— 更新排查数据 pcData ————
|
2025-08-19 11:06:16 +08:00
|
|
|
|
pcData =
|
|
|
|
|
[myData, deptData, superDeptData].asMap().entries.map((entry) {
|
|
|
|
|
final idx = entry.key;
|
|
|
|
|
final pdMap = (entry.value['pd'] as Map<String, dynamic>?) ?? {};
|
|
|
|
|
return {
|
|
|
|
|
'index': idx + 1,
|
|
|
|
|
'detail': {
|
|
|
|
|
'jiancha': pdMap['check_count']?.toString() ?? '0',
|
|
|
|
|
'yinhuan': pdMap['hidden_count']?.toString() ?? '0',
|
|
|
|
|
'yanshou': pdMap['rectify_count']?.toString() ?? '0',
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}).toList();
|
2025-08-06 13:43:22 +08:00
|
|
|
|
});
|
|
|
|
|
} catch (e) {
|
2025-08-07 17:33:16 +08:00
|
|
|
|
debugPrint('加载首页数据失败:$e');
|
2025-08-06 13:43:22 +08:00
|
|
|
|
}
|
2025-07-11 11:03:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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),
|
2025-07-22 13:34:34 +08:00
|
|
|
|
Container(
|
2025-08-08 10:52:15 +08:00
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
borderRadius: BorderRadius.circular(5),
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
),
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
ListItemFactory.createBuildSimpleSection("隐患播报"),
|
2025-08-19 11:06:16 +08:00
|
|
|
|
hiddenList.isEmpty
|
2025-08-08 10:52:15 +08:00
|
|
|
|
? SizedBox(
|
2025-08-19 11:06:16 +08:00
|
|
|
|
height: 150,
|
|
|
|
|
child: Center(child: CircularProgressIndicator()),
|
|
|
|
|
)
|
2025-08-08 10:52:15 +08:00
|
|
|
|
: HiddenRollWidget(hiddenList: hiddenList),
|
|
|
|
|
],
|
2025-08-19 11:06:16 +08:00
|
|
|
|
),
|
2025-07-22 13:34:34 +08:00
|
|
|
|
),
|
2025-07-11 11:03:21 +08:00
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
|
_buildPCDataSection(),
|
2025-07-22 13:34:34 +08:00
|
|
|
|
SizedBox(height: 20),
|
2025-07-11 11:03:21 +08:00
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
2025-08-19 11:06:16 +08:00
|
|
|
|
children:
|
|
|
|
|
workInfos.map((info) {
|
|
|
|
|
return SizedBox(
|
|
|
|
|
width: itemWidth,
|
|
|
|
|
child: _buildGridItem(
|
|
|
|
|
context,
|
|
|
|
|
icon: info["icon"]!,
|
|
|
|
|
title: info["num"]!,
|
|
|
|
|
subtitle: info["detail"]!,
|
|
|
|
|
index: info["index"]!,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}).toList(),
|
2025-07-11 11:03:21 +08:00
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget _buildPCDataSection() {
|
|
|
|
|
return Container(
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
borderRadius: BorderRadius.circular(8),
|
|
|
|
|
),
|
|
|
|
|
child: Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
|
|
children: [
|
|
|
|
|
ListItemFactory.createBuildSimpleSection("排查数据"),
|
|
|
|
|
...pcData.map(_widgetPCDataItem),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-19 11:06:16 +08:00
|
|
|
|
double _itemWidth(BuildContext context) =>
|
|
|
|
|
(MediaQuery.of(context).size.width - 20) / 4;
|
2025-07-15 08:32:50 +08:00
|
|
|
|
|
|
|
|
|
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,
|
2025-08-19 11:06:16 +08:00
|
|
|
|
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 6: //安全承诺
|
|
|
|
|
pushPage(SafetyCommitmentPage(), context);
|
|
|
|
|
break;
|
|
|
|
|
case 7:
|
|
|
|
|
pushPage(StudyGardenPage(), context);
|
|
|
|
|
break;
|
|
|
|
|
case 9:
|
|
|
|
|
pushPage(EquipmentInspectionListPage(), context);
|
|
|
|
|
break;
|
|
|
|
|
case 8: // 安全检查
|
|
|
|
|
await pushPage(SafecheckTabList(), context);
|
|
|
|
|
break;
|
|
|
|
|
case 10:
|
|
|
|
|
pushPage(SafetyMeetingListPage(), context);
|
|
|
|
|
break;
|
|
|
|
|
case 11:
|
|
|
|
|
pushPage(HomeNfcListPage(), context);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_onRefresh();
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}).toList(),
|
2025-07-15 08:32:50 +08:00
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-11 11:03:21 +08:00
|
|
|
|
Widget _buildIconButton({
|
2025-07-15 08:32:50 +08:00
|
|
|
|
required BuildContext context,
|
|
|
|
|
required String iconPath,
|
2025-07-11 11:03:21 +08:00
|
|
|
|
required String label,
|
2025-07-15 08:32:50 +08:00
|
|
|
|
required VoidCallback onPressed,
|
|
|
|
|
int unreadCount = 0,
|
2025-07-11 11:03:21 +08:00
|
|
|
|
}) {
|
2025-07-15 08:32:50 +08:00
|
|
|
|
final w = _itemWidth(context);
|
2025-07-11 11:03:21 +08:00
|
|
|
|
return InkWell(
|
2025-07-15 08:32:50 +08:00
|
|
|
|
onTap: onPressed,
|
2025-07-11 11:03:21 +08:00
|
|
|
|
borderRadius: BorderRadius.circular(8),
|
2025-07-15 08:32:50 +08:00
|
|
|
|
child: SizedBox(
|
|
|
|
|
width: w,
|
|
|
|
|
child: Stack(
|
|
|
|
|
clipBehavior: Clip.none,
|
2025-07-11 11:03:21 +08:00
|
|
|
|
children: [
|
2025-07-15 08:32:50 +08:00
|
|
|
|
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(
|
2025-07-24 09:38:06 +08:00
|
|
|
|
right: 20,
|
|
|
|
|
top: -5,
|
2025-07-15 08:32:50 +08:00
|
|
|
|
child: Container(
|
2025-08-19 11:06:16 +08:00
|
|
|
|
padding: const EdgeInsets.symmetric(
|
|
|
|
|
horizontal: 4,
|
|
|
|
|
vertical: 2,
|
|
|
|
|
),
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
color: Colors.red,
|
|
|
|
|
borderRadius: BorderRadius.circular(10),
|
|
|
|
|
),
|
|
|
|
|
constraints: const BoxConstraints(
|
|
|
|
|
minWidth: 16,
|
|
|
|
|
minHeight: 16,
|
|
|
|
|
),
|
2025-08-06 13:43:22 +08:00
|
|
|
|
child: Center(
|
2025-08-19 11:06:16 +08:00
|
|
|
|
child: Text(
|
2025-08-06 13:43:22 +08:00
|
|
|
|
unreadCount > 99 ? '99+' : '$unreadCount',
|
2025-08-19 11:06:16 +08:00
|
|
|
|
style: const TextStyle(
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
fontSize: 11,
|
|
|
|
|
height: 1,
|
|
|
|
|
),
|
2025-08-06 13:43:22 +08:00
|
|
|
|
textAlign: TextAlign.center,
|
2025-07-15 08:32:50 +08:00
|
|
|
|
),
|
2025-08-19 11:06:16 +08:00
|
|
|
|
),
|
2025-07-15 08:32:50 +08:00
|
|
|
|
),
|
|
|
|
|
),
|
2025-07-11 11:03:21 +08:00
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget _widgetPCDataItem(Map<String, dynamic> item) {
|
|
|
|
|
final detail = item["detail"] as Map<String, String>;
|
|
|
|
|
final values = [detail["jiancha"]!, detail["yinhuan"]!, detail["yanshou"]!];
|
|
|
|
|
const labels = ["检查数", "发现隐患数", "已验收隐患数"];
|
|
|
|
|
const colors = [Color(0xFF3283FF), Color(0xFFF37B1D), Color(0xFF41B852)];
|
2025-08-06 13:43:22 +08:00
|
|
|
|
final idx = int.tryParse(item["index"].toString())! - 1;
|
2025-07-11 11:03:21 +08:00
|
|
|
|
|
|
|
|
|
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: [
|
2025-08-19 11:06:16 +08:00
|
|
|
|
Text(
|
|
|
|
|
values[i],
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
color: colors[i],
|
|
|
|
|
fontSize: 18,
|
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
|
),
|
|
|
|
|
),
|
2025-07-11 11:03:21 +08:00
|
|
|
|
const SizedBox(height: 4),
|
2025-08-19 11:06:16 +08:00
|
|
|
|
Text(
|
|
|
|
|
labels[i],
|
|
|
|
|
style: TextStyle(color: Colors.black38, fontSize: 15),
|
|
|
|
|
),
|
2025-07-11 11:03:21 +08:00
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
Container(
|
|
|
|
|
color: idx == 0 ? Colors.red : Colors.blue,
|
|
|
|
|
width: 20,
|
|
|
|
|
height: 40,
|
|
|
|
|
child: Column(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
|
children: [
|
2025-08-19 11:06:16 +08:00
|
|
|
|
Text(
|
|
|
|
|
tagLabels[idx][0],
|
|
|
|
|
style: TextStyle(color: Colors.white, fontSize: 12),
|
|
|
|
|
),
|
|
|
|
|
Text(
|
|
|
|
|
tagLabels[idx][1],
|
|
|
|
|
style: TextStyle(color: Colors.white, fontSize: 12),
|
|
|
|
|
),
|
2025-07-11 11:03:21 +08:00
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
SizedBox(height: 10),
|
|
|
|
|
Divider(height: 1, color: Colors.black12),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-19 11:06:16 +08:00
|
|
|
|
Widget _buildGridItem(
|
|
|
|
|
BuildContext context, {
|
|
|
|
|
required String icon,
|
|
|
|
|
required String title,
|
|
|
|
|
required String subtitle,
|
|
|
|
|
required int index,
|
|
|
|
|
}) {
|
2025-07-11 11:03:21 +08:00
|
|
|
|
return GestureDetector(
|
|
|
|
|
onTap: () {
|
|
|
|
|
if (index == 1) {
|
2025-08-06 09:19:51 +08:00
|
|
|
|
pushPage(HomeDangerPage(), context);
|
2025-07-11 11:03:21 +08:00
|
|
|
|
} else if (index == 2) {
|
2025-07-22 13:34:34 +08:00
|
|
|
|
pushPage(DangerWaitListPage(DangerType.wait, 2), context);
|
2025-07-11 11:03:21 +08:00
|
|
|
|
} else if (index == 3) {
|
2025-07-22 13:34:34 +08:00
|
|
|
|
pushPage(DangerWaitListPage(DangerType.expired, 3), context);
|
2025-07-15 08:32:50 +08:00
|
|
|
|
} else if (index == 4) {
|
2025-07-22 13:34:34 +08:00
|
|
|
|
pushPage(DangerWaitListPage(DangerType.waitAcceptance, 4), context);
|
2025-07-15 08:32:50 +08:00
|
|
|
|
} else if (index == 5) {
|
2025-07-22 13:34:34 +08:00
|
|
|
|
pushPage(DangerWaitListPage(DangerType.acceptance, 5), context);
|
2025-07-11 11:03:21 +08:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
child: Container(
|
|
|
|
|
padding: const EdgeInsets.all(8),
|
2025-08-19 11:06:16 +08:00
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
color: const Color(0xfbf9f9ff),
|
|
|
|
|
borderRadius: BorderRadius.circular(6),
|
|
|
|
|
),
|
2025-07-11 11:03:21 +08:00
|
|
|
|
child: Row(
|
|
|
|
|
children: [
|
|
|
|
|
Image.asset(icon, width: 35, height: 35),
|
2025-07-28 14:22:07 +08:00
|
|
|
|
const SizedBox(width: 5),
|
2025-07-11 11:03:21 +08:00
|
|
|
|
Expanded(
|
|
|
|
|
child: Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: [
|
2025-08-19 11:06:16 +08:00
|
|
|
|
Text(
|
|
|
|
|
title,
|
|
|
|
|
style: const TextStyle(
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
|
),
|
|
|
|
|
maxLines: 1,
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
),
|
2025-07-11 11:03:21 +08:00
|
|
|
|
const SizedBox(height: 4),
|
2025-08-19 11:06:16 +08:00
|
|
|
|
Text(
|
|
|
|
|
subtitle,
|
|
|
|
|
style: const TextStyle(fontSize: 14, color: Colors.black),
|
|
|
|
|
maxLines: 1,
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
),
|
2025-07-11 11:03:21 +08:00
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|