HarmonyOS开发:设备分组——设备分类管理

举报
Jack20 发表于 2026/06/25 21:36:17 2026/06/25
【摘要】 HarmonyOS开发:设备分组——设备分类管理📌 核心要点:设备分组让几十个设备井井有条,按房间分、按类型分、按自定义标签分,批量控制一个分组搞定,分组数据用关系型数据库持久化保平安。 背景与动机你家有30个智能设备。客厅5个灯、2个空调、1个窗帘;卧室3个灯、1个空调;厨房2个灯、1个油烟机……打开App,30个设备平铺在一个列表里。你要关客厅的灯,得在30个设备里找5个客厅灯,一个...

HarmonyOS开发:设备分组——设备分类管理

📌 核心要点:设备分组让几十个设备井井有条,按房间分、按类型分、按自定义标签分,批量控制一个分组搞定,分组数据用关系型数据库持久化保平安。

背景与动机

你家有30个智能设备。客厅5个灯、2个空调、1个窗帘;卧室3个灯、1个空调;厨房2个灯、1个油烟机……

打开App,30个设备平铺在一个列表里。你要关客厅的灯,得在30个设备里找5个客厅灯,一个一个关。找着找着,你关了卧室的灯——因为列表太长,滚着滚着就搞混了。

这不是用户的问题,是你的设计有问题。

设备分组就是解决"找设备难、批量操作难"的问题。 按房间分组,客厅的设备在一起,卧室的设备在一起;按类型分组,所有灯在一起,所有空调在一起。关客厅灯?选中"客厅"分组,一键全关。

分组看起来简单——不就是给设备打个标签嘛?但做起来有几个关键问题:一个设备能不能属于多个分组?分组层级要不要支持(客厅→灯→主灯)?批量控制失败了怎么办?分组数据怎么持久化?

核心原理

分组模型

设备分组有两种基本模型:房间分组类型分组

flowchart TD
    A[全部设备] --> B{分组维度}
    
    B -->|按房间| C[客厅]
    B -->|按房间| D[卧室]
    B -->|按房间| E[厨房]
    B -->|按房间| F[卫生间]
    
    B -->|按类型| G[灯光]
    B -->|按类型| H[空调]
    B -->|按类型| I[窗帘]
    B -->|按类型| J[传感器]
    
    C --> C1[客厅主灯]
    C --> C2[客厅射灯]
    C --> C3[客厅空调]
    C --> C4[客厅窗帘]
    
    G --> G1[客厅主灯]
    G --> G2[客厅射灯]
    G --> G3[卧室灯]
    G --> G4[厨房灯]
    
    C1 -.->|同一设备可属于多个分组| G1
    
    classDef root fill:#1565C0,color:#fff,stroke:#0D47A1
    classDef room fill:#2E7D32,color:#fff,stroke:#1B5E20
    classDef type fill:#E65100,color:#fff,stroke:#BF360C
    classDef device fill:#6A1B9A,color:#fff,stroke:#4A148C

    class A,B,root
    class C,D,E,F,room
    class G,H,I,J,type
    class C1,C2,C3,C4,G1,G2,G3,G4,device

分组与设备的关系

一个设备可以属于多个分组。 客厅主灯既属于"客厅"分组,也属于"灯光"分组。这是多对多关系,不是树形结构。

关系 说明 示例
设备→房间 一个设备属于一个房间 客厅主灯→客厅
设备→类型 一个设备属于一个类型 客厅主灯→灯光
设备→自定义分组 一个设备可属于多个自定义分组 客厅主灯→“回家模式设备组”

批量控制

分组的核心价值是批量控制。选中一个分组,对所有设备执行同一操作。

批量控制的关键问题:部分失败怎么办? 30个灯关了28个,2个因为离线关不了——告诉用户"成功"还是"失败"?

答案:部分成功。 返回结果包含成功数和失败数,失败的设备标红提示用户。不要因为2个失败就回滚28个成功的——那样用户更崩溃。

