- core/localization: 新增约 60 个翻译键(含参数化方法),中英双语覆盖 - shared/widgets: CommonDialog 默认参数国际化 - features/home: 完成页操作步骤指引、状态栏串口连接状态、程序列表状态标签 - features/programs: 表头状态列、表单验证提示、导入/模板操作反馈、删除确认(参数化) - features/program_detail: 步骤列表/表单标题、删除确认、速度档位显示(参数化) - features/device: run_state_provider 错误消息改为错误码 - features/settings: 升级页、密码面板、语言面板、U盘导入面板、串口配置面板全部替换 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
272 lines
9.1 KiB
Dart
272 lines
9.1 KiB
Dart
import 'package:flutter/material.dart';
|
||
import '../../../core/localization/app_localizations.dart';
|
||
import '../../../core/theme/app_theme.dart';
|
||
import '../../../shared/widgets/common_button.dart';
|
||
import '../../programs/models/step.dart' as models;
|
||
|
||
/// 步骤列表组件
|
||
class StepList extends StatefulWidget {
|
||
final int programId;
|
||
final List<models.Step> steps;
|
||
final int? selectedStepId;
|
||
final void Function(int?) onStepSelected;
|
||
final void Function() onAddStep;
|
||
final void Function(int oldIndex, int newIndex)? onReorder;
|
||
final void Function(List<int> stepIds)? onDeleteSteps;
|
||
|
||
const StepList({
|
||
super.key,
|
||
required this.programId,
|
||
required this.steps,
|
||
this.selectedStepId,
|
||
required this.onStepSelected,
|
||
required this.onAddStep,
|
||
this.onReorder,
|
||
this.onDeleteSteps,
|
||
});
|
||
|
||
@override
|
||
State<StepList> createState() => _StepListState();
|
||
}
|
||
|
||
class _StepListState extends State<StepList> {
|
||
final Set<int> _selectedIds = {};
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final l10n = AppLocalizations.of(context);
|
||
final allSelected = _selectedIds.length == widget.steps.length && widget.steps.isNotEmpty;
|
||
|
||
return Container(
|
||
color: Colors.white,
|
||
child: Column(
|
||
children: [
|
||
// 标题
|
||
Container(
|
||
height: 50,
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
decoration: BoxDecoration(
|
||
color: AppTheme.primaryColor.withValues(alpha: 0.1),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Icon(Icons.list, color: AppTheme.primaryColor, size: 20),
|
||
const SizedBox(width: 12),
|
||
Text(
|
||
l10n?.stepList ?? '步骤列表',
|
||
style: TextStyle(
|
||
color: AppTheme.primaryColor,
|
||
fontWeight: FontWeight.w600,
|
||
),
|
||
),
|
||
const Spacer(),
|
||
Text(
|
||
l10n?.stepsCountLabel(widget.steps.length) ?? '${widget.steps.length} 步',
|
||
style: TextStyle(
|
||
color: AppTheme.textSecondary,
|
||
fontSize: 12,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
|
||
// 表头
|
||
Container(
|
||
height: 40,
|
||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||
decoration: BoxDecoration(
|
||
border: Border(
|
||
bottom: BorderSide(color: AppTheme.idleColor.withValues(alpha: 0.2)),
|
||
),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
SizedBox(
|
||
width: 40,
|
||
child: Checkbox(
|
||
value: allSelected,
|
||
onChanged: (value) {
|
||
setState(() {
|
||
if (value == true) {
|
||
_selectedIds.clear();
|
||
_selectedIds.addAll(widget.steps.map((s) => s.id!));
|
||
} else {
|
||
_selectedIds.clear();
|
||
}
|
||
});
|
||
},
|
||
),
|
||
),
|
||
SizedBox(width: 40, child: Text('#', style: TextStyle(fontSize: 12))),
|
||
Expanded(child: Text(l10n?.stepName ?? '名称', style: TextStyle(fontSize: 12))),
|
||
SizedBox(width: 60, child: Text(l10n?.position ?? '孔位', style: TextStyle(fontSize: 12))),
|
||
],
|
||
),
|
||
),
|
||
|
||
// 步骤列表(可拖拽排序)
|
||
Expanded(
|
||
child: widget.steps.isEmpty
|
||
? Center(
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Icon(Icons.add_circle_outline, size: 48, color: AppTheme.idleColor),
|
||
const SizedBox(height: 12),
|
||
Text(l10n?.noSteps ?? '暂无步骤', style: TextStyle(color: AppTheme.textSecondary)),
|
||
],
|
||
),
|
||
)
|
||
: ReorderableListView.builder(
|
||
padding: const EdgeInsets.all(8),
|
||
itemCount: widget.steps.length,
|
||
onReorder: (oldIndex, newIndex) {
|
||
if (widget.onReorder != null) {
|
||
// 调整 newIndex(ReorderableListView 的特殊行为)
|
||
if (newIndex > oldIndex) newIndex -= 1;
|
||
widget.onReorder!(oldIndex, newIndex);
|
||
}
|
||
},
|
||
itemBuilder: (context, index) {
|
||
final step = widget.steps[index];
|
||
final isSelected = widget.selectedStepId == step.id || _selectedIds.contains(step.id);
|
||
return _buildStepItem(step, isSelected, index);
|
||
},
|
||
),
|
||
),
|
||
|
||
// 底部操作栏
|
||
Container(
|
||
height: 60,
|
||
padding: const EdgeInsets.all(12),
|
||
decoration: BoxDecoration(
|
||
border: Border(
|
||
top: BorderSide(color: AppTheme.idleColor.withValues(alpha: 0.2)),
|
||
),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
// 添加按钮
|
||
CommonButton(
|
||
text: l10n?.add ?? '添加',
|
||
icon: Icons.add,
|
||
type: ButtonType.primary,
|
||
onPressed: widget.onAddStep,
|
||
),
|
||
const SizedBox(width: 12),
|
||
// 删除按钮
|
||
if (_selectedIds.isNotEmpty)
|
||
CommonButton(
|
||
text: l10n?.delete ?? '删除',
|
||
icon: Icons.delete,
|
||
type: ButtonType.danger,
|
||
onPressed: () => _showDeleteConfirmDialog(context),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 步骤项
|
||
Widget _buildStepItem(models.Step step, bool isSelected, int index) {
|
||
return Container(
|
||
key: ValueKey(step.id),
|
||
margin: const EdgeInsets.symmetric(vertical: 2),
|
||
decoration: BoxDecoration(
|
||
color: isSelected ? AppTheme.primaryLight.withValues(alpha: 0.3) : Colors.white,
|
||
borderRadius: BorderRadius.circular(4),
|
||
border: isSelected ? Border.all(color: AppTheme.primaryColor, width: 2) : null,
|
||
),
|
||
child: ListTile(
|
||
dense: true,
|
||
leading: Checkbox(
|
||
value: _selectedIds.contains(step.id),
|
||
onChanged: (value) {
|
||
setState(() {
|
||
if (value == true) {
|
||
_selectedIds.add(step.id!);
|
||
} else {
|
||
_selectedIds.remove(step.id!);
|
||
}
|
||
});
|
||
},
|
||
),
|
||
title: Row(
|
||
children: [
|
||
Container(
|
||
width: 30,
|
||
alignment: Alignment.center,
|
||
child: Text(
|
||
'${step.stepNo}',
|
||
style: TextStyle(
|
||
color: AppTheme.primaryColor,
|
||
fontWeight: FontWeight.bold,
|
||
),
|
||
),
|
||
),
|
||
const SizedBox(width: 8),
|
||
Expanded(child: Text(step.name)),
|
||
Container(
|
||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||
decoration: BoxDecoration(
|
||
color: AppTheme.primaryColor.withValues(alpha: 0.1),
|
||
borderRadius: BorderRadius.circular(4),
|
||
),
|
||
child: Text(
|
||
step.position,
|
||
style: TextStyle(
|
||
color: AppTheme.primaryColor,
|
||
fontSize: 12,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
trailing: Icon(Icons.drag_handle, color: AppTheme.idleColor),
|
||
onTap: () => widget.onStepSelected(step.id),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 显示删除确认对话框
|
||
void _showDeleteConfirmDialog(BuildContext context) {
|
||
final l10n = AppLocalizations.of(context);
|
||
showDialog(
|
||
context: context,
|
||
builder: (ctx) => AlertDialog(
|
||
title: Text(l10n?.confirm ?? '确认'),
|
||
content: Text(
|
||
_selectedIds.length == 1
|
||
? l10n?.deleteStepConfirmSingle ?? '确定要删除此步骤吗?'
|
||
: l10n?.deleteStepConfirmMultiple(_selectedIds.length) ?? '确定要删除选中的 ${_selectedIds.length} 个步骤吗?',
|
||
),
|
||
actions: [
|
||
TextButton(
|
||
onPressed: () => Navigator.of(ctx).pop(),
|
||
child: Text(l10n?.cancel ?? '取消'),
|
||
),
|
||
ElevatedButton(
|
||
style: ElevatedButton.styleFrom(
|
||
backgroundColor: AppTheme.errorColor,
|
||
foregroundColor: Colors.white,
|
||
),
|
||
onPressed: () {
|
||
Navigator.of(ctx).pop();
|
||
if (widget.onDeleteSteps != null) {
|
||
widget.onDeleteSteps!(_selectedIds.toList());
|
||
}
|
||
setState(() {
|
||
_selectedIds.clear();
|
||
});
|
||
},
|
||
child: Text(l10n?.confirm ?? '确认'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
} |