USB 摄像头适配iPadOS17+
This commit is contained in:
@@ -25,23 +25,12 @@ class _RobotParamSettingsPageState extends State<RobotParamSettingsPage>
|
||||
String? _errorMessage;
|
||||
DeviceRunParamEntity? _param;
|
||||
|
||||
// 表单控制器(仅可编辑的5个字段)
|
||||
late TextEditingController _runSpeedController;
|
||||
late TextEditingController _leftForwardGainController;
|
||||
late TextEditingController _leftBackwardGainController;
|
||||
late TextEditingController _rightForwardGainController;
|
||||
late TextEditingController _rightBackwardGainController;
|
||||
// 表单控制器:字段名 -> 输入框控制器(所有展示字段均可编辑,数据驱动)
|
||||
final Map<String, TextEditingController> _controllers = {};
|
||||
|
||||
String get _deviceId => widget.robot['name'] as String? ?? '';
|
||||
|
||||
// ============ 字段定义 ============
|
||||
static const _editableKeys = {
|
||||
'runSpeed',
|
||||
'leftForwardGain',
|
||||
'leftBackwardGain',
|
||||
'rightForwardGain',
|
||||
'rightBackwardGain',
|
||||
};
|
||||
|
||||
static const _skipKeys = {
|
||||
'createBy', 'createTime', 'updateBy', 'updateTime', 'delFlag', 'remark',
|
||||
@@ -49,10 +38,38 @@ class _RobotParamSettingsPageState extends State<RobotParamSettingsPage>
|
||||
|
||||
static const _fieldLabels = {
|
||||
'id': 'ID',
|
||||
'deviceId': '设备ID',
|
||||
'deviceId': '设备序列号',
|
||||
'siteId': '场站ID',
|
||||
'orgId': '组织ID',
|
||||
'runSpeed': '运行速度',
|
||||
// ===== 设备参数实体字段(取自后端 @Excel 中文名)=====
|
||||
'productModel': '产品型号',
|
||||
'deviceType': '设备类型',
|
||||
'rtkMode': 'RTK模式',
|
||||
'cameraNum': '摄像头数量',
|
||||
'powerType': '动力类型',
|
||||
'wheelType': '车轮类型',
|
||||
'engineType': '发动机类型',
|
||||
'hasHost': '是否搭载上位机',
|
||||
'walkingDriveType': '行走驱动类型',
|
||||
'cutterDriveType': '割刀驱动类型',
|
||||
'rtkAntennasNum': 'RTK天线数',
|
||||
'handleType': '手柄类型',
|
||||
'pcbVersion': 'PCB硬件版本',
|
||||
'dimensions': '长宽高',
|
||||
'bladeWidth': '割刀宽度',
|
||||
'speedRatio': '转速比',
|
||||
'cellularInfo': '蜂窝网络通信信息',
|
||||
'bluetoothName': '蓝牙广播名',
|
||||
'hardwareIdentifiers': '硬件标识信息',
|
||||
'imuModel': 'IMU型号',
|
||||
'tempHumiditySensor': '温湿度传感器',
|
||||
'barometerPresent': '气压计在位',
|
||||
'ultrasonicPresent': '超声波在位',
|
||||
'dacPresent': 'DAC在位',
|
||||
'operator': '质检员',
|
||||
'inspectionNo': '检测报告号',
|
||||
'dealer': '经销商',
|
||||
'header1': 'Header1',
|
||||
'header2': 'Header2',
|
||||
'cmd': 'CMD',
|
||||
@@ -106,56 +123,33 @@ class _RobotParamSettingsPageState extends State<RobotParamSettingsPage>
|
||||
void initState() {
|
||||
super.initState();
|
||||
_tabController = TabController(length: 2, vsync: this);
|
||||
_initControllers();
|
||||
_loadData();
|
||||
}
|
||||
|
||||
void _initControllers() {
|
||||
_runSpeedController = TextEditingController();
|
||||
_leftForwardGainController = TextEditingController();
|
||||
_leftBackwardGainController = TextEditingController();
|
||||
_rightForwardGainController = TextEditingController();
|
||||
_rightBackwardGainController = TextEditingController();
|
||||
}
|
||||
|
||||
/// 根据接口返回的全部字段动态创建输入控制器(跳过 _skipKeys),全部可编辑
|
||||
void _fillControllers(DeviceRunParamEntity param) {
|
||||
_runSpeedController.text = param.runSpeed.toStringAsFixed(0);
|
||||
_leftForwardGainController.text = param.leftForwardGain.toString();
|
||||
_leftBackwardGainController.text = param.leftBackwardGain.toString();
|
||||
_rightForwardGainController.text = param.rightForwardGain.toString();
|
||||
_rightBackwardGainController.text = param.rightBackwardGain.toString();
|
||||
}
|
||||
|
||||
TextEditingController? _controllerForKey(String key) {
|
||||
switch (key) {
|
||||
case 'runSpeed': return _runSpeedController;
|
||||
case 'leftForwardGain': return _leftForwardGainController;
|
||||
case 'leftBackwardGain': return _leftBackwardGainController;
|
||||
case 'rightForwardGain': return _rightForwardGainController;
|
||||
case 'rightBackwardGain': return _rightBackwardGainController;
|
||||
default: return null;
|
||||
for (final c in _controllers.values) {
|
||||
c.dispose();
|
||||
}
|
||||
_controllers.clear();
|
||||
for (final entry in param.rawData.entries) {
|
||||
if (_skipKeys.contains(entry.key)) continue;
|
||||
_controllers[entry.key] = TextEditingController(
|
||||
text: entry.value?.toString() ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _showReadonlyTip() {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('暂不支持修改'),
|
||||
duration: Duration(seconds: 1),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
margin: EdgeInsets.only(bottom: 80, left: 80, right: 80),
|
||||
),
|
||||
);
|
||||
}
|
||||
/// 数值字段用数字键盘,其余用文本键盘(依据接口返回值的原始类型判断)
|
||||
bool _isNumericField(String key) => _param?.rawData[key] is num;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tabController.dispose();
|
||||
_runSpeedController.dispose();
|
||||
_leftForwardGainController.dispose();
|
||||
_leftBackwardGainController.dispose();
|
||||
_rightForwardGainController.dispose();
|
||||
_rightBackwardGainController.dispose();
|
||||
for (final c in _controllers.values) {
|
||||
c.dispose();
|
||||
}
|
||||
_controllers.clear();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -193,7 +187,9 @@ class _RobotParamSettingsPageState extends State<RobotParamSettingsPage>
|
||||
setState(() => _isSaving = true);
|
||||
|
||||
try {
|
||||
// 🔐 前置权限校验:只有 code=200 && data=true 才允许保存
|
||||
// ⛔ 业务变更:设备参数保存不再校验权限,保留代码备查
|
||||
// (原逻辑:先调 GET /iot/device/hasPermission,通过后再弹"权限通过"确认框)
|
||||
/*
|
||||
final permissionService = GetIt.I<DevicePermissionService>();
|
||||
final hasPermission = await permissionService.checkPermission(_deviceId);
|
||||
|
||||
@@ -204,7 +200,6 @@ class _RobotParamSettingsPageState extends State<RobotParamSettingsPage>
|
||||
return;
|
||||
}
|
||||
|
||||
// 权限通过提示
|
||||
await showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
@@ -226,30 +221,32 @@ class _RobotParamSettingsPageState extends State<RobotParamSettingsPage>
|
||||
),
|
||||
);
|
||||
if (!mounted) return;
|
||||
*/
|
||||
|
||||
final siteId = GetIt.I<SiteCubit>().state.selectedSite?.id ?? 0;
|
||||
final orgId = GetIt.I<AppUserCubit>().state.user?.orgId ?? 0;
|
||||
|
||||
final useCase = GetIt.I<SaveDeviceRunParamUseCase>();
|
||||
final params = <String, dynamic>{
|
||||
'id': _param!.id,
|
||||
'deviceId': _deviceId,
|
||||
'siteId': siteId,
|
||||
'orgId': orgId,
|
||||
'runSpeed': int.tryParse(_runSpeedController.text) ?? _param!.runSpeed,
|
||||
'leftForwardGain':
|
||||
double.tryParse(_leftForwardGainController.text) ??
|
||||
_param!.leftForwardGain,
|
||||
'leftBackwardGain':
|
||||
double.tryParse(_leftBackwardGainController.text) ??
|
||||
_param!.leftBackwardGain,
|
||||
'rightForwardGain':
|
||||
double.tryParse(_rightForwardGainController.text) ??
|
||||
_param!.rightForwardGain,
|
||||
'rightBackwardGain':
|
||||
double.tryParse(_rightBackwardGainController.text) ??
|
||||
_param!.rightBackwardGain,
|
||||
};
|
||||
// 以接口原始数据为底,叠加各输入框编辑后的值(数值字段按原类型回转)
|
||||
final params = <String, dynamic>{..._param!.rawData};
|
||||
for (final entry in _controllers.entries) {
|
||||
final original = _param!.rawData[entry.key];
|
||||
final text = entry.value.text.trim();
|
||||
if (original is int) {
|
||||
params[entry.key] = int.tryParse(text) ?? original;
|
||||
} else if (original is double) {
|
||||
params[entry.key] = double.tryParse(text) ?? original;
|
||||
} else if (original is num) {
|
||||
params[entry.key] = num.tryParse(text) ?? original;
|
||||
} else {
|
||||
params[entry.key] = text;
|
||||
}
|
||||
}
|
||||
// 关键字段兜底:接口未返回时用当前上下文补齐
|
||||
params['id'] ??= _param!.id;
|
||||
params['deviceId'] ??= _deviceId;
|
||||
params['siteId'] ??= siteId;
|
||||
params['orgId'] ??= orgId;
|
||||
|
||||
final result = await useCase.execute(params);
|
||||
|
||||
@@ -411,14 +408,11 @@ class _RobotParamSettingsPageState extends State<RobotParamSettingsPage>
|
||||
child: Column(
|
||||
children: List.generate(keys.length, (i) {
|
||||
final key = keys[i];
|
||||
final label = _fieldLabels[key] ?? key;
|
||||
final isEditable = _editableKeys.contains(key);
|
||||
final isLast = i == keys.length - 1;
|
||||
final value = raw[key];
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
_buildFieldRow(key, label, value, isEditable),
|
||||
_buildFieldRow(key),
|
||||
if (!isLast) _buildDivider(),
|
||||
],
|
||||
);
|
||||
@@ -446,65 +440,54 @@ class _RobotParamSettingsPageState extends State<RobotParamSettingsPage>
|
||||
}
|
||||
|
||||
// ============ 单行字段 ============
|
||||
Widget _buildFieldRow(
|
||||
String key,
|
||||
String label,
|
||||
dynamic value,
|
||||
bool editable,
|
||||
) {
|
||||
final controller = editable ? _controllerForKey(key) : null;
|
||||
final displayValue = value?.toString() ?? '-';
|
||||
Widget _buildFieldRow(String key) {
|
||||
final controller = _controllers[key];
|
||||
final isNumeric = _isNumericField(key);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 90,
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xFF4E5969),
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
width: 130,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 主行:始终显示字段名(英文 key),避免中英混排
|
||||
Text(
|
||||
key,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xFF4E5969),
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
// 副行:有中文映射才显示(小字灰色),没有则不显示
|
||||
if (_fieldLabels[key] != null)
|
||||
Text(
|
||||
_fieldLabels[key]!,
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
color: Color(0xFF86909C),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: editable
|
||||
? TextField(
|
||||
controller: controller,
|
||||
keyboardType:
|
||||
const TextInputType.numberWithOptions(decimal: true),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[\d.]')),
|
||||
],
|
||||
style: const TextStyle(fontSize: 14, color: Color(0xFF1D2129)),
|
||||
decoration: _inputDecoration(),
|
||||
)
|
||||
: GestureDetector(
|
||||
onTap: _showReadonlyTip,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 10,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF2F3F5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(color: const Color(0xFFE5E6EB)),
|
||||
),
|
||||
child: Text(
|
||||
displayValue,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xFF86909C),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
keyboardType: isNumeric
|
||||
? const TextInputType.numberWithOptions(decimal: true)
|
||||
: TextInputType.text,
|
||||
inputFormatters: isNumeric
|
||||
? [FilteringTextInputFormatter.allow(RegExp(r'[\d.\-]'))]
|
||||
: null,
|
||||
style: const TextStyle(fontSize: 14, color: Color(0xFF1D2129)),
|
||||
decoration: _inputDecoration(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user