分组数据持久化

分组数据必须持久化,否则App重启后分组就没了。用鸿蒙的关系型数据库(RDB)存储分组信息,表结构如下:

  • device_group表:分组ID、分组名称、分组类型(房间/类型/自定义)、图标、排序
  • device_group_member表:分组ID、设备ID(多对多关联表)

代码实战

基础用法:分组数据模型与数据库

用关系型数据库存储分组信息,实现分组的增删改查。

// services/DeviceGroupService.ets
// 设备分组服务 - 分组管理、批量操作、数据持久化

import { relationalStore } from '@kit.ArkData'

// 分组信息
interface DeviceGroup {
  groupId: string
  name: string
  type: 'room' | 'deviceType' | 'custom'  // 分组类型
  icon?: Resource
  sortOrder: number       // 排序序号
  deviceCount: number     // 设备数量(查询时计算)
}

// 分组成员
interface GroupMember {
  groupId: string
  deviceId: string
}

// 批量控制结果
interface BatchControlResult {
  totalCount: number
  successCount: number
  failedDevices: string[]  // 控制失败的设备ID
}

class DeviceGroupService {
  private rdbStore?: relationalStore.RdbStore
  private readonly DB_NAME = 'smart_home.db'
  private readonly GROUP_TABLE = 'device_group'
  private readonly MEMBER_TABLE = 'device_group_member'

  // 初始化数据库
  async init(context: Context): Promise<void> {
    const config: relationalStore.StoreConfig = {
      name: this.DB_NAME,
      securityLevel: relationalStore.SecurityLevel.S1
    }

    this.rdbStore = await relationalStore.getRdbStore(context, config)

    // 创建分组表
    const createGroupTable = `
      CREATE TABLE IF NOT EXISTS ${this.GROUP_TABLE} (
        group_id TEXT PRIMARY KEY,
        name TEXT NOT NULL,
        type TEXT NOT NULL,
        sort_order INTEGER DEFAULT 0,
        created_at INTEGER,
        updated_at INTEGER
      )
    `
    await this.rdbStore.executeSql(createGroupTable)

    // 创建分组成员表(多对多关联)
    const createMemberTable = `
      CREATE TABLE IF NOT EXISTS ${this.MEMBER_TABLE} (
        group_id TEXT NOT NULL,
        device_id TEXT NOT NULL,
        PRIMARY KEY (group_id, device_id)
      )
    `
    await this.rdbStore.executeSql(createMemberTable)
  }

  // 创建分组
  async createGroup(group: DeviceGroup): Promise<boolean> {
    if (!this.rdbStore) return false

    try {
      const valueBucket: relationalStore.ValuesBucket = {
        'group_id': group.groupId,
        'name': group.name,
        'type': group.type,
        'sort_order': group.sortOrder,
        'created_at': Date.now(),
        'updated_at': Date.now()
      }
      await this.rdbStore.insert(this.GROUP_TABLE, valueBucket)
      return true
    } catch (err) {
      console.error(`创建分组失败: ${JSON.stringify(err)}`)
      return false
    }
  }

  // 添加设备到分组
  async addDeviceToGroup(groupId: string, deviceId: string): Promise<boolean> {
    if (!this.rdbStore) return false

    try {
      const valueBucket: relationalStore.ValuesBucket = {
        'group_id': groupId,
        'device_id': deviceId
      }
      await this.rdbStore.insert(this.MEMBER_TABLE, valueBucket)
      return true
    } catch (err) {
      console.error(`添加设备到分组失败: ${JSON.stringify(err)}`)
      return false
    }
  }

  // 从分组移除设备
  async removeDeviceFromGroup(groupId: string, deviceId: string): Promise<boolean> {
    if (!this.rdbStore) return false

    try {
      const predicates = new relationalStore.RdbPredicates(this.MEMBER_TABLE)
      predicates.equalTo('group_id', groupId).and().equalTo('device_id', deviceId)
      await this.rdbStore.delete(predicates)
      return true
    } catch (err) {
      console.error(`移除设备失败: ${JSON.stringify(err)}`)
      return false
    }
  }

