请添加图片描述

一、状态变量设计:7 个变量的"生命周期协议栈"

变量 类型 角色 数据流向
atomicStatus string 全局状态描述 Service → UI
abilityPhase string 当前 Ability 生命周期阶段 Service → UI
activeFormId string 当前活跃的卡片 ID FormExtension → Service → UI
formClickCount number 卡片被点击的累计次数 FormExtension → Service → UI
formLaunchContext FormLaunchContext | null Want 拉起的完整上下文 UIAbility → Service → UI
atomicLogs AtomicLogItem[] 生命周期事件日志 Service → UI
highlightStage number 当前高亮的链路阶段索引 Service → UI
关键设计决策

formLaunchContext 使用联合类型 FormLaunchContext | null

@StorageLink('formLaunchContext') formLaunchContext: FormLaunchContext | null = null

与设备管理页面的 localDevice 一样使用了 null 联合类型,但语义不同:

页面 变量 null 的含义
设备管理 localDevice 系统尚未返回本机设备信息
元服务卡片 formLaunchContext 尚未收到任何 Want 拉起请求

null 在这里表达的是**“事件尚未发生”**,而非"数据尚未加载"。这是一个重要的语义区分——设备信息是"一定会来"的(初始化时必然获取),而 Want 上下文是"可能不来"的(用户可能从不从卡片启动应用)。

highlightStage 使用数字索引而非字符串标识

@StorageLink('highlightStage') highlightStage: number = 0

这个变量直接对应 LIFECYCLE_STAGES 数组的索引,用于驱动 ChainSection 中的视觉高亮。选择数字而非字符串(如 'onCreate')的原因:

  • 数字索引天然支持 ForEach 中的 index 参数直接比较:index === this.highlightStage
  • 避免了字符串匹配的性能开销和拼写错误风险
  • 阶段顺序是固定的(桌面 → 卡片 → UIAbility),不会动态变化

formClickCount 是唯一使用 number 类型的状态变量

@StorageLink('formClickCount') formClickCount: number = 0

在之前所有页面中,状态变量要么是 stringboolean、数组,要么是联合类型对象。formClickCount 是第一个纯数值型状态变量,它的设计意图非常明确——量化卡片的交互频次,为开发者提供一个可观测的指标。


二、视觉体系:暗色主题的"终端风格"

这是整个 Demo 中唯一使用暗色主题的页面:

.backgroundColor('#0F1118')  // 页面背景:近黑色

对比其他页面:

页面 背景色 主题
WantFlow #F4F6F8 浅色
HMDFS #F4F6F8 浅色
设备管理 #F4F6F8 浅色
元服务卡片 #0F1118 暗色

暗色主题的选择不是随意的,它与页面的内容语义高度匹配:

  • 生命周期日志类似终端输出,暗色背景更符合"控制台"的视觉隐喻
  • 链路阶段高亮使用绿色 #6EE7B7,在暗色背景上形成类似"终端高亮"的效果
  • 阶段指示器使用 / 字符,暗色背景让这些字符更清晰
暗色主题下的色彩编码
// 面板背景
.backgroundColor('#151B2B')  // 深蓝色面板

// 文本层级
Text().fontColor('#F8FAFC')  // 标题:近白色
Text().fontColor('#E2E8F0')  // 正文:浅灰白
Text().fontColor('#CBD5E1')  // 次要文本:中灰
Text().fontColor('#94A3B8')  // 辅助文本:暗灰
Text().fontColor('#64748B')  // 标签/描述:深灰

// 强调色
Text().fontColor('#6EE7B7')  // 绿色:阶段指示/就绪状态
Text().fontColor('#93C5FD')  // 蓝色:日志来源标识

