diff --git a/app/lib/app.dart b/app/lib/app.dart index 3b9920d..491a7a8 100644 --- a/app/lib/app.dart +++ b/app/lib/app.dart @@ -54,12 +54,10 @@ class NuanjiApp extends StatelessWidget { // 异步恢复 SyncEngine 持久化队列(fire-and-forget,不阻塞 UI) syncEngine.restorePendingQueue(); - // 认证成功后注入 JWT token 到 ApiClient + // 认证状态监听:登出时清除 token + // 注意:登录时 token 由 AuthRepository.login() 直接注入 ApiClient authBloc.stream.listen((state) { - if (state is Authenticated) { - // TODO: 从 SecureStorage 读取 token 并设置 - // apiClient.setToken(token); - } else { + if (state is! Authenticated) { apiClient.clearToken(); } }); diff --git a/app/lib/core/routing/app_router.dart b/app/lib/core/routing/app_router.dart index e5867a3..b6302d5 100644 --- a/app/lib/core/routing/app_router.dart +++ b/app/lib/core/routing/app_router.dart @@ -260,7 +260,7 @@ class _AppShell extends StatelessWidget { body: child, floatingActionButton: selectedIndex == 0 ? FloatingActionButton( - onPressed: () => context.go('/editor'), + onPressed: () => context.push('/editor'), child: const Icon(Icons.edit_rounded), ) : null, diff --git a/app/lib/features/calendar/views/calendar_page.dart b/app/lib/features/calendar/views/calendar_page.dart index 8af64ee..4bf6c59 100644 --- a/app/lib/features/calendar/views/calendar_page.dart +++ b/app/lib/features/calendar/views/calendar_page.dart @@ -385,7 +385,7 @@ class _EmptyDayView extends StatelessWidget { ), const SizedBox(height: 24), FilledButton.tonal( - onPressed: () => context.go('/editor'), + onPressed: () => context.push('/editor'), child: const Text('写一篇'), ), ], @@ -420,7 +420,7 @@ class _DayJournalList extends StatelessWidget { side: BorderSide(color: colorScheme.outlineVariant), ), child: InkWell( - onTap: () => context.go('/editor?id=${journal.id}'), + onTap: () => context.push('/editor?id=${journal.id}'), borderRadius: BorderRadius.circular(16), child: Padding( padding: const EdgeInsets.all(16), diff --git a/app/lib/features/class_/views/class_page.dart b/app/lib/features/class_/views/class_page.dart index b48e551..76aa561 100644 --- a/app/lib/features/class_/views/class_page.dart +++ b/app/lib/features/class_/views/class_page.dart @@ -296,7 +296,7 @@ class _DiaryWallCard extends StatelessWidget { side: BorderSide(color: colorScheme.outlineVariant), ), child: InkWell( - onTap: () => context.go('/editor?id=${journal.id}'), + onTap: () => context.push('/editor?id=${journal.id}'), borderRadius: BorderRadius.circular(16), child: Padding( padding: const EdgeInsets.all(16), @@ -417,7 +417,7 @@ class _TopicsTab extends StatelessWidget { side: BorderSide(color: colorScheme.outlineVariant), ), child: InkWell( - onTap: () => context.go('/editor?topic=${topic.id}'), + onTap: () => context.push('/editor?topic=${topic.id}'), borderRadius: BorderRadius.circular(16), child: Padding( padding: const EdgeInsets.all(16), diff --git a/app/lib/features/editor/views/editor_page.dart b/app/lib/features/editor/views/editor_page.dart index 7316140..e50b1da 100644 --- a/app/lib/features/editor/views/editor_page.dart +++ b/app/lib/features/editor/views/editor_page.dart @@ -50,7 +50,13 @@ class EditorPage extends StatelessWidget { ), child: _EditorView( journalId: journalId, - onSaveComplete: () => context.pop(), + onSaveComplete: () { + if (context.canPop()) { + context.pop(); + } else { + context.go('/home'); + } + }, ), ); } @@ -202,7 +208,13 @@ class _EditorView extends StatelessWidget { children: [ // 返回按钮 IconButton( - onPressed: () => context.pop(), + onPressed: () { + if (context.canPop()) { + context.pop(); + } else { + context.go('/home'); + } + }, icon: const Icon(Icons.arrow_back_rounded), tooltip: '返回', ), diff --git a/app/lib/features/home/views/home_page.dart b/app/lib/features/home/views/home_page.dart index bb9ca3f..87d455c 100644 --- a/app/lib/features/home/views/home_page.dart +++ b/app/lib/features/home/views/home_page.dart @@ -173,7 +173,7 @@ class _QuickMoodCard extends StatelessWidget { children: moods.map((mood) { final isTop = topMood == mood.$3; return GestureDetector( - onTap: () => context.go('/editor'), + onTap: () => context.push('/editor'), child: Column( children: [ Container( @@ -225,7 +225,7 @@ class _JournalList extends StatelessWidget { side: BorderSide(color: colorScheme.outlineVariant), ), child: InkWell( - onTap: () => context.go('/editor?id=${journal.id}'), + onTap: () => context.push('/editor?id=${journal.id}'), borderRadius: BorderRadius.circular(16), child: Padding( padding: const EdgeInsets.all(16), @@ -292,7 +292,7 @@ class _EmptyJournalState extends StatelessWidget { style: theme.textTheme.bodyLarge?.copyWith(color: colorScheme.onSurface.withValues(alpha: 0.5))), const SizedBox(height: 24), FilledButton.icon( - onPressed: () => context.go('/editor'), + onPressed: () => context.push('/editor'), icon: const Icon(Icons.add_rounded), label: const Text('写日记'), ), diff --git a/app/lib/features/templates/views/template_gallery_page.dart b/app/lib/features/templates/views/template_gallery_page.dart index 4b55d9a..efe319a 100644 --- a/app/lib/features/templates/views/template_gallery_page.dart +++ b/app/lib/features/templates/views/template_gallery_page.dart @@ -139,7 +139,7 @@ class _TemplateCard extends StatelessWidget { child: InkWell( onTap: () { // 使用模板创建日记 - context.go('/editor?template=${template.id}'); + context.push('/editor?template=${template.id}'); }, borderRadius: BorderRadius.circular(16), child: Padding( diff --git a/app/lib/widgets/responsive_scaffold.dart b/app/lib/widgets/responsive_scaffold.dart index 5809acd..8fadc08 100644 --- a/app/lib/widgets/responsive_scaffold.dart +++ b/app/lib/widgets/responsive_scaffold.dart @@ -1,6 +1,8 @@ // 暖记响应式骨架 — 三级自适应布局 // 手机: 底部 TabBar | 平板: 侧边导航 | 桌面: 三栏 +// Web 平台始终使用底部 TabBar(移动端布局)以保证导航交互正常 +import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/material.dart'; import '../core/constants/breakpoints.dart'; @@ -31,7 +33,11 @@ class _ResponsiveScaffoldState extends State { @override Widget build(BuildContext context) { final width = MediaQuery.sizeOf(context).width; - final deviceType = Breakpoints.getDeviceType(width); + // Web 平台:始终使用移动端底部 TabBar 布局 + // 原因:Web 上 NavigationRail 点击事件可能被 Flutter CanvasKit 拦截 + final deviceType = kIsWeb + ? DeviceType.mobile + : Breakpoints.getDeviceType(width); switch (deviceType) { case DeviceType.mobile: diff --git a/crates/erp-server/src/db.rs b/crates/erp-server/src/db.rs index a1db23d..fecab5b 100644 --- a/crates/erp-server/src/db.rs +++ b/crates/erp-server/src/db.rs @@ -4,7 +4,15 @@ use std::time::Duration; use crate::config::DatabaseConfig; pub async fn connect(config: &DatabaseConfig) -> anyhow::Result { - let mut opt = ConnectOptions::new(&config.url); + // 确保 URL 包含 client_encoding=utf8,防止 Windows 上编码错乱 + let url = if config.url.contains("client_encoding") { + config.url.clone() + } else { + let separator = if config.url.contains('?') { '&' } else { '?' }; + format!("{}{}client_encoding=utf8", config.url, separator) + }; + + let mut opt = ConnectOptions::new(&url); opt.max_connections(config.max_connections) .min_connections(config.min_connections) .connect_timeout(Duration::from_secs(10)) diff --git a/docs/opendesign/warm-notes-journal-app.md b/docs/opendesign/warm-notes-journal-app.md new file mode 100644 index 0000000..c71a68e --- /dev/null +++ b/docs/opendesign/warm-notes-journal-app.md @@ -0,0 +1,183 @@ + + + + + + +暖记 — 多终端手账日记App 设计稿 + + + +
+
Design System v1.0
+

暖记 · 手账日记

+

用温暖的方式,记录每一天。面向学生的多终端手账日记App,融合日记记录、贴纸装饰、模板系统和心情追踪。

+
+ 温暖治愈风 + iOS / Android / iPad / Desktop + 11 核心页面 + 学生向 +
+
+
+
全部页面
+
11个核心页面覆盖完整用户流程 — 从启动到日常使用
+ +
+
+
视觉系统
+
暖记的色彩体系
+
+
Background
#FFF8F0
+
Accent
#E07A5F
+
Secondary
#81B29A
+
Tertiary
#F2CC8F
+
Rose
#D4A5A5
+
Foreground
#2D2420
+
Muted
#8B7E74
+
Border
#E8DDD4
+
+
+
+

暖记 Warm Notes — 多终端手账日记App设计稿

+

11 核心页面 · iOS / Android / iPad / Desktop · 温暖治愈视觉系统

+
+ + + +