  // 获取所有分组
  async getAllGroups(): Promise<DeviceGroup[]> {
    if (!this.rdbStore) return []

    try {
      const predicates = new relationalStore.RdbPredicates(this.GROUP_TABLE)
      predicates.orderByAsc('sort_order')

      const resultSet = await this.rdbStore.query(predicates, ['group_id', 'name', 'type', 'sort_order'])
      const groups: DeviceGroup[] = []

      while (resultSet.goToNextRow()) {
        const groupId = resultSet.getString(resultSet.getColumnIndex('group_id'))
        const name = resultSet.getString(resultSet.getColumnIndex('name'))
        const type = resultSet.getString(resultSet.getColumnIndex('type')) as 'room' | 'deviceType' | 'custom'
        const sortOrder = resultSet.getLong(resultSet.getColumnIndex('sort_order'))

        // 查询分组内的设备数量
        const countPredicates = new relationalStore.RdbPredicates(this.MEMBER_TABLE)
        countPredicates.equalTo('group_id', groupId)
        const count = await this.rdbStore.query(countPredicates, ['COUNT(*)'])

        let deviceCount = 0
        if (count.goToNextRow()) {
          deviceCount = count.getLong(0)
        }
        count.close()

        groups.push({ groupId, name, type, sortOrder, deviceCount })
      }

      resultSet.close()
      return groups
    } catch (err) {
      console.error(`查询分组失败: ${JSON.stringify(err)}`)
      return []
    }
  }

  // 获取分组内的设备ID列表
  async getGroupDeviceIds(groupId: string): Promise<string[]> {
    if (!this.rdbStore) return []

    try {
      const predicates = new relationalStore.RdbPredicates(this.MEMBER_TABLE)
      predicates.equalTo('group_id', groupId)

      const resultSet = await this.rdbStore.query(predicates, ['device_id'])
      const deviceIds: string[] = []

      while (resultSet.goToNextRow()) {
        deviceIds.push(resultSet.getString(resultSet.getColumnIndex('device_id')))
      }

      resultSet.close()
      return deviceIds
    } catch (err) {
      console.error(`查询分组设备失败: ${JSON.stringify(err)}`)
      return []
    }
  }

  // 删除分组
  async deleteGroup(groupId: string): Promise<boolean> {
    if (!this.rdbStore) return false

    try {
      // 先删除分组成员
      const memberPredicates = new relationalStore.RdbPredicates(this.MEMBER_TABLE)
      memberPredicates.equalTo('group_id', groupId)
      await this.rdbStore.delete(memberPredicates)

      // 再删除分组
      const groupPredicates = new relationalStore.RdbPredicates(this.GROUP_TABLE)
      groupPredicates.equalTo('group_id', groupId)
      await this.rdbStore.delete(groupPredicates)

      return true
    } catch (err) {
      console.error(`删除分组失败: ${JSON.stringify(err)}`)
      return false
    }
  }

  // 批量控制分组内所有设备
  async batchControlGroup(
    groupId: string,
    property: string,
    value: Object,
    executor: (deviceId: string, property: string, value: Object) => Promise<boolean>
  ): Promise<BatchControlResult> {
    const deviceIds = await this.getGroupDeviceIds(groupId)
    let successCount = 0
    const failedDevices: string[] = []

    // 并发控制:最多同时5个请求
    const batchSize = 5
    for (let i = 0; i < deviceIds.length; i += batchSize) {
      const batch = deviceIds.slice(i, i + batchSize)
      const results = await Promise.allSettled(
        batch.map(deviceId => executor(deviceId, property, value))
      )

      results.forEach((result, index) => {
        if (result.status === 'fulfilled' && result.value) {
          successCount++
        } else {
          failedDevices.push(batch[index])
        }
      })
    }

    return {
      totalCount: deviceIds.length,
      successCount,
      failedDevices
    }
  }
}

