import 'package:flutter/material.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/i18n/app_localizations.dart'; import '../../../../core/services/issuer_credit_service.dart'; /// 信用评级页面 /// /// 四因子信用评分:核销率35% + (1-Breakage率)25% + 市场存续20% + 用户满意度20% /// AI建议列表:信用提升建议 class CreditPage extends StatefulWidget { const CreditPage({super.key}); @override State createState() => _CreditPageState(); } class _CreditPageState extends State { final _creditService = IssuerCreditService(); bool _isLoading = true; String? _error; CreditModel? _credit; @override void initState() { super.initState(); _loadCredit(); } Future _loadCredit() async { setState(() { _isLoading = true; _error = null; }); try { final credit = await _creditService.getCredit(); if (!mounted) return; setState(() { _credit = credit; _isLoading = false; }); } catch (e) { debugPrint('[CreditPage] loadCredit error: $e'); if (!mounted) return; setState(() { _isLoading = false; _error = e.toString(); }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(context.t('credit_title'))), body: _isLoading ? const Center(child: CircularProgressIndicator()) : _error != null ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error_outline, size: 48, color: AppColors.textTertiary), const SizedBox(height: 16), Text(_error!, style: const TextStyle(color: AppColors.textSecondary)), const SizedBox(height: 16), ElevatedButton(onPressed: _loadCredit, child: Text(context.t('retry'))), ], ), ) : RefreshIndicator( onRefresh: _loadCredit, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.all(20), child: Column( children: [ // Score Gauge _buildScoreGauge(context), const SizedBox(height: 24), // Four Factors _buildFactorsCard(context), const SizedBox(height: 20), // Tier Progress _buildTierProgress(context), const SizedBox(height: 20), // AI Suggestions _buildAiSuggestions(context), const SizedBox(height: 20), // Credit History _buildCreditHistory(context), ], ), ), ), ); } Widget _buildScoreGauge(BuildContext context) { final credit = _credit!; return Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(16), border: Border.all(color: AppColors.borderLight), ), child: Column( children: [ Container( width: 120, height: 120, decoration: BoxDecoration( shape: BoxShape.circle, gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [AppColors.creditAA, AppColors.creditAA.withValues(alpha: 0.3)], ), ), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(credit.grade, style: const TextStyle(fontSize: 32, fontWeight: FontWeight.w700, color: Colors.white)), Text('${credit.score}${context.t('credit_score_unit')}', style: const TextStyle(fontSize: 14, color: Colors.white70)), ], ), ), ), const SizedBox(height: 16), Text(context.t('credit_score_label'), style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600)), const SizedBox(height: 4), Text(context.t('credit_gap_label'), style: const TextStyle(fontSize: 13, color: AppColors.textSecondary)), ], ), ); } Widget _buildFactorsCard(BuildContext context) { final factors = _credit!.factors; final factorColors = [AppColors.success, AppColors.info, AppColors.primary, AppColors.warning]; return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(12), border: Border.all(color: AppColors.borderLight), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(context.t('credit_factors'), style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600)), const SizedBox(height: 16), ...factors.asMap().entries.map((entry) { final f = entry.value; final color = factorColors[entry.key % factorColors.length]; final score = f.score / 100; return Padding( padding: const EdgeInsets.only(bottom: 16), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(f.name, style: const TextStyle(fontSize: 13)), Text( '${f.score.toInt()}${context.t('credit_score_unit')} (${(f.weight * 100).toInt()}%)', style: const TextStyle(fontSize: 12, color: AppColors.textSecondary), ), ], ), const SizedBox(height: 6), ClipRRect( borderRadius: BorderRadius.circular(4), child: LinearProgressIndicator( value: score.clamp(0.0, 1.0), backgroundColor: AppColors.gray100, valueColor: AlwaysStoppedAnimation(color), minHeight: 8, ), ), ], ), ); }), if (factors.isEmpty) const Padding( padding: EdgeInsets.all(16), child: Center(child: Text(context.t('credit_no_factors'), style: const TextStyle(color: AppColors.textTertiary))), ), ], ), ); } Widget _buildTierProgress(BuildContext context) { final credit = _credit!; final tierColors = [AppColors.tierSilver, AppColors.tierGold, AppColors.tierPlatinum, AppColors.tierDiamond]; final tiers = credit.tiers; return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(12), border: Border.all(color: AppColors.borderLight), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(context.t('credit_tier_title'), style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600)), const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: tiers.asMap().entries.map((entry) { final index = entry.key; final name = entry.value; final isReached = index <= credit.currentTierIndex; final color = tierColors[index % tierColors.length]; return Column( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: isReached ? color.withValues(alpha: 0.15) : AppColors.gray100, shape: BoxShape.circle, border: isReached ? Border.all(color: color, width: 2) : null, ), child: Icon( Icons.star_rounded, color: isReached ? color : AppColors.textTertiary, size: 22, ), ), const SizedBox(height: 6), Text( name, style: TextStyle( fontSize: 12, color: isReached ? color : AppColors.textTertiary, fontWeight: isReached ? FontWeight.w600 : FontWeight.w400, ), ), ], ); }).toList(), ), const SizedBox(height: 12), Text( context.t('credit_tier_progress'), style: const TextStyle(fontSize: 12, color: AppColors.textSecondary), ), ], ), ); } Widget _buildAiSuggestions(BuildContext context) { final suggestions = _credit!.suggestions; final icons = [Icons.trending_up_rounded, Icons.notification_important_rounded, Icons.rate_review_rounded]; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.auto_awesome_rounded, color: AppColors.primary, size: 20), const SizedBox(width: 8), Text(context.t('credit_ai_title'), style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600)), ], ), const SizedBox(height: 12), if (suggestions.isEmpty) Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: AppColors.primarySurface, borderRadius: BorderRadius.circular(10), ), child: Center( child: Text(context.t('credit_no_suggestions'), style: const TextStyle(color: AppColors.textSecondary)), ), ) else ...suggestions.asMap().entries.map((entry) { final icon = icons[entry.key % icons.length]; final text = entry.value; return Container( margin: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: AppColors.primarySurface, borderRadius: BorderRadius.circular(10), ), child: Row( children: [ Icon(icon, color: AppColors.primary, size: 20), const SizedBox(width: 12), Expanded( child: Text(text, style: const TextStyle(fontSize: 13, color: AppColors.textSecondary)), ), ], ), ); }), ], ); } Widget _buildCreditHistory(BuildContext context) { // Credit history not yet provided by API, keeping placeholder return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(12), border: Border.all(color: AppColors.borderLight), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(context.t('credit_history_title'), style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600)), const SizedBox(height: 12), const Padding( padding: EdgeInsets.all(16), child: Center(child: Text('--', style: TextStyle(color: AppColors.textTertiary))), ), ], ), ); } }