import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:qhd_prevention/common/route_model.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/customWidget/work_tab_icon_grid.dart'; import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/customWidget/IconBadgeButton.dart'; import 'package:qhd_prevention/pages/home/unit/unit_join_list_page.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/tools/tools.dart'; import 'package:qhd_prevention/common/route_aware_state.dart'; import 'package:qhd_prevention/common/route_service.dart'; class UnitTabPage extends StatefulWidget { const UnitTabPage({super.key}); @override State createState() => _UnitTabPageState(); } class _UnitTabPageState extends RouteAwareState { // 原始 master 定义(固定顺序,不随权限变动) final List> _masterButtons = [ { "icon": "assets/images/unit_ico1.png", "title": "服务单位管理", "unreadCount": 0, }, { "icon": "assets/images/unit_ico2.png", "title": "就职单位管理", "unreadCount": 0, }, ]; // title -> 后端 menuPerm 映射(基于你提供的路由配置) final Map _permMapping = { "服务单位管理": "dashboard-Unit-Management-Managee-Service-Unit-Management", "就职单位管理": "dashboard-Unit-Management-Employment-Unit", }; // 当前哪些按钮应显示(与 master 顺序对应) late List _visible; @override void initState() { super.initState(); // 初始:全部显示,避免短暂白屏(路由加载后会更新) _visible = List.filled(_masterButtons.length, true); // 监听路由变化,路由加载完成或变更时刷新可见性 RouteService().addListener(_onRouteUpdated); // 尝试一次应用路由(如果已经加载) WidgetsBinding.instance.addPostFrameCallback((_) => _updateVisibilityFromRoute()); } @override void dispose() { try { RouteService().removeListener(_onRouteUpdated); } catch (_) {} super.dispose(); } void _onRouteUpdated() { // 当路由配置更新时重计算按钮可见性 _updateVisibilityFromRoute(); } void _updateVisibilityFromRoute() { final routeService = RouteService(); // routesLoaded: 当 mainTabs 非空时认为路由加载完毕(开始严格按后端配置显示) final bool routesLoaded = routeService.mainTabs.isNotEmpty; final List next = List.filled(_masterButtons.length, false); for (int i = 0; i < _masterButtons.length; i++) { final title = _masterButtons[i]['title'] as String; final perm = _permMapping[title] ?? ''; if (!routesLoaded) { // 路由还没加载:保持默认显示(避免闪烁) next[i] = true; continue; } if (perm.isEmpty) { // 没有映射:默认隐藏 next[i] = false; continue; } // 优先使用 RouteService.findRouteByPerm 获取路由节点(该方法会考虑节点 visible) final RouteModel? node = routeService.findRouteByPerm(perm); if (node != null) { // 按你的要求:以 showFlag == 1 判断是否显示(兼容旧字段) next[i] = (node.showFlag == 1) || (node.visible); } else { // 如果没有找到完全匹配的 menuPerm,有可能后端使用不同层级或不同 perms 命名, // 这里做一次容错:在所有顶级下递归查找 menuPerm 相等项(不依赖 findRouteByPerm)。 RouteModel? fallback; for (final top in routeService.allRoutes) { fallback = _findRouteRecursiveByPerm(top, perm); if (fallback != null) break; } next[i] = fallback != null ? ((fallback.showFlag == 1) || (fallback.visible)) : false; } } // 只有在发生变化时才 setState if (!listEquals(next, _visible)) { setState(() { _visible = next; }); } } /// 递归:在以 node 为根的子树中查找第一个 menuPerms 等于 targetPerm 的节点 /// (不依赖 RouteService.findRouteByPerm,作为 fallback) RouteModel? _findRouteRecursiveByPerm(RouteModel node, String targetPerm) { if ((node.menuPerms ?? '') == targetPerm) return node; for (final c in node.children) { final res = _findRouteRecursiveByPerm(c, targetPerm); if (res != null) return res; } return null; } // 当页面可见时拉取数据 @override Future onVisible() async { await _getData(); } Future _getData() async { // TODO: 如果需要从后端拉取角标/统计,在这里实现并 setState 更新 _masterButtons[*]['unreadCount'] // await Future.delayed(Duration(milliseconds: 100)); } void _handleIconTap(int index) async { final title = _masterButtons[index]['title'] as String; switch (title) { case '服务单位管理': ToastUtil.showNormal(context, '您还没有参与项目'); break; case '就职单位管理': pushPage(UnitJoinListPage(), context); break; default: break; } // 点击后可以刷新数据 await _getData(); } @override Widget build(BuildContext context) { final double screenW = MediaQuery.of(context).size.width; final double bannerHeight = 618 / 1125 * screenW; // 根据可见按钮数量决定 icon 区高度(每行最多 4 个) final visibleButtons = >[]; for (int i = 0; i < _masterButtons.length; i++) { if (i < _visible.length && _visible[i]) visibleButtons.add(_masterButtons[i]); } final int visibleCount = visibleButtons.length; final int perRow = 4; final int rows = visibleCount == 0 ? 0 : ((visibleCount + perRow - 1) ~/ perRow); // 样式参数(如需微调) const double verticalPadding = 30.0; const double perRowHeight = 110.0; // 单行高度(图标 + 文本 + 内间距) const double rowSpacing = 20.0; final double iconSectionHeight = visibleCount == 0 ? 150.0 : (verticalPadding + rows * perRowHeight + (rows - 1) * rowSpacing); const double iconOverlapBanner = 30.0; return PopScope( canPop: true, child: Scaffold( extendBodyBehindAppBar: true, appBar: MyAppbar( title: '单位管理', backgroundColor: Colors.transparent, ), body: ListView( physics: const AlwaysScrollableScrollPhysics(), padding: EdgeInsets.zero, children: [ SizedBox( height: bannerHeight + iconSectionHeight, child: Stack( clipBehavior: Clip.none, children: [ Positioned( top: 0, left: 0, right: 0, height: bannerHeight, child: _buildBannerSection(bannerHeight), ), Positioned( left: 10, right: 10, top: bannerHeight - iconOverlapBanner, height: iconSectionHeight, child: _buildIconSection(context, visibleButtons, rows), ), ], ), ), ], ), ), ); } // Banner Widget _buildBannerSection(double height) { return Stack( children: [ Image.asset( "assets/images/unit_banner.jpg", width: MediaQuery.of(context).size.width, height: height, fit: BoxFit.cover, ), ], ); } // 根据 visibleButtons 与行数构建 icon 区 Widget _buildIconSection(BuildContext context, List> visibleButtons, int rows) { if (visibleButtons.isEmpty) { if (RouteService().mainTabs.isNotEmpty) { 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('暂无权限访问的功能')), ); } else { 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: SizedBox()), ); } } // 构建每一行(每行最多 4 个) final List rowsWidgets = []; final int perRow = 4; for (int r = 0; r < rows; r++) { final start = r * perRow; final end = (start + perRow) > visibleButtons.length ? visibleButtons.length : (start + perRow); final rowItems = visibleButtons.sublist(start, end); rowsWidgets.add( Row( children: List.generate(perRow, (i) { final idx = start + i; if (idx < visibleButtons.length) { final btn = visibleButtons[idx]; // 找到在 master 中的真实索引(用于 onTap) final masterIndex = _masterButtons.indexWhere((m) => m['title'] == btn['title']); return Expanded( child: Center(child: _buildIconButton(btn, masterIndex)), ); } else { return const Expanded(child: SizedBox.shrink()); } }), ), ); if (r != rows - 1) rowsWidgets.add(const SizedBox(height: 20)); } return Container( padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 5), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2))], ), child: Column(children: rowsWidgets), ); } Widget _buildIconButton(Map info, int masterIndex) { final unread = (info['unreadCount'] ?? 0) is int ? info['unreadCount'] as int : int.tryParse('${info['unreadCount']}') ?? 0; return IconBadgeButton( iconPath: info['icon'] ?? '', title: info['title'] ?? '', unreadCount: unread, onTap: () => _handleIconTap(masterIndex), ); } }