export default new DeviceGroupService()
export type { DeviceGroup, BatchControlResult }

进阶用法:分组管理UI

分组列表展示、设备分配、拖拽排序。

// components/GroupManager.ets
// 分组管理组件 - 分组列表、设备分配、批量操作

import DeviceGroupService, { DeviceGroup, BatchControlResult } from '../services/DeviceGroupService'

@Component
export struct GroupManager {
  @State groups: DeviceGroup[] = []
  @State selectedGroupId: string = ''
  @State batchResult: BatchControlResult | null = null
  @State isBatching: boolean = false

  // 设备控制回调
  onControlDevice?: (deviceId: string, property: string, value: Object) => Promise<boolean>

  aboutToAppear() {
    this.loadGroups()
  }

  async loadGroups() {
    this.groups = await DeviceGroupService.getAllGroups()
  }

  // 批量关灯
  async batchTurnOff(groupId: string) {
    this.isBatching = true
    this.selectedGroupId = groupId

    this.batchResult = await DeviceGroupService.batchControlGroup(
      groupId,
      'power',
      false,
      this.onControlDevice || (async () => true)
    )

    this.isBatching = false
  }

  // 批量开灯
  async batchTurnOn(groupId: string) {
    this.isBatching = true
    this.selectedGroupId = groupId

    this.batchResult = await DeviceGroupService.batchControlGroup(
      groupId,
      'power',
      true,
      this.onControlDevice || (async () => true)
    )

    this.isBatching = false
  }

  build() {
    Column() {
      // 分组类型Tab
      Tabs({ index: 0 }) {
        TabContent() {
          this.GroupList('room')
        }.tabBar('按房间')

        TabContent() {
          this.GroupList('deviceType')
        }.tabBar('按类型')

        TabContent() {
          this.GroupList('custom')
        }.tabBar('自定义')
      }
      .width('100%')
      .layoutWeight(1)
      .barMode(BarMode.Fixed)

      // 批量操作结果
      if (this.batchResult) {
        this.BatchResultBar()
      }
    }
    .width('100%')
    .height('100%')
  }

  // 分组列表
  @Builder GroupList(type: string) {
    List({ space: 8 }) {
      ForEach(
        this.groups.filter(g => g.type === type),
        (group: DeviceGroup) => {
          ListItem() {
            Row() {
              // 分组图标
              Column() {
                Text(group.name.charAt(0))
                  .fontSize(20)
                  .fontColor('#FFFFFF')
                  .fontWeight(FontWeight.Bold)
              }
              .width(44)
              .height(44)
              .borderRadius(12)
              .backgroundColor(this.getGroupColor(group.type))
              .justifyContent(FlexAlign.Center)

              // 分组名称和设备数
              Column() {
                Text(group.name)
                  .fontSize(15)
                  .fontColor('#333333')
                  .fontWeight(FontWeight.Medium)
                Text(`${group.deviceCount}个设备`)
                  .fontSize(12)
                  .fontColor('#999999')
                  .margin({ top: 2 })
              }
              .alignItems(HorizontalAlign.Start)
              .margin({ left: 12 })
              .layoutWeight(1)

              // 批量操作按钮
              Row() {
                // 全开
                Text('全开')
                  .fontSize(12)
                  .fontColor('#007DFF')
                  .padding({ left: 10, right: 10, top: 4, bottom: 4 })
                  .backgroundColor('#F0F7FF')
                  .borderRadius(12)
                  .onClick(() => this.batchTurnOn(group.groupId))

                // 全关
                Text('全关')
                  .fontSize(12)
                  .fontColor('#FF5722')
                  .padding({ left: 10, right: 10, top: 4, bottom: 4 })
                  .backgroundColor('#FFF3E0')
                  .borderRadius(12)
                  .margin({ left: 8 })
                  .onClick(() => this.batchTurnOff(group.groupId))
              }

              // 箭头
              Image($r('sys.media.ohos_ic_public_arrow_right'))
                .width(16).height(16).fillColor('#CCCCCC')
                .margin({ left: 8 })
            }
            .width('100%')
            .padding(12)
            .backgroundColor('#FFFFFF')
            .borderRadius(12)
            .onClick(() => {
              this.selectedGroupId = group.groupId
              // 跳转到分组详情页
            })
          }
        }
      }

      // 添加分组按钮
      ListItem() {
        Row() {
          Text('+').fontSize(20).fontColor('#007DFF')
          Text('添加分组').fontSize(14).fontColor('#007DFF').margin({ left: 8 })
        }
        .width('100%')
        .padding(16)
        .justifyContent(FlexAlign.Center)
        .backgroundColor('#FFFFFF')
        .borderRadius(12)
        .border({ width: 1, color: '#E0E0E0', style: BorderStyle.Dashed })
      }
    }
    .width('100%')
    .padding({ left: 16, right: 16, top: 8 })
  }

