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

311 lines
10 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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<UnitTabPage> createState() => _UnitTabPageState();
}
class _UnitTabPageState extends RouteAwareState<UnitTabPage> {
// 原始 master 定义(固定顺序,不随权限变动)
final List<Map<String, dynamic>> _masterButtons = [
{
"icon": "assets/images/unit_ico1.png",
"title": "服务单位管理",
"unreadCount": 0,
},
{
"icon": "assets/images/unit_ico2.png",
"title": "就职单位管理",
"unreadCount": 0,
},
];
// title -> 后端 menuPerm 映射(基于你提供的路由配置)
final Map<String, String> _permMapping = {
"服务单位管理": "dashboard-Unit-Management-Managee-Service-Unit-Management",
"就职单位管理": "dashboard-Unit-Management-Employment-Unit",
};
// 当前哪些按钮应显示(与 master 顺序对应)
late List<bool> _visible;
@override
void initState() {
super.initState();
// 初始:全部显示,避免短暂白屏(路由加载后会更新)
_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();
}
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
Future<void> onVisible() async {
await _getData();
}
Future<void> _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 = <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;
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<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('暂无权限访问的功能')),
);
} 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<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));
}
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<String, dynamic> 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),
);
}
}