这里建立了一个五级灰度文本层级(从 #F8FAFC#64748B),比浅色主题的三级灰度(#1A1A2E#5C6B7A#94A3B8)更精细。暗色主题需要更多灰度层级来维持可读性,因为暗色背景上的文本对比度天然较低。


三、ChainSection:生命周期链路的"可视化时间线"

这是整个页面中最核心的面板,它将鸿蒙元服务的启动链路可视化为一个三阶段的时间线:

ForEach(LIFECYCLE_STAGES, (stage: string, index: number) => {
  Row({ space: 10 }) {
    Text(index === this.highlightStage ? '●' : '○')  // 阶段指示器
    Column({ space: 2 }) {
      Text(stage)  // 阶段名称
      // 阶段描述(根据 index 条件渲染)
    }
  }
})
三阶段的语义
索引 阶段名称 描述 对应 API
0 桌面 → 卡片 EntryFormAbility.onAddForm · 用户把卡片添加到桌面 FormExtensionAbility
1 卡片 → Want FormLink action=router · params 透传到 Want.parameters FormLink
2 Want → UIAbility 冷启动 onCreate / 热启动 onNewWant UIAbility
高亮机制的双重视觉编码
Text(index === this.highlightStage ? '●' : '○')
  .fontColor(index === this.highlightStage ? '#6EE7B7' : '#64748B')

.backgroundColor(index === this.highlightStage ? '#172033' : '#111827')

当前阶段同时触发三个视觉变化:

  1. 指示器:空心圆 → 实心圆
  2. 文字色:暗灰 #64748B → 亮白 #F8FAFC
  3. 背景色:深色 #111827 → 稍亮 #172033

这种三重编码确保了即使在不同的显示环境下(高亮度/低亮度),当前阶段都能被清晰识别。

条件描述的设计
if (index === 0) {
  Text('EntryFormAbility.onAddForm · 用户把卡片添加到桌面')
} else if (index === 1) {
  Text('FormLink action=router · params 透传到 Want.parameters')
} else if (index === 2) {
  Text('冷启动 onCreate / 热启动 onNewWant')
}

每个阶段附带一行技术描述,将用户可感知的操作(“把卡片添加到桌面”)与底层 API 调用EntryFormAbility.onAddForm)关联起来。这种"操作 → API"的映射是鸿蒙开发中最容易混淆的部分,通过可视化呈现大幅降低了理解门槛。


四、LaunchSection:Want 上下文的"解构展示"

if (this.formLaunchContext === null) {
  Text('尚未收到 Want,请从桌面图标或卡片启动应用')
} else {
  this.InfoRow('来源', this.formLaunchContext.launchSource)
  this.InfoRow('模式', this.formLaunchContext.launchMode)
  this.InfoRow('action', this.formLaunchContext.action.length > 0 ? this.formLaunchContext.action : '-')
  this.InfoRow('formId', this.formLaunchContext.formId.length > 0 ? this.formLaunchContext.formId : '-')
  this.InfoRow('dimension', this.formLaunchContext.dimension)
  this.InfoRow('参数', this.formLaunchContext.paramsPreview)
}
InfoRow 复用组件
@Builder
InfoRow(label: string, value: string) {
  Row({ space: 8 }) {
    Text(label)
      .fontSize(12)
      .fontColor('#64748B')
      .width(88)
    Text(value)
      .fontSize(12)
      .fontColor('#E2E8F0')
      .layoutWeight(1)
  }
  .width('100%')
}

这是一个参数化的 Builder 方法,接受 labelvalue 两个参数,生成统一的键值对行。它在 LaunchSectionFormSection 中被复用,实现了"一次定义,多处使用"。

label 固定宽度 88value 使用 layoutWeight(1) 占据剩余空间——这种布局确保了所有键值对的标签列对齐,形成整齐的表格效果。

空值处理策略
this.InfoRow('action', this.formLaunchContext.action.length > 0 ? this.formLaunchContext.action : '-')
this.InfoRow('formId', this.formLaunchContext.formId.length > 0 ? this.formLaunchContext.formId : '-')

对于可能为空的字符串字段(actionformId),使用 '-' 作为占位符。这与 formLaunchContext === null 时的整体空状态处理形成了两层空值防御

层级 条件 展示
整体为空 formLaunchContext === null “尚未收到 Want,请从桌面图标或卡片启动应用”
字段为空 action.length === 0 显示 -

五、FormSection:卡片状态的"仪表盘"

this.InfoRow('activeFormId', this.activeFormId.length > 0 ? this.activeFormId : '未添加卡片')
this.InfoRow('卡片点击次数', `${this.formClickCount}`)
this.InfoRow('FormExtension', 'EntryFormAbility · onAddForm / onUpdateForm / onRemoveForm')
this.InfoRow('卡片页面', 'atomic/widgets/AtomicCard.ets · FormLink router')

四行信息分为两类:

动态数据(前两行)

  • activeFormId:当前活跃卡片的 ID,由 FormExtensionAbility.onAddForm 写入
  • formClickCount:卡片被点击的累计次数,每次 FormLink 触发时递增

