/// 门店分组下的主题页面(对应 web 后台 /theme/page/org/{orgId}/photos)。 class ThemePage { const ThemePage({ required this.themeId, required this.name, this.themePhoto, this.active = false, this.orderNo, }); /// 主题 ID(tv_theme.id)。 final int themeId; /// 主题名称。 final String name; /// 主题预览图 URL。 final String? themePhoto; /// 是否为当前分组激活主题。 final bool active; /// 排序号。 final int? orderNo; factory ThemePage.fromJson(Map json) { final rawActive = json['active']; return ThemePage( themeId: (json['themeId'] ?? json['theme_id'] ?? json['id'] ?? 0) as int, name: (json['name'] ?? json['themeName'] ?? '')?.toString() ?? '', themePhoto: (json['themePhoto'] ?? json['theme_photo'])?.toString(), active: rawActive == true || (rawActive is num && rawActive.toInt() == 1) || (rawActive is String && rawActive.toLowerCase() == 'true'), orderNo: (json['orderNo'] ?? json['order_no']) is num ? ((json['orderNo'] ?? json['order_no']) as num).toInt() : null, ); } } /// 门店设备分组及其主题列表(界面配置)。 class ThemeGroup { const ThemeGroup({ required this.groupId, required this.groupName, required this.themes, this.themeLimit = 6, }); /// 设备分组 ID。 final int groupId; /// 设备分组名称。 final String groupName; /// 分组下主题数量上限。 final int themeLimit; /// 分组下已添加的主题列表。 final List themes; factory ThemeGroup.fromJson(Map json) { final themesRaw = json['themes']; final themes = themesRaw is List ? themesRaw .whereType>() .map(ThemePage.fromJson) .where((t) => t.themeId > 0) .toList() : []; themes.sort((a, b) => (a.orderNo ?? 0).compareTo(b.orderNo ?? 0)); final rawLimit = json['themeLimit'] ?? json['theme_limit']; final themeLimit = rawLimit is num && rawLimit.toInt() > 0 ? rawLimit.toInt() : 6; return ThemeGroup( groupId: (json['groupId'] ?? json['group_id'] ?? 0) as int, groupName: (json['groupName'] ?? json['group_name'] ?? '')?.toString() ?? '未命名分组', themeLimit: themeLimit, themes: themes, ); } /// 当前激活的主题名称(空表示未激活任何主题)。 String? get activeThemeName { final active = themes.where((t) => t.active).toList(); if (active.isEmpty) return null; return active.first.name; } }