QinGang_interested/lib/pages/home/unit/unit_tab_page.dart

311 lines
10 KiB
Dart
Raw Normal View History

2026-03-12 10:23:21 +08:00
import 'package:flutter/foundation.dart';
2025-12-12 09:11:30 +08:00
import 'package:flutter/material.dart';
2026-03-12 10:23:21 +08:00
import 'package:qhd_prevention/common/route_model.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
2025-12-12 09:11:30 +08:00
import 'package:qhd_prevention/customWidget/work_tab_icon_grid.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/customWidget/IconBadgeButton.dart';
2025-12-24 16:07:53 +08:00
import 'package:qhd_prevention/pages/home/unit/unit_join_list_page.dart';
2025-12-12 09:11:30 +08:00
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/tools/tools.dart';
import 'package:qhd_prevention/common/route_aware_state.dart';
2026-03-12 10:23:21 +08:00
import 'package:qhd_prevention/common/route_service.dart';
2025-12-12 09:11:30 +08:00
class UnitTabPage extends StatefulWidget {
const UnitTabPage({super.key});
@override
State<UnitTabPage> createState() => _UnitTabPageState();
}
class _UnitTabPageState extends RouteAwareState<UnitTabPage> {
2026-03-12 10:23:21 +08:00
// 原始 master 定义(固定顺序,不随权限变动)
final List<Map<String, dynamic>> _masterButtons = [
2025-12-12 09:11:30 +08:00
{
"icon": "assets/images/unit_ico1.png",
"title": "服务单位管理",
"unreadCount": 0,
2026-03-12 10:23:21 +08:00
},
{
2025-12-12 09:11:30 +08:00
"icon": "assets/images/unit_ico2.png",
"title": "就职单位管理",
"unreadCount": 0,
2026-03-12 10:23:21 +08:00
},
2025-12-12 09:11:30 +08:00
];
2026-03-12 10:23:21 +08:00
// title -> 后端 menuPerm 映射(基于你提供的路由配置)
final Map<String, String> _permMapping = {
"服务单位管理": "dashboard-Unit-Management-Managee-Service-Unit-Management",
"就职单位管理": "dashboard-Unit-Management-Employment-Unit",
};
// 当前哪些按钮应显示(与 master 顺序对应)
late List<bool> _visible;
2025-12-12 09:11:30 +08:00
@override
void initState() {
super.initState();
2026-03-12 10:23:21 +08:00
// 初始:全部显示,避免短暂白屏(路由加载后会更新)
_visible = List<bool>.filled(_masterButtons.length, true);
// 监听路由变化,路由加载完成或变更时刷新可见性
RouteService().addListener(_onRouteUpdated);
// 尝试一次应用路由(如果已经加载)
WidgetsBinding.instance.addPostFrameCallback((_) => _updateVisibilityFromRoute());
}
@override
void dispose() {
try {
RouteService().removeListener(_onRouteUpdated);
} catch (_) {}
super.dispose();
}
void _onRouteUpdated() {
// 当路由配置更新时重计算按钮可见性
_updateVisibilityFromRoute();
2025-12-12 09:11:30 +08:00
}
2026-03-12 10:23:21 +08:00
void _updateVisibilityFromRoute() {
final routeService = RouteService();
// routesLoaded: 当 mainTabs 非空时认为路由加载完毕(开始严格按后端配置显示)
final bool routesLoaded = routeService.mainTabs.isNotEmpty;
final List<bool> next = List<bool>.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
2025-12-12 09:11:30 +08:00
Future<void> onVisible() async {
2026-03-12 10:23:21 +08:00
await _getData();
2025-12-12 09:11:30 +08:00
}
2026-03-12 10:23:21 +08:00
Future<void> _getData() async {
// TODO: 如果需要从后端拉取角标/统计,在这里实现并 setState 更新 _masterButtons[*]['unreadCount']
// await Future.delayed(Duration(milliseconds: 100));
2025-12-12 09:11:30 +08:00
}
void _handleIconTap(int index) async {
2026-03-12 10:23:21 +08:00
final title = _masterButtons[index]['title'] as String;
switch (title) {
case '服务单位管理':
ToastUtil.showNormal(context, '您还没有参与项目');
2025-12-12 09:11:30 +08:00
break;
2026-03-12 10:23:21 +08:00
case '就职单位管理':
2025-12-24 16:07:53 +08:00
pushPage(UnitJoinListPage(), context);
break;
default:
break;
2025-12-12 09:11:30 +08:00
}
2026-03-12 10:23:21 +08:00
// 点击后可以刷新数据
await _getData();
2025-12-12 09:11:30 +08:00
}
2026-03-12 10:23:21 +08:00
2025-12-12 09:11:30 +08:00
@override
Widget build(BuildContext context) {
2026-03-12 10:23:21 +08:00
final double screenW = MediaQuery.of(context).size.width;
final double bannerHeight = 618 / 1125 * screenW;
// 根据可见按钮数量决定 icon 区高度(每行最多 4 个)
final visibleButtons = <Map<String, dynamic>>[];
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;
2025-12-12 09:11:30 +08:00
return PopScope(
2026-02-28 14:38:07 +08:00
canPop: true,
2025-12-12 09:11:30 +08:00
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,
2026-03-12 10:23:21 +08:00
child: _buildIconSection(context, visibleButtons, rows),
2025-12-12 09:11:30 +08:00
),
],
),
),
],
),
),
);
}
2026-03-12 10:23:21 +08:00
// Banner
2025-12-12 09:11:30 +08:00
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,
),
],
);
}
2026-03-12 10:23:21 +08:00
// 根据 visibleButtons 与行数构建 icon 区
Widget _buildIconSection(BuildContext context, List<Map<String, dynamic>> 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('暂无权限访问的功能')),
2025-12-12 09:11:30 +08:00
);
} else {
2026-03-12 10:23:21 +08:00
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()),
2025-12-12 09:11:30 +08:00
);
}
2026-03-12 10:23:21 +08:00
}
// 构建每一行(每行最多 4 个)
final List<Widget> 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));
}
2025-12-12 09:11:30 +08:00
2026-03-12 10:23:21 +08:00
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),
2025-12-12 09:11:30 +08:00
);
}
2026-03-12 10:23:21 +08:00
Widget _buildIconButton(Map<String, dynamic> info, int masterIndex) {
final unread = (info['unreadCount'] ?? 0) is int ? info['unreadCount'] as int : int.tryParse('${info['unreadCount']}') ?? 0;
2025-12-12 09:11:30 +08:00
return IconBadgeButton(
iconPath: info['icon'] ?? '',
title: info['title'] ?? '',
2026-03-12 10:23:21 +08:00
unreadCount: unread,
onTap: () => _handleIconTap(masterIndex),
2025-12-12 09:11:30 +08:00
);
}
2026-03-12 10:23:21 +08:00
}