From 973bb56af6177f9dbf1828f00f8ebd19e9e66f76 Mon Sep 17 00:00:00 2001 From: iven Date: Mon, 1 Jun 2026 22:45:56 +0800 Subject: [PATCH] =?UTF-8?q?feat(app):=20=E7=BC=96=E8=BE=91=E5=99=A8?= =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=8C=89=E9=92=AE=E6=8E=A5=E5=85=A5=E5=88=86?= =?UTF-8?q?=E4=BA=AB=E9=9D=A2=E6=9D=BF=20=E2=80=94=20ShareBottomSheet=20+?= =?UTF-8?q?=20sharedToClass=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/editor/views/editor_page.dart | 71 ++++++++++- .../editor/widgets/share_bottom_sheet.dart | 119 ++++++++++++++++++ 2 files changed, 184 insertions(+), 6 deletions(-) create mode 100644 app/lib/features/editor/widgets/share_bottom_sheet.dart diff --git a/app/lib/features/editor/views/editor_page.dart b/app/lib/features/editor/views/editor_page.dart index ac8abba..4808768 100644 --- a/app/lib/features/editor/views/editor_page.dart +++ b/app/lib/features/editor/views/editor_page.dart @@ -17,6 +17,7 @@ import '../../../core/constants/design_tokens.dart'; import '../../../data/models/journal_element.dart'; import '../../../data/models/journal_entry.dart'; import '../../../data/repositories/journal_repository.dart'; +import '../../../data/repositories/class_repository.dart'; import '../bloc/editor_bloc.dart'; import '../widgets/handwriting_canvas.dart'; import '../widgets/stroke_model.dart'; @@ -25,6 +26,7 @@ import '../widgets/editor_toolbar.dart'; import '../widgets/text_input_overlay.dart'; import '../widgets/image_picker_handler.dart'; import '../widgets/sticker_picker_sheet.dart'; +import '../widgets/share_bottom_sheet.dart'; /// 手账编辑器页面 class EditorPage extends StatelessWidget { @@ -55,12 +57,10 @@ class EditorPage extends StatelessWidget { child: _EditorView( journalId: journalId, templateId: templateId, + savedJournalId: savedJournalId, + repo: repo, onSaveComplete: () { - if (context.canPop()) { - context.pop(); - } else { - context.go('/home'); - } + _showShareSheetAndNavigate(context, repo, savedJournalId); }, ), ); @@ -152,14 +152,73 @@ class EditorPage extends StatelessWidget { await repo.addElement(element); } } + + /// 显示分享面板并在用户选择后导航 + static void _showShareSheetAndNavigate( + BuildContext context, + JournalRepository repo, + String? savedJournalId, + ) { + // 尝试获取用户的班级信息 + String? userClassId; + String userClassName = '我的班级'; + + try { + context.read(); + // Phase 1 简化:不等待异步调用,使用默认值 + userClassId = null; // TODO: 从 AuthBloc/ClassBloc 获取真实班级 ID + } catch (_) { + // ClassRepository 不可用(未注入) + } + + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (sheetContext) => ShareBottomSheet( + classId: userClassId, + className: userClassName, + onDecision: (shareToClass) async { + // 更新日记的 sharedToClass 状态 + if (savedJournalId != null) { + try { + final entry = await repo.getJournal(savedJournalId); + if (entry != null) { + await repo.updateJournal( + entry.copyWith(sharedToClass: shareToClass), + ); + } + } catch (e) { + debugPrint('更新分享状态失败: $e'); + } + } + + // 导航返回 + if (!context.mounted) return; + if (context.canPop()) { + context.pop(); + } else { + context.go('/home'); + } + }, + ), + ); + } } class _EditorView extends StatelessWidget { final String? journalId; final String? templateId; + final String? savedJournalId; + final JournalRepository repo; final VoidCallback onSaveComplete; - const _EditorView({this.journalId, this.templateId, required this.onSaveComplete}); + const _EditorView({ + this.journalId, + this.templateId, + this.savedJournalId, + required this.repo, + required this.onSaveComplete, + }); @override Widget build(BuildContext context) { diff --git a/app/lib/features/editor/widgets/share_bottom_sheet.dart b/app/lib/features/editor/widgets/share_bottom_sheet.dart new file mode 100644 index 0000000..3f8a793 --- /dev/null +++ b/app/lib/features/editor/widgets/share_bottom_sheet.dart @@ -0,0 +1,119 @@ +// 分享 BottomSheet — 编辑器完成后选择分享到班级或仅自己可见 +// +// 设计要点: +// - 温暖友好的文案(面向小学生) +// - 分享到班级(有班级时显示)/ 仅自己可见 +// - 无班级时提示加入班级后可分享 + +import 'package:flutter/material.dart'; + +/// 编辑器完成后的分享选择面板 +class ShareBottomSheet extends StatelessWidget { + final String? classId; + final String className; + final void Function(bool shareToClass) onDecision; + + const ShareBottomSheet({ + super.key, + required this.classId, + required this.className, + required this.onDecision, + }); + + @override + Widget build(BuildContext context) { + final hasClass = classId != null && classId.isNotEmpty; + final theme = Theme.of(context); + + return Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: theme.colorScheme.surface, + borderRadius: const BorderRadius.vertical(top: Radius.circular(22)), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 拖拽条 + Center( + child: Container( + margin: const EdgeInsets.only(bottom: 16), + width: 40, + height: 4, + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(2), + ), + ), + ), + Text( + '日记写好了!', + style: theme.textTheme.titleMedium, + ), + const SizedBox(height: 8), + Text( + '要分享给老师和同学们看看吗?', + style: theme.textTheme.bodyMedium?.copyWith( + color: Colors.grey.shade600, + ), + ), + const SizedBox(height: 24), + + // 分享到班级 + if (hasClass) ...[ + SizedBox( + width: double.infinity, + height: 52, + child: FilledButton.icon( + onPressed: () { + onDecision(true); + Navigator.pop(context); + }, + icon: const Icon(Icons.group), + label: Text('分享到 $className'), + style: FilledButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + ), + ), + const SizedBox(height: 12), + ], + + // 仅自己可见 + SizedBox( + width: double.infinity, + height: 52, + child: OutlinedButton.icon( + onPressed: () { + onDecision(false); + Navigator.pop(context); + }, + icon: const Icon(Icons.lock_outline), + label: const Text('仅自己可见'), + style: OutlinedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + ), + ), + + // 无班级时的提示 + if (!hasClass) ...[ + const SizedBox(height: 12), + Text( + '加入班级后可以分享给老师和同学哦', + style: theme.textTheme.bodySmall?.copyWith( + color: Colors.grey.shade500, + ), + ), + ], + + const SizedBox(height: 8), + ], + ), + ); + } +}