机器列表和远程喊话等ui

This commit is contained in:
2026-11-06 08:47:36 +08:00
parent e6165b180b
commit 84eec7da96
25 changed files with 2662 additions and 1214 deletions

View File

@@ -1,3 +1,7 @@
import 'package:maibu_satabot_v2/core/localization/app_localizations.dart';
import '../../domain/entities/device_model_info.dart';
/// 机器人数据模型
class RobotDataModel {
final String name;
@@ -11,6 +15,28 @@ class RobotDataModel {
final String task;
final bool hasHost;
/// 产品型号(getSiteList 返回,如「割草机产品MC700」)
final String? productName;
/// 机型编码(getSiteList 返回,如 MC700PRO)—— 列表机型展示以此为准
final String? productModel;
/// 设备类型(getSiteList 返回的 deviceType)
///
/// 展示位置在版本号(PRO / PLUS / STD)之前:`MC700 · PRO 旗舰版`。
final String? deviceType;
/// 归属信息:当前挂在哪个组织 / 场站 / 负责人下。
///
/// 后端 getSiteList 不一定回传这些字段,因此全部可空:
/// 拿不到时页面显示「未知」,不编造。
final int? orgId;
final String? orgName;
final int? siteId;
final String? siteName;
final int? userId;
final String? userName;
const RobotDataModel({
required this.name,
required this.id,
@@ -22,6 +48,15 @@ class RobotDataModel {
required this.battery,
required this.task,
this.hasHost = false,
this.productName,
this.productModel,
this.deviceType,
this.orgId,
this.orgName,
this.siteId,
this.siteName,
this.userId,
this.userName,
});
/// 从 JSON 创建数据模型
@@ -49,9 +84,60 @@ class RobotDataModel {
battery: batteryValue,
task: json['task'] ?? json['currentTask'] ?? '待机中',
hasHost: json['hasHost'] as bool? ?? false,
productName: _pickStr(json['productName']),
productModel: _pickStr(json['productModel']),
deviceType: _pickStr(json['deviceType'] ?? json['device_type']),
orgId: _pickInt(json, const ['orgId', 'org_id', 'organizationId']),
orgName: _pickStr(
json['orgName'] ?? json['org_name'] ?? json['organizationName'],
),
siteId: _pickInt(json, const ['siteId', 'site_id']),
siteName: _pickStr(json['siteName'] ?? json['site_name']),
userId: _pickInt(json, const [
'userId',
'user_id',
'ownerId',
'owner_id',
'managerId',
]),
userName: _pickStr(
json['userName'] ??
json['user_name'] ??
json['ownerName'] ??
json['nickName'] ??
json['managerName'],
),
);
}
/// 多候选 key 里取第一个能转成整数的值
static int? _pickInt(Map<String, dynamic> json, List<String> keys) {
for (final key in keys) {
final value = json[key];
if (value is int) return value;
if (value is num) return value.toInt();
if (value is String) {
final parsed = int.tryParse(value.trim());
if (parsed != null) return parsed;
}
}
return null;
}
/// 机型信息(系列 + 版本),由 productModel 解析
DeviceModelInfo get modelInfo => DeviceModelInfo.parse(productModel);
/// 列表卡「型号 · 版本」文案:`MC700 · PRO 旗舰版`
/// productModel 为 null / 空串时统一显示未知;
/// deviceType 排在版本号之前。
String modelLabelOf(AppLocalizations l10n) =>
modelInfo.labelOf(l10n, deviceType: deviceType);
static String? _pickStr(dynamic value) {
final s = value?.toString().trim() ?? '';
return s.isEmpty ? null : s;
}
/// 转换为 JSON
Map<String, dynamic> toJson() {
return {
@@ -64,6 +150,15 @@ class RobotDataModel {
'statusCode': statusCode,
'battery': battery,
'task': task,
'productName': productName,
'productModel': productModel,
'deviceType': deviceType,
'orgId': orgId,
'orgName': orgName,
'siteId': siteId,
'siteName': siteName,
'userId': userId,
'userName': userName,
};
}

View File

@@ -52,7 +52,12 @@ class VoicePresetStore {
VoicePreset(id: 'default_polite', name: '礼貌提示', text: '请注意避让,感谢配合。'),
];
static Future<List<VoicePreset>> loadPresets(String deviceName) async {
/// 首次进入时落盘一份出厂默认;[fallback] 允许调用方传入已本地化的默认值
static Future<List<VoicePreset>> loadPresets(
String deviceName, {
List<VoicePreset>? fallback,
}) async {
final defaults = fallback ?? VoicePresetStore.defaults;
final prefs = await SharedPreferences.getInstance();
final raw = prefs.getString(_keyOf(deviceName));
if (raw == null || raw.isEmpty) {