364 lines
12 KiB
Dart
364 lines
12 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||
import 'package:fpdart/fpdart.dart' hide State;
|
||
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 '../../../../core/di/injection.dart';
|
||
import '../../../devices/presentation/bloc/devices_cubit.dart';
|
||
|
||
class WorkParamsCard extends StatefulWidget {
|
||
const WorkParamsCard({super.key});
|
||
|
||
@override
|
||
State<WorkParamsCard> createState() => _WorkParamsCardState();
|
||
}
|
||
|
||
class _WorkParamsCardState extends State<WorkParamsCard> {
|
||
List<DeviceRunStatisticsEntity>? _statistics;
|
||
String? _errorMessage;
|
||
bool _isLoading = false; // 默认不加载
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final deviceState = context.watch<DevicesCubit>().state;
|
||
final currentDevice = deviceState.selectedDevice;
|
||
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),
|
||
const Text(
|
||
'作业参数',
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
fontWeight: FontWeight.bold,
|
||
color: Colors.black,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
GestureDetector(
|
||
onTap: () {
|
||
_handleInfoTap(currentDevice); // 包装在同步函数中
|
||
}, // 点击事件处理
|
||
child: Row(
|
||
children: [
|
||
Icon(Icons.info, size: 14, color: Colors.grey[400]),
|
||
const SizedBox(width: 4),
|
||
Text(
|
||
'查看详情',
|
||
style: TextStyle(fontSize: 12, color: Colors.grey[400]),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 24),
|
||
IntrinsicHeight(
|
||
child: Row(
|
||
children: [
|
||
_buildParamItem('作业面积', '0', '亩'), // 默认值
|
||
_buildDivider(),
|
||
_buildParamItem('作业里程', '0', 'km'),
|
||
_buildDivider(),
|
||
_buildParamItem('作业时长', '0', 'h'),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
// 点击事件处理
|
||
Future<void> _handleInfoTap(DeviceEntity? currentDevice) async {
|
||
print("点击触摸了历史作业");
|
||
setState(() {
|
||
_isLoading = true; // 开始加载
|
||
_errorMessage = null; // 清除错误信息
|
||
});
|
||
print("开始历史作业");
|
||
try {
|
||
final useCase = DeviceWorkHostrirty(sl());
|
||
print("发送获取历史作业记录开始");
|
||
//displayName
|
||
final deviceId = currentDevice?.deviceName.toString() ?? '';
|
||
final params = BindDeviceParams(deviceId, 'your_device_alias');
|
||
final result = await useCase(params);
|
||
print("发送获取历史作业记录结束");
|
||
result.fold(
|
||
(failure) {
|
||
setState(() {
|
||
_errorMessage = failure.message;
|
||
_isLoading = false;
|
||
});
|
||
},
|
||
(data) {
|
||
setState(() {
|
||
_statistics = data;
|
||
_isLoading = false;
|
||
});
|
||
_showDetailsDialog(context, _statistics!); // 展示弹窗
|
||
},
|
||
);
|
||
} catch (e) {
|
||
print("捕获异常: $e");
|
||
setState(() {
|
||
_errorMessage = '请求失败: $e';
|
||
_isLoading = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
// 弹窗展示详情
|
||
// 弹窗展示详情(从底部弹出)
|
||
// 弹窗展示详情(从底部弹出,覆盖底部导航栏)
|
||
void _showDetailsDialog(
|
||
BuildContext context,
|
||
List<DeviceRunStatisticsEntity> 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, // 占据屏幕 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)),
|
||
const Text(
|
||
'历史记录详情',
|
||
style: TextStyle(
|
||
fontSize: 20,
|
||
fontWeight: FontWeight.bold,
|
||
color: Colors.black87,
|
||
),
|
||
),
|
||
// 右上角关闭按钮(X号)
|
||
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
|
||
? // 无数据时显示占位
|
||
const Center(
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Icon(
|
||
Icons.description_outlined,
|
||
size: 60,
|
||
color: Colors.grey,
|
||
),
|
||
SizedBox(height: 16),
|
||
Text(
|
||
'暂无历史记录',
|
||
style: 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(
|
||
Icons.calendar_today,
|
||
'开始时间',
|
||
item.startTime,
|
||
),
|
||
_buildDetailRow(
|
||
Icons.calendar_today_outlined,
|
||
'结束时间',
|
||
item.endTime,
|
||
),
|
||
_buildDetailRow(
|
||
Icons.square_foot,
|
||
'工作面积',
|
||
'${item.workArea} 亩',
|
||
),
|
||
_buildDetailRow(
|
||
Icons.directions_car,
|
||
'工作距离',
|
||
'${item.distance?.toStringAsFixed(2) ?? '未计算'} km',
|
||
),
|
||
_buildDetailRow(
|
||
Icons.timer,
|
||
'花费时间',
|
||
'${item.time} 小时',
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}).toList(),
|
||
),
|
||
),
|
||
),
|
||
// 移除原有底部关闭按钮
|
||
],
|
||
),
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
// 构建详情行
|
||
Widget _buildDetailRow(IconData icon, String label, 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(
|
||
'$label: ',
|
||
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(String label, String value, String unit) {
|
||
return Expanded(
|
||
child: Column(
|
||
children: [
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Icon(
|
||
label == '作业面积'
|
||
? Icons.aspect_ratio
|
||
: label == '作业里程'
|
||
? Icons.local_shipping_outlined
|
||
: Icons.access_time,
|
||
size: 14,
|
||
color: Colors.black38,
|
||
),
|
||
const SizedBox(width: 4),
|
||
Text(
|
||
label,
|
||
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,
|
||
);
|
||
}
|
||
}
|