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';
|
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 {
|
|
|
|
|
|
final result = await pushPage(
|
|
|
|
|
|
ScanPage(type: ScanType.Onboarding),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
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 = [];
|
|
|
|
|
|
|
|
|
|
|
|
Map<String, int> workStats = {'total': 36, 'processed': 30, 'pending': 6};
|
|
|
|
|
|
|
|
|
|
|
|
List<Map<String, dynamic>> checkLists = [
|
|
|
|
|
|
{
|
|
|
|
|
|
"title": "电工班车间清单",
|
|
|
|
|
|
"type": "隐患排查",
|
|
|
|
|
|
"time": "2025-11-17",
|
|
|
|
|
|
"color": Color(0xFF4CAF50),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"title": "工地区消防点检清单",
|
|
|
|
|
|
"type": "消防点检",
|
|
|
|
|
|
"time": "2025-11-17",
|
|
|
|
|
|
"color": Color(0xFF2196F3),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"title": "消防专项检查清单",
|
2026-03-12 10:23:21 +08:00
|
|
|
|
"type": "重点作业",
|
2025-12-12 09:11:30 +08:00
|
|
|
|
"time": "2025-11-17",
|
|
|
|
|
|
"color": Color(0xFFFF9800),
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
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-03-12 10:23:21 +08:00
|
|
|
|
{"icon": "assets/images/ico4.png", "title": "隐患治理", "unreadCount": 0}, // 原“隐患处理”改为“隐患治理”
|
|
|
|
|
|
{"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",
|
|
|
|
|
|
"危险作业": "dashboard-Hazardous-Work",
|
|
|
|
|
|
"隐患治理": "dashboard-Hazard-Management",
|
|
|
|
|
|
"重点作业": "", // 无对应,暂时留空
|
|
|
|
|
|
"口门门禁": "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-02-28 14:38:07 +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-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
|
|
|
|
|
|
|
|
|
|
// 更新按钮可见性
|
|
|
|
|
|
final List<bool> newVisibility = List<bool>.filled(buttonInfos.length, false);
|
|
|
|
|
|
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-03-12 10:23:21 +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 =
|
|
|
|
|
|
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 =
|
|
|
|
|
|
(_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-03-12 10:23:21 +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,
|
|
|
|
|
|
top: (bannerHeight - (iconSectionHeight * 0.4)) + iconSectionHeight - 10,
|
|
|
|
|
|
height: notificationHeight,
|
|
|
|
|
|
child: _buildNotificationBar(notificationHeight - 2),
|
|
|
|
|
|
),
|
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-03-12 10:23:21 +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-03-12 10:23:21 +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-03-12 10:23:21 +08:00
|
|
|
|
height: _showFloatingAppBar ? (statusBar + _floatingBarHeight) : 0,
|
2025-12-12 09:11:30 +08:00
|
|
|
|
decoration: BoxDecoration(
|
2026-03-12 10:23:21 +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-03-12 10:23:21 +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-03-12 10:23:21 +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']!);
|
|
|
|
|
|
final bool showJoin = true; // 加入企业图标始终显示(或根据业务决定)
|
|
|
|
|
|
|
|
|
|
|
|
final List<Widget> children = [];
|
|
|
|
|
|
if (showScan) {
|
|
|
|
|
|
children.add(
|
|
|
|
|
|
GestureDetector(
|
|
|
|
|
|
onTap: startScan,
|
|
|
|
|
|
child: Container(
|
|
|
|
|
|
width: 30,
|
|
|
|
|
|
height: 30,
|
|
|
|
|
|
alignment: Alignment.center,
|
|
|
|
|
|
child: Image.asset("assets/icon-apps/home_saoyisao.png", width: 20, height: 20),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
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,
|
|
|
|
|
|
child: Image.asset("assets/icon-apps/home_add.png", width: 20, height: 20),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-12 09:11:30 +08:00
|
|
|
|
return Positioned(
|
|
|
|
|
|
top: topOffset,
|
2026-02-28 14:38:07 +08:00
|
|
|
|
right: 8,
|
2026-03-12 10:23:21 +08:00
|
|
|
|
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-03-12 10:23:21 +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,
|
|
|
|
|
|
style: const TextStyle(color: Colors.black87, fontSize: 14),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
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-03-12 10:23:21 +08:00
|
|
|
|
Widget _buildIconSection(BuildContext context, List<Map<String, dynamic>> visibleButtons, double height) {
|
|
|
|
|
|
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),
|
|
|
|
|
|
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2))],
|
|
|
|
|
|
),
|
|
|
|
|
|
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-03-12 10:23:21 +08:00
|
|
|
|
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2))],
|
|
|
|
|
|
),
|
|
|
|
|
|
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());
|
|
|
|
|
|
}
|
|
|
|
|
|
}),
|
|
|
|
|
|
),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
],
|
2026-03-12 10:23:21 +08:00
|
|
|
|
)
|
|
|
|
|
|
: 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) {
|
|
|
|
|
|
return GestureDetector(
|
2026-03-12 10:23:21 +08:00
|
|
|
|
onTap: () => _handleIconTap(info['title']),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
child: Column(
|
|
|
|
|
|
children: [
|
|
|
|
|
|
Container(
|
|
|
|
|
|
width: 50,
|
|
|
|
|
|
height: 50,
|
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
|
color: const Color(0xFFE8F4FD),
|
|
|
|
|
|
borderRadius: BorderRadius.circular(25),
|
|
|
|
|
|
),
|
2026-03-12 10:23:21 +08:00
|
|
|
|
child: Center(child: Image.asset(info['icon'], width: 30, height: 30)),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
),
|
|
|
|
|
|
const SizedBox(height: 6),
|
|
|
|
|
|
SizedBox(
|
|
|
|
|
|
width: 70,
|
|
|
|
|
|
child: Text(
|
|
|
|
|
|
info['title'],
|
|
|
|
|
|
style: const TextStyle(fontSize: 12, color: Colors.black87),
|
|
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
|
|
maxLines: 2,
|
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _handleIconTap(String title) {
|
|
|
|
|
|
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;
|
|
|
|
|
|
default:
|
2026-03-12 10:23:21 +08:00
|
|
|
|
ToastUtil.showNormal(context, '功能开发中...');
|
2025-12-12 09:11:30 +08:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Widget _buildWorkStatsSection() {
|
|
|
|
|
|
double buttonHeight = 45.0;
|
|
|
|
|
|
return Container(
|
|
|
|
|
|
padding: const EdgeInsets.all(1),
|
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
|
borderRadius: BorderRadius.circular(12),
|
2026-03-12 10:23:21 +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: [
|
|
|
|
|
|
Container(
|
|
|
|
|
|
width: screenWidth(context),
|
|
|
|
|
|
height: buttonHeight,
|
|
|
|
|
|
child: Row(
|
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
|
|
|
|
children: [
|
|
|
|
|
|
Expanded(
|
|
|
|
|
|
child: GestureDetector(
|
2026-03-12 10:23:21 +08:00
|
|
|
|
onTap: () => setState(() => _isMobileSelected = true),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
child: Container(
|
|
|
|
|
|
decoration: BoxDecoration(
|
2026-03-12 10:23:21 +08:00
|
|
|
|
color: _isMobileSelected ? const Color(0xFFE9F4FE) : Colors.white,
|
|
|
|
|
|
borderRadius: const BorderRadius.only(topLeft: Radius.circular(12)),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
),
|
|
|
|
|
|
child: Center(
|
|
|
|
|
|
child: Text(
|
|
|
|
|
|
"手机端",
|
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
|
fontSize: 14,
|
2026-03-12 10:23:21 +08:00
|
|
|
|
color: _isMobileSelected ? Colors.black : Colors.blue,
|
2025-12-12 09:11:30 +08:00
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
Image.asset(
|
|
|
|
|
|
'assets/images/${_isMobileSelected ? 'img2.png' : 'img1.png'}',
|
|
|
|
|
|
fit: BoxFit.cover,
|
|
|
|
|
|
height: buttonHeight,
|
|
|
|
|
|
width: 30,
|
|
|
|
|
|
),
|
|
|
|
|
|
Expanded(
|
|
|
|
|
|
child: GestureDetector(
|
2026-03-12 10:23:21 +08:00
|
|
|
|
onTap: () => setState(() => _isMobileSelected = false),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
child: Container(
|
|
|
|
|
|
decoration: BoxDecoration(
|
2026-03-12 10:23:21 +08:00
|
|
|
|
color: !_isMobileSelected ? const Color(0xFFE9F4FE) : Colors.white,
|
|
|
|
|
|
borderRadius: const BorderRadius.only(topRight: Radius.circular(12)),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
),
|
|
|
|
|
|
child: Center(
|
|
|
|
|
|
child: Text(
|
|
|
|
|
|
"电脑端",
|
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
|
fontSize: 15,
|
2026-03-12 10:23:21 +08:00
|
|
|
|
color: !_isMobileSelected ? Colors.black : Colors.blue,
|
2025-12-12 09:11:30 +08:00
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
Padding(
|
2026-03-12 10:23:21 +08:00
|
|
|
|
padding: const EdgeInsets.all(15),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
child: Column(
|
|
|
|
|
|
children: [
|
|
|
|
|
|
Container(
|
|
|
|
|
|
width: double.infinity,
|
|
|
|
|
|
padding: const EdgeInsets.symmetric(vertical: 0),
|
|
|
|
|
|
child: RichText(
|
|
|
|
|
|
text: TextSpan(
|
|
|
|
|
|
children: [
|
2026-03-12 10:23:21 +08:00
|
|
|
|
const TextSpan(text: "今日有工作项", style: TextStyle(fontSize: 16, color: Colors.black87)),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
TextSpan(
|
|
|
|
|
|
text: " ${workStats['total']}",
|
2026-03-12 10:23:21 +08:00
|
|
|
|
style: const TextStyle(fontSize: 16, color: Color(0xFF2A75F8), fontWeight: FontWeight.bold),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
),
|
2026-03-12 10:23:21 +08:00
|
|
|
|
const TextSpan(text: "个", style: TextStyle(fontSize: 16, color: Colors.black87)),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
const SizedBox(height: 15),
|
|
|
|
|
|
Row(
|
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
|
children: [
|
|
|
|
|
|
RichText(
|
|
|
|
|
|
text: TextSpan(
|
|
|
|
|
|
children: [
|
2026-03-12 10:23:21 +08:00
|
|
|
|
const TextSpan(text: "已处理工作项:", style: TextStyle(fontSize: 14, color: Colors.black87)),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
TextSpan(
|
|
|
|
|
|
text: " ${workStats['processed']}",
|
2026-03-12 10:23:21 +08:00
|
|
|
|
style: const TextStyle(fontSize: 14, color: Colors.greenAccent, fontWeight: FontWeight.bold),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
),
|
2026-03-12 10:23:21 +08:00
|
|
|
|
const TextSpan(text: "个", style: TextStyle(fontSize: 14, color: Colors.black87)),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
RichText(
|
|
|
|
|
|
text: TextSpan(
|
|
|
|
|
|
children: [
|
2026-03-12 10:23:21 +08:00
|
|
|
|
const TextSpan(text: "待处理工作项:", style: TextStyle(fontSize: 14, color: Colors.black87)),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
TextSpan(
|
2026-03-12 10:23:21 +08:00
|
|
|
|
text: " ${workStats['pending']}",
|
|
|
|
|
|
style: const TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
),
|
2026-03-12 10:23:21 +08:00
|
|
|
|
const TextSpan(text: "个", style: TextStyle(fontSize: 14, color: Colors.black87)),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Widget _buildCheckListSection() {
|
|
|
|
|
|
return Column(
|
|
|
|
|
|
children: [
|
|
|
|
|
|
Row(
|
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
2026-03-12 10:23:21 +08:00
|
|
|
|
children: const [
|
|
|
|
|
|
Text(' 待办清单', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
|
|
|
|
|
SizedBox(),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
],
|
|
|
|
|
|
),
|
2026-03-12 10:23:21 +08:00
|
|
|
|
const SizedBox(height: 8),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
Container(
|
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
|
borderRadius: BorderRadius.circular(12),
|
2026-03-12 10:23:21 +08:00
|
|
|
|
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2))],
|
2025-12-12 09:11:30 +08:00
|
|
|
|
),
|
2026-03-12 10:23:21 +08:00
|
|
|
|
child: Column(children: [...checkLists.map((item) => _buildCheckListItem(item))]),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Widget _buildCheckListItem(Map<String, dynamic> item) {
|
|
|
|
|
|
return Container(
|
|
|
|
|
|
padding: const EdgeInsets.all(15),
|
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
|
border: Border(bottom: BorderSide(color: Colors.grey[200]!, width: 1)),
|
|
|
|
|
|
),
|
|
|
|
|
|
child: Row(
|
|
|
|
|
|
children: [
|
|
|
|
|
|
Expanded(
|
|
|
|
|
|
child: Column(
|
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
|
children: [
|
|
|
|
|
|
Row(
|
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
|
children: [
|
|
|
|
|
|
Flexible(
|
|
|
|
|
|
child: Text(
|
|
|
|
|
|
item['title'],
|
2026-03-12 10:23:21 +08:00
|
|
|
|
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold, color: Colors.black87),
|
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
|
maxLines: 1,
|
2025-12-12 09:11:30 +08:00
|
|
|
|
),
|
|
|
|
|
|
),
|
2026-03-12 10:23:21 +08:00
|
|
|
|
const SizedBox(width: 8),
|
|
|
|
|
|
Text(item['type'], style: TextStyle(fontSize: 12, color: Colors.grey[500])),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
],
|
|
|
|
|
|
),
|
2026-03-12 10:23:21 +08:00
|
|
|
|
const SizedBox(height: 8),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
Row(
|
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
|
children: [
|
|
|
|
|
|
Flexible(
|
|
|
|
|
|
child: Container(
|
2026-03-12 10:23:21 +08:00
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
|
color: Colors.grey[100],
|
|
|
|
|
|
borderRadius: BorderRadius.circular(4),
|
|
|
|
|
|
),
|
|
|
|
|
|
child: Text(
|
|
|
|
|
|
"类型:${item['type']}",
|
2026-03-12 10:23:21 +08:00
|
|
|
|
style: const TextStyle(fontSize: 12, color: Colors.blue),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
2026-03-12 10:23:21 +08:00
|
|
|
|
const SizedBox(width: 8),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
Flexible(
|
|
|
|
|
|
child: Text(
|
|
|
|
|
|
"时间:${item['time']}",
|
2026-03-12 10:23:21 +08:00
|
|
|
|
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2026-03-12 10:23:21 +08:00
|
|
|
|
}
|