import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import '../../../../core/di/injection_container.dart'; /// 监控状态枚举 enum MonitorStatus { pending, // 待开启 active, // 监控中 } /// 监控页面 - 显示监控状态和控制 /// 展示用户序列号、社区信息,未来将接入实时监控视频流 class MiningPage extends ConsumerStatefulWidget { const MiningPage({super.key}); @override ConsumerState createState() => _MiningPageState(); } class _MiningPageState extends ConsumerState { // 当前监控状态 MonitorStatus _monitorStatus = MonitorStatus.pending; // 用户数据(从存储加载) String _serialNumber = '--'; String? _avatarSvg; String? _avatarUrl; String? _localAvatarPath; // 本地头像文件路径 // 授权数据(从 authorization-service 获取) String _community = '--'; String _province = '--'; String _city = '--'; @override void initState() { super.initState(); // 先同步检查本地头像,再异步加载其他数据 _checkLocalAvatarSync(); _loadUserData(); _loadAuthorizationData(); } /// 同步检查本地头像文件(在 build 之前快速获取) void _checkLocalAvatarSync() { WidgetsBinding.instance.addPostFrameCallback((_) async { final accountService = ref.read(accountServiceProvider); final localPath = await accountService.getLocalAvatarPath(); if (mounted && localPath != null && _localAvatarPath == null) { setState(() { _localAvatarPath = localPath; }); } }); } /// 加载用户数据 Future _loadUserData() async { final accountService = ref.read(accountServiceProvider); // 并行加载所有数据 final results = await Future.wait([ accountService.getUserSerialNum(), accountService.getAvatarSvg(), accountService.getAvatarUrl(), accountService.getLocalAvatarPath(), ]); final serialNum = results[0] as String?; final avatarSvg = results[1] as String?; final avatarUrl = results[2] as String?; final localAvatarPath = results[3] as String?; if (mounted) { setState(() { _serialNumber = serialNum?.toString() ?? '--'; _avatarSvg = avatarSvg; _avatarUrl = avatarUrl; _localAvatarPath = localAvatarPath; }); // 如果有远程URL但没有本地缓存,后台下载并缓存 if (avatarUrl != null && avatarUrl.isNotEmpty && localAvatarPath == null) { _downloadAndCacheAvatar(avatarUrl); } } } /// 后台下载并缓存头像 Future _downloadAndCacheAvatar(String url) async { final accountService = ref.read(accountServiceProvider); final localPath = await accountService.downloadAndCacheAvatar(url); if (mounted && localPath != null) { setState(() { _localAvatarPath = localPath; }); } } /// 加载授权数据(社区、省、市) Future _loadAuthorizationData() async { try { debugPrint('[MiningPage] 开始加载授权数据...'); final authorizationService = ref.read(authorizationServiceProvider); final summary = await authorizationService.getMyAuthorizationSummary(); if (mounted) { setState(() { _community = summary.communityName ?? '--'; _city = summary.cityCompanyName ?? '--'; _province = summary.provinceCompanyName ?? '--'; }); debugPrint('[MiningPage] 授权数据加载成功:'); debugPrint('[MiningPage] 社区: $_community'); debugPrint('[MiningPage] 市公司: $_city'); debugPrint('[MiningPage] 省公司: $_province'); } } catch (e, stackTrace) { debugPrint('[MiningPage] 加载授权数据失败: $e'); debugPrint('[MiningPage] 堆栈: $stackTrace'); // 失败时保持默认值 '--' } } /// 显示帮助信息 void _showHelpInfo() { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('监控说明'), content: const Text('监控是您查看榴莲种植实况的工具。\n\n' '开启监控后,您可以实时查看种植基地的视频流。\n\n' '目前功能正在开发中,敬请期待。'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('知道了'), ), ], ), ); } /// 开启监控 void _startMonitor() { setState(() { _monitorStatus = MonitorStatus.active; }); // TODO: 未来在这里连接实时监控视频流 } /// 关闭监控 void _stopMonitor() { setState(() { _monitorStatus = MonitorStatus.pending; }); // TODO: 未来在这里断开视频流连接 } /// 获取状态文本 String _getStatusText() { switch (_monitorStatus) { case MonitorStatus.pending: return '开启监控'; case MonitorStatus.active: return '监控中'; } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, body: Container( width: double.infinity, height: double.infinity, decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Color(0xFFFFF5E6), Color(0xFFFFE4B5), ], ), ), child: SafeArea( child: Column( children: [ // 顶部标题栏 _buildAppBar(), // 用户信息区域 _buildUserInfo(), const SizedBox(height: 24), // 挖矿状态区域 Expanded( child: _buildMiningStatusArea(), ), ], ), ), ), ); } /// 构建顶部标题栏 Widget _buildAppBar() { return Container( height: 56, padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( children: [ // 帮助按钮(左侧) GestureDetector( onTap: _showHelpInfo, child: Container( width: 48, height: 48, alignment: Alignment.center, child: Container( width: 24, height: 24, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( color: const Color(0xFF8B5A2B), width: 2, ), ), child: const Center( child: Text( 'i', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: Color(0xFF8B5A2B), ), ), ), ), ), ), // 标题 const Expanded( child: Text( '监控', style: TextStyle( fontSize: 18, fontFamily: 'Inter', fontWeight: FontWeight.w700, height: 1.25, letterSpacing: -0.27, color: Color(0xFF5D4037), ), textAlign: TextAlign.center, ), ), // 关闭按钮(仅在监控开启时显示) if (_monitorStatus == MonitorStatus.active) GestureDetector( onTap: _stopMonitor, child: Container( width: 48, height: 48, alignment: Alignment.center, child: const Icon( Icons.close, size: 24, color: Color(0xFF8B5A2B), ), ), ) else const SizedBox(width: 48), // 占位保持布局平衡 ], ), ); } /// 构建用户信息区域 Widget _buildUserInfo() { // 构建省市显示文本 String locationText = ''; if (_province != '--' && _city != '--') { locationText = '$_province · $_city'; } else if (_province != '--') { locationText = _province; } else if (_city != '--') { locationText = _city; } return Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( children: [ // 头像 Container( width: 80, height: 80, decoration: BoxDecoration( borderRadius: BorderRadius.circular(40), color: const Color(0xFFFFF5E6), ), child: ClipRRect( borderRadius: BorderRadius.circular(40), child: _buildAvatarContent(), ), ), const SizedBox(width: 16), // 用户信息 Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '序列号$_serialNumber', style: const TextStyle( fontSize: 20, fontFamily: 'Inter', fontWeight: FontWeight.w700, height: 1.25, letterSpacing: -0.3, color: Color(0xFF5D4037), ), ), const SizedBox(height: 4), Text( '社区: $_community${locationText.isNotEmpty ? ' / $locationText' : ''}', style: const TextStyle( fontSize: 14, fontFamily: 'Inter', height: 1.5, color: Color(0xFF8B5A2B), ), overflow: TextOverflow.ellipsis, ), ], ), ), ], ), ); } /// 构建监控状态区域 Widget _buildMiningStatusArea() { return Padding( padding: const EdgeInsets.all(16), child: Container( width: double.infinity, decoration: BoxDecoration( color: const Color(0x66FFFFFF), borderRadius: BorderRadius.circular(12), ), child: _monitorStatus == MonitorStatus.active ? _buildMonitorActiveView() : _buildMonitorPendingView(), ), ); } /// 构建监控待开启视图 Widget _buildMonitorPendingView() { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 开启监控按钮 GestureDetector( onTap: _startMonitor, child: Container( width: 100, height: 100, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.transparent, border: Border.all( color: const Color(0xFFD4AF37).withOpacity(0.5), width: 3, ), ), child: Icon( Icons.videocam_outlined, size: 48, color: const Color(0xFFD4AF37).withOpacity(0.5), ), ), ), const SizedBox(height: 16), // 状态文本 Text( _getStatusText(), style: const TextStyle( fontSize: 18, fontFamily: 'Inter', fontWeight: FontWeight.w500, height: 1.56, color: Color(0xFF8B5A2B), ), ), const SizedBox(height: 8), const Text( '点击开启实时监控', style: TextStyle( fontSize: 14, fontFamily: 'Inter', color: Color(0x998B5A2B), ), ), ], ); } /// 构建监控中视图 Widget _buildMonitorActiveView() { return Column( children: [ // 视频流占位区域 Expanded( child: Container( margin: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.black87, borderRadius: BorderRadius.circular(8), ), child: const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.videocam, size: 64, color: Color(0xFFD4AF37), ), SizedBox(height: 16), Text( '监控中', style: TextStyle( fontSize: 18, fontFamily: 'Inter', fontWeight: FontWeight.w600, color: Color(0xFFD4AF37), ), ), SizedBox(height: 8), Text( '视频流功能开发中...', style: TextStyle( fontSize: 14, fontFamily: 'Inter', color: Colors.white70, ), ), ], ), ), ), ), // 底部提示 Padding( padding: const EdgeInsets.only(bottom: 16), child: Text( '点击右上角 × 关闭监控', style: TextStyle( fontSize: 12, fontFamily: 'Inter', color: const Color(0xFF8B5A2B).withOpacity(0.6), ), ), ), ], ); } /// 构建头像内容(优先本地文件,其次网络URL,最后SVG) Widget _buildAvatarContent() { // 1. 优先显示本地缓存的头像文件 if (_localAvatarPath != null && _localAvatarPath!.isNotEmpty) { final file = File(_localAvatarPath!); // 同步检查文件是否存在 if (file.existsSync()) { return Image.file( file, width: 80, height: 80, fit: BoxFit.cover, gaplessPlayback: true, // 防止图片切换时闪烁 errorBuilder: (context, error, stackTrace) { // 本地文件加载失败,尝试网络URL return _buildNetworkOrSvgAvatar(); }, ); } } // 2. 没有本地缓存,尝试网络URL或SVG return _buildNetworkOrSvgAvatar(); } /// 构建网络URL或SVG头像 Widget _buildNetworkOrSvgAvatar() { // 尝试显示网络图片URL if (_avatarUrl != null && _avatarUrl!.isNotEmpty) { return Image.network( _avatarUrl!, width: 80, height: 80, fit: BoxFit.cover, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; return const Center( child: SizedBox( width: 24, height: 24, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Color(0xFFD4AF37)), ), ), ); }, errorBuilder: (context, error, stackTrace) { // 加载失败时显示SVG或默认头像 return _buildSvgOrDefaultAvatar(); }, ); } // 显示SVG或默认头像 return _buildSvgOrDefaultAvatar(); } /// 构建SVG或默认头像 /// 注意:_avatarSvg 可能存储的是 URL(用户上传的图片)或 SVG 字符串(随机生成的头像) Widget _buildSvgOrDefaultAvatar() { if (_avatarSvg != null && _avatarSvg!.isNotEmpty) { // 检测是否是 URL(用户上传的头像图片) if (_avatarSvg!.startsWith('http://') || _avatarSvg!.startsWith('https://')) { return Image.network( _avatarSvg!, width: 80, height: 80, fit: BoxFit.cover, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; return const Center( child: SizedBox( width: 24, height: 24, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Color(0xFFD4AF37)), ), ), ); }, errorBuilder: (context, error, stackTrace) { return const Icon( Icons.person, size: 40, color: Color(0xFF8B5A2B), ); }, ); } // 检测是否是 SVG 字符串(随机生成的 SVG 头像) if (_avatarSvg!.contains('