  // 批量操作结果
  @Builder BatchResultBar() {
    Row() {
      if (this.batchResult!.failedDevices.length === 0) {
        Image($r('sys.media.ohos_ic_public_ok'))
          .width(16).height(16).fillColor('#4CAF50')
        Text(`全部成功(${this.batchResult!.successCount}个设备)`)
          .fontSize(13).fontColor('#4CAF50').margin({ left: 8 })
      } else {
        Image($r('sys.media.ohos_ic_public_fail'))
          .width(16).height(16).fillColor('#FF9800')
        Text(`成功${this.batchResult!.successCount}个,失败${this.batchResult!.failedDevices.length}`)
          .fontSize(13).fontColor('#FF9800').margin({ left: 8 })
      }
    }
    .width('100%')
    .padding(12)
    .backgroundColor(this.batchResult!.failedDevices.length === 0 ? '#E8F5E9' : '#FFF3E0')
    .padding({ left: 16, right: 16 })
  }

  // 分组颜色
  private getGroupColor(type: string): string {
    const colors: Record<string, string> = {
      'room': '#007DFF',
      'deviceType': '#4CAF50',
      'custom': '#FF9800'
    }
    return colors[type] || '#999999'
  }
}

完整示例:设备分组主页面

整合分组服务、分组管理组件,实现完整的设备分组管理体验。

// pages/DeviceGroupPage.ets
// 设备分组主页面

import DeviceGroupService, { DeviceGroup } from '../services/DeviceGroupService'
import DeviceControlManager from '../services/DeviceControlManager'
import { GroupManager } from '../components/GroupManager'
import { promptAction } from '@kit.ArkUI'

@Entry
@Component
struct DeviceGroupPage {
  @State groups: DeviceGroup[] = []
  @State showCreateDialog: boolean = false
  @State newGroupName: string = ''
  @State newGroupType: string = 'room'

  aboutToAppear() {
    this.initGroups()
  }

