- 添加 CodeGraph、Android 和通用 gitignore 配置 - 创建项目元数据文件跟踪 Flutter 项目属性 - 添加 Codex AI 指导文档 AGENTS.md 说明项目架构 - 配置代码分析选项 analysis_options.yaml - 设置 Android 应用清单权限和 Kiosk 模式配置 - 实现中英文国际化支持 AppLocalizations - 配置 GoRouter 应用路由导航 - 创建明亮工业控制风格的主题配置 AppTheme
243 lines
6.7 KiB
Dart
243 lines
6.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import '../../../core/localization/app_localizations.dart';
|
|
import '../../../core/theme/app_theme.dart';
|
|
import '../../device/providers/run_state_provider.dart';
|
|
|
|
/// 运行状态监控面板 - 暗色工业风格
|
|
/// 显示当前孔位、步骤、倒计时、进度条、参数详情
|
|
class RunStatusMonitor extends ConsumerWidget {
|
|
const RunStatusMonitor({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final l10n = AppLocalizations.of(context);
|
|
final runState = ref.watch(runStateProvider);
|
|
|
|
if (runState.status == RunStatus.idle) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
|
|
return Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: AppTheme.cardBg,
|
|
borderRadius: BorderRadius.circular(8),
|
|
border: Border.all(color: AppTheme.borderSubtle, width: 1),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// 标题 + 程序名
|
|
Row(
|
|
children: [
|
|
Text(
|
|
l10n?.runningMonitor ?? '运行状态监控',
|
|
style: const TextStyle(
|
|
color: AppTheme.textHeading,
|
|
fontSize: 15,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
const Spacer(),
|
|
Text(
|
|
runState.currentProgram?.name ?? '',
|
|
style: const TextStyle(
|
|
color: AppTheme.accentPrimary,
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w700,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 14),
|
|
|
|
// 进度信息横排 (孔位 / 步骤 / 剩余时间)
|
|
Row(
|
|
children: [
|
|
// 当前孔位
|
|
_buildInfoBlock(
|
|
label: l10n?.currentHole ?? '当前孔位',
|
|
value: runState.currentWell ?? '--',
|
|
valueColor: AppTheme.textHeading,
|
|
),
|
|
const SizedBox(width: 20),
|
|
// 当前步骤
|
|
_buildInfoBlock(
|
|
label: l10n?.currentStep ?? '当前步骤',
|
|
value: '${l10n?.stepNo ?? '步骤'} ${runState.currentStepIndex + 1}',
|
|
subValue: runState.currentStep?.name ?? '--',
|
|
valueColor: AppTheme.accentInfo,
|
|
),
|
|
const SizedBox(width: 20),
|
|
// 剩余时间
|
|
_buildInfoBlock(
|
|
label: l10n?.remainingTime ?? '剩余时间',
|
|
value: runState.formattedRemainingTime,
|
|
valueColor: AppTheme.textHeading,
|
|
valueSize: 20,
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 14),
|
|
|
|
// 总进度条
|
|
_buildProgressBar(l10n, runState),
|
|
|
|
const SizedBox(height: 14),
|
|
|
|
// 步骤参数
|
|
if (runState.currentStep != null)
|
|
_buildStepParams(l10n, runState.currentStep!),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 信息块
|
|
Widget _buildInfoBlock({
|
|
required String label,
|
|
required String value,
|
|
String? subValue,
|
|
Color valueColor = AppTheme.textHeading,
|
|
double valueSize = 16,
|
|
}) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
label,
|
|
style: const TextStyle(
|
|
color: AppTheme.textTertiary,
|
|
fontSize: 11,
|
|
),
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
value,
|
|
style: TextStyle(
|
|
color: valueColor,
|
|
fontSize: valueSize,
|
|
fontWeight: FontWeight.w600,
|
|
fontFamily: 'monospace',
|
|
),
|
|
),
|
|
if (subValue != null) ...[
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
subValue,
|
|
style: const TextStyle(
|
|
color: AppTheme.textSecondary,
|
|
fontSize: 11,
|
|
),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
/// 进度条
|
|
Widget _buildProgressBar(AppLocalizations? l10n, RunState runState) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Text(
|
|
l10n?.progress ?? '总进度',
|
|
style: const TextStyle(
|
|
color: AppTheme.textTertiary,
|
|
fontSize: 11,
|
|
),
|
|
),
|
|
const Spacer(),
|
|
Text(
|
|
runState.formattedProgress,
|
|
style: const TextStyle(
|
|
color: AppTheme.accentPrimary,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
fontFamily: 'monospace',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 6),
|
|
ClipRRect(
|
|
borderRadius: BorderRadius.circular(4),
|
|
child: LinearProgressIndicator(
|
|
value: runState.progress,
|
|
minHeight: 8,
|
|
backgroundColor: const Color(0xFF1E293B),
|
|
valueColor: AlwaysStoppedAnimation<Color>(AppTheme.accentPrimary),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
/// 步骤参数详情
|
|
Widget _buildStepParams(AppLocalizations? l10n, dynamic step) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
l10n?.stepParams ?? '步骤参数',
|
|
style: const TextStyle(
|
|
color: AppTheme.textTertiary,
|
|
fontSize: 11,
|
|
),
|
|
),
|
|
const SizedBox(height: 2),
|
|
if (step.mixTime > 0)
|
|
_buildParamRow(
|
|
l10n?.speed ?? '转速',
|
|
'${step.mixSpeed}',
|
|
),
|
|
if (step.magnetTime > 0)
|
|
_buildParamRow(
|
|
l10n?.temperature ?? '温度',
|
|
'65.0 °C',
|
|
),
|
|
_buildParamRow(
|
|
l10n?.duration ?? '持续时间',
|
|
step.mixTime > 0 ? '${step.mixTime} min' : '--',
|
|
),
|
|
_buildParamRow(
|
|
l10n?.sampleVolume ?? '样品体积',
|
|
'10.0 mL',
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
/// 参数行
|
|
Widget _buildParamRow(String label, String value) {
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 2),
|
|
child: Row(
|
|
children: [
|
|
Text(
|
|
label,
|
|
style: const TextStyle(
|
|
color: AppTheme.textTertiary,
|
|
fontSize: 11,
|
|
),
|
|
),
|
|
const Spacer(),
|
|
Text(
|
|
value,
|
|
style: const TextStyle(
|
|
color: AppTheme.textPrimary,
|
|
fontSize: 11,
|
|
fontFamily: 'monospace',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|