diff --git a/lib/features/v2/device_list/presentation/cubit/rename_device_cubit.dart b/lib/features/v2/device_list/presentation/cubit/rename_device_cubit.dart new file mode 100644 index 00000000..60df7906 --- /dev/null +++ b/lib/features/v2/device_list/presentation/cubit/rename_device_cubit.dart @@ -0,0 +1,61 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../domain/usecases/update_device_alias_usecase.dart'; +import 'rename_device_state.dart'; + +/// 设备改名 Cubit +/// +/// 调用链:UI → RenameDeviceCubit → UpdateDeviceAliasUseCase → Repository → DataSource, +/// 页面不再直接依赖用例或数据源。 +class RenameDeviceCubit extends Cubit { + final UpdateDeviceAliasUseCase _updateDeviceAlias; + + RenameDeviceCubit(this._updateDeviceAlias) : super(const RenameDeviceState()); + + /// 下发修改设备别名(后端只写 deviceAlias,deviceName 保持不变) + Future rename({required String deviceId, required String alias}) async { + if (state.isSubmitting) return; + + emit( + RenameDeviceState( + isSubmitting: true, + deviceId: deviceId, + alias: alias, + ), + ); + + final result = await _updateDeviceAlias.execute( + deviceId: deviceId, + deviceAlias: alias, + ); + + result.fold( + (failure) => emit( + RenameDeviceState( + errorMessage: failure.message, + deviceId: deviceId, + alias: alias, + ), + ), + (updated) => emit( + updated + ? RenameDeviceState( + isSuccess: true, + deviceId: deviceId, + alias: alias, + ) + : RenameDeviceState( + errorMessage: '', + deviceId: deviceId, + alias: alias, + ), + ), + ); + } + + /// 结果已被界面消费,复位避免重复弹提示 + void reset() { + if (state.isSubmitting) return; + emit(const RenameDeviceState()); + } +} diff --git a/lib/features/v2/device_list/presentation/cubit/rename_device_state.dart b/lib/features/v2/device_list/presentation/cubit/rename_device_state.dart new file mode 100644 index 00000000..b84c6d9b --- /dev/null +++ b/lib/features/v2/device_list/presentation/cubit/rename_device_state.dart @@ -0,0 +1,39 @@ +import 'package:equatable/equatable.dart'; + +/// 设备改名(写 deviceAlias)提交状态 +/// +/// 一次性结果用 [isSuccess] / [errorMessage] 表达: +/// [errorMessage] 非 null 即失败,空串表示只知失败、文案由 UI 套本地化提示。 +class RenameDeviceState extends Equatable { + /// 请求进行中(用于拦截重复提交) + final bool isSubmitting; + + /// 后端已成功写入 deviceAlias + final bool isSuccess; + + /// 失败原因;null 表示尚无结果 + final String? errorMessage; + + /// 本次操作的设备 ID + final String? deviceId; + + /// 本次提交的新别名 + final String? alias; + + const RenameDeviceState({ + this.isSubmitting = false, + this.isSuccess = false, + this.errorMessage, + this.deviceId, + this.alias, + }); + + @override + List get props => [ + isSubmitting, + isSuccess, + errorMessage, + deviceId, + alias, + ]; +} diff --git a/lib/features/v2/device_list/presentation/widgets/rename_device_dialog.dart b/lib/features/v2/device_list/presentation/widgets/rename_device_dialog.dart index cf291667..53ced633 100644 --- a/lib/features/v2/device_list/presentation/widgets/rename_device_dialog.dart +++ b/lib/features/v2/device_list/presentation/widgets/rename_device_dialog.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; /// 修改设备名称(设计稿:设备卡片铅笔图标 → 底部弹窗) /// @@ -63,8 +64,18 @@ class _RenameDeviceSheetState extends State<_RenameDeviceSheet> { static const Color _fieldBorder = Color(0xFFDCEBFA); static const Color _divider = Color(0xFFF0F4FA); - /// 快捷命名(最后一项是「自定义」:清空输入,自由填写) - static const List _presets = ['1号机', '2号机', '北区', '南区', '测试车', '自定义']; + /// 快捷命名代码(最后一项 custom:清空输入,自由填写) + static const List _presetKeys = [ + 'preset_1', + 'preset_2', + 'preset_north', + 'preset_south', + 'preset_test', + 'preset_custom', + ]; + + static String _presetLabel(AppLocalizations l10n, String key) => + l10n.translate('device_list_v2.$key'); late final TextEditingController _controller = TextEditingController( text: widget.currentName, @@ -104,10 +115,11 @@ class _RenameDeviceSheetState extends State<_RenameDeviceSheet> { } /// 快捷命名:前五项作为名称填入(可继续编辑),自定义则清空待输入 - void _applyPreset(String preset) { + void _applyPreset(String key) { + final label = _presetLabel(AppLocalizations.of(context), key); setState(() { _empty = false; - _controller.text = preset == '自定义' ? '' : preset; + _controller.text = key == 'preset_custom' ? '' : label; _controller.selection = TextSelection.collapsed( offset: _controller.text.length, ); @@ -117,6 +129,7 @@ class _RenameDeviceSheetState extends State<_RenameDeviceSheet> { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); final keyboard = MediaQuery.of(context).viewInsets.bottom; return Container( decoration: const BoxDecoration( @@ -141,9 +154,9 @@ class _RenameDeviceSheetState extends State<_RenameDeviceSheet> { const SizedBox(height: 16), const Divider(height: 1, thickness: 0.6, color: _divider), const SizedBox(height: 16), - const Text( - '设备名称', - style: TextStyle( + Text( + l10n.translate('device_list_v2.rename_label'), + style: const TextStyle( fontSize: 13.5, fontWeight: FontWeight.w700, color: _navy, @@ -154,24 +167,26 @@ class _RenameDeviceSheetState extends State<_RenameDeviceSheet> { const SizedBox(height: 8), Text( _empty - ? '名称不能为空' - : '支持中文、英文、数字和常用符号,建议 1-${widget.maxLength} 个字符', + ? l10n.translate('device_list_v2.rename_empty') + : l10n + .translate('device_list_v2.rename_hint') + .replaceAll('%s', '${widget.maxLength}'), style: TextStyle( fontSize: 11.5, color: _empty ? _danger : _hint, ), ), const SizedBox(height: 16), - const Text( - '快捷命名', - style: TextStyle( + Text( + l10n.translate('device_list_v2.rename_quick'), + style: const TextStyle( fontSize: 13.5, fontWeight: FontWeight.w700, color: _navy, ), ), const SizedBox(height: 10), - _buildPresets(), + _buildPresets(l10n), const SizedBox(height: 20), _buildActions(), const SizedBox(height: 12), @@ -201,12 +216,14 @@ class _RenameDeviceSheetState extends State<_RenameDeviceSheet> { Widget _buildHeader() { return Row( children: [ - const Expanded( + Expanded( child: Text( - '修改设备名称', + AppLocalizations.of( + context, + ).translate('device_list_v2.rename_title'), maxLines: 1, overflow: TextOverflow.ellipsis, - style: TextStyle( + style: const TextStyle( fontSize: 20, fontWeight: FontWeight.w800, color: _navy, @@ -333,14 +350,16 @@ class _RenameDeviceSheetState extends State<_RenameDeviceSheet> { color: _navy, ), cursorColor: _blue, - decoration: const InputDecoration( + decoration: InputDecoration( isDense: true, border: InputBorder.none, enabledBorder: InputBorder.none, focusedBorder: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - hintText: '请输入设备名称', - hintStyle: TextStyle(fontSize: 16, color: _hint), + contentPadding: const EdgeInsets.symmetric(vertical: 16), + hintText: AppLocalizations.of( + context, + ).translate('device_list_v2.rename_field_hint'), + hintStyle: const TextStyle(fontSize: 16, color: _hint), ), ), ), @@ -372,14 +391,14 @@ class _RenameDeviceSheetState extends State<_RenameDeviceSheet> { } /// 快捷命名胶囊 - Widget _buildPresets() { + Widget _buildPresets(AppLocalizations l10n) { return Wrap( spacing: 8, runSpacing: 8, children: [ - for (final p in _presets) + for (final key in _presetKeys) GestureDetector( - onTap: () => _applyPreset(p), + onTap: () => _applyPreset(key), behavior: HitTestBehavior.opaque, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 9), @@ -389,7 +408,7 @@ class _RenameDeviceSheetState extends State<_RenameDeviceSheet> { border: Border.all(color: _fieldBorder), ), child: Text( - p, + _presetLabel(l10n, key), style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w600, @@ -416,9 +435,11 @@ class _RenameDeviceSheetState extends State<_RenameDeviceSheet> { color: _softBlue, borderRadius: BorderRadius.circular(14), ), - child: const Text( - '取消', - style: TextStyle( + child: Text( + AppLocalizations.of( + context, + ).translate('device_list_v2.action_cancel'), + style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w700, color: _blue, @@ -446,9 +467,11 @@ class _RenameDeviceSheetState extends State<_RenameDeviceSheet> { ), ], ), - child: const Text( - '保存', - style: TextStyle( + child: Text( + AppLocalizations.of( + context, + ).translate('device_list_v2.action_save'), + style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w700, color: Colors.white, @@ -469,7 +492,9 @@ class _RenameDeviceSheetState extends State<_RenameDeviceSheet> { const SizedBox(width: 6), Flexible( child: Text( - '仅修改显示名称,不影响 SN、绑定关系和设备权限', + AppLocalizations.of( + context, + ).translate('device_list_v2.rename_footer'), textAlign: TextAlign.center, style: const TextStyle(fontSize: 11.5, color: _hint), ),