import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fpdart/fpdart.dart' hide State; import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; import 'package:maibu_satabot_v2/features/devices/domain/entities/device_entity.dart'; import 'package:maibu_satabot_v2/features/devices/domain/usecases/device_work_hostrirty_usecase.dart'; import 'package:maibu_satabot_v2/features/devices/domain/entities/device_run_hostrity_entity.dart'; import 'package:maibu_satabot_v2/features/devices/domain/errors/device_failure.dart'; import 'package:maibu_satabot_v2/features/devices/presentation/bloc/device_status_bloc.dart'; import 'package:maibu_satabot_v2/features/devices/presentation/bloc/device_status_state.dart'; import '../../../../core/di/injection.dart'; import '../../../devices/presentation/bloc/devices_cubit.dart'; import 'package:intl/intl.dart'; class WorkParamsCard extends StatefulWidget { const WorkParamsCard({super.key}); @override State createState() => _WorkParamsCardState(); } class _WorkParamsCardState extends State { List? _statistics; String? _errorMessage; bool _isLoading = false; // 计时相关 DateTime? _firstTimestamp; // 第一次时间(开始时间) int _totalWorkMinutes = 0; // 总作业分钟 @override Widget build(BuildContext context) { final deviceState = context.watch().state; final currentDevice = deviceState.selectedDevice; final statusState = context.watch().state; double? distance; double? workArea; if (statusState is DeviceStatusUpdated) { workArea = double.tryParse(statusState.status.workingArea ?? ''); distance = workArea != null ? workArea / 0.7 : null; // ====================== 计算作业时长(核心) ====================== _calculateWorkingTime(statusState.status.timestamp); } return Container( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(24), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.02), blurRadius: 10, offset: const Offset(0, 4))], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Container( width: 4, height: 16, decoration: BoxDecoration(color: Colors.blueAccent, borderRadius: BorderRadius.circular(2)), ), const SizedBox(width: 8), Text( AppLocalizations.of(context).translate('home.work_params'), style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black), overflow: TextOverflow.ellipsis, ), ], ), GestureDetector( onTap: () { if (currentDevice != null) { _handleInfoTap(currentDevice); } }, child: Row( children: [ Icon(Icons.info, size: 14, color: Colors.grey[400]), const SizedBox(width: 4), Text( AppLocalizations.of(context).translate('home.view_details'), style: TextStyle(fontSize: 12, color: Colors.grey[400]), overflow: TextOverflow.ellipsis, ), ], ), ), ], ), const SizedBox(height: 24), IntrinsicHeight( child: Row( children: [ _buildParamItem(context, 'home.work_area', '${workArea?.toStringAsFixed(1) ?? '0'}', '㎡'), _buildDivider(), _buildParamItem(context, 'home.work_distance', '${distance?.toStringAsFixed(1) ?? '0'}', 'm'), _buildDivider(), // 显示实时计算的作业时长 _buildParamItem(context, 'home.work_duration', '${_totalWorkMinutes}', 'min'), ], ), ), ], ), ); } // ====================== 核心:根据时间戳计算时长 ====================== // 修复时间解析:适配 20-1-11-31 08:00:00 格式 void _calculateWorkingTime(String timestampStr) { if (timestampStr.isEmpty) return; try { // 格式:年-月-日-星期 时:分:秒 // 我们只取 年-月-日 时:分:秒 来解析 List parts = timestampStr.split(' '); if (parts.length != 2) return; String datePart = parts[0]; // 20-1-11-31 String timePart = parts[1]; // 08:00:00 List dateSegments = datePart.split('-'); if (dateSegments.length < 3) return; // 只取 年-月-日,丢弃星期 String cleanDate = '${dateSegments[0]}-${dateSegments[1]}-${dateSegments[2]}'; DateTime currentTime = DateFormat("yy-MM-dd HH:mm:ss").parse('$cleanDate $timePart'); // 记录第一次时间 if (_firstTimestamp == null) { setState(() { _firstTimestamp = currentTime; }); } // 计算分钟 final diff = currentTime.difference(_firstTimestamp!); setState(() { _totalWorkMinutes = diff.inMinutes; }); } catch (e) { debugPrint("时间解析失败: $e"); } } Future _handleInfoTap(DeviceEntity currentDevice) async { setState(() { _isLoading = true; _errorMessage = null; }); try { final useCase = DeviceWorkHostrirty(sl()); final deviceId = currentDevice.deviceName.toString() ?? ''; final params = BindDeviceParams(deviceId, 'your_device_alias'); final result = await useCase(params); result.fold( (failure) { setState(() { _errorMessage = failure.message; _isLoading = false; }); }, (data) { setState(() { _statistics = data; _isLoading = false; }); _showDetailsDialog(context, _statistics!); }, ); } catch (e) { setState(() { _errorMessage = '请求失败: $e'; _isLoading = false; }); } } void _showDetailsDialog(BuildContext context, List data) { showModalBottomSheet( context: context, isScrollControlled: true, useRootNavigator: true, shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20))), builder: (context) { return Container( height: MediaQuery.of(context).size.height * 0.75, padding: const EdgeInsets.all(20), decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Center(child: Container(width: 40, height: 4)), Text( AppLocalizations.of(context).translate('home.history_details'), style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black87), overflow: TextOverflow.ellipsis, ), IconButton( onPressed: () => Navigator.pop(context), icon: const Icon(Icons.close, color: Colors.grey, size: 24), padding: EdgeInsets.zero, constraints: const BoxConstraints(), ), ], ), const SizedBox(height: 10), Expanded( child: data.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.description_outlined, size: 60, color: Colors.grey), const SizedBox(height: 16), Text(AppLocalizations.of(context).translate('home.no_history'), style: const TextStyle(fontSize: 16, color: Colors.grey)), ], ), ) : SingleChildScrollView( child: Column( children: data.map((item) { return Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.blue.withOpacity(0.05), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.blue.withOpacity(0.2), width: 1), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildDetailRow(context, Icons.calendar_today, 'home.start_time', item.startTime), _buildDetailRow(context, Icons.calendar_today_outlined, 'home.end_time', item.endTime), _buildDetailRow(context, Icons.square_foot, 'home.working_area', '${item.workArea} ㎡'), if (item.distance != null && item.distance! > 0) _buildDetailRow(context, Icons.directions_car, 'home.working_distance', '${item.distance!.toStringAsFixed(2)} m'), _buildDetailRow(context, Icons.timer, 'home.time_spent', '${item.time} min'), ], ), ); }).toList(), ), ), ), ], ), ); }, ); } Widget _buildDetailRow(BuildContext context, IconData icon, String labelKey, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( children: [ Icon(icon, size: 16, color: Colors.blue), const SizedBox(width: 8), Text( AppLocalizations.of(context).translate(labelKey) + ': ', style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.black87), ), Expanded( child: Text(value, style: const TextStyle(fontSize: 14, color: Colors.black54)), ), ], ), ); } Widget _buildParamItem(BuildContext context, String labelKey, String value, String unit) { return Expanded( child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( labelKey == 'home.work_area' ? Icons.aspect_ratio : labelKey == 'home.work_distance' ? Icons.local_shipping_outlined : Icons.access_time, size: 14, color: Colors.black38, ), const SizedBox(width: 4), Text(AppLocalizations.of(context).translate(labelKey), style: const TextStyle(fontSize: 12, color: Colors.black38)), ], ), const SizedBox(height: 12), RichText( text: TextSpan( children: [ TextSpan( text: value, style: const TextStyle(fontSize: 28, fontWeight: FontWeight.w700, color: Colors.black, fontFamily: 'Inter'), ), TextSpan( text: ' $unit', style: const TextStyle(fontSize: 12, fontWeight: FontWeight.normal, color: Colors.black54), ), ], ), ), ], ), ); } Widget _buildDivider() { return VerticalDivider(color: Colors.black.withOpacity(0.05), thickness: 1, indent: 10, endIndent: 10); } }