Files
flutterApp/lib/features/pv_control/presentation/widgets/pv_task_dialog.dart

232 lines
7.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:maibu_satabot_v2/core/di/injection.dart';
import 'package:maibu_satabot_v2/features/devices/domain/entities/device_task_entity.dart';
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/device_task_cubit.dart';
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/device_task_state.dart';
import 'package:maibu_satabot_v2/core/localization/app_localizations.dart';
import '../theme/pv_colors.dart';
/// 「当前任务」弹窗:展示路径规划模块正在执行的任务。
///
/// 入口为遥控 HUD 顶栏右上第一项「任务」。数据源与路径规划作业面板同源——
/// 全局单例 [DeviceTaskCubit] 的 currentTask(用 BlocBuilder 实时订阅,
/// 任务状态变化自动刷新);接口没给的字段统一显示「无」。
class PvTaskDialog {
const PvTaskDialog._();
static Future<void> show(BuildContext context) {
return showDialog<void>(
context: context,
builder: (_) => BlocProvider.value(
// 共享全局实例:弹窗只订阅状态,不负责 cubit 生命周期
value: sl<DeviceTaskCubit>(),
child: const _PvTaskDialogContent(),
),
);
}
}
class _PvTaskDialogContent extends StatelessWidget {
const _PvTaskDialogContent();
@override
Widget build(BuildContext context) {
return BlocBuilder<DeviceTaskCubit, DeviceTaskState>(
builder: (context, state) {
final task = state.currentTask;
return Dialog(
backgroundColor: Colors.transparent,
insetPadding: const EdgeInsets.symmetric(horizontal: 60),
child: Container(
padding: const EdgeInsets.fromLTRB(16, 8, 12, 14),
decoration: PvColors.panel(radius: 14),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题行 + 关闭
Row(
children: [
const Icon(
Icons.assignment_outlined,
color: PvColors.accentBright,
size: 20,
),
const SizedBox(width: 8),
Text(
AppLocalizations.of(
context,
).translate('pv_control.task_current'),
style: PvType.cardTitle,
),
const Spacer(),
IconButton(
icon: const Icon(
Icons.close,
color: PvColors.textSecondary,
size: 20,
),
visualDensity: VisualDensity.compact,
padding: EdgeInsets.zero,
constraints: const BoxConstraints(
minWidth: 32,
minHeight: 32,
),
onPressed: () => Navigator.of(context).pop(),
),
],
),
if (task == null)
const _EmptyTask()
else
_TaskFields(task: task),
],
),
),
);
},
);
}
}
/// 无执行任务:路径规划侧 currentTask 为空(未下发/已完成/被清除)。
class _EmptyTask extends StatelessWidget {
const _EmptyTask();
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 18),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.info_outline,
color: PvColors.textTertiary,
size: 18,
),
const SizedBox(width: 8),
Text(
AppLocalizations.of(
context,
).translate('pv_control.task_none_running'),
style: PvType.body,
),
],
),
);
}
}
/// 任务数据项:与路径规划作业面板展示的字段对齐
/// (任务编号 / 设备 / 状态,另补路线、地块、创建时间),空字段显示「无」。
class _TaskFields extends StatelessWidget {
const _TaskFields({required this.task});
final DeviceTaskEntity task;
@override
Widget build(BuildContext context) {
final tr = AppLocalizations.of(context);
final (statusColor, statusText) = _statusOf(context, task);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_row(context, tr.translate('pv_control.task_field_no'), '#${task.id}'),
_row(
context,
tr.translate('pv_control.task_field_device'),
task.deviceId,
),
_row(
context,
tr.translate('pv_control.task_field_status'),
statusText,
valueColor: statusColor,
),
_row(
context,
tr.translate('pv_control.task_field_route'),
task.routeId?.toString(),
),
_row(
context,
tr.translate('pv_control.task_field_site'),
task.siteId?.toString(),
),
_row(
context,
tr.translate('pv_control.task_field_create_time'),
task.createTime,
),
],
);
}
/// 状态映射与路径规划页一致:NEW 蓝 / EXECUTING 绿 / PAUSE 橙,
/// 其余用接口给的翻译文案;颜色换成本模块语义色保持风格统一。
(Color, String) _statusOf(BuildContext context, DeviceTaskEntity t) {
final tr = AppLocalizations.of(context);
final translate = t.taskStatusTranslate.isNotEmpty
? t.taskStatusTranslate
: t.taskStatus;
switch (t.taskStatus) {
case 'NEW':
return (
PvColors.info,
translate.isEmpty
? tr.translate('pv_control.task_none_value')
: tr.translate('pv_control.task_new'),
);
case 'EXECUTING':
return (PvColors.online, tr.translate('pv_control.task_executing'));
case 'PAUSE':
return (PvColors.warning, tr.translate('pv_control.task_paused'));
default:
return (
PvColors.textSecondary,
translate.isEmpty
? tr.translate('pv_control.task_none_value')
: translate,
);
}
}
Widget _row(
BuildContext context,
String label,
String? value, {
Color? valueColor,
}) {
final text = (value == null || value.isEmpty)
? AppLocalizations.of(context).translate('pv_control.task_none_value')
: value;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 76,
child: Text(label, style: PvType.label),
),
Expanded(
child: Text(
text,
style: PvType.body.copyWith(
color: valueColor ?? PvColors.textPrimary,
fontWeight: valueColor != null
? FontWeight.w700
: FontWeight.w400,
),
),
),
],
),
);
}
}