静态参考(后两行)

  • FormExtension:列出 FormExtensionAbility 的三个核心回调
  • 卡片页面:指向卡片组件的文件路径和路由方式

静态参考行的设计意图是**“代码导航提示”**——告诉开发者去哪里找对应的实现代码。这在 Demo 场景中非常有价值,因为它将 UI 展示与源码位置关联起来。


六、ActionSection:两个操作的"语义对比"

Button('刷新桌面卡片')
  .backgroundColor('#2563EB')  // 蓝色
  .onClick(() => {
    AtomicLifecycleService.refreshActiveForm()
  })

Button('模拟记录前台')
  .backgroundColor('#334155')  // 深灰
  .onClick(() => {
    AtomicLifecycleService.recordAbilityLifecycle('onForeground', '手动模拟前台恢复')
  })

两个按钮的颜色差异暗示了操作的性质不同:

按钮 颜色 操作性质 实际效果
刷新桌面卡片 蓝色 #2563EB 真实操作 调用 formProvider.updateForm 更新卡片 UI
模拟记录前台 深灰 #334155 模拟操作 手动注入一条 onForeground 日志,用于演示

"模拟记录前台"按钮的存在揭示了一个重要的开发现实:UIAbility 的 onForeground 回调只能在系统调度时触发,开发者无法在应用内直接调用它。因此,Demo 提供了一个"模拟"按钮,让开发者可以在不切换应用的情况下观察日志输出。


七、LogSection:生命周期日志的"终端输出"

ForEach(this.atomicLogs, (item: AtomicLogItem) => {
  Column({ space: 2 }) {
    Row({ space: 8 }) {
      Text(item.time)           // 时间戳
      Text(`${item.source} · ${item.phase}`)  // 来源 · 阶段
    }
    Text(item.detail)           // 详细信息
  }
})

每条日志包含三层信息:

层级 字段 示例
第一层 time 23:14:02
第二层 source + phase FormExt · onAddForm
第三层 detail formId=12345, dimension=1x2

与设备管理页面的日志面板相比,这里的日志结构更丰富:

维度 设备管理页面 元服务卡片页面
字段数 3(time, layer, message) 4(time, source, phase, detail)
布局 单行(Row) 双行(Column > Row + Text)
信息密度 低(一行一条) 高(两行一条)

双行布局的选择是因为生命周期事件的信息量更大——sourcephase 需要分开标识(来源可能是 FormExtUIAbilityPage,阶段可能是 onCreateonForegroundonAddForm 等),而 detail 包含具体的参数信息。


八、GuideSection:验证步骤的"操作手册"

Text('1. 长按桌面 → 服务卡片 → 添加「元服务链路卡片」')
Text('2. 观察 FormExtension onAddForm 日志与 activeFormId')
Text('3. 点击卡片 → 查看 onCreate/onNewWant 与 Want 参数')
Text('4. 回桌面再点卡片 → 验证热启动 onNewWant')
Text('5. 点「刷新桌面卡片」→ formProvider.updateForm 回写卡片')

这五行文本构成了一套完整的端到端验证流程,每一步都对应一个可观测的状态变化:

步骤 操作 可观测变化
1 添加卡片到桌面 activeFormId 从空变为有值,日志出现 onAddForm
2 观察日志 atomicLogs 新增记录
3 点击卡片 formLaunchContextnull 变为有值,highlightStage 推进到 2
4 再次点击 日志出现 onNewWant(而非 onCreate),证明是热启动
5 刷新卡片 formClickCount 被回写到卡片 UI

步骤 3 和 4 的对比是整个验证流程的核心教学点——它让开发者直观理解"冷启动"和"热启动"的区别:

  • 首次点击:onCreate → 应用从零创建
  • 再次点击:onNewWant → 应用已存在,系统传递新的 Want

九、与之前页面的架构对比

维度 WantFlow HMDFS 设备管理 元服务卡片
主题 浅色 浅色 浅色 暗色
状态变量数 8 7 7 7
面板数 6 6 6 7
联合类型 1 个 1 个
枚举使用 1 个 1 个(LIFECYCLE_STAGES)
Builder 复用 InfoRow(参数化)
门控机制 双重
操作按钮数 5 6 5 2
引导面板 ApiPanel GuideSection
新增的设计模式