  // 初始化默认分组
  async initGroups() {
    await DeviceGroupService.init(getContext(this))
    const existingGroups = await DeviceGroupService.getAllGroups()

    if (existingGroups.length === 0) {
      // 首次使用,创建默认房间分组
      const defaultRooms: DeviceGroup[] = [
        { groupId: 'room-living', name: '客厅', type: 'room', sortOrder: 0, deviceCount: 0 },
        { groupId: 'room-bedroom', name: '卧室', type: 'room', sortOrder: 1, deviceCount: 0 },
        { groupId: 'room-kitchen', name: '厨房', type: 'room', sortOrder: 2, deviceCount: 0 },
        { groupId: 'room-bathroom', name: '卫生间', type: 'room', sortOrder: 3, deviceCount: 0 }
      ]

      // 创建默认类型分组
      const defaultTypes: DeviceGroup[] = [
        { groupId: 'type-light', name: '灯光', type: 'deviceType', sortOrder: 0, deviceCount: 0 },
        { groupId: 'type-ac', name: '空调', type: 'deviceType', sortOrder: 1, deviceCount: 0 },
        { groupId: 'type-curtain', name: '窗帘', type: 'deviceType', sortOrder: 2, deviceCount: 0 },
        { groupId: 'type-sensor', name: '传感器', type: 'deviceType', sortOrder: 3, deviceCount: 0 }
      ]

      for (const group of [...defaultRooms, ...defaultTypes]) {
        await DeviceGroupService.createGroup(group)
      }

      // 为默认分组添加设备(示例)
      await DeviceGroupService.addDeviceToGroup('room-living', 'light-001')
      await DeviceGroupService.addDeviceToGroup('room-living', 'ac-001')
      await DeviceGroupService.addDeviceToGroup('room-living', 'curtain-001')
      await DeviceGroupService.addDeviceToGroup('type-light', 'light-001')
      await DeviceGroupService.addDeviceToGroup('type-ac', 'ac-001')
    }

    this.groups = await DeviceGroupService.getAllGroups()
  }

  // 创建自定义分组
  async createCustomGroup() {
    if (!this.newGroupName.trim()) {
      promptAction.showToast({ message: '请输入分组名称' })
      return
    }

    const group: DeviceGroup = {
      groupId: `custom_${Date.now()}`,
      name: this.newGroupName,
      type: 'custom',
      sortOrder: this.groups.filter(g => g.type === 'custom').length,
      deviceCount: 0
    }

    const success = await DeviceGroupService.createGroup(group)
    if (success) {
      this.showCreateDialog = false
      this.newGroupName = ''
      this.groups = await DeviceGroupService.getAllGroups()
      promptAction.showToast({ message: '分组创建成功' })
    }
  }

  build() {
    Navigation() {
      Column() {
        // 分组管理组件
        GroupManager({
          onControlDevice: async (deviceId: string, property: string, value: Object) => {
            DeviceControlManager.controlDevice(deviceId, property, value)
            return true
          }
        })
      }
      .width('100%')
      .height('100%')
      .backgroundColor('#F8F8F8')
    }
    .title('设备分组')
    .titleMode(NavigationTitleMode.Mini)

    // 创建分组弹窗
    .bindContentCover(this.showCreateDialog, this.CreateGroupDialog(), {
      modalTransition: ModalTransition.DEFAULT
    })
  }

  // 创建分组对话框
  @Builder CreateGroupDialog() {
    Column() {
      Text('创建自定义分组')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 24 })

      // 分组名称输入
      TextInput({ placeholder: '输入分组名称', text: this.newGroupName })
        .fontSize(16)
        .onChange((value: string) => { this.newGroupName = value })
        .margin({ bottom: 16 })

      // 按钮
      Row() {
        Button('取消')
          .width('45%')
          .height(44)
          .backgroundColor('#F5F5F5')
          .fontColor('#333333')
          .borderRadius(22)
          .onClick(() => { this.showCreateDialog = false })

        Button('创建')
          .width('45%')
          .height(44)
          .backgroundColor('#007DFF')
          .fontColor('#FFFFFF')
          .borderRadius(22)
          .onClick(() => this.createCustomGroup())
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceBetween)
    }
    .padding(24)
    .backgroundColor('#FFFFFF')
    .borderRadius({ topLeft: 24, topRight: 24 })
  }
}

踩坑与注意事项

1. 分组与设备的多对多关系

一个设备属于"客厅"分组,也属于"灯光"分组。删除"客厅"分组时,设备还在"灯光"分组里。删除设备时,要从所有分组中移除。

解法: 用关联表(device_group_member)管理多对多关系。删除分组时只删关联记录,不删设备。删除设备时,先从所有分组中移除关联,再删设备。

2. 批量控制的并发问题

30个设备同时发控制指令,MQTT可能扛不住(尤其是Broker性能有限的时候)。设备端也可能处理不过来——网关同时收到30条指令,CPU飙到100%,然后卡死。

