part of 'studio_app.dart'; class StudioDashboardPage extends StatelessWidget { const StudioDashboardPage({ super.key, required this.tab, required this.user, required this.activities, required this.rootNodes, required this.onOpenNode, }); final StudioTab tab; final StudioUser user; final List activities; final List rootNodes; final ValueChanged onOpenNode; @override Widget build(BuildContext context) { final quickNodes = _allLeaves(rootNodes).take(6).toList(); return SingleChildScrollView( child: Padding( padding: const EdgeInsets.only(bottom: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Card( child: Padding( padding: const EdgeInsets.all(20), child: Row( children: [ Container( width: 72, height: 72, decoration: BoxDecoration( gradient: LinearGradient( colors: [ Theme.of( context, ).colorScheme.primary.withOpacity(0.25), Theme.of( context, ).colorScheme.tertiary.withOpacity(0.20), ], ), borderRadius: BorderRadius.circular(24), ), child: const Icon( Icons.space_dashboard_rounded, size: 38, ), ), const SizedBox(width: 18), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '工作台', style: Theme.of(context).textTheme.headlineSmall ?.copyWith(fontWeight: FontWeight.w700), ), const SizedBox(height: 6), Text( '左侧菜单已映射自 studio.html 的核心入口,点击即可打开多标签页。', style: Theme.of(context).textTheme.bodyMedium ?.copyWith(color: Colors.white70), ), ], ), ), Wrap( spacing: 10, runSpacing: 10, children: [ _MiniBadge(text: user.workspace), _MiniBadge(text: user.languageCode.toUpperCase()), ], ), ], ), ), ), const SizedBox(height: 14), LayoutBuilder( builder: (context, constraints) { final cardWidth = constraints.maxWidth >= 1100 ? (constraints.maxWidth - 32) / 3 : constraints.maxWidth >= 760 ? (constraints.maxWidth - 16) / 2 : constraints.maxWidth; return Wrap( spacing: 16, runSpacing: 16, children: [ SizedBox( width: cardWidth, child: _InfoPanel( title: '会话状态', value: user.displayName, icon: Icons.person_rounded, subtitle: '${user.role} · 已登录', ), ), SizedBox( width: cardWidth, child: _InfoPanel( title: '打开页面', value: '${activities.length} 条记录', icon: Icons.tab_rounded, subtitle: '支持多标签并行工作', ), ), SizedBox( width: cardWidth, child: _InfoPanel( title: '运行方式', value: '桌面端', icon: Icons.desktop_windows_rounded, subtitle: 'Flutter Windows / Web 共用', ), ), ], ); }, ), const SizedBox(height: 16), Card( child: Padding( padding: const EdgeInsets.all(18), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '快捷入口', style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.w700, ), ), const SizedBox(height: 12), Wrap( spacing: 12, runSpacing: 12, children: quickNodes .map( (node) => ActionChip( avatar: Icon(node.icon, size: 18), label: Text(node.title), onPressed: () => onOpenNode(node), ), ) .toList(), ), ], ), ), ), const SizedBox(height: 16), LayoutBuilder( builder: (context, constraints) { final leftWidth = constraints.maxWidth >= 980 ? (constraints.maxWidth - 16) * 0.58 : constraints.maxWidth; final rightWidth = constraints.maxWidth >= 980 ? (constraints.maxWidth - 16) * 0.42 : constraints.maxWidth; if (constraints.maxWidth >= 980) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: leftWidth, child: _ActivityPanel(activities: activities), ), const SizedBox(width: 16), SizedBox( width: rightWidth, child: _FeaturePanel(onOpenNode: onOpenNode), ), ], ); } return Column( children: [ _ActivityPanel(activities: activities), const SizedBox(height: 16), _FeaturePanel(onOpenNode: onOpenNode), ], ); }, ), ], ), ), ); } List _allLeaves(List nodes) { final result = []; for (final node in nodes) { if (node.children.isEmpty) { result.add(node); } else { result.addAll(_allLeaves(node.children)); } } return result; } } class _InfoPanel extends StatelessWidget { const _InfoPanel({ required this.title, required this.value, required this.icon, required this.subtitle, }); final String title; final String value; final IconData icon; final String subtitle; @override Widget build(BuildContext context) { return Card( child: Padding( padding: const EdgeInsets.all(18), child: Row( children: [ Container( width: 50, height: 50, decoration: BoxDecoration( color: Theme.of(context).colorScheme.primary.withOpacity(0.15), borderRadius: BorderRadius.circular(16), ), child: Icon(icon), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: Theme.of(context).textTheme.bodySmall ?.copyWith(color: Colors.white60), ), const SizedBox(height: 6), Text( value, style: Theme.of(context).textTheme.titleLarge ?.copyWith(fontWeight: FontWeight.w700), ), const SizedBox(height: 4), Text( subtitle, style: Theme.of(context).textTheme.bodyMedium ?.copyWith(color: Colors.white70), ), ], ), ), ], ), ), ); } } class _ActivityPanel extends StatelessWidget { const _ActivityPanel({required this.activities}); final List activities; @override Widget build(BuildContext context) { return Card( child: Padding( padding: const EdgeInsets.all(18), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '最近活动', style: Theme.of(context).textTheme.titleLarge ?.copyWith(fontWeight: FontWeight.w700), ), const SizedBox(height: 12), ...activities.take(8).map( (item) => Padding( padding: const EdgeInsets.only(bottom: 12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Icon(Icons.bolt_rounded, size: 18), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.title, style: Theme.of(context).textTheme.bodyLarge ?.copyWith(fontWeight: FontWeight.w600), ), const SizedBox(height: 4), Text( item.detail, style: Theme.of(context).textTheme.bodyMedium ?.copyWith(color: Colors.white70), ), ], ), ), ], ), ), ), ], ), ), ); } } class _FeaturePanel extends StatelessWidget { const _FeaturePanel({required this.onOpenNode}); final ValueChanged onOpenNode; @override Widget build(BuildContext context) { final nodes = _allLeaves(studioNavTree); return Card( child: Padding( padding: const EdgeInsets.all(18), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '功能覆盖', style: Theme.of(context).textTheme.titleLarge ?.copyWith(fontWeight: FontWeight.w700), ), const SizedBox(height: 12), const Text( '当前 Flutter 桌面端实现了 Studio 页面的核心交互:登录、导航、标签页、JSON/SQL 编辑、日志和运维任务。', ), const SizedBox(height: 14), Wrap( spacing: 10, runSpacing: 10, children: nodes .take(5) .map( (node) => FilledButton.tonal( onPressed: () => onOpenNode(node), child: Text(node.title), ), ) .toList(), ), ], ), ), ); } List _allLeaves(List nodes) { final result = []; for (final node in nodes) { if (node.children.isEmpty) { result.add(node); } else { result.addAll(_allLeaves(node.children)); } } return result; } }