参数化 Builder(InfoRow

这是整个 Demo 中第一个使用参数化 Builder的页面。之前的页面中,每个面板都是独立的 @Builder 方法,没有参数传递。InfoRow 的出现标志着 UI 组件复用意识的提升:

@Builder
InfoRow(label: string, value: string) { ... }

这种模式在 LaunchSection(6 次调用)和 FormSection(4 次调用)中总共被调用了 10 次,大幅减少了重复代码。

ForEach 中的 index 参数利用

ForEach(LIFECYCLE_STAGES, (stage: string, index: number) => { ... })

ChainSection 充分利用了 ForEachindex 参数,将其与 highlightStage 比较来驱动视觉状态。这是 ForEach 在 Demo 中最复杂的用法——不仅用于列表渲染,还用于状态驱动的视觉编码。


十、潜在风险与优化建议

1. LIFECYCLE_STAGES 的描述与索引硬绑定
if (index === 0) {
  Text('EntryFormAbility.onAddForm · ...')
} else if (index === 1) {
  Text('FormLink action=router · ...')
} else if (index === 2) {
  Text('冷启动 onCreate / 热启动 onNewWant')
}

描述文本通过 index 硬编码绑定,如果 LIFECYCLE_STAGES 数组的顺序发生变化,描述会错位。

建议:将阶段名称和描述合并为结构化数据:

interface LifecycleStage {
  name: string
  description: string
}

const STAGES: LifecycleStage[] = [
  { name: '桌面 → 卡片', description: 'EntryFormAbility.onAddForm · ...' },
  { name: '卡片 → Want', description: 'FormLink action=router · ...' },
  { name: 'Want → UIAbility', description: '冷启动 onCreate / 热启动 onNewWant' }
]
2. formClickCount 缺少重置机制
@StorageLink('formClickCount') formClickCount: number = 0

点击次数只增不减,长时间使用后数值会持续增大。对于 Demo 场景,开发者可能需要重置计数器来重新验证。

建议:在 ActionSection 中增加一个"重置计数"按钮:

Button('重置计数')
  .backgroundColor('#EF4444')
  .onClick(() => {
    AtomicLifecycleService.resetFormClickCount()
  })
3. GuideSection 的步骤编号硬编码
Text('1. 长按桌面 → 服务卡片 → ...')
Text('2. 观察 FormExtension onAddForm 日志与 activeFormId')

步骤编号硬编码在文本中,如果后续需要插入或删除步骤,需要手动修改所有编号。

建议:使用 ForEach 动态生成:

ForEach(this.guideSteps, (step: string, index: number) => {
  Text(`${index + 1}. ${step}`)
    .fontSize(12)
    .fontColor('#94A3B8')
})
4. highlightStage 缺少自动推进逻辑的可视化
@StorageLink('highlightStage') highlightStage: number = 0

highlightStage 由 Service 层控制,但 UI 层没有任何控件可以让开发者手动推进阶段(用于调试)。

建议:在 ActionSection 中增加一个调试按钮:

Button('推进阶段')
  .backgroundColor('#334155')
  .onClick(() => {
    AtomicLifecycleService.advanceHighlightStage()
  })
5. 暗色主题下的按钮对比度
Button('模拟记录前台')
  .backgroundColor('#334155')

深灰按钮 #334155 在暗色背景 #0F1118 上的对比度较低,可能在某些屏幕上不够醒目。

建议:增加按钮边框以提升可见性:

Button('模拟记录前台')
  .backgroundColor('#334155')
  .borderWidth(1)
  .borderColor('#475569')

完整代码

import { AtomicLifecycleService } from '../atomic/AtomicLifecycleService'
import { AtomicLogItem, FormLaunchContext, LIFECYCLE_STAGES } from '../atomic/AtomicTypes'



struct Index {
  ('atomicStatus') atomicStatus: string = '元服务 / 卡片链路就绪'
  ('abilityPhase') abilityPhase: string = '等待启动'
  ('activeFormId') activeFormId: string = ''
  ('formClickCount') formClickCount: number = 0
  ('formLaunchContext') formLaunchContext: FormLaunchContext | null = null
  ('atomicLogs') atomicLogs: AtomicLogItem[] = []
  ('highlightStage') highlightStage: number = 0

  aboutToAppear(): void {
    AtomicLifecycleService.recordPageReady()
  }

  build() {
    Scroll() {
      Column({ space: 16 }) {
        this.HeaderSection()
        this.ChainSection()
        this.LaunchSection()
        this.FormSection()
        this.ActionSection()
        this.LogSection()
        this.GuideSection()
      }
      .width('100%')
      .padding(16)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#0F1118')
  }

  
  HeaderSection() {
    Column({ space: 6 }) {
      Text('元服务与卡片 · 全链路生命周期')
        .fontSize(22)
        .fontWeight(FontWeight.Bold)
        .fontColor('#F8FAFC')

      Text(this.atomicStatus)
        .fontSize(13)
        .fontColor('#94A3B8')

      Text(`当前阶段 · ${this.abilityPhase}`)
        .fontSize(12)
        .fontColor('#6EE7B7')
    }
    .alignItems(HorizontalAlign.Start)
    .width('100%')
  }

  
  ChainSection() {
    Column({ space: 10 }) {
      Text('桌面 → 卡片 → UIAbility 拉起链路')
        .fontSize(14)
        .fontWeight(FontWeight.Medium)
        .fontColor('#E2E8F0')

      ForEach(LIFECYCLE_STAGES, (stage: string, index: number) => {
        Row({ space: 10 }) {
          Text(index === this.highlightStage ? '●' : '○')
            .fontSize(14)
            .fontColor(index === this.highlightStage ? '#6EE7B7' : '#64748B')
            .width(18)

          Column({ space: 2 }) {
            Text(stage)
              .fontSize(13)
              .fontColor(index === this.highlightStage ? '#F8FAFC' : '#CBD5E1')
            if (index === 0) {
              Text('EntryFormAbility.onAddForm · 用户把卡片添加到桌面')
                .fontSize(11)
                .fontColor('#64748B')
            } else if (index === 1) {
              Text('FormLink action=router · params 透传到 Want.parameters')
                .fontSize(11)
                .fontColor('#64748B')
            } else if (index === 2) {
              Text('冷启动 onCreate / 热启动 onNewWant')
                .fontSize(11)
                .fontColor('#64748B')
            }
          }
          .alignItems(HorizontalAlign.Start)
          .layoutWeight(1)
        }
        .width('100%')
        .padding({ top: 4, bottom: 4 })
        .backgroundColor(index === this.highlightStage ? '#172033' : '#111827')
        .borderRadius(8)
        .padding(10)
      }, (stage: string, index: number) => `${stage}_${index}`)
    }
    .width('100%')
    .padding(14)
    .backgroundColor('#151B2B')
    .borderRadius(14)
  }

  
  LaunchSection() {
    Column({ space: 8 }) {
      Text('Want 拉起上下文')
        .fontSize(14)
        .fontWeight(FontWeight.Medium)
        .fontColor('#E2E8F0')

      if (this.formLaunchContext === null) {
        Text('尚未收到 Want,请从桌面图标或卡片启动应用')
          .fontSize(12)
          .fontColor('#94A3B8')
      } else {
        this.InfoRow('来源', this.formLaunchContext.launchSource)
        this.InfoRow('模式', this.formLaunchContext.launchMode)
        this.InfoRow('action', this.formLaunchContext.action.length > 0 ? this.formLaunchContext.action : '-')
        this.InfoRow('formId', this.formLaunchContext.formId.length > 0 ? this.formLaunchContext.formId : '-')
        this.InfoRow('dimension', this.formLaunchContext.dimension)
        this.InfoRow('参数', this.formLaunchContext.paramsPreview)
      }
    }
    .alignItems(HorizontalAlign.Start)
    .width('100%')
    .padding(14)
    .backgroundColor('#151B2B')
    .borderRadius(14)
  }

  
  FormSection() {
    Column({ space: 8 }) {
      Text('卡片状态')
        .fontSize(14)
        .fontWeight(FontWeight.Medium)
        .fontColor('#E2E8F0')

      this.InfoRow('activeFormId', this.activeFormId.length > 0 ? this.activeFormId : '未添加卡片')
      this.InfoRow('卡片点击次数', `${this.formClickCount}`)
      this.InfoRow('FormExtension', 'EntryFormAbility · onAddForm / onUpdateForm / onRemoveForm')
      this.InfoRow('卡片页面', 'atomic/widgets/AtomicCard.ets · FormLink router')
    }
    .alignItems(HorizontalAlign.Start)
    .width('100%')
    .padding(14)
    .backgroundColor('#151B2B')
    .borderRadius(14)
  }

  
  ActionSection() {
    Row({ space: 10 }) {
      Button('刷新桌面卡片')
        .layoutWeight(1)
        .backgroundColor('#2563EB')
        .onClick(() => {
          AtomicLifecycleService.refreshActiveForm()
        })

      Button('模拟记录前台')
        .layoutWeight(1)
        .backgroundColor('#334155')
        .onClick(() => {
          AtomicLifecycleService.recordAbilityLifecycle('onForeground', '手动模拟前台恢复')
        })
    }
    .width('100%')
  }

  
  LogSection() {
    Column({ space: 8 }) {
      Text('生命周期日志')
        .fontSize(14)
        .fontWeight(FontWeight.Medium)
        .fontColor('#E2E8F0')

      if (this.atomicLogs.length === 0) {
        Text('暂无日志')
          .fontSize(12)
          .fontColor('#94A3B8')
      } else {
        ForEach(this.atomicLogs, (item: AtomicLogItem) => {
          Column({ space: 2 }) {
            Row({ space: 8 }) {
              Text(item.time)
                .fontSize(10)
                .fontColor('#64748B')
              Text(`${item.source} · ${item.phase}`)
                .fontSize(11)
                .fontColor('#93C5FD')
            }
            Text(item.detail)
              .fontSize(12)
              .fontColor('#CBD5E1')
          }
          .alignItems(HorizontalAlign.Start)
          .width('100%')
          .padding({ top: 6, bottom: 6 })
        }, (item: AtomicLogItem) => item.id)
      }
    }
    .alignItems(HorizontalAlign.Start)
    .width('100%')
    .padding(14)
    .backgroundColor('#151B2B')
    .borderRadius(14)
  }

  
  GuideSection() {
    Column({ space: 6 }) {
      Text('验证步骤')
        .fontSize(14)
        .fontWeight(FontWeight.Medium)
        .fontColor('#E2E8F0')

      Text('1. 长按桌面 → 服务卡片 → 添加「元服务链路卡片」')
        .fontSize(12)
        .fontColor('#94A3B8')
      Text('2. 观察 FormExtension onAddForm 日志与 activeFormId')
        .fontSize(12)
        .fontColor('#94A3B8')
      Text('3. 点击卡片 → 查看 onCreate/onNewWant 与 Want 参数')
        .fontSize(12)
        .fontColor('#94A3B8')
      Text('4. 回桌面再点卡片 → 验证热启动 onNewWant')
        .fontSize(12)
        .fontColor('#94A3B8')
      Text('5. 点「刷新桌面卡片」→ formProvider.updateForm 回写卡片')
        .fontSize(12)
        .fontColor('#94A3B8')
    }
    .alignItems(HorizontalAlign.Start)
    .width('100%')
    .padding(14)
    .backgroundColor('#151B2B')
    .borderRadius(14)
  }

  
  InfoRow(label: string, value: string) {
    Row({ space: 8 }) {
      Text(label)
        .fontSize(12)
        .fontColor('#64748B')
        .width(88)
      Text(value)
        .fontSize(12)
        .fontColor('#E2E8F0')
        .layoutWeight(1)
    }
    .width('100%')
  }
}

在这里插入图片描述

十一、总结

这个页面在整个 Demo 架构中扮演**“全链路观测站”**的角色:

  • 纵向:它覆盖了从桌面卡片(FormExtensionAbility)到应用启动(UIAbility)的完整生命周期
  • 横向:它将 Want 参数、卡片状态、操作日志、验证步骤整合在一个统一的视图中
  • 教学:通过 GuideSection 提供了端到端的验证流程,让开发者可以按步骤验证每个环节

从设计模式的角度看,这个页面引入了两个重要的新模式:

  • 参数化 Builder(InfoRow:实现了 UI 组件的复用,减少了 80% 的重复布局代码
  • 索引驱动的高亮(highlightStage:将数组索引与视觉状态绑定,实现了"时间线"式的阶段展示

如果说设备管理页面是"地基",HMDFS 页面是"楼层",那么这个页面就是**“监控室”**——它不直接参与业务操作,而是提供了一个全局视角,让开发者可以观测和验证整个元服务生命周期的运转状态。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