import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:new_learn/core/api/auth/api.dart'; import 'package:new_learn/core/api/report/api.dart'; import 'package:new_learn/core/api/report/model.dart'; import 'package:new_learn/core/config/app_config.dart'; import 'package:new_learn/core/logger/app_talker.dart'; import 'package:new_learn/core/network/dio_client.dart'; import 'package:new_learn/core/providers/user_provider.dart'; /// ============================================================ /// 【StudentInfoPage】诊断报告欢迎介绍页 —— 对标 Vue studentInfo.vue /// /// 路由参数(GoRouteState.extra): /// context.go('/studentInfo', extra: {'type': 'placementTest'}); /// - type: placementTest(入门测)| exitTest(出门测) /// /// 完整链路:登录成功/全局拦截 → 此页 → H5壳子 → reportComputation(入门测) /// 或 switchTab 首页(出门测) /// ============================================================ class StudentInfoPage extends ConsumerStatefulWidget { const StudentInfoPage({super.key}); @override ConsumerState createState() => _StudentInfoPageState(); } class _StudentInfoPageState extends ConsumerState { UserReportType _report = const UserReportType(); bool _loading = true; String _reportType = 'placementTest'; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { final extra = GoRouterState.of(context).extra as Map? ?? {}; _reportType = (extra['type'] as String?) ?? 'placementTest'; _loadReport(); }); } Future _loadReport() async { setState(() => _loading = true); try { final isExit = _reportType == 'exitTest'; final apiPath = isExit ? '/api/v1/UserReport/GetCurrentAfterReport' : '/api/v1/UserReport/GetCurrentReport'; talker.debug('studentInfo 调 $apiPath'); final report = isExit ? await ReportApi.instance.getCurrentAfterReport() : await ReportApi.instance.getCurrentReport(); talker.debug( 'studentInfo 响应: userName=${report.userName} ' 'subjectReports.length=${report.subjectReports.length} ' 'raw=${report.subjectReports.toString()}', ); if (mounted) { setState(() { _report = report; _loading = false; }); } } catch (e) { talker.error('加载报告数据失败', e); if (mounted) setState(() => _loading = false); } } /// 按钮点击:subjectReports 为空 → 降级置 false 回首页;否则进 H5 壳子 Future _onStart() async { if (_report.subjectReports.isEmpty) { // 降级:只改本地标记,不调后端 talker.debug('studentInfo: subjectReports 为空,降级本地置 false'); if (_reportType == 'exitTest') { await ref.read(currentUserProvider.notifier).markShowAfterReportFalse(); } else { await ref.read(currentUserProvider.notifier).markShowReportFalse(); } if (mounted) context.go('/'); return; } // 进 H5 壳子 context.push('/reportWebView', extra: { 'type': _reportType, }); } @override Widget build(BuildContext context) { final user = ref.watch(currentUserProvider).value; final userName = _report.userName.isNotEmpty ? _report.userName : (user?.userName ?? ''); return Scaffold( backgroundColor: const Color(0xFFF5F8FF), appBar: AppBar( backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( icon: const Icon(Icons.arrow_back_ios_new, color: Color(0xFF1D2B47)), onPressed: () => context.canPop() ? context.pop() : context.go('/'), ), ), body: _loading ? const Center(child: CircularProgressIndicator()) : SafeArea( child: Column( children: [ // 顶部问候 Padding( padding: const EdgeInsets.symmetric(horizontal: 28), child: Row( children: [ const Text( 'Hi . ', style: TextStyle( fontSize: 28, fontWeight: FontWeight.w900, color: Colors.white, ), ), Text( userName, style: const TextStyle( fontSize: 28, fontWeight: FontWeight.w900, color: Colors.white, ), ), ], ), ), // 科目卡片列表 Expanded( child: SingleChildScrollView( padding: const EdgeInsets.fromLTRB(28, 120, 28, 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( padding: EdgeInsets.only(bottom: 12), child: Text( '诊断科目分析', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF1D2B47), ), ), ), Container( width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), ), child: _report.subjectReports.isEmpty ? const Center( child: Text( '暂无数据', style: TextStyle(color: Color(0xFF687FAD)), ), ) : Wrap( spacing: 10, runSpacing: 10, children: _report.subjectReports .map((s) => _SubjectCard( subject: s, )) .toList(), ), ), ], ), ), ), // 底部按钮 Padding( padding: const EdgeInsets.fromLTRB(29, 20, 29, 20), child: SizedBox( width: double.infinity, height: 50, child: ElevatedButton( onPressed: _onStart, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF3D7DF5), foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(25), ), elevation: 0, ), child: const Text( '开启智能诊断报告', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), ), ), ), ], ), ), ); } } class _SubjectCard extends StatelessWidget { const _SubjectCard({required this.subject}); final SubjectReportType subject; @override Widget build(BuildContext context) { return Container( width: 72, height: 88, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), gradient: const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Color(0xFFF5F8FF), Color(0xFFE6EEFF)], ), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ ClipRRect( borderRadius: BorderRadius.circular(8), child: subject.icon.isNotEmpty ? Image.network( subject.icon, width: 40, height: 40, fit: BoxFit.cover, errorBuilder: (_, __, ___) => const Icon( Icons.menu_book, size: 40, color: Color(0xFF3D7DF5), ), ) : const Icon( Icons.menu_book, size: 40, color: Color(0xFF3D7DF5), ), ), const SizedBox(height: 8), Text( subject.subjectName, style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: Color(0xFF1D2B47), ), ), ], ), ); } }