diff --git a/app/lib/core/routing/app_router.dart b/app/lib/core/routing/app_router.dart index b6302d5..4ae6dff 100644 --- a/app/lib/core/routing/app_router.dart +++ b/app/lib/core/routing/app_router.dart @@ -1,4 +1,5 @@ -// 暖记路由表 — go_router 20 页面 + 认证守卫 +// 暖记路由表 — go_router + 认证守卫 +// 对齐 Open Design: TabBar = 首页/日历/发现/我的,中心 FAB = 写日记 // // 路由守卫逻辑: // - 未认证用户访问受保护路由 → 重定向到 /login @@ -24,6 +25,8 @@ import '../../features/editor/views/editor_page.dart'; import '../../features/auth/views/login_page.dart'; import '../../features/auth/views/role_selection_page.dart'; import '../../features/auth/views/class_code_join_page.dart'; +import '../../features/onboarding/views/splash_page.dart'; +import '../../features/onboarding/views/onboarding_page.dart'; import '../../features/class_/views/class_page.dart'; import '../../features/teacher/views/teacher_page.dart'; import '../../features/parent/views/parent_page.dart'; @@ -38,7 +41,7 @@ final _rootNavigatorKey = GlobalKey(); final _shellNavigatorKey = GlobalKey(); /// 不需要认证的白名单路径 -const _publicPaths = ['/login', '/role-selection', '/class-code']; +const _publicPaths = ['/splash', '/onboarding', '/login', '/role-selection', '/class-code']; /// 创建路由配置 — 需要注入 AuthBloc GoRouter createAppRouter(AuthBloc authBloc) { @@ -91,6 +94,18 @@ GoRouter createAppRouter(AuthBloc authBloc) { refreshListenable: _AuthListenable(authBloc), routes: [ + // 启动页 & 引导页(无 Shell,无需认证) + GoRoute( + path: '/splash', + name: 'splash', + builder: (context, state) => const SplashPage(), + ), + GoRoute( + path: '/onboarding', + name: 'onboarding', + builder: (context, state) => const OnboardingPage(), + ), + // 认证路由(无 Shell) GoRoute( path: '/login', @@ -108,7 +123,7 @@ GoRouter createAppRouter(AuthBloc authBloc) { builder: (context, state) => const ClassCodeJoinPage(), ), - // 主 Shell 路由(底部导航 + 侧边导航) + // 主 Shell 路由(底部导航: 首页/日历/发现/我的) ShellRoute( navigatorKey: _shellNavigatorKey, builder: (context, state, child) { @@ -129,14 +144,10 @@ GoRouter createAppRouter(AuthBloc authBloc) { name: 'calendar', builder: (context, state) => const CalendarPage(), ), + // 发现页(复用搜索页,后续可替换为独立 DiscoverPage) GoRoute( - path: '/mood', - name: 'mood', - builder: (context, state) => const MoodPage(), - ), - GoRoute( - path: '/search', - name: 'search', + path: '/discover', + name: 'discover', builder: (context, state) => const SearchPage(), ), GoRoute( @@ -154,9 +165,17 @@ GoRouter createAppRouter(AuthBloc authBloc) { parentNavigatorKey: _rootNavigatorKey, builder: (context, state) { final journalId = state.uri.queryParameters['id']; - return EditorPage(journalId: journalId); + final templateId = state.uri.queryParameters['template']; + return EditorPage(journalId: journalId, templateId: templateId); }, ), + // 心情追踪(全屏,从首页心情卡片进入) + GoRoute( + path: '/mood', + name: 'mood', + parentNavigatorKey: _rootNavigatorKey, + builder: (context, state) => const MoodPage(), + ), GoRoute( path: '/class', name: 'class', @@ -203,13 +222,12 @@ GoRouter createAppRouter(AuthBloc authBloc) { ); } -/// 路径 → Tab index 映射 +/// 路径 → Tab index 映射(4 项: 首页=0, 日历=1, 发现=2, 我的=3) int _selectedIndexFromLocation(String location) { if (location.startsWith('/calendar')) return 1; - if (location.startsWith('/mood')) return 2; - if (location.startsWith('/search')) return 3; - if (location.startsWith('/profile')) return 4; - return 0; + if (location.startsWith('/discover')) return 2; + if (location.startsWith('/profile')) return 3; + return 0; // /home 或未知路径 } /// AuthBloc 变化监听器 — 驱动 GoRouter refreshListenable @@ -230,6 +248,8 @@ class _AuthListenable extends ChangeNotifier { } /// App Shell — 包裹 ResponsiveScaffold +/// TabBar: 首页(0) / 日历(1) / 发现(2) / 我的(3) +/// 中心 FAB: 写日记 class _AppShell extends StatelessWidget { const _AppShell({ required this.selectedIndex, @@ -250,20 +270,13 @@ class _AppShell extends StatelessWidget { case 1: context.go('/calendar'); case 2: - context.go('/mood'); + context.go('/discover'); case 3: - context.go('/search'); - case 4: context.go('/profile'); } }, body: child, - floatingActionButton: selectedIndex == 0 - ? FloatingActionButton( - onPressed: () => context.push('/editor'), - child: const Icon(Icons.edit_rounded), - ) - : null, + onCenterButtonPressed: () => context.push('/editor'), ); } } diff --git a/app/lib/features/editor/views/editor_page.dart b/app/lib/features/editor/views/editor_page.dart index 7cdec9c..ac8abba 100644 --- a/app/lib/features/editor/views/editor_page.dart +++ b/app/lib/features/editor/views/editor_page.dart @@ -29,8 +29,9 @@ import '../widgets/sticker_picker_sheet.dart'; /// 手账编辑器页面 class EditorPage extends StatelessWidget { final String? journalId; + final String? templateId; - const EditorPage({super.key, this.journalId}); + const EditorPage({super.key, this.journalId, this.templateId}); @override Widget build(BuildContext context) { @@ -53,6 +54,7 @@ class EditorPage extends StatelessWidget { ), child: _EditorView( journalId: journalId, + templateId: templateId, onSaveComplete: () { if (context.canPop()) { context.pop(); @@ -154,9 +156,10 @@ class EditorPage extends StatelessWidget { class _EditorView extends StatelessWidget { final String? journalId; + final String? templateId; final VoidCallback onSaveComplete; - const _EditorView({this.journalId, required this.onSaveComplete}); + const _EditorView({this.journalId, this.templateId, required this.onSaveComplete}); @override Widget build(BuildContext context) { @@ -227,7 +230,11 @@ class _EditorView extends StatelessWidget { // 日记标题 Expanded( child: Text( - journalId != null ? '编辑日记' : '新建日记', + journalId != null + ? '编辑日记' + : templateId != null + ? '从模板新建' + : '新建日记', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, ),