import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../providers/auth_provider.dart'; import '../../utils/i10n.dart'; import '../../utils/permissions.dart'; import '../../utils/responsive.dart'; /// 管理中心页(底部导航第 4 个 tab):各管理模块入口。 /// /// 模块可见性由 [SessionPermissions] 统一驱动,与 [router.dart] 的路由可达性 /// 保持一致: /// - 用户管理:所有已登录账号可见(数据范围由后端按会话权限收敛); /// - 渠道商管理:管理员 + 渠道商可见,代理商不可见; /// - 其余模块(设备/激活码/公司介绍/滚动字幕)对所有账号可见。 class AdminHubPage extends ConsumerWidget { const AdminHubPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = AppLocalizations.of(context); // 注:按需求「设备分组」整合进设备管理功能(设备详情/设备列表内可改分组), // 「应用管理」移除以简化管理页。故网格不再单列这两项。 final scheme = Theme.of(context).colorScheme; final user = ref.watch(authProvider).user; // 菜单可见性与路由可达性由同一套 SessionPermissions 谓词驱动: // - 用户管理:所有已登录账号(各角色看自己范围内用户); // - 渠道商管理:管理员 + 渠道商(代理商不可见,路由层亦会拦截其深链)。 final showUserManagement = SessionPermissions.canViewUserManagement(user); final showChannelManagement = SessionPermissions.canViewChannelManagement(user); final items = [ _MenuItem( title: t.deviceManagement, icon: Icons.devices_outlined, color: scheme.primary, route: '/admin/device', ), _MenuItem( title: t.activationCode, icon: Icons.vpn_key_outlined, color: Colors.amber, route: '/admin/activation', ), if (showUserManagement) _MenuItem( title: t.userManagement, icon: Icons.people_outlined, color: scheme.outline, route: '/admin/sys/user', ), _MenuItem( title: t.companyIntro, icon: Icons.business_outlined, color: Colors.teal, route: '/admin/intro/store-list', ), _MenuItem( title: t.marqueeManagement, icon: Icons.subtitles_outlined, color: Colors.deepPurple, route: '/admin/marquee', ), if (showChannelManagement) _MenuItem( title: t.channelManagement, icon: Icons.account_tree_outlined, color: Colors.blue, route: '/admin/channel', ), ]; return Scaffold( appBar: AppBar(title: Text(t.admin)), body: GridView.builder( padding: const EdgeInsets.all(16), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: context.responsive.gridCount(minTileWidth: 300), mainAxisSpacing: 12, crossAxisSpacing: 12, childAspectRatio: 1.2, ), itemCount: items.length, itemBuilder: (context, index) { final item = items[index]; return Card( child: InkWell( onTap: () => context.push(item.route), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(item.icon, size: 40, color: item.color), const SizedBox(height: 8), Text( item.title, style: Theme.of(context).textTheme.titleMedium, ), ], ), ), ); }, ), ); } } class _MenuItem { const _MenuItem({ required this.title, required this.icon, required this.color, required this.route, }); final String title; final IconData icon; final Color color; final String route; }