From ae1aa21445694eea2a495416d7b61b915af56ef7 Mon Sep 17 00:00:00 2001 From: sion <450702724@qq.com> Date: Mon, 23 Mar 2026 00:08:26 +0800 Subject: [PATCH] youhua --- .../lib/ui/pages/mine/mine_page.dart | 548 +++++++++--------- 1 file changed, 261 insertions(+), 287 deletions(-) diff --git a/flutter_monisuo/lib/ui/pages/mine/mine_page.dart b/flutter_monisuo/lib/ui/pages/mine/mine_page.dart index c103982..c4a0b90 100644 --- a/flutter_monisuo/lib/ui/pages/mine/mine_page.dart +++ b/flutter_monisuo/lib/ui/pages/mine/mine_page.dart @@ -4,6 +4,21 @@ import 'package:provider/provider.dart'; import '../../../providers/auth_provider.dart'; import '../auth/login_page.dart'; +/// 菜单项数据模型 +class _MenuItem { + final IconData icon; + final String title; + final String? subtitle; + final VoidCallback onTap; + + const _MenuItem({ + required this.icon, + required this.title, + this.subtitle, + required this.onTap, + }); +} + /// 我的页面 - 使用 shadcn_ui 现代化设计 class MinePage extends StatefulWidget { const MinePage({super.key}); @@ -19,22 +34,19 @@ class _MinePageState extends State with AutomaticKeepAliveClientMixin @override Widget build(BuildContext context) { super.build(context); - final theme = ShadTheme.of(context); return Scaffold( - backgroundColor: theme.colorScheme.background, body: Consumer( builder: (context, auth, _) { - final user = auth.user; return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( children: [ - _buildUserCard(user), + _UserCard(user: auth.user), const SizedBox(height: 16), - _buildMenuList(context, auth), + _MenuList(onShowComingSoon: _showComingSoon, onShowAbout: _showAboutDialog), const SizedBox(height: 24), - _buildLogoutButton(context, auth), + _LogoutButton(onLogout: () => _handleLogout(auth)), ], ), ); @@ -43,208 +55,13 @@ class _MinePageState extends State with AutomaticKeepAliveClientMixin ); } - Widget _buildUserCard(user) { - final theme = ShadTheme.of(context); - - return ShadCard( - padding: const EdgeInsets.all(20), - child: Row( - children: [ - CircleAvatar( - radius: 32, - backgroundColor: theme.colorScheme.primary.withValues(alpha: 0.2), - child: Text( - user?.avatarText ?? 'U', - style: TextStyle( - fontSize: 24, - color: theme.colorScheme.primary, - fontWeight: FontWeight.bold, - ), - ), - ), - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - user?.username ?? '未登录', - style: theme.textTheme.h3.copyWith( - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 6), - ShadBadge( - backgroundColor: theme.colorScheme.primary.withValues(alpha: 0.2), - child: Text( - '普通用户', - style: TextStyle( - fontSize: 12, - color: theme.colorScheme.primary, - ), - ), - ), - ], - ), - ), - Icon( - LucideIcons.chevronRight, - color: theme.colorScheme.mutedForeground, - ), - ], - ), - ); - } - - Widget _buildMenuList(BuildContext context, AuthProvider auth) { - final theme = ShadTheme.of(context); - - final menuItems = [ - _MenuItem( - icon: LucideIcons.userCheck, - title: '实名认证', - subtitle: '完成实名认证,解锁更多功能', - onTap: () => _showComingSoon('实名认证'), - ), - _MenuItem( - icon: LucideIcons.shield, - title: '安全设置', - subtitle: '密码、二次验证等安全设置', - onTap: () => _showComingSoon('安全设置'), - ), - _MenuItem( - icon: LucideIcons.bell, - title: '消息通知', - subtitle: '管理消息推送设置', - onTap: () => _showComingSoon('消息通知'), - ), - _MenuItem( - icon: LucideIcons.settings, - title: '系统设置', - subtitle: '主题、语言等偏好设置', - onTap: () => _showComingSoon('系统设置'), - ), - _MenuItem( - icon: LucideIcons.info, - title: '关于我们', - subtitle: '版本信息与用户协议', - onTap: () => _showAboutDialog(), - ), - ]; - - return ShadCard( - padding: EdgeInsets.zero, - child: Column( - children: menuItems.asMap().entries.map((entry) { - final index = entry.key; - final item = entry.value; - final isLast = index == menuItems.length - 1; - - return Column( - children: [ - _buildMenuItem(item, index), - if (!isLast) - Divider( - color: theme.colorScheme.border, - height: 1, - indent: 56, - ), - ], - ); - }).toList(), - ), - ); - } - - Widget _buildMenuItem(_MenuItem item, int index) { - final theme = ShadTheme.of(context); - - return InkWell( - onTap: item.onTap, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - child: Row( - children: [ - Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: theme.colorScheme.primary.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(10), - ), - child: Icon( - item.icon, - size: 20, - color: theme.colorScheme.primary, - ), - ), - const SizedBox(width: 12), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - item.title, - style: theme.textTheme.small.copyWith( - fontWeight: FontWeight.w500, - ), - ), - if (item.subtitle != null) ...[ - const SizedBox(height: 2), - Text( - item.subtitle!, - style: theme.textTheme.muted.copyWith(fontSize: 11), - ), - ], - ], - ), - ), - Icon( - LucideIcons.chevronRight, - size: 18, - color: theme.colorScheme.mutedForeground, - ), - ], - ), - ), - ); - } - - Widget _buildLogoutButton(BuildContext context, AuthProvider auth) { - return SizedBox( - width: double.infinity, - height: 48, - child: ShadButton.destructive( - onPressed: () => _showLogoutDialog(context, auth), - child: const Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(LucideIcons.logOut, size: 18), - SizedBox(width: 8), - Text( - '退出登录', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ), - ); - } - void _showComingSoon(String feature) { showShadDialog( context: context, builder: (context) => ShadDialog.alert( title: const Row( children: [ - Icon( - LucideIcons.construction, - color: Color(0xFFFF9800), - size: 20, - ), + Icon(LucideIcons.construction, color: Color(0xFFFF9800), size: 20), SizedBox(width: 8), Text('功能开发中'), ], @@ -260,38 +77,6 @@ class _MinePageState extends State with AutomaticKeepAliveClientMixin ); } - void _showLogoutDialog(BuildContext context, AuthProvider auth) { - showShadDialog( - context: context, - builder: (context) => ShadDialog.alert( - title: const Text('确认退出'), - description: const Text('确定要退出登录吗?'), - actions: [ - ShadButton.outline( - child: const Text('取消'), - onPressed: () => Navigator.of(context).pop(), - ), - ShadButton.destructive( - child: const Text('退出'), - onPressed: () async { - Navigator.of(context).pop(); - await auth.logout(); - // 登出成功,直接导航到登录页 - if (context.mounted) { - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (_) => const LoginPage(), - ), - (route) => false, - ); - } - }, - ), - ], - ), - ); - } - void _showAboutDialog() { final theme = ShadTheme.of(context); @@ -300,17 +85,7 @@ class _MinePageState extends State with AutomaticKeepAliveClientMixin builder: (context) => ShadDialog( title: Row( children: [ - CircleAvatar( - radius: 20, - backgroundColor: theme.colorScheme.primary.withValues(alpha: 0.2), - child: Text( - '₿', - style: TextStyle( - fontSize: 20, - color: theme.colorScheme.primary, - ), - ), - ), + _AppLogo(radius: 20, fontSize: 20), const SizedBox(width: 12), const Text('模拟所'), ], @@ -319,40 +94,11 @@ class _MinePageState extends State with AutomaticKeepAliveClientMixin mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - '虚拟货币模拟交易平台', - style: theme.textTheme.muted, - ), + Text('虚拟货币模拟交易平台', style: theme.textTheme.muted), const SizedBox(height: 16), - Row( - children: [ - Icon( - LucideIcons.code, - size: 14, - color: theme.colorScheme.mutedForeground, - ), - const SizedBox(width: 6), - Text( - '版本: 1.0.0', - style: theme.textTheme.muted.copyWith(fontSize: 12), - ), - ], - ), + _InfoRow(icon: LucideIcons.code, text: '版本: 1.0.0'), const SizedBox(height: 8), - Row( - children: [ - Icon( - LucideIcons.heart, - size: 14, - color: theme.colorScheme.mutedForeground, - ), - const SizedBox(width: 6), - Text( - 'Built with Flutter & shadcn_ui', - style: theme.textTheme.muted.copyWith(fontSize: 12), - ), - ], - ), + const _InfoRow(icon: LucideIcons.heart, text: 'Built with Flutter & shadcn_ui'), ], ), actions: [ @@ -364,18 +110,246 @@ class _MinePageState extends State with AutomaticKeepAliveClientMixin ), ); } + + void _handleLogout(AuthProvider auth) { + showShadDialog( + context: context, + builder: (ctx) => ShadDialog.alert( + title: const Text('确认退出'), + description: const Text('确定要退出登录吗?'), + actions: [ + ShadButton.outline( + child: const Text('取消'), + onPressed: () => Navigator.of(ctx).pop(), + ), + ShadButton.destructive( + child: const Text('退出'), + onPressed: () async { + Navigator.of(ctx).pop(); + await auth.logout(); + if (ctx.mounted) { + Navigator.of(ctx).pushAndRemoveUntil( + MaterialPageRoute(builder: (_) => const LoginPage()), + (route) => false, + ); + } + }, + ), + ], + ), + ); + } } -class _MenuItem { +/// 用户卡片组件 +class _UserCard extends StatelessWidget { + final dynamic user; + + const _UserCard({required this.user}); + + @override + Widget build(BuildContext context) { + final theme = ShadTheme.of(context); + + return ShadCard( + padding: const EdgeInsets.all(20), + child: Row( + children: [ + _AppLogo(radius: 32, fontSize: 24, text: user?.avatarText), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + user?.username ?? '未登录', + style: theme.textTheme.h3.copyWith(fontWeight: FontWeight.bold), + ), + const SizedBox(height: 6), + ShadBadge( + backgroundColor: theme.colorScheme.primary.withValues(alpha: 0.2), + child: Text( + '普通用户', + style: TextStyle(fontSize: 12, color: theme.colorScheme.primary), + ), + ), + ], + ), + ), + Icon(LucideIcons.chevronRight, color: theme.colorScheme.mutedForeground), + ], + ), + ); + } +} + +/// 应用 Logo 组件 +class _AppLogo extends StatelessWidget { + final double radius; + final double fontSize; + final String? text; + + const _AppLogo({required this.radius, required this.fontSize, this.text}); + + @override + Widget build(BuildContext context) { + final theme = ShadTheme.of(context); + + return CircleAvatar( + radius: radius, + backgroundColor: theme.colorScheme.primary.withValues(alpha: 0.2), + child: Text( + text ?? '₿', + style: TextStyle( + fontSize: fontSize, + color: theme.colorScheme.primary, + fontWeight: FontWeight.bold, + ), + ), + ); + } +} + +/// 信息行组件 +class _InfoRow extends StatelessWidget { final IconData icon; - final String title; - final String? subtitle; - final VoidCallback onTap; + final String text; - _MenuItem({ - required this.icon, - required this.title, - this.subtitle, - required this.onTap, - }); + const _InfoRow({required this.icon, required this.text}); + + @override + Widget build(BuildContext context) { + final theme = ShadTheme.of(context); + + return Row( + children: [ + Icon(icon, size: 14, color: theme.colorScheme.mutedForeground), + const SizedBox(width: 6), + Text(text, style: theme.textTheme.muted.copyWith(fontSize: 12)), + ], + ); + } +} + +/// 菜单列表组件 +class _MenuList extends StatelessWidget { + final void Function(String) onShowComingSoon; + final VoidCallback onShowAbout; + + const _MenuList({required this.onShowComingSoon, required this.onShowAbout}); + + @override + Widget build(BuildContext context) { + final theme = ShadTheme.of(context); + final items = _buildMenuItems(); + + return ShadCard( + padding: EdgeInsets.zero, + child: Column( + children: [ + for (var i = 0; i < items.length; i++) ...[ + _MenuItemTile(item: items[i]), + if (i < items.length - 1) + Divider(color: theme.colorScheme.border, height: 1, indent: 56), + ], + ], + ), + ); + } + + List<_MenuItem> _buildMenuItems() { + return [ + _MenuItem(icon: LucideIcons.userCheck, title: '实名认证', subtitle: '完成实名认证,解锁更多功能', onTap: () => onShowComingSoon('实名认证')), + _MenuItem(icon: LucideIcons.shield, title: '安全设置', subtitle: '密码、二次验证等安全设置', onTap: () => onShowComingSoon('安全设置')), + _MenuItem(icon: LucideIcons.bell, title: '消息通知', subtitle: '管理消息推送设置', onTap: () => onShowComingSoon('消息通知')), + _MenuItem(icon: LucideIcons.settings, title: '系统设置', subtitle: '主题、语言等偏好设置', onTap: () => onShowComingSoon('系统设置')), + _MenuItem(icon: LucideIcons.info, title: '关于我们', subtitle: '版本信息与用户协议', onTap: onShowAbout), + ]; + } +} + +/// 菜单项组件 +class _MenuItemTile extends StatelessWidget { + final _MenuItem item; + + const _MenuItemTile({required this.item}); + + @override + Widget build(BuildContext context) { + final theme = ShadTheme.of(context); + + return InkWell( + onTap: item.onTap, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row( + children: [ + _MenuIcon(icon: item.icon), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(item.title, style: theme.textTheme.small.copyWith(fontWeight: FontWeight.w500)), + if (item.subtitle != null) ...[ + const SizedBox(height: 2), + Text(item.subtitle!, style: theme.textTheme.muted.copyWith(fontSize: 11)), + ], + ], + ), + ), + Icon(LucideIcons.chevronRight, size: 18, color: theme.colorScheme.mutedForeground), + ], + ), + ), + ); + } +} + +/// 菜单图标组件 +class _MenuIcon extends StatelessWidget { + final IconData icon; + + const _MenuIcon({required this.icon}); + + @override + Widget build(BuildContext context) { + final theme = ShadTheme.of(context); + + return Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: theme.colorScheme.primary.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(10), + ), + child: Icon(icon, size: 20, color: theme.colorScheme.primary), + ); + } +} + +/// 退出登录按钮 +class _LogoutButton extends StatelessWidget { + final VoidCallback onLogout; + + const _LogoutButton({required this.onLogout}); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: double.infinity, + height: 48, + child: ShadButton.destructive( + onPressed: onLogout, + child: const Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(LucideIcons.logOut, size: 18), + SizedBox(width: 8), + Text('退出登录', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)), + ], + ), + ), + ); + } }