QinGang_interested/lib/pages/home/home_page.dart

1128 lines
37 KiB
Dart
Raw Normal View History

2026-03-12 10:23:21 +08:00
// home_page.dart (适配新菜单配置)
2025-12-12 09:11:30 +08:00
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:qhd_prevention/common/route_aware_state.dart';
import 'package:qhd_prevention/common/route_service.dart';
2026-02-28 14:38:07 +08:00
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
2025-12-12 09:11:30 +08:00
import 'package:qhd_prevention/customWidget/custom_button.dart';
2026-03-12 10:23:21 +08:00
import 'package:qhd_prevention/customWidget/toast_util.dart';
2026-02-28 14:38:07 +08:00
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/pages/home/Study/study_tab_list_page.dart';
2026-04-14 16:24:31 +08:00
import 'package:qhd_prevention/pages/home/Tap/work_tab_list_page.dart';
2026-03-25 16:09:17 +08:00
import 'package:qhd_prevention/pages/home/doorAndCar/doorCar_tab_page.dart';
2026-04-10 17:25:59 +08:00
import 'package:qhd_prevention/pages/home/keyTasks/key_tasks_tab_page.dart';
2025-12-12 09:11:30 +08:00
import 'package:qhd_prevention/pages/home/scan_page.dart';
import 'package:qhd_prevention/pages/home/unit/unit_tab_page.dart';
import 'package:qhd_prevention/pages/main_tab.dart';
2026-02-28 14:38:07 +08:00
import 'package:qhd_prevention/pages/mine/onboarding_full_page.dart';
2025-12-24 16:07:53 +08:00
import 'package:qhd_prevention/pages/user/choose_userFirm_page.dart';
2025-12-12 09:11:30 +08:00
import 'package:qhd_prevention/pages/user/firm_list_page.dart';
2026-02-28 14:38:07 +08:00
import 'package:qhd_prevention/services/auth_service.dart';
2026-03-12 10:23:21 +08:00
import 'package:qhd_prevention/services/scan_service.dart';
2025-12-12 09:11:30 +08:00
import 'package:qhd_prevention/tools/h_colors.dart';
import 'package:qhd_prevention/tools/tools.dart';
import 'package:shared_preferences/shared_preferences.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key, required this.isChooseFirm}) : super(key: key);
final bool isChooseFirm;
@override
HomePageState createState() => HomePageState();
}
class HomePageState extends RouteAwareState<HomePage>
with WidgetsBindingObserver, SingleTickerProviderStateMixin {
final PageController _pageController = PageController();
final ScrollController _scrollController = ScrollController();
2026-03-12 10:23:21 +08:00
bool _isShowCheckLogin = false;
2025-12-12 09:11:30 +08:00
int _currentPage = 0;
2026-03-12 10:23:21 +08:00
bool _isMobileSelected = true;
2025-12-12 09:11:30 +08:00
2026-02-28 14:38:07 +08:00
void startScan() async {
2026-04-23 17:49:42 +08:00
final result = await pushPage(ScanPage(type: ScanType.Onboarding), context);
2026-03-12 10:23:21 +08:00
if (result == null) return;
ScanService.scan(context, result);
2025-12-12 09:11:30 +08:00
}
static const String _hiddenCacheKey = 'hidden_roll_cache';
2026-03-12 10:23:21 +08:00
// 上面按钮显示状态(与 buttonInfos 顺序对应)
2025-12-12 09:11:30 +08:00
List<bool> _buttonVisibility = [];
2026-03-12 10:23:21 +08:00
// 页面模块可见性(由路由驱动)
bool _showNotificationBar = true;
bool _showWorkStats = true;
bool _showCheckList = true;
2025-12-12 09:11:30 +08:00
List totalList = [];
2026-03-25 16:09:17 +08:00
// 工作统计(示例)
Map<String, int> workStats = {'total': 0, 'processed': 0, 'pending': 0};
// 检查清单示例
List<dynamic> checkLists = [];
2026-04-23 17:49:42 +08:00
2026-03-25 16:09:17 +08:00
// List<Map<String, dynamic>> checkLists = [
// {
// "title": "电工班车间清单",
// "type": "隐患排查",
// "time": "2025-11-17",
// "color": Color(0xFF4CAF50),
// },
// {
// "title": "工地区消防点检清单",
// "type": "消防点检",
// "time": "2025-11-17",
// "color": Color(0xFF2196F3),
// },
// {
// "title": "消防专项检查清单",
// "type": "安环检查",
// "time": "2025-11-17",
// "color": Color(0xFFFF9800),
// },
// ];
2025-12-12 09:11:30 +08:00
final List<String> _notifications = [
"系统通知今晚20:00 将进行系统维护,请提前保存数据。",
"安全提示:施工区请佩戴安全帽并系好安全带。",
"公告本周五集团例会在多功能厅召开9:00准时开始。",
"提醒:请尽快完成隐患整改清单中的待办项。",
];
int _notifIndex = 0;
late final PageController _notifPageController;
Timer? _notifTimer;
2026-03-12 10:23:21 +08:00
// 图标按钮定义(顺序固定)
2025-12-12 09:11:30 +08:00
List<Map<String, dynamic>> buttonInfos = [
{"icon": "assets/images/ico1.png", "title": "单位管理", "unreadCount": 0},
{"icon": "assets/images/ico2.png", "title": "现场监管", "unreadCount": 0},
{"icon": "assets/images/ico3.png", "title": "危险作业", "unreadCount": 0},
2026-04-23 17:49:42 +08:00
{"icon": "assets/images/ico4.png", "title": "隐患治理", "unreadCount": 0},
// 原“隐患处理”改为“隐患治理”
2026-03-12 10:23:21 +08:00
{"icon": "assets/images/ico5.png", "title": "重点作业", "unreadCount": 0},
2025-12-12 09:11:30 +08:00
{"icon": "assets/images/ico6.png", "title": "口门门禁", "unreadCount": 0},
{"icon": "assets/images/ico7.png", "title": "入港培训", "unreadCount": 0},
];
2026-03-12 10:23:21 +08:00
// 浮动 AppBar
2025-12-12 09:11:30 +08:00
bool _showFloatingAppBar = false;
2026-03-12 10:23:21 +08:00
static const double _triggerOffset = 30.0;
2025-12-12 09:11:30 +08:00
static const double _floatingBarHeight = 56.0;
2026-03-12 10:23:21 +08:00
// 按钮标题 -> 权限标识映射(基于新菜单配置)
final Map<String, String> _titleToPerm = {
"单位管理": "dashboard-Unit-Management",
"现场监管": "dashboard-Site-Supervision",
2026-04-23 17:49:42 +08:00
"危险作业": "dashboard-Hazardous-Work",
2026-03-12 10:23:21 +08:00
"隐患治理": "dashboard-Hazard-Management",
2026-04-10 17:25:59 +08:00
"重点作业": "dashboard-Hazard-Management", // 无对应,暂时留空
2026-03-12 10:23:21 +08:00
"口门门禁": "dashboard-Gate-Access-Control",
"入港培训": "dashboard-Study-Training",
};
// 模块权限映射(基于新菜单配置)
final Map<String, String> _modulePerms = {
"notification": "dashboard-roll-notice",
"todoStats": "dashboard-todo-sort",
"checklist": "dashboard-todo-list",
"scan": "dashboard-scan",
2026-04-01 17:53:42 +08:00
"joinFirm": "dashboard-start-work",
2026-03-12 10:23:21 +08:00
};
2026-02-28 14:38:07 +08:00
2026-04-23 17:49:42 +08:00
int pcType = 1;
2026-03-25 16:09:17 +08:00
2025-12-12 09:11:30 +08:00
@override
void initState() {
super.initState();
2026-02-28 14:38:07 +08:00
_isShowCheckLogin = widget.isChooseFirm;
2025-12-12 09:11:30 +08:00
2026-03-12 10:23:21 +08:00
// 初始按钮全隐藏,避免闪烁
_buttonVisibility = List<bool>.filled(buttonInfos.length, false);
2025-12-12 09:11:30 +08:00
2026-03-12 10:23:21 +08:00
_notifPageController = PageController(initialPage: 0);
2025-12-12 09:11:30 +08:00
_notifTimer = Timer.periodic(const Duration(seconds: 3), (timer) {
if (!mounted) return;
final next = (_notifIndex + 1) % _notifications.length;
_notifIndex = next;
try {
_notifPageController.animateToPage(
next,
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
);
} catch (_) {}
2026-02-28 14:38:07 +08:00
_getNeedSafetyCommitment();
2025-12-12 09:11:30 +08:00
setState(() {});
});
_scrollController.addListener(_onScroll);
WidgetsBinding.instance.addObserver(this);
2026-03-12 10:23:21 +08:00
RouteService().addListener(onRouteConfigLoaded);
Future.microtask(() {
_updateModuleAndButtonVisibility();
2025-12-12 09:11:30 +08:00
});
2026-04-23 17:49:42 +08:00
if (_isShowCheckLogin) {
2026-03-25 16:09:17 +08:00
_getToDoWorkList(pcType);
}
2026-03-12 10:23:21 +08:00
}
@override
void dispose() {
_notifTimer?.cancel();
_notifPageController.dispose();
_pageController.dispose();
_scrollController.removeListener(_onScroll);
_scrollController.dispose();
WidgetsBinding.instance.removeObserver(this);
try {
RouteService().removeListener(onRouteConfigLoaded);
} catch (_) {}
super.dispose();
}
2025-12-12 09:11:30 +08:00
2026-03-12 10:23:21 +08:00
void onRouteConfigLoaded() {
if (!mounted) return;
_updateModuleAndButtonVisibility();
2026-03-05 16:12:47 +08:00
}
2026-03-06 16:15:20 +08:00
2026-03-12 10:23:21 +08:00
/// 根据路由配置更新按钮和模块可见性
void _updateModuleAndButtonVisibility() {
final routeService = RouteService();
final mainTabs = routeService.mainTabs;
if (mainTabs.isEmpty) {
// 路由未加载,保持全隐藏
2026-02-28 14:38:07 +08:00
return;
}
2026-03-12 10:23:21 +08:00
// 更新按钮可见性
2026-04-23 17:49:42 +08:00
final List<bool> newVisibility = List<bool>.filled(
buttonInfos.length,
false,
);
2026-03-12 10:23:21 +08:00
for (int i = 0; i < buttonInfos.length; i++) {
final title = buttonInfos[i]['title'] as String;
final perm = _titleToPerm[title];
if (perm == null || perm.isEmpty) continue;
try {
newVisibility[i] = routeService.hasPerm(perm);
} catch (e) {
debugPrint('权限检查异常: $perm, $e');
}
}
// 更新模块可见性
final bool showNotif = routeService.hasPerm(_modulePerms['notification']!);
final bool showTodo = routeService.hasPerm(_modulePerms['todoStats']!);
final bool showChecklist = routeService.hasPerm(_modulePerms['checklist']!);
setState(() {
_buttonVisibility = newVisibility;
_showNotificationBar = showNotif;
_showWorkStats = showTodo;
_showCheckList = showChecklist;
});
}
Future<void> _getNeedSafetyCommitment() async {
if (_isShowCheckLogin) return;
2026-02-28 14:38:07 +08:00
final prefs = await SharedPreferences.getInstance();
final phone = prefs.getString('savePhone') ?? '';
final pwd = prefs.getString('savePass') ?? '';
final result = await AuthApi.userLoginCheckFirm({'phone': phone});
if (result['success']) {
final resData = result['data'];
List firmList = resData['userCorpInfoCOList'] ?? [];
if (firmList.isNotEmpty) {
_isShowCheckLogin = true;
CustomAlertDialog.showAlert(
context,
title: '温馨提示',
content: '您的入职申请已通过',
confirmText: '立即入职',
onConfirm: () async {
Map data = {'unitId': firmList.first['corpinfoId'] ?? ''};
final res = await AuthService.gbsLogin(phone, pwd, data);
LoadingDialogHelper.hide();
if (res['success'] == true) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => const MainPage(isChooseFirm: true),
),
);
}
},
);
} else {
_isShowCheckLogin = false;
}
}
}
2025-12-12 09:11:30 +08:00
void _onScroll() {
2026-04-23 17:49:42 +08:00
final offset =
_scrollController.hasClients ? _scrollController.offset : 0.0;
2025-12-12 09:11:30 +08:00
final shouldShow = offset >= _triggerOffset;
if (shouldShow != _showFloatingAppBar) {
2026-03-12 10:23:21 +08:00
setState(() => _showFloatingAppBar = shouldShow);
2025-12-12 09:11:30 +08:00
}
}
@override
Future<void> onVisible() async {
final current = CurrentTabNotifier.of(context)?.currentIndex ?? 0;
const myIndex = 0;
2026-03-12 10:23:21 +08:00
if (current != myIndex) return;
// 可在此刷新角标等
2025-12-12 09:11:30 +08:00
}
2026-03-12 10:23:21 +08:00
Future<void> _onRefresh() async {
await Future.delayed(const Duration(seconds: 1));
2025-12-12 09:11:30 +08:00
}
@override
2026-03-12 10:23:21 +08:00
Widget build(BuildContext context) {
const double notificationHeightConst = 60.0;
double bannerHeight = 738 / 1125 * screenWidth(context);
2025-12-12 09:11:30 +08:00
2026-03-12 10:23:21 +08:00
// 计算可见按钮列表
final visibleButtons = <Map<String, dynamic>>[];
for (int i = 0; i < buttonInfos.length; i++) {
if (i < _buttonVisibility.length && _buttonVisibility[i]) {
visibleButtons.add(buttonInfos[i]);
2025-12-12 09:11:30 +08:00
}
}
2026-03-12 10:23:21 +08:00
final double iconSectionHeight =
2026-04-23 17:49:42 +08:00
visibleButtons.isEmpty
? 0.0
: (visibleButtons.length <= 4 ? 120.0 : 220.0);
2025-12-12 09:11:30 +08:00
2026-03-12 10:23:21 +08:00
final double notificationHeight =
2026-04-23 17:49:42 +08:00
(_showNotificationBar && iconSectionHeight >= 0)
? notificationHeightConst
: 0.0;
2025-12-12 09:11:30 +08:00
2026-03-12 10:23:21 +08:00
final double extraSpacing = _showNotificationBar ? 60.0 : 12.0;
2025-12-12 09:11:30 +08:00
final double stackBottom =
2026-04-23 17:49:42 +08:00
bannerHeight -
(iconSectionHeight * 0.4) +
iconSectionHeight +
extraSpacing;
2025-12-12 09:11:30 +08:00
final double statusBar = MediaQuery.of(context).padding.top;
2026-03-12 10:23:21 +08:00
final topPadding = Platform.isIOS ? 0 : 14;
2025-12-12 09:11:30 +08:00
return PopScope(
canPop: false,
child: Scaffold(
extendBodyBehindAppBar: true,
body: Stack(
children: [
RefreshIndicator(
2026-03-12 10:23:21 +08:00
backgroundColor: Colors.white,
color: Colors.blue,
2025-12-12 09:11:30 +08:00
onRefresh: _onRefresh,
child: ListView(
controller: _scrollController,
physics: const AlwaysScrollableScrollPhysics(),
padding: const EdgeInsets.all(0),
children: [
SizedBox(
height: stackBottom,
child: Stack(
clipBehavior: Clip.none,
children: [
2026-03-12 10:23:21 +08:00
// Banner
2025-12-12 09:11:30 +08:00
Positioned(
top: 0,
left: 0,
right: 0,
height: bannerHeight,
child: _buildBannerSection(bannerHeight),
),
2026-03-12 10:23:21 +08:00
// 通知栏
if (_showNotificationBar)
Positioned(
left: 10,
right: 10,
2026-04-23 17:49:42 +08:00
top:
(bannerHeight - (iconSectionHeight * 0.4)) +
iconSectionHeight -
10,
2026-03-12 10:23:21 +08:00
height: notificationHeight,
2026-04-23 17:49:42 +08:00
child: _buildNotificationBar(
notificationHeight - 2,
),
2026-03-12 10:23:21 +08:00
),
2025-12-12 09:11:30 +08:00
2026-03-12 10:23:21 +08:00
// 图标区
2025-12-12 09:11:30 +08:00
Positioned(
left: 10,
right: 10,
2026-03-12 10:23:21 +08:00
top: bannerHeight - (iconSectionHeight * 0.4),
2025-12-12 09:11:30 +08:00
height: iconSectionHeight,
2026-04-23 17:49:42 +08:00
child: _buildIconSection(
context,
visibleButtons,
iconSectionHeight,
),
2025-12-12 09:11:30 +08:00
),
],
),
),
2026-03-12 10:23:21 +08:00
if (widget.isChooseFirm && _showWorkStats) ...[
2025-12-12 09:11:30 +08:00
Padding(
2026-04-23 17:49:42 +08:00
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 10,
),
2025-12-12 09:11:30 +08:00
child: _buildWorkStatsSection(),
),
2026-03-12 10:23:21 +08:00
],
2025-12-12 09:11:30 +08:00
2026-03-12 10:23:21 +08:00
if (widget.isChooseFirm && _showCheckList) ...[
2025-12-12 09:11:30 +08:00
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: _buildCheckListSection(),
),
const SizedBox(height: 20),
2026-02-28 14:38:07 +08:00
],
2025-12-12 09:11:30 +08:00
],
),
),
2026-03-12 10:23:21 +08:00
// 浮动 AppBar
2025-12-12 09:11:30 +08:00
Positioned(
top: 0,
left: 0,
right: 0,
child: AnimatedContainer(
duration: const Duration(milliseconds: 0),
2026-04-23 17:49:42 +08:00
height:
_showFloatingAppBar ? (statusBar + _floatingBarHeight) : 0,
2025-12-12 09:11:30 +08:00
decoration: BoxDecoration(
2026-04-23 17:49:42 +08:00
color:
_showFloatingAppBar
? h_AppBarColor()
: Colors.transparent,
boxShadow:
_showFloatingAppBar
? [
BoxShadow(
color: Colors.black.withOpacity(0.08),
blurRadius: 6,
),
]
: [],
2025-12-12 09:11:30 +08:00
),
child: SafeArea(
bottom: false,
child: Opacity(
opacity: _showFloatingAppBar ? 1.0 : 0.0,
2026-04-23 17:49:42 +08:00
child: SizedBox(
height: _floatingBarHeight,
child: const SizedBox(),
),
2025-12-12 09:11:30 +08:00
),
),
),
),
2026-03-12 10:23:21 +08:00
// 顶部中央标题
2025-12-12 09:11:30 +08:00
Positioned(
2026-03-12 10:23:21 +08:00
top: statusBar + topPadding,
2025-12-12 09:11:30 +08:00
width: screenWidth(context),
2026-02-28 14:38:07 +08:00
child: Center(
child: Text(
'秦港-相关方安全管理',
2026-04-23 17:49:42 +08:00
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.w600,
color: Colors.white,
),
2025-12-12 09:11:30 +08:00
),
2026-02-28 14:38:07 +08:00
),
2025-12-12 09:11:30 +08:00
),
2026-03-12 10:23:21 +08:00
// 右上角图标(扫码、加入企业)
2025-12-12 09:11:30 +08:00
_buildFixedTopIcons(context),
],
),
),
);
}
2025-12-24 16:07:53 +08:00
Future<void> _joinFirm() async {
2026-02-28 14:38:07 +08:00
pushPage(FirmListPage(isBack: true), context);
2025-12-24 16:07:53 +08:00
}
2025-12-12 09:11:30 +08:00
Widget _buildFixedTopIcons(BuildContext context) {
final double statusBar = MediaQuery.of(context).padding.top;
final double topOffset = statusBar + 12;
2026-03-12 10:23:21 +08:00
final routeService = RouteService();
final bool showScan = routeService.hasPerm(_modulePerms['scan']!);
2026-04-01 17:53:42 +08:00
final bool showJoin = routeService.hasPerm(_modulePerms['joinFirm']!);
2026-03-12 10:23:21 +08:00
final List<Widget> children = [];
if (showScan) {
children.add(
GestureDetector(
onTap: startScan,
child: Container(
width: 30,
height: 30,
alignment: Alignment.center,
2026-04-23 17:49:42 +08:00
child: Image.asset(
"assets/icon-apps/home_saoyisao.png",
width: 20,
height: 20,
),
2026-03-12 10:23:21 +08:00
),
),
);
}
if (showJoin) {
if (children.isNotEmpty) children.add(const SizedBox(width: 8));
children.add(
GestureDetector(
onTap: _joinFirm,
child: Container(
width: 30,
height: 30,
alignment: Alignment.center,
2026-04-23 17:49:42 +08:00
child: Image.asset(
"assets/icon-apps/home_add.png",
width: 20,
height: 20,
),
2026-03-12 10:23:21 +08:00
),
),
);
}
2026-04-23 17:49:42 +08:00
return Positioned(top: topOffset, right: 8, child: Row(children: children));
2025-12-12 09:11:30 +08:00
}
Widget _buildNotificationBar(double notificationHeight) {
return Material(
color: Colors.transparent,
child: Container(
height: notificationHeight,
decoration: BoxDecoration(
2026-03-12 10:23:21 +08:00
color: const Color(0xFFE6F5FF),
2025-12-12 09:11:30 +08:00
borderRadius: BorderRadius.circular(5),
2026-03-12 10:23:21 +08:00
border: Border.all(color: Colors.white, width: 1),
2025-12-12 09:11:30 +08:00
boxShadow: [
2026-04-23 17:49:42 +08:00
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 6,
offset: const Offset(0, 2),
),
2025-12-12 09:11:30 +08:00
],
),
2026-03-12 10:23:21 +08:00
child: Row(
2025-12-12 09:11:30 +08:00
children: [
2026-03-12 10:23:21 +08:00
const SizedBox(width: 12),
Image.asset('assets/images/ico8.png', width: 30, height: 25),
const SizedBox(width: 12),
Expanded(
child: SizedBox(
height: notificationHeight,
child: PageView.builder(
controller: _notifPageController,
scrollDirection: Axis.vertical,
physics: const NeverScrollableScrollPhysics(),
itemCount: _notifications.length,
itemBuilder: (context, index) {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Text(
_notifications[index],
maxLines: 1,
overflow: TextOverflow.ellipsis,
2026-04-23 17:49:42 +08:00
style: const TextStyle(
color: Colors.black87,
fontSize: 14,
),
2026-03-12 10:23:21 +08:00
),
),
);
},
2025-12-12 09:11:30 +08:00
),
2026-03-12 10:23:21 +08:00
),
2025-12-12 09:11:30 +08:00
),
2026-03-12 10:23:21 +08:00
const Icon(Icons.chevron_right, color: Colors.black26),
const SizedBox(width: 8),
2025-12-12 09:11:30 +08:00
],
),
),
);
}
Widget _buildBannerSection(double bannerHeight) {
2026-03-12 10:23:21 +08:00
return Image.asset(
"assets/images/banner.png",
width: MediaQuery.of(context).size.width,
height: bannerHeight,
fit: BoxFit.cover,
2025-12-12 09:11:30 +08:00
);
}
2026-04-23 17:49:42 +08:00
Widget _buildIconSection(
BuildContext context,
List<Map<String, dynamic>> visibleButtons,
double height,
) {
2026-03-12 10:23:21 +08:00
if (RouteService().mainTabs.isNotEmpty && visibleButtons.isEmpty) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
2026-04-23 17:49:42 +08:00
boxShadow: const [
BoxShadow(
color: Colors.black12,
blurRadius: 6,
offset: Offset(0, 2),
),
],
2026-03-12 10:23:21 +08:00
),
child: const Center(child: Text('暂无权限访问的功能')),
);
}
final hasSecondRow = visibleButtons.length > 4;
final double containerMinHeight = height > 0 ? height : 120.0;
2025-12-12 09:11:30 +08:00
return Container(
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 5),
2026-03-12 10:23:21 +08:00
constraints: BoxConstraints(minHeight: containerMinHeight),
2025-12-12 09:11:30 +08:00
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
2026-04-23 17:49:42 +08:00
boxShadow: const [
BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2)),
2025-12-12 09:11:30 +08:00
],
),
2026-04-23 17:49:42 +08:00
child:
widget.isChooseFirm
? Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: List.generate(4, (i) {
if (i < visibleButtons.length) {
return Expanded(
child: Center(
child: _buildIconButton(visibleButtons[i], context),
),
);
} else {
return const Expanded(child: SizedBox());
}
}),
),
if (hasSecondRow) const SizedBox(height: 20),
if (hasSecondRow)
Row(
children: List.generate(4, (i) {
final idx = 4 + i;
if (idx < visibleButtons.length) {
return Expanded(
child: Center(
child: _buildIconButton(
visibleButtons[idx],
context,
),
),
);
} else {
return const Expanded(child: SizedBox());
}
}),
),
],
)
: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'assets/images/ico1.png',
width: 100,
height: 100,
),
const SizedBox(height: 10),
CustomButton(
text: '点击入职企业',
onPressed:
() => pushPage(FirmListPage(isBack: true), context),
),
],
),
),
2025-12-12 09:11:30 +08:00
);
}
Widget _buildIconButton(Map<String, dynamic> info, BuildContext context) {
2026-04-23 17:49:42 +08:00
final int badgeNum = info['unreadCount'] as int;
2025-12-12 09:11:30 +08:00
return GestureDetector(
2026-04-23 17:49:42 +08:00
onTap: () {
_handleIconTap(info['title']);
},
2025-12-12 09:11:30 +08:00
child: Column(
2026-04-23 17:49:42 +08:00
mainAxisSize: MainAxisSize.min,
2025-12-12 09:11:30 +08:00
children: [
2026-04-23 17:49:42 +08:00
Stack(
clipBehavior: Clip.none,
children: [
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: const Color(0xFFE8F4FD),
borderRadius: BorderRadius.circular(25),
),
child: Center(
child: Image.asset(info['icon'], width: 30, height: 30),
),
),
if (badgeNum > 0)
Positioned(
top: -5,
right: -5,
child: Container(
padding:
const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
constraints:
const BoxConstraints(minWidth: 16, minHeight: 16),
child: Center(
child: Text(
badgeNum > 99 ? '99+' : badgeNum.toString(),
style: const TextStyle(
color: Colors.white, fontSize: 10, height: 1),
),
),
),
),
],
2025-12-12 09:11:30 +08:00
),
const SizedBox(height: 6),
2026-04-23 17:49:42 +08:00
Padding(
padding: const EdgeInsets.symmetric(horizontal: 6.0),
2025-12-12 09:11:30 +08:00
child: Text(
info['title'],
2026-04-23 17:49:42 +08:00
style: const TextStyle(fontSize: 13, color: Colors.black87),
2025-12-12 09:11:30 +08:00
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
],
),
);
}
2026-04-23 17:49:42 +08:00
void _handleIconTap(String title) async {
2025-12-12 09:11:30 +08:00
switch (title) {
case "单位管理":
pushPage(UnitTabPage(), context);
break;
case "入港培训":
2026-02-28 14:38:07 +08:00
pushPage(StudyTabListPage(), context);
2025-12-12 09:11:30 +08:00
break;
2026-03-25 16:09:17 +08:00
case "口门门禁":
pushPage(DoorcarTabPage(), context);
break;
2026-04-10 17:25:59 +08:00
case "重点作业":
pushPage(KeyTasksTabPage(), context);
break;
2026-04-23 17:49:42 +08:00
case "危险作业":
await pushPage(WorkTabListPage(), context);
break;
2025-12-12 09:11:30 +08:00
default:
2026-03-12 10:23:21 +08:00
ToastUtil.showNormal(context, '功能开发中...');
2025-12-12 09:11:30 +08:00
break;
}
2026-04-23 17:49:42 +08:00
_getToDoWorkList(pcType);
2025-12-12 09:11:30 +08:00
}
Widget _buildWorkStatsSection() {
return Container(
2026-03-25 16:09:17 +08:00
padding: const EdgeInsets.all(15),
2025-12-12 09:11:30 +08:00
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
2026-03-25 16:09:17 +08:00
boxShadow: const [
BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2)),
],
2025-12-12 09:11:30 +08:00
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
2026-03-25 16:09:17 +08:00
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 0),
child: RichText(
text: TextSpan(
children: [
TextSpan(
text: "今日有工作项",
style: TextStyle(fontSize: 16, color: Colors.black87),
),
TextSpan(
text: " ${workStats['total']}",
style: const TextStyle(
fontSize: 16,
color: Color(0xFF2A75F8),
fontWeight: FontWeight.bold,
),
),
TextSpan(
text: "",
style: TextStyle(fontSize: 16, color: Colors.black87),
),
],
),
),
),
const SizedBox(height: 15),
2025-12-12 09:11:30 +08:00
Container(
width: screenWidth(context),
2026-03-25 16:09:17 +08:00
height: 36,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.grey[300]!, width: 1),
),
2025-12-12 09:11:30 +08:00
child: Row(
children: [
Expanded(
child: GestureDetector(
2026-03-25 16:09:17 +08:00
onTap: () {
setState(() {
_isMobileSelected = true;
});
2026-04-23 17:49:42 +08:00
pcType = 1;
2026-03-25 16:09:17 +08:00
_getToDoWorkList(pcType);
},
2025-12-12 09:11:30 +08:00
child: Container(
decoration: BoxDecoration(
2026-03-25 16:09:17 +08:00
color:
2026-04-23 17:49:42 +08:00
_isMobileSelected
? const Color(0xFF2A75F8)
: Colors.white,
2026-03-25 16:09:17 +08:00
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(20),
bottomLeft: Radius.circular(20),
),
2025-12-12 09:11:30 +08:00
),
child: Center(
child: Text(
"手机端",
style: TextStyle(
fontSize: 14,
2026-04-23 17:49:42 +08:00
color:
_isMobileSelected
? Colors.white
: Colors.black87,
2025-12-12 09:11:30 +08:00
fontWeight: FontWeight.w500,
),
),
),
),
),
),
Expanded(
child: GestureDetector(
2026-03-25 16:09:17 +08:00
onTap: () {
setState(() {
_isMobileSelected = false;
});
2026-04-23 17:49:42 +08:00
pcType = 2;
2026-03-25 16:09:17 +08:00
_getToDoWorkList(pcType);
},
2025-12-12 09:11:30 +08:00
child: Container(
decoration: BoxDecoration(
2026-04-23 17:49:42 +08:00
color:
!_isMobileSelected
? const Color(0xFF2A75F8)
: Colors.white,
2026-03-25 16:09:17 +08:00
borderRadius: const BorderRadius.only(
topRight: Radius.circular(20),
bottomRight: Radius.circular(20),
),
2025-12-12 09:11:30 +08:00
),
child: Center(
child: Text(
"电脑端",
style: TextStyle(
2026-03-25 16:09:17 +08:00
fontSize: 14,
2026-04-23 17:49:42 +08:00
color:
!_isMobileSelected
? Colors.white
: Colors.black87,
2025-12-12 09:11:30 +08:00
fontWeight: FontWeight.w500,
),
),
),
),
),
),
],
),
),
2026-03-25 16:09:17 +08:00
// const SizedBox(height: 15),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// RichText(
// text: TextSpan(
// children: [
// TextSpan(
// text: "已处理工作项:",
// style: TextStyle(fontSize: 14, color: Colors.black87),
// ),
// TextSpan(
// text: " ${workStats['processed']}",
// style: const TextStyle(
// fontSize: 14,
// color: Colors.greenAccent,
// fontWeight: FontWeight.bold,
// ),
// ),
// TextSpan(
// text: "个",
// style: TextStyle(fontSize: 14, color: Colors.black87),
// ),
// ],
// ),
// ),
// RichText(
// text: TextSpan(
// children: [
// TextSpan(
// text: "待处理工作项:",
// style: TextStyle(fontSize: 14, color: Colors.black87),
// ),
// TextSpan(
// text: " ${workStats['processed']}",
// style: const TextStyle(
// fontSize: 14,
// color: Colors.orange,
// fontWeight: FontWeight.bold,
// ),
// ),
// TextSpan(
// text: "个",
// style: TextStyle(fontSize: 14, color: Colors.black87),
// ),
// ],
// ),
// ),
// ],
// ),
2025-12-12 09:11:30 +08:00
],
),
);
}
2026-03-25 16:09:17 +08:00
// Widget _buildCheckListSection() {
// return Column(
// children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: const [
// Text(' 待办清单', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
// SizedBox(),
// ],
// ),
// const SizedBox(height: 8),
// Container(
// decoration: BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.circular(12),
// boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2))],
// ),
// child: Column(children: [...checkLists.map((item) => _buildCheckListItem(item))]),
// ),
// ],
// );
// }
2025-12-12 09:11:30 +08:00
Widget _buildCheckListSection() {
2026-03-25 16:09:17 +08:00
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: const [
BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2)),
],
),
child: Column(
children: checkLists.map((item) => _buildCheckListItem(item)).toList(),
),
2025-12-12 09:11:30 +08:00
);
}
Widget _buildCheckListItem(Map<String, dynamic> item) {
2026-03-25 16:09:17 +08:00
return GestureDetector(
onTap: () async {
2026-04-23 17:49:42 +08:00
if (pcType == 2) {
2026-03-25 16:09:17 +08:00
return;
}
2026-04-23 17:49:42 +08:00
switch (item['appName']) {
2026-03-30 16:21:06 +08:00
case "隐患治理":
case "隐患管理":
case "风险管控应用":
// await pushPage(ApplicationPageTwo(), context);
break;
case "消防检查":
// await pushPage(FireManagementTabPage(), context);
break;
2026-04-02 10:11:58 +08:00
case "口门门禁管理":
2026-03-30 16:21:06 +08:00
case "一级口门管理":
await pushPage(DoorcarTabPage(), context);
break;
case "安全环保检查":
// await pushPage(SafecheckTabList(), context);
break;
}
2026-03-25 16:09:17 +08:00
_getToDoWorkList(pcType);
},
child: Container(
padding: const EdgeInsets.all(15),
2026-04-23 17:49:42 +08:00
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: Colors.grey[200]!, width: 1),
),
),
2026-03-25 16:09:17 +08:00
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
2026-04-02 17:34:28 +08:00
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
2026-04-23 17:49:42 +08:00
Text(
item['title'],
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
SizedBox(height: 4),
Text(
item['content'],
style: TextStyle(fontSize: 12, color: Colors.grey[500]),
),
2026-04-02 17:34:28 +08:00
// ],
// ),
2026-03-25 16:09:17 +08:00
const Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
2026-04-23 17:49:42 +08:00
Image.asset(
'assets/images/mine1.png',
width: 15,
height: 15,
),
2026-03-25 16:09:17 +08:00
Container(
2026-04-23 17:49:42 +08:00
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2,
),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(4),
),
child: Text(
"类型:${item['appName']}",
style: const TextStyle(
fontSize: 12,
color: Colors.blue,
),
),
2026-03-25 16:09:17 +08:00
),
],
2025-12-12 09:11:30 +08:00
),
2026-04-23 17:49:42 +08:00
Text(
"时间:${item['createTime']}",
style: const TextStyle(
fontSize: 12,
color: Colors.grey,
),
),
2026-03-25 16:09:17 +08:00
],
),
],
),
2025-12-12 09:11:30 +08:00
),
2026-03-25 16:09:17 +08:00
],
),
2025-12-12 09:11:30 +08:00
),
);
}
2026-03-25 16:09:17 +08:00
// 获取待办事项
void _getToDoWorkList(int type) async {
Map data = {
2026-04-23 17:49:42 +08:00
"eqAppFlag": type == 1 ? "1" : "",
"eqPcFlag": type == 1 ? "" : "1",
2026-03-25 16:09:17 +08:00
"eqStatus": "1",
"pageIndex": '1',
"pageSize": '999',
};
final result = await TodoApi.getTodoList(data);
2026-04-23 17:49:42 +08:00
final specialWork = await SpecialWorkApi.specialWorkTaskLogTotalCount();
int specialWorkNum = 0;
try {
if (specialWork['success']) {
List<dynamic> specialWorkList = specialWork['data'] ?? [];
for (var item in specialWorkList) {
if (item is Map) {
final total = item['todoCount'];
specialWorkNum += int.tryParse('$total') ?? 0;
}
}
}
setState(() {
for (var section in buttonInfos) {
if (section['title'] == '危险作业') {
section['unreadCount'] = specialWorkNum;
}
}
});
} catch (e) {}
2026-03-25 16:09:17 +08:00
if (result['success']) {
setState(() {
2026-04-23 17:49:42 +08:00
workStats['total'] = result['totalCount'];
checkLists = result['data'];
2026-03-25 16:09:17 +08:00
// checkLists = result['data'];
2026-04-23 17:49:42 +08:00
int m = checkLists.length;
2026-03-25 16:09:17 +08:00
});
}
}
2026-04-23 17:49:42 +08:00
}