import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:airhub_app/theme/design_tokens.dart'; import 'package:airhub_app/widgets/animated_gradient_background.dart'; import 'package:airhub_app/widgets/glass_dialog.dart'; import 'package:airhub_app/widgets/ios_toast.dart'; import 'package:airhub_app/features/spirit/domain/entities/spirit.dart'; import 'package:airhub_app/features/spirit/presentation/controllers/spirit_controller.dart'; class AgentManagePage extends ConsumerStatefulWidget { const AgentManagePage({super.key}); @override ConsumerState createState() => _AgentManagePageState(); } class _AgentManagePageState extends ConsumerState { @override Widget build(BuildContext context) { final spiritsAsync = ref.watch(spiritControllerProvider); return Scaffold( backgroundColor: Colors.transparent, body: Stack( children: [ const AnimatedGradientBackground(), Column( children: [ _buildHeader(context), Expanded( child: spiritsAsync.when( loading: () => const Center( child: CircularProgressIndicator(color: Colors.white), ), error: (error, _) => Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( '加载失败', style: TextStyle( color: Colors.white.withOpacity(0.8), fontSize: 16, ), ), const SizedBox(height: 12), GestureDetector( onTap: () => ref.read(spiritControllerProvider.notifier).refresh(), child: Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(20), ), child: const Text( '重试', style: TextStyle(color: Colors.white), ), ), ), ], ), ), data: (spirits) { if (spirits.isEmpty) { return Center( child: Text( '暂无角色记忆', style: TextStyle( color: Colors.white.withOpacity(0.6), fontSize: 16, ), ), ); } return ListView.builder( padding: EdgeInsets.only( top: 20, left: 20, right: 20, bottom: 40 + MediaQuery.of(context).padding.bottom, ), itemCount: spirits.length, itemBuilder: (context, index) { return _buildAgentCard(spirits[index]); }, ); }, ), ), ], ), ], ), ); } Widget _buildHeader(BuildContext context) { return Container( padding: EdgeInsets.only( top: MediaQuery.of(context).padding.top + 20, left: AppSpacing.lg, right: AppSpacing.lg, bottom: AppSpacing.md, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ GestureDetector( onTap: () => Navigator.pop(context), child: Container( width: 40, height: 40, decoration: BoxDecoration( color: AppColors.iconBtnBg, borderRadius: BorderRadius.circular(AppRadius.button), ), child: const Icon( Icons.arrow_back_ios_new, color: AppColors.textPrimary, size: 18, ), ), ), Text('角色记忆', style: AppTextStyles.title), GestureDetector( onTap: () { showGlassDialog( context: context, title: '什么是角色记忆?', description: '角色记忆是您与 AI 互动产生的人格数据,它是独立的数字资产,可以在不同设备间迁移,或分享给好友。', confirmText: '确定', onConfirm: () => Navigator.pop(context), ); }, child: Container( width: 44, height: 44, decoration: BoxDecoration( color: AppColors.iconBtnBg, borderRadius: BorderRadius.circular(AppRadius.button), border: Border.all(color: AppColors.iconBtnBorder), ), child: const Center( child: Text( '?', style: TextStyle( fontSize: 18, color: AppColors.textSecondary, fontWeight: FontWeight.w500, ), ), ), ), ), ], ), ); } Widget _buildAgentCard(Spirit spirit) { final dateStr = spirit.createdAt != null ? spirit.createdAt!.substring(0, 10).replaceAll('-', '/') : ''; return Container( margin: const EdgeInsets.only(bottom: 16), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: const Color(0xFFD4A373), // Fallback gradient: const LinearGradient(colors: AppColors.gradientCapybara), borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: const Color(0xFFC9A07A).withOpacity(0.25), blurRadius: 20, offset: const Offset(0, 8), ), ], ), child: Stack( children: [ // Top highlight layer Positioned( left: 0, right: 0, top: 0, height: 60, child: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.white.withOpacity(0.12), Colors.transparent], ), borderRadius: const BorderRadius.vertical( top: Radius.circular(20), ), ), ), ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Text( dateStr, style: TextStyle( color: Colors.white.withOpacity(0.85), fontSize: 12, ), ), ], ), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 48, height: 48, decoration: BoxDecoration( color: Colors.white.withOpacity(0.25), borderRadius: BorderRadius.circular(12), ), alignment: Alignment.center, child: spirit.avatar != null && spirit.avatar!.isNotEmpty ? ClipRRect( borderRadius: BorderRadius.circular(12), child: Image.network( spirit.avatar!, width: 48, height: 48, fit: BoxFit.cover, errorBuilder: (_, __, ___) => const Text('🧠', style: TextStyle(fontSize: 24)), ), ) : const Text('🧠', style: TextStyle(fontSize: 24)), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( spirit.name, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white, shadows: [ Shadow( color: Color(0x1A000000), blurRadius: 2, offset: Offset(0, 1), ), ], ), ), ], ), ), ], ), const SizedBox(height: 12), _buildDetailRow('状态:', spirit.isActive ? '活跃' : '未激活'), const SizedBox(height: 12), Container(height: 1, color: Colors.white.withOpacity(0.2)), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ _buildActionBtn( '解绑', isDanger: true, onTap: () => _showUnbindDialog(spirit), ), const SizedBox(width: 8), _buildActionBtn( '删除', isDanger: true, onTap: () => _showDeleteDialog(spirit), ), ], ), ], ), ], ), ); } Widget _buildDetailRow(String label, String value) { return RichText( text: TextSpan( style: TextStyle(fontSize: 14, color: Colors.white.withOpacity(0.85)), children: [ TextSpan(text: label), TextSpan( text: value, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w500, ), ), ], ), ); } Widget _buildActionBtn( String text, { bool isDanger = false, bool isInject = false, VoidCallback? onTap, }) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.white.withOpacity(0.3)), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ if (isDanger) ...[ Icon( Icons.link_off, size: 14, color: AppColors.danger.withOpacity(0.9), ), const SizedBox(width: 4), ] else if (isInject) ...[ Icon(Icons.download, size: 14, color: const Color(0xFFB07D5A)), const SizedBox(width: 4), ], Text( text, style: TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: isDanger ? AppColors.danger : (isInject ? const Color(0xFFB07D5A) : Colors.white), ), ), ], ), ), ); } void _showUnbindDialog(Spirit spirit) { showGlassDialog( context: context, title: '确认解绑角色记忆?', description: '解绑后,该角色记忆将与当前设备断开连接,但数据会保留在云端。', cancelText: '取消', confirmText: '确认解绑', isDanger: true, onConfirm: () async { Navigator.pop(context); // Close dialog final success = await ref.read(spiritControllerProvider.notifier).unbind(spirit.id); if (mounted) { AppToast.show(context, success ? '已解绑: ${spirit.name}' : '解绑失败'); } }, ); } void _showDeleteDialog(Spirit spirit) { showGlassDialog( context: context, title: '确认删除角色记忆?', description: '删除后,该角色记忆数据将无法恢复。', cancelText: '取消', confirmText: '确认删除', isDanger: true, onConfirm: () async { Navigator.pop(context); final success = await ref.read(spiritControllerProvider.notifier).delete(spirit.id); if (mounted) { AppToast.show(context, success ? '已删除: ${spirit.name}' : '删除失败'); } }, ); } }