解法: 批量控制做并发限制,最多同时5个请求。用Promise.allSettled配合分批处理,一批5个完成后再发下一批。虽然慢一点,但不会把设备搞崩。

3. 分组排序的持久化

用户拖拽调整了分组顺序,刷新后又回到原来的顺序——因为排序没持久化。

解法: 分组表加sort_order字段,拖拽排序后更新sort_order值。查询时按sort_order排序。拖拽排序的UI实现可以用List组件的onDragStartonDrop回调。

4. 分组名称的国际化

“客厅”"卧室"这些分组名称,海外用户看不懂。但分组名称是用户创建的,不是你预置的——你不能替用户翻译。

解法: 预置分组用国际化资源($r('app.string.room_living')),用户自定义分组用用户输入的文本。预置分组的名称显示用资源ID,自定义分组的名称直接显示文本。

5. 空分组的处理

用户创建了一个分组但没添加设备,分组列表里显示"0个设备"。这个分组要不要显示?显示吧,占地方;不显示吧,用户以为分组丢了。

解法: 空分组正常显示,但样式上做区分——设备数为0时,分组名称用灰色,右侧显示"点击添加设备"的提示。不要自动删除空分组,那是用户的数据。

HarmonyOS 6适配说明

HarmonyOS 6对设备分组做了几项改进:

  1. 关系型数据库性能优化relationalStore的批量插入性能提升3倍,初始化大量分组数据更快。新增batchInsertAPI,一次插入多条记录。

  2. 拖拽排序API增强List组件新增onMove回调,支持列表项拖拽重排序,不需要自己计算拖拽位置和交换逻辑。

  3. 分组智能推荐:HarmonyOS 6的智能家居Kit新增分组推荐API,根据设备类型和位置自动推荐分组方案。新用户首次使用时,系统自动按设备类型创建分组,不用手动创建。

  4. 批量控制结果通知:新增@ohos.notification的批量操作结果通知,批量控制完成后,即使App在后台,用户也能看到结果通知。

适配代码示例:

// HarmonyOS 6 拖拽排序
List() {
  ForEach(this.groups, (group: DeviceGroup) => {
    ListItem() {
      // 列表项内容...
    }
  })
}
.onMove((from: number, to: number) => {
  // 拖拽排序回调
  const item = this.groups.splice(from, 1)[0]
  this.groups.splice(to, 0, item)
  
  // 更新排序字段
  this.groups.forEach((g, index) => {
    g.sortOrder = index
    // 持久化排序
    DeviceGroupService.updateSortOrder(g.groupId, index)
  })
})

// HarmonyOS 6 批量插入
const valueBuckets: relationalStore.ValuesBucket[] = groups.map(group => ({
  'group_id': group.groupId,
  'name': group.name,
  'type': group.type,
  'sort_order': group.sortOrder,
  'created_at': Date.now(),
  'updated_at': Date.now()
}))
await this.rdbStore.batchInsert(this.GROUP_TABLE, valueBuckets)

总结

设备分组是智能家居App的"收纳术"。没有分组,30个设备平铺在列表里,找设备靠眼力;有了分组,按房间、按类型、按自定义标签,一目了然,批量操作一键搞定。

分组的核心是多对多关系批量控制。多对多关系用关联表管理,批量控制做并发限制和部分失败处理。数据持久化用关系型数据库,分组排序用sort_order字段。

维度 评价
学习难度 ⭐⭐⭐ 分组本身不难,但多对多关系、批量控制、拖拽排序组合起来有一定复杂度
使用频率 ⭐⭐⭐⭐ 设备多了必须分组,否则用户体验极差
重要程度 ⭐⭐⭐⭐ 分组是智能家居App的基础设施,没有分组就没有批量操作

一句话:设备不多的时候分组是锦上添花,设备多了之后分组是雪中送炭。 别等用户骂了才加。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。