Files
CloudHome-Ops/lib/app/router.dart

356 lines
14 KiB
Dart
Raw Permalink Normal View History

import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../providers/auth_provider.dart';
import '../utils/permissions.dart';
import '../screens/admin/admin_hub_page.dart';
import '../screens/admin/app/app_version_page.dart';
import '../screens/admin/app/tv_app_page.dart';
import '../screens/admin/channel/channel_management_page.dart';
import '../screens/admin/intro/intro_category_page.dart';
import '../screens/admin/intro/intro_store_content_page.dart';
import '../screens/admin/intro/intro_store_list_page.dart';
import '../screens/admin/marquee/marquee_management_page.dart';
import '../screens/admin/media/media_list_page.dart';
import '../screens/admin/media/media_rank_page.dart';
import '../screens/admin/sys/config_page.dart';
import '../screens/admin/sys/log_page.dart';
import '../screens/admin/sys/menu_page.dart';
import '../screens/admin/sys/role_page.dart';
import '../screens/admin/sys/schedule_page.dart';
import '../screens/admin/sys/user_page.dart';
import '../screens/device/device_activation_page.dart';
import '../screens/device/device_group_page.dart';
import '../screens/device/device_scan_page.dart';
import '../screens/device/store_config_page.dart';
import '../screens/home/device_detail_page.dart';
import '../screens/home/device_list_page.dart';
import '../screens/home/home_page.dart';
import '../screens/login/login_page.dart';
import '../models/store_info.dart';
import '../screens/profile/settings_page.dart';
import '../screens/store/org_tree_page.dart';
import '../screens/store/store_detail_page.dart';
import '../screens/store/store_list_page.dart';
import '../widgets/app_bottom_nav.dart';
/// 全局路由配置。
///
/// 路由表:
/// - `/login` 登录页(未登录时重定向到此)
/// - `/` 首页仪表盘(底部导航第 1 个 tab)
/// - `/device` 设备列表(底部导航第 2 个 tab)
/// - `/device/:id` 设备详情
/// - `/device/group` 设备分组
/// - `/device/activation` 激活码管理
/// - `/device/store-config` 门店配置
/// - `/store` 门店管理(底部导航第 3 个 tab)
/// - `/store/org-tree` 组织架构
/// - `/store/:id` 门店详情
/// - `/admin` 管理中心(底部导航第 4 个 tab)
/// - `/admin/sys/*` 系统管理子模块
/// - `/admin/app/*` 应用管理子模块
/// - `/admin/media/*` 媒资管理子模块
/// - `/admin/intro/*` 公司介绍子模块
/// - `/profile` 我的(底部导航第 5 个 tab)
/// - `/profile/settings` 设置
final appRouterProvider = Provider<GoRouter>((ref) {
// 登录态变化时触发路由重定向刷新,而不重建整个 GoRouter。
final refreshNotifier = ValueNotifier<int>(0);
ref
..onDispose(refreshNotifier.dispose)
..listen(authProvider, (_, __) => refreshNotifier.value++);
return GoRouter(
initialLocation: '/',
refreshListenable: refreshNotifier,
redirect: (context, state) {
final auth = ref.read(authProvider);
final loggedIn = auth.isLoggedIn;
final isLoginRoute = state.matchedLocation == '/login';
if (!loggedIn && !isLoginRoute) {
return '/login';
}
if (loggedIn && isLoginRoute) {
return '/';
}
// 功能模块按账号权限收敛(菜单可见性与此处路由可达性由同一套
// [SessionPermissions] 谓词驱动,确保一致):
// - 系统管理中仅 /admin/sys/user(用户管理)对所有已登录账号开放;
// 其余 /admin/sys/*(角色/菜单/配置/日志/定时任务)仅管理员可访问;
// - 媒资 /admin/media/*、应用 /admin/app/*、公司介绍(分类维度)
// /admin/intro/category 仅管理员可见/可访问;
// - 渠道商管理 /admin/channel 对管理员与渠道商开放,代理商不可访问;
// 非管理员(受限账号)即使通过深链/返回键进入上述受限路由也重定向回
// 管理首页,作为入口收敛的兜底,防止越权查看/操作跨组织数据。
// 注:公司介绍门店维度入口(/admin/intro/store-list、/admin/intro/
// store-content/*)按门店维度展示、数据已由 getMyStores 收敛,对所有账号开放。
final loc = state.matchedLocation;
final user = ref.read(authProvider).user;
final cat = SessionPermissions.category(user);
if (cat != AccountCategory.admin) {
final isUserManagement =
loc == '/admin/sys/user' || loc.startsWith('/admin/sys/user/');
final isSysModule = loc.startsWith('/admin/sys') && !isUserManagement;
if (isSysModule ||
loc.startsWith('/admin/media') ||
loc.startsWith('/admin/app') ||
loc.startsWith('/admin/intro/category')) {
return '/admin';
}
// 代理商(非渠道商)访问渠道商管理 → 弹回(其菜单中本就隐藏该入口)。
if (loc.startsWith('/admin/channel') &&
cat != AccountCategory.channelMerchant) {
return '/admin';
}
}
return null;
},
routes: [
GoRoute(
path: '/login',
name: 'login',
builder: (context, state) => const LoginPage(),
),
StatefulShellRoute.indexedStack(
builder: (context, state, navigationShell) =>
AppBottomNav(shell: navigationShell),
branches: [
// 首页(仪表盘)
StatefulShellBranch(
routes: [
GoRoute(
path: '/',
name: 'home',
builder: (context, state) => const HomePage(),
),
],
),
// 设备
StatefulShellBranch(
routes: [
GoRoute(
path: '/device',
name: 'device',
builder: (context, state) => const DeviceListPage(),
routes: [
// 具名子路由(group/activation/store-config/scan)必须放在参数路由 :id 之前,
// 否则 `/device/scan` 会被 `:id` 匹配成设备 ID="scan",导致进入错误的详情页。
GoRoute(
path: 'group',
name: 'deviceGroup',
builder: (context, state) => const DeviceGroupPage(),
),
GoRoute(
path: 'activation',
name: 'deviceActivation',
builder: (context, state) =>
const DeviceActivationPage(),
),
GoRoute(
path: 'store-config',
name: 'storeConfig',
builder: (context, state) => const StoreConfigPage(),
),
GoRoute(
path: 'scan',
name: 'deviceScan',
builder: (context, state) {
final mode =
state.uri.queryParameters['mode'] ?? 'query';
final deviceId =
state.uri.queryParameters['deviceId'];
final storeId =
state.uri.queryParameters['storeId'];
final storeName =
state.uri.queryParameters['storeName'];
final groupId =
state.uri.queryParameters['groupId'];
return DeviceScanPage(
mode: mode,
deviceId: deviceId,
storeId: storeId,
storeName: storeName,
groupId: groupId,
);
},
),
GoRoute(
path: ':id',
name: 'deviceDetail',
builder: (context, state) {
final id = state.pathParameters['id'] ?? '';
return DeviceDetailPage(deviceId: id);
},
),
],
),
],
),
// 门店
StatefulShellBranch(
routes: [
GoRoute(
path: '/store',
name: 'store',
builder: (context, state) => const StoreListPage(),
routes: [
GoRoute(
path: 'org-tree',
name: 'orgTree',
builder: (context, state) => const OrgTreePage(),
),
GoRoute(
path: ':id',
name: 'storeDetail',
builder: (context, state) {
final id = state.pathParameters['id'] ?? '';
return StoreDetailPage(storeId: id);
},
),
],
),
],
),
// 管理
StatefulShellBranch(
routes: [
GoRoute(
path: '/admin',
name: 'admin',
builder: (context, state) => const AdminHubPage(),
routes: [
// 管理模块内的设备/激活码别名路由:复用设备分支的页面组件,
// 但在 /admin 分支栈下 push,使底部导航仍停留「管理」tab,
// 返回键可正常 pop 回管理首页,避免此前 push 到 /device 分支栈导致的返回键异常。
GoRoute(
path: 'device',
name: 'adminDevice',
builder: (context, state) => const DeviceListPage(),
),
GoRoute(
path: 'activation',
name: 'adminActivation',
builder: (context, state) =>
const DeviceActivationPage(),
),
// 系统管理
GoRoute(
path: 'sys/user',
name: 'sysUser',
builder: (context, state) => const UserPage(),
),
GoRoute(
path: 'sys/role',
name: 'sysRole',
builder: (context, state) => const RolePage(),
),
GoRoute(
path: 'sys/menu',
name: 'sysMenu',
builder: (context, state) => const MenuPage(),
),
GoRoute(
path: 'sys/config',
name: 'sysConfig',
builder: (context, state) => const ConfigPage(),
),
GoRoute(
path: 'sys/log',
name: 'sysLog',
builder: (context, state) => const LogPage(),
),
GoRoute(
path: 'sys/schedule',
name: 'sysSchedule',
builder: (context, state) => const SchedulePage(),
),
// 应用管理
GoRoute(
path: 'app/version',
name: 'appVersion',
builder: (context, state) => const AppVersionPage(),
),
GoRoute(
path: 'app/tv-app',
name: 'tvApp',
builder: (context, state) => const TvAppPage(),
),
// 滚动字幕管理
GoRoute(
path: 'marquee',
name: 'marqueeManagement',
builder: (context, state) => const MarqueeManagementPage(),
),
// 渠道商管理
GoRoute(
path: 'channel',
name: 'channelManagement',
builder: (context, state) => const ChannelManagementPage(),
),
// 媒资管理
GoRoute(
path: 'media/list',
name: 'mediaList',
builder: (context, state) => const MediaListPage(),
),
GoRoute(
path: 'media/rank',
name: 'mediaRank',
builder: (context, state) => const MediaRankPage(),
),
// 公司介绍(门店维度)
GoRoute(
path: 'intro/store-list',
name: 'introStoreList',
builder: (context, state) => const IntroStoreListPage(),
),
GoRoute(
path: 'intro/store-content/:storeId',
name: 'introStoreContent',
builder: (context, state) {
final storeId = state.pathParameters['storeId'] ?? '';
final store = state.extra is StoreInfo
? state.extra as StoreInfo
: null;
return IntroStoreContentPage(
storeId: storeId,
store: store,
);
},
),
// 公司介绍(原分类维度,保留作为管理备用入口)
GoRoute(
path: 'intro/category',
name: 'introCategory',
builder: (context, state) => const IntroCategoryPage(),
),
],
),
],
),
// 我的
StatefulShellBranch(
routes: [
GoRoute(
path: '/profile',
name: 'profile',
builder: (context, state) => const SettingsPage(),
routes: [
GoRoute(
path: 'settings',
name: 'profileSettings',
builder: (context, state) => const SettingsPage(),
),
],
),
],
),
],
),
],
);
});