/// 管理后台仪表板 /// 包含所有管理功能的导航 import 'package:flutter/material.dart'; import '../../utils/web_theme.dart'; import 'widgets/admin_sidebar.dart'; import 'llm_observability_screen.dart'; import 'public_model_management_screen.dart'; import 'system_presets_management_screen.dart'; import 'public_templates_management_screen.dart'; import 'enhanced_templates_management_screen.dart'; import 'user_management_screen.dart'; import 'role_management_screen.dart'; import 'subscription_management_screen.dart'; import 'billing_audit_screen.dart'; import 'package:get_it/get_it.dart'; import 'package:ainoval/services/api_service/repositories/impl/admin/llm_observability_repository_impl.dart'; import 'package:ainoval/widgets/analytics/analytics_card.dart'; import 'package:ainoval/widgets/analytics/model_usage_chart.dart'; import 'package:ainoval/models/analytics_data.dart'; import 'package:ainoval/models/admin/llm_observability_models.dart'; class AdminDashboardScreen extends StatefulWidget { const AdminDashboardScreen({super.key}); @override State createState() => _AdminDashboardScreenState(); } class _AdminDashboardScreenState extends State { int _selectedIndex = 0; final List _screens = [ const AdminOverviewScreen(), // 0: 仪表板 const LLMObservabilityScreen(), // 1: LLM可观测性 const UserManagementScreen(), // 2: 用户管理(替换占位页) const RoleManagementScreen(), // 3: 角色管理(替换占位页) const SubscriptionManagementScreen(), // 4: 订阅管理(替换占位页) const PublicModelManagementScreen(), // 5: 公共模型 const SystemPresetsManagementScreen(), // 6: 系统预设 const PublicTemplatesManagementScreen(), // 7: 公共模板 const AdminSystemSettingsScreen(), // 8: 系统配置 const EnhancedTemplatesManagementScreen(), // 9: 增强模板 const BillingAuditScreen(), // 10: 计费审计 ]; @override Widget build(BuildContext context) { return Scaffold( backgroundColor: WebTheme.getBackgroundColor(context), body: Row( children: [ AdminSidebar( selectedIndex: _selectedIndex, onItemSelected: (index) { setState(() { _selectedIndex = index; }); }, ), VerticalDivider( thickness: 1, width: 1, color: WebTheme.getBorderColor(context), ), Expanded( child: _screens[_selectedIndex], ), ], ), ); } } // AdminNavigationItem 类已移除,现在使用 AdminSidebar 统一管理导航 /// 管理后台概览页面 class AdminOverviewScreen extends StatefulWidget { const AdminOverviewScreen({super.key}); @override State createState() => _AdminOverviewScreenState(); } class _AdminOverviewScreenState extends State { late LLMObservabilityRepositoryImpl _repository; bool _loading = true; String? _error; Map _overview = const {}; List _modelUsage = const []; @override void initState() { super.initState(); _repository = GetIt.instance(); _loadData(); } Future _loadData() async { setState(() { _loading = true; _error = null; }); try { final results = await Future.wait([ _repository.getOverviewStatistics(), _repository.getModelStatistics(), ]); final overview = results[0] as Map; final modelStats = results[1] as List; setState(() { _overview = overview; _modelUsage = _buildModelUsageFromStats(modelStats); }); } catch (e) { setState(() { _error = e.toString(); }); } finally { setState(() { _loading = false; }); } } List _buildModelUsageFromStats(List stats) { if (stats.isEmpty) return const []; // 优先使用 Token 占比,没有则按调用次数占比 int totalTokens = 0; for (final s in stats) { totalTokens += s.statistics.totalTokens; } final bool useTokens = totalTokens > 0; final int totalBase = useTokens ? totalTokens : stats.fold(0, (acc, s) => acc + s.statistics.totalCalls); if (totalBase == 0) return const []; final List result = []; const palette = ['#3B82F6', '#8B5CF6', '#10B981', '#F59E0B', '#EF4444', '#06B6D4']; for (int i = 0; i < stats.length; i++) { final s = stats[i]; final int base = useTokens ? s.statistics.totalTokens : s.statistics.totalCalls; final int pct = ((base / totalBase) * 100).round(); result.add(ModelUsageData( modelName: s.modelName, percentage: pct, totalTokens: useTokens ? base : s.statistics.totalTokens, color: palette[i % palette.length], )); } // 只取前8个,避免图例过长 result.sort((a, b) => b.totalTokens.compareTo(a.totalTokens)); return result.take(8).toList(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: WebTheme.getBackgroundColor(context), appBar: AppBar( backgroundColor: WebTheme.getBackgroundColor(context), foregroundColor: WebTheme.getTextColor(context), title: Text( '管理后台仪表板', style: TextStyle(color: WebTheme.getTextColor(context)), ), centerTitle: true, elevation: 0, actions: [ IconButton( onPressed: _loadData, icon: const Icon(Icons.refresh), tooltip: '刷新', ), ], ), body: Align( alignment: Alignment.topCenter, child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 1600), child: Padding( padding: const EdgeInsets.all(16), child: _error != null ? Center(child: Text('加载失败: $_error')) : (_loading ? const Center(child: CircularProgressIndicator()) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildOverviewStatsRow(context), const SizedBox(height: 16), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 模型占比图 Expanded( child: AnalyticsCard( title: '模型占比', value: '', child: ModelUsageChart( data: _modelUsage, viewMode: AnalyticsViewMode.daily, ), ), ), const SizedBox(width: 16), // 模块入口卡片区 Expanded( child: GridView.count( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), crossAxisCount: 2, crossAxisSpacing: 16, mainAxisSpacing: 16, childAspectRatio: 1.6, children: const [ OverviewCard( title: 'LLM可观测性', description: '监控AI模型调用日志、性能指标和错误统计', icon: Icons.visibility, count: '—', ), OverviewCard( title: '用户管理', description: '管理用户账户,查看用户统计信息', icon: Icons.people, count: '—', ), OverviewCard( title: '公共模型', description: '管理和配置系统可用的AI模型', icon: Icons.cloud, count: '—', ), OverviewCard( title: '系统预设', description: '管理系统级别的预设配置', icon: Icons.smart_button, count: '—', ), ], ), ), ], ), const SizedBox(height: 16), Expanded( child: GridView.count( crossAxisCount: 3, crossAxisSpacing: 16, mainAxisSpacing: 16, childAspectRatio: 1.2, children: const [ OverviewCard( title: '公共模板', description: '管理公共提示词模板库', icon: Icons.article, count: '—', ), OverviewCard( title: '增强模板', description: '管理用户提交的增强模板', icon: Icons.auto_awesome, count: '—', ), OverviewCard( title: '订阅管理', description: '订阅套餐、配额与结算', icon: Icons.subscriptions, count: '—', ), ], ), ), ], )), ), ), ), ); } Widget _buildOverviewStatsRow(BuildContext context) { final totalCalls = (_overview['totalCalls'] ?? 0).toString(); final successfulCalls = (_overview['successfulCalls'] ?? 0).toString(); final failedCalls = (_overview['failedCalls'] ?? 0).toString(); final successRate = ((_overview['successRate'] ?? 0.0) as num).toDouble(); return Row( children: [ Expanded( child: AnalyticsOverviewCard( title: '总调用次数', value: totalCalls, icon: Icons.analytics, subtitle: '统计范围内的模型调用总数', ), ), const SizedBox(width: 16), Expanded( child: AnalyticsOverviewCard( title: '成功次数', value: successfulCalls, icon: Icons.check_circle, subtitle: '无错误完成的调用次数', ), ), const SizedBox(width: 16), Expanded( child: AnalyticsOverviewCard( title: '失败次数', value: failedCalls, icon: Icons.error_outline, subtitle: '发生错误的调用次数', ), ), const SizedBox(width: 16), Expanded( child: AnalyticsOverviewCard( title: '成功率', value: '${successRate.toStringAsFixed(1)}%', icon: Icons.percent, subtitle: '成功调用占比', ), ), ], ); } } class OverviewCard extends StatelessWidget { final String title; final String description; final IconData icon; final String? count; const OverviewCard({ super.key, required this.title, required this.description, required this.icon, this.count, }); @override Widget build(BuildContext context) { return Card( color: WebTheme.getCardColor(context), elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide( color: WebTheme.getBorderColor(context), width: 1, ), ), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( icon, size: 32, color: WebTheme.getTextColor(context), ), const Spacer(), if (count != null) Text( count!, style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: WebTheme.getTextColor(context), ), ), ], ), const SizedBox(height: 12), Text( title, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: WebTheme.getTextColor(context), ), ), const SizedBox(height: 8), Text( description, style: TextStyle( fontSize: 14, color: WebTheme.getSecondaryTextColor(context), ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ), ); } } /// 占位页面 - 用户管理 class AdminUsersScreen extends StatelessWidget { const AdminUsersScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: WebTheme.getBackgroundColor(context), appBar: AppBar( backgroundColor: WebTheme.getBackgroundColor(context), foregroundColor: WebTheme.getTextColor(context), title: Text( '用户管理', style: TextStyle(color: WebTheme.getTextColor(context)), ), elevation: 0, ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.people_outline, size: 64, color: WebTheme.getSecondaryTextColor(context), ), const SizedBox(height: 16), Text( '用户管理页面', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: WebTheme.getTextColor(context), ), ), const SizedBox(height: 8), Text( '此功能正在开发中...', style: TextStyle( fontSize: 16, color: WebTheme.getSecondaryTextColor(context), ), ), ], ), ), ); } } /// 占位页面 - 角色管理 class AdminRolesScreen extends StatelessWidget { const AdminRolesScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: WebTheme.getBackgroundColor(context), appBar: AppBar( backgroundColor: WebTheme.getBackgroundColor(context), foregroundColor: WebTheme.getTextColor(context), title: Text( '角色管理', style: TextStyle(color: WebTheme.getTextColor(context)), ), elevation: 0, ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.security_outlined, size: 64, color: WebTheme.getSecondaryTextColor(context), ), const SizedBox(height: 16), Text( '角色管理页面', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: WebTheme.getTextColor(context), ), ), const SizedBox(height: 8), Text( '此功能正在开发中...', style: TextStyle( fontSize: 16, color: WebTheme.getSecondaryTextColor(context), ), ), ], ), ), ); } } /// 占位页面 - 订阅管理 class AdminSubscriptionScreen extends StatelessWidget { const AdminSubscriptionScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: WebTheme.getBackgroundColor(context), appBar: AppBar( backgroundColor: WebTheme.getBackgroundColor(context), foregroundColor: WebTheme.getTextColor(context), title: Text( '订阅管理', style: TextStyle(color: WebTheme.getTextColor(context)), ), elevation: 0, ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.subscriptions_outlined, size: 64, color: WebTheme.getSecondaryTextColor(context), ), const SizedBox(height: 16), Text( '订阅管理页面', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: WebTheme.getTextColor(context), ), ), const SizedBox(height: 8), Text( '此功能正在开发中...', style: TextStyle( fontSize: 16, color: WebTheme.getSecondaryTextColor(context), ), ), ], ), ), ); } } /// 占位页面 - 系统设置 class AdminSystemSettingsScreen extends StatelessWidget { const AdminSystemSettingsScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: WebTheme.getBackgroundColor(context), appBar: AppBar( backgroundColor: WebTheme.getBackgroundColor(context), foregroundColor: WebTheme.getTextColor(context), title: Text( '系统配置', style: TextStyle(color: WebTheme.getTextColor(context)), ), elevation: 0, ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.settings_outlined, size: 64, color: WebTheme.getSecondaryTextColor(context), ), const SizedBox(height: 16), Text( '系统配置页面', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: WebTheme.getTextColor(context), ), ), const SizedBox(height: 8), Text( '此功能正在开发中...', style: TextStyle( fontSize: 16, color: WebTheme.getSecondaryTextColor(context), ), ), ], ), ), ); } }