修改名字接口
This commit is contained in:
@@ -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<RenameDeviceState> {
|
||||
final UpdateDeviceAliasUseCase _updateDeviceAlias;
|
||||
|
||||
RenameDeviceCubit(this._updateDeviceAlias) : super(const RenameDeviceState());
|
||||
|
||||
/// 下发修改设备别名(后端只写 deviceAlias,deviceName 保持不变)
|
||||
Future<void> 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());
|
||||
}
|
||||
}
|
||||
@@ -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<Object?> get props => [
|
||||
isSubmitting,
|
||||
isSuccess,
|
||||
errorMessage,
|
||||
deviceId,
|
||||
alias,
|
||||
];
|
||||
}
|
||||
@@ -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<String> _presets = ['1号机', '2号机', '北区', '南区', '测试车', '自定义'];
|
||||
/// 快捷命名代码(最后一项 custom:清空输入,自由填写)
|
||||
static const List<String> _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),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user