【鸿蒙原生开发会议随记 Pro】用 NavPathStack 收拢会议页面跳转和返回刷新 原创
头像 小雨同学 2026-06-09 10:28:07    发布
1 浏览 0 点赞 0 收藏


前言

我在《会议随记 Pro》里整理启动链路以后,很快遇到了另一个更实际的问题:页面越来越多,页面之间的关系也开始变复杂。

早期页面少的时候,会议列表进入会议详情,会议详情再进入会议编辑,直接在页面里写跳转逻辑还能接受。后来项目里陆续增加了主 Tab、新建会议、会议详情、会议编辑、项目详情、联系人详情、设置页。如果继续让每个页面自己决定下一个页面怎么打开,后面会很难维护。

真正让我重新处理导航逻辑的,是返回刷新。

会议详情页进入编辑页以后,编辑页可能会修改标题、标签、参会人,也可能会调整时间轴笔记。编辑页保存并返回以后,详情页要重新读取当前会议;详情页再返回会议列表时,列表也要知道会议数据已经变化。这个过程如果只靠页面之间互相约定,时间一长就很容易忘记哪条路径负责刷新。

我后来把页面跳转集中到 NavPathStack。首页维护一份全局页面栈,页面名称统一注册,页面之间只传业务参数。详情页进入编辑页时,把返回后的刷新动作绑定在这次跳转上。底部 Tab 页面不需要关心业务页面怎么打开,它只负责展示自己的内容。

这里最容易混在一起的其实有三件事。


事情负责对象项目里的处理
页面怎么打开NavPathStack首页统一注册页面名称
页面打开哪条数据meetingId 等业务参数页面之间只传必要 ID
返回以后谁刷新当前页面或全局刷新信号详情页用返回回调,列表用刷新 key

NavigationNavPathStack 在这个项目里不只是页面跳转工具,它们更像页面关系的中心。启动链路负责把应用带到首页,首页导航栈再负责业务页面之间的流转。只要这个边界立住,后续增加桌面卡片入口、通知入口、项目详情页、联系人详情页时,页面之间不会互相缠在一起。

img

一、跳转要有一个入口

《会议随记 Pro》的首页 Index.ets 里提供了一份全局导航栈。

@Provide('appStack') appStack: NavPathStack = new NavPathStack();

这个 appStack 不属于某个详情页,也不属于某个列表页。它由首页提供出来,后面的会议详情页、会议编辑页需要返回或者继续打开新页面时,都可以通过 @Consume('appStack') 拿到同一份导航栈。

我更愿意让页面跳转从首页出去。会议列表不需要知道会议详情页的组件文件在哪里,详情页也不需要知道编辑页怎么创建。它们只需要知道目标页面名称和当前业务 ID。

首页同时把页面名称注册到 pageMaps() 里。项目里当前已经有 mainTabswelcomemeetingNewmeetingDetailmeetingEdit、联系人详情、项目详情、关于页、设置页等映射。页面名称和组件之间的对应关系都放在这里,业务页面只通过名称入栈。

这个处理对项目结构的影响很直接。


页面关系页面之间传什么组件由谁注册
会议列表进入详情页meetingIdIndex.ets
会议详情进入编辑页meetingIdIndex.ets
项目列表进入项目详情projectIdIndex.ets
联系人列表进入联系人详情contactIdIndex.ets

这里我会保留一个习惯:页面名称和业务参数分开处理。

meetingDetail 是页面名称,表示要打开会议详情页。meetingId 是业务参数,表示详情页要处理哪一条会议记录。它们放在一起看很容易混淆,拆开以后会好维护很多。后面要复用同一个详情页,或者要给详情页追加来源标记、刷新策略,也不会影响页面注册方式。

底部 Tab 页面也会因此变轻。会议列表页只负责显示会议列表,点击一条会议时把 meetingId 交给导航栈。至于详情页组件怎么创建、它在页面栈里处在哪一层,都交给统一的导航入口。

二、参数不要传得太满

会议详情页不需要拿到一整条会议对象。它真正需要的是 meetingId

这个判断来自真实项目里的数据结构。会议详情页打开以后,不只是展示标题和摘要,还要读取会议主记录、时间轴笔记、待办、评论和参会人。如果列表页把完整对象传过去,详情页拿到的只是一份快照。编辑页修改标题以后,详情页手里的旧对象马上过期。

所以我在这个项目里更倾向于传业务 ID。详情页拿到 meetingId,再通过 Repository 加载当前会议。编辑页也是一样,拿到同一个 meetingId,自己读取要编辑的数据。


参数方式适合场景在这个项目里的处理
传完整对象临时确认页、一次性展示页不用于会议详情和会议编辑
传业务 ID详情页、编辑页、需要重新读取数据的页面会议详情和会议编辑采用这个方式
传筛选条件列表页、搜索页、聚合页会议列表和项目列表继续沿用
传入口动作启动页、通知跳转、桌面卡片入口进入首页后再转换成页面动作

参数越少,返回刷新越容易处理。详情页不需要判断上一个页面传来的对象是不是过期,也不需要合并局部字段。编辑页返回以后,详情页直接根据 meetingId 重新读取当前会议,页面状态就能回到最新数据上。

这里还有一个容易忽略的边界。导航栈只负责页面关系,不负责数据仓库。页面要显示哪条记录,可以通过参数决定;这条记录怎么读取、怎么更新、怎么处理关联数据,仍然要回到 Repository。把这两层混在一起,后面会出现页面能打开,但数据刷新很难查的问题。

我之前在页面跳转里踩过一个坑。列表页跳详情页时传了完整对象,详情页显示出来没问题;详情页再进入编辑页,编辑完返回以后,详情页标题没有更新。继续排查才发现,详情页展示的是旧对象,不是重新查询后的会议记录。改成只传 meetingId 后,这类问题会少很多。

三、返回刷新要绑定在这次跳转上

详情页进入编辑页时,我不会只写一个普通的 pushPath。这个跳转本身就带着后续动作:编辑页返回以后,详情页要重新加载。

真实项目里可以这样处理。

private handleEdit(): void {  if (!this.meeting) {    return;  }​  if (this.isPlaying && this.player) {    this.player.pause();  }​  const param: MeetingDetailParam = {    meetingId: this.meeting.id  };​  this.appStack.pushPath({    name: 'meetingEdit',    param: param,    onPop: () => {      this.loadData();    }  });}

这段逻辑里有两个动作我会保留。详情页进入编辑页前,先暂停正在播放的录音,避免用户编辑会议时音频还在继续播放。然后把 loadData() 绑定到这次入栈的 onPop 上。编辑页返回时,详情页重新读取当前会议。

这个写法的好处不在 API 调用本身,而在刷新关系清楚。详情页打开编辑页,编辑页返回以后详情页刷新。这是一条当前页面栈里的关系,不需要变成全局监听,也不需要让编辑页知道详情页内部方法。

编辑页保存时,做的事情也应该收住。它更新会议数据,通知全局会议数据已经变化,然后调用 appStack.pop() 返回上一页。详情页通过 onPop 重新加载,列表和工作台通过全局刷新信号感知变化。


刷新方式适合的页面关系在项目里的位置
onPop 回调详情页打开编辑页,编辑完成后详情页重新加载详情页进入编辑页时绑定
MeetingReloadKey列表页、工作台、其他不在当前栈顶的页面感知数据变化保存、删除、编辑完成后通知
页面初始化加载页面首次打开,根据当前业务 ID 加载数据详情页、编辑页、项目详情页
Tab 显示检查Tab 重新出现时检查是否需要刷新列表页和工作台

把这些刷新方式放在不同位置,后面排查问题会轻松一些。详情页不用等待全局信号来判断自己要不要刷新,列表页也不用知道详情页和编辑页之间发生了什么。每个页面只处理自己所在位置能确定的事情。

四、用一个小页面验证状态链路

为了把这套关系看清楚,我把真实项目里的页面栈压缩成一个小页面。这个页面里保留三种状态:会议列表、会议详情、会议编辑。它不连接真实数据库,会议数据保存在页面状态里。

这个示例不是为了替代真实项目里的 NavPathStack。它的作用是把页面栈、业务 ID、保存动作、列表刷新、详情重载、编辑返回这几件事放在同一个界面里观察。真实项目里再把这个状态链路迁回 NavPathStack + onPop + Repository + RefreshUtil

我在这个小页面里把状态更新放在同一个组件里,原因很实际。文章示例需要稳定展示运行结果,不能让页面栈、标题更新和计数器状态分散在多个 NavDestination 子页面之间。完整项目可以用更细的组件拆分,文章里的演示页先保证状态链路足够清楚。

这个小页面会跑出一条固定路径。

会议列表 → 会议详情 → 会议编辑 → 保存并返回 → 会议详情 → 返回列表

每一步对应的状态变化如下。


操作页面栈列表刷新次数详情重载次数编辑返回次数
初始进入列表meetingList000
点击会议进入详情meetingList → meetingDetail010
点击编辑标题meetingList → meetingDetail → meetingEdit010
保存并返回详情meetingList → meetingDetail121
返回列表meetingList121

这样跑出来以后,截图里就能观察到四个结果。

第一,当前页面栈会随着列表、详情、编辑切换而变化。第二,编辑保存以后,详情页标题会显示新标题。第三,列表刷新次数、详情重载次数、编辑返回次数都会增加。第四,返回列表后,对应会议卡片也会显示保存后的标题。

五、迁回真实项目时怎么处理

这个小页面为了便于观察,把列表、详情、编辑三种状态压缩到一个 Index.ets 里。真实项目不会这样写。项目里仍然是列表页、详情页、编辑页分开,页面栈交给 NavPathStack,数据读写交给 Repository,跨页面通知交给 RefreshUtil

迁回真实项目时,我会保留下面这几条关系。


小页面里的逻辑真实项目里的处理
currentPage 模拟页面栈NavPathStack 管理页面栈
currentMeetingId页面跳转时传入的业务 ID
syncDetailSnapshot()详情页里的 loadData()
saveAndBackToDetail()编辑页保存会议,再调用 appStack.pop()
listRefreshCountRefreshUtil.notifyMeetingUpdate() 后列表感知刷新
detailReloadCount详情页通过 onPop 重新加载
editReturnCount编辑页保存并返回这一条路径的观察值

这里最值得保留的是边界,而不是演示代码的组件组织方式。真实项目里我不会把所有页面都塞进一个文件,也不会让编辑页直接知道列表怎么刷新。编辑页只负责保存和返回。详情页负责返回后的重新加载。列表页和工作台通过全局刷新信号感知会议数据变化。

这个边界很适合《会议随记 Pro》现在的结构。会议详情页本身已经有播放器、时间轴、待办、评论、参会人等模块,编辑页保存以后,详情页必须重新加载;会议列表和工作台不在当前页面栈顶部,只要通过全局刷新信号知道数据变化就够了。

总结

NavPathStack 在这个项目里解决的是页面关系维护问题。页面名称统一注册以后,列表页和详情页不需要互相导入组件。页面之间只传 meetingId 这类业务参数,具体数据继续交给 Repository 读取。详情页打开编辑页时,把返回后的重新加载绑定在这次跳转上,编辑页只负责保存并返回。

这套处理我会继续用在《会议随记 Pro》的业务页面里。会议详情、会议编辑、项目详情、联系人详情、设置页都可以放在同一份导航栈里。编辑页保存以后,当前详情页通过 onPop 重新加载;列表和工作台通过全局刷新信号感知数据变化。页面栈管页面关系,数据层管数据读取,这个边界保留下来,后面继续增加页面时会少很多绕路。

这几个点在项目里要分开处理:

  • 当前页面栈由 NavPathStack 维护
  • 当前业务数据由 meetingId 决定
  • 详情页返回刷新由 onPop 触发
  • 列表和工作台刷新由 MeetingReloadKey 通知
  • 编辑页只负责保存数据和返回上一页

我在《会议随记 Pro》里已经使用了这套页面跳转和返回刷新处理,应用目前已经上架华为应用市场。里面包含会议录音、时间轴笔记、联系人、项目、标签管理和多设备适配这些功能。对鸿蒙原生应用的完整实现感兴趣的话,可以下载体验一下:会议随记 Pro

完整代码

interface MeetingItem {  id: string;  title: string;  summary: string;  updatedAt: number;}​interface RouteLog {  id: number;  action: string;  detail: string;}​enum DemoPage {  List = 0,  Detail = 1,  Edit = 2}​@Entry@Componentstruct Index {  @State currentPage: DemoPage = DemoPage.List;​  @State meetings: MeetingItem[] = [    {      id: 'meeting-001',      title: '产品评审会',      summary: '确认 1.3 版本多设备适配范围',      updatedAt: 1717819200000    },    {      id: 'meeting-002',      title: '录音链路复盘',      summary: '整理录音状态机、保存流程和权限降级',      updatedAt: 1717905600000    },    {      id: 'meeting-003',      title: '桌面卡片讨论',      summary: '确认 FormID 管理和卡片刷新时机',      updatedAt: 1717992000000    }  ];​  @State currentMeetingId: string = 'meeting-001';​  @State detailTitle: string = '产品评审会';  @State detailSummary: string = '确认 1.3 版本多设备适配范围';  @State detailUpdatedAt: number = 1717819200000;​  @State editTitleDraft: string = '';​  @State listRefreshCount: number = 0;  @State detailReloadCount: number = 0;  @State editReturnCount: number = 0;​  @State stackText: string = 'meetingList';  @State logSeed: number = 0;  @State logs: RouteLog[] = [];​  private addLog(action: string, detail: string): void {    const next: RouteLog = {      id: this.logSeed + 1,      action: action,      detail: detail    };​    this.logSeed = next.id;    this.logs = [next, ...this.logs].slice(0, 12);  }​  private setStack(names: string[]): void {    this.stackText = names.join('  →  ');  }​  private getMeetingById(meetingId: string): MeetingItem | undefined {    return this.meetings.find((item: MeetingItem) => item.id === meetingId);  }​  private syncDetailSnapshot(meetingId: string, reason: string): void {    const current = this.getMeetingById(meetingId);​    if (!current) {      this.detailTitle = '会议不存在';      this.detailSummary = '当前 meetingId 没有对应的会议记录';      this.detailUpdatedAt = 0;      this.addLog('detail empty', `没有找到会议,meetingId=${meetingId}`);      return;    }​    this.currentMeetingId = meetingId;    this.detailTitle = current.title;    this.detailSummary = current.summary;    this.detailUpdatedAt = current.updatedAt;    this.detailReloadCount += 1;​    this.addLog('detail reload', `${reason},meetingId=${meetingId}`);  }​  private prepareEditDraft(): void {    const current = this.getMeetingById(this.currentMeetingId);​    if (current) {      this.editTitleDraft = current.title;      return;    }​    this.editTitleDraft = '未命名会议';  }​  private openDetail(meetingId: string): void {    this.syncDetailSnapshot(meetingId, '打开详情页时同步会议快照');    this.setStack(['meetingList', 'meetingDetail']);    this.currentPage = DemoPage.Detail;    this.addLog('push detail', `会议列表进入会议详情,meetingId=${meetingId}`);  }​  private openEdit(): void {    this.prepareEditDraft();    this.setStack(['meetingList', 'meetingDetail', 'meetingEdit']);    this.currentPage = DemoPage.Edit;    this.addLog('push edit', `会议详情进入会议编辑,meetingId=${this.currentMeetingId}`);  }​  private saveAndBackToDetail(): void {    const finalTitle = this.editTitleDraft.trim().length > 0 ? this.editTitleDraft.trim() : '未命名会议';    const savedAt = Date.now();​    let savedSummary = this.detailSummary;​    const nextItems = this.meetings.map((item: MeetingItem): MeetingItem => {      if (item.id === this.currentMeetingId) {        savedSummary = item.summary;​        return {          id: item.id,          title: finalTitle,          summary: item.summary,          updatedAt: savedAt        };      }​      return item;    });​    this.meetings = nextItems;​    this.detailTitle = finalTitle;    this.detailSummary = savedSummary;    this.detailUpdatedAt = savedAt;​    this.listRefreshCount += 1;    this.detailReloadCount += 1;    this.editReturnCount += 1;​    this.setStack(['meetingList', 'meetingDetail']);    this.currentPage = DemoPage.Detail;​    this.addLog('save', `会议 ${this.currentMeetingId} 的标题保存为:${finalTitle}`);    this.addLog('list refresh', `列表刷新次数增加到 ${this.listRefreshCount}`);    this.addLog('detail reload', `详情重载次数增加到 ${this.detailReloadCount}`);    this.addLog('edit return', `编辑返回次数增加到 ${this.editReturnCount}`);  }​  private backToList(): void {    this.setStack(['meetingList']);    this.currentPage = DemoPage.List;    this.addLog('pop detail', '会议详情返回会议列表');  }​  private manualRefreshList(): void {    this.listRefreshCount += 1;    this.addLog('list refresh', `手动刷新会议列表,刷新次数 ${this.listRefreshCount}`);  }​  private manualReloadDetail(): void {    this.syncDetailSnapshot(this.currentMeetingId, '详情页手动重新同步会议数据');  }​  @Builder  private StatCard(label: string, value: string) {    Column({ space: 6 }) {      Text(label)        .fontSize(12)        .fontColor('#64748B')​      Text(value)        .fontSize(18)        .fontWeight(FontWeight.Bold)        .fontColor('#0F172A')        .maxLines(2)        .textOverflow({ overflow: TextOverflow.Ellipsis })    }    .alignItems(HorizontalAlign.Start)    .layoutWeight(1)    .padding(14)    .backgroundColor(Color.White)    .borderRadius(16)  }​  @Builder  private StackCard() {    Column({ space: 12 }) {      Text('当前页面栈')        .fontSize(13)        .fontColor('#64748B')​      Text(this.stackText)        .fontSize(17)        .fontWeight(FontWeight.Medium)        .fontColor('#0F172A')        .width('100%')    }    .width('100%')    .padding(14)    .backgroundColor(Color.White)    .borderRadius(16)  }​  @Builder  private MeetingCard(item: MeetingItem) {    Column({ space: 8 }) {      Row() {        Column({ space: 4 }) {          Text(item.title)            .fontSize(17)            .fontWeight(FontWeight.Medium)            .fontColor('#0F172A')​          Text(item.summary)            .fontSize(13)            .fontColor('#64748B')            .lineHeight(20)            .maxLines(2)            .textOverflow({ overflow: TextOverflow.Ellipsis })        }        .layoutWeight(1)        .alignItems(HorizontalAlign.Start)​        Text(this.currentMeetingId === item.id ? '当前' : '打开')          .fontSize(12)          .fontColor(this.currentMeetingId === item.id ? '#2563EB' : '#64748B')          .padding({            left: 10,            right: 10,            top: 4,            bottom: 4          })          .backgroundColor(this.currentMeetingId === item.id ? '#DBEAFE' : '#F1F5F9')          .borderRadius(12)      }      .width('100%')​      Text(`updatedAt=${item.updatedAt}`)        .fontSize(11)        .fontColor('#94A3B8')        .width('100%')    }    .width('100%')    .padding(16)    .backgroundColor(Color.White)    .borderRadius(18)    .shadow({      radius: 10,      color: '#10000000',      offsetX: 0,      offsetY: 3    })    .onClick(() => {      this.openDetail(item.id);    })  }​  @Builder  private CounterPanel() {    Column({ space: 12 }) {      Row({ space: 12 }) {        this.StatCard('当前 meetingId', this.currentMeetingId)        this.StatCard('列表刷新次数', this.listRefreshCount.toString())      }      .width('100%')​      Row({ space: 12 }) {        this.StatCard('详情重载次数', this.detailReloadCount.toString())        this.StatCard('编辑返回次数', this.editReturnCount.toString())      }      .width('100%')    }    .width('100%')  }​  @Builder  private BuildListPage() {    Column({ space: 18 }) {      Column({ space: 8 }) {        Text('会议列表')          .fontSize(26)          .fontWeight(FontWeight.Bold)          .fontColor('#0F172A')​        Text('列表页只展示会议摘要。点击一条会议后,页面栈会进入会议详情,当前 meetingId 会同步到页面层。')          .fontSize(14)          .fontColor('#475569')          .lineHeight(22)      }      .width('100%')      .alignItems(HorizontalAlign.Start)​      this.StackCard()      this.CounterPanel()​      Button('手动刷新列表')        .width('100%')        .height(44)        .backgroundColor('#2563EB')        .fontColor(Color.White)        .borderRadius(22)        .onClick(() => {          this.manualRefreshList();        })​      Column({ space: 12 }) {        ForEach(this.meetings, (item: MeetingItem) => {          this.MeetingCard(item)        }, (item: MeetingItem) => `${item.id}-${item.updatedAt}-${this.listRefreshCount}`)      }      .width('100%')​      this.LogPanel()    }    .width('100%')  }​  @Builder  private BuildDetailPage() {    Column({ space: 18 }) {      Column({ space: 8 }) {        Text('会议详情')          .fontSize(26)          .fontWeight(FontWeight.Bold)          .fontColor('#0F172A')​        Text('详情页展示当前会议快照。编辑页保存返回后,标题、更新时间和刷新计数会一起变化。')          .fontSize(14)          .fontColor('#475569')          .lineHeight(22)      }      .width('100%')      .alignItems(HorizontalAlign.Start)​      this.StackCard()​      Column({ space: 12 }) {        Text(this.detailTitle)          .fontSize(22)          .fontWeight(FontWeight.Bold)          .fontColor('#0F172A')​        Text(this.detailSummary)          .fontSize(15)          .fontColor('#475569')          .lineHeight(24)​        Text(`meetingId=${this.currentMeetingId}`)          .fontSize(12)          .fontColor('#64748B')​        Text(`updatedAt=${this.detailUpdatedAt}`)          .fontSize(12)          .fontColor('#64748B')      }      .width('100%')      .alignItems(HorizontalAlign.Start)      .padding(18)      .backgroundColor(Color.White)      .borderRadius(20)​      Row({ space: 12 }) {        Button('编辑标题')          .layoutWeight(1)          .height(44)          .backgroundColor('#2563EB')          .fontColor(Color.White)          .borderRadius(22)          .onClick(() => {            this.openEdit();          })​        Button('重新加载')          .layoutWeight(1)          .height(44)          .backgroundColor('#E2E8F0')          .fontColor('#0F172A')          .borderRadius(22)          .onClick(() => {            this.manualReloadDetail();          })      }      .width('100%')​      Button('返回列表')        .width('100%')        .height(44)        .backgroundColor('#0F766E')        .fontColor(Color.White)        .borderRadius(22)        .onClick(() => {          this.backToList();        })​      this.CounterPanel()      this.LogPanel()    }    .width('100%')  }​  @Builder  private BuildEditPage() {    Column({ space: 18 }) {      Column({ space: 8 }) {        Text('会议编辑')          .fontSize(26)          .fontWeight(FontWeight.Bold)          .fontColor('#0F172A')​        Text('编辑页只负责修改标题。保存动作会更新会议数组、详情快照和三个统计值。')          .fontSize(14)          .fontColor('#475569')          .lineHeight(22)      }      .width('100%')      .alignItems(HorizontalAlign.Start)​      this.StackCard()​      Column({ space: 12 }) {        Text('会议标题')          .fontSize(14)          .fontColor('#64748B')​        TextInput({          text: this.editTitleDraft,          placeholder: '请输入会议标题'        })          .height(48)          .fontSize(16)          .backgroundColor('#F8FAFC')          .borderRadius(14)          .padding({            left: 12,            right: 12          })          .onChange((value: string) => {            this.editTitleDraft = value;          })​        Text(`meetingId=${this.currentMeetingId}`)          .fontSize(12)          .fontColor('#94A3B8')      }      .width('100%')      .padding(18)      .backgroundColor(Color.White)      .borderRadius(20)​      Button('保存并返回')        .width('100%')        .height(46)        .backgroundColor('#2563EB')        .fontColor(Color.White)        .borderRadius(23)        .onClick(() => {          this.saveAndBackToDetail();        })​      this.CounterPanel()      this.LogPanel()    }    .width('100%')  }​  @Builder  private LogPanel() {    Column({ space: 12 }) {      Text('导航日志')        .fontSize(18)        .fontWeight(FontWeight.Bold)        .fontColor('#0F172A')        .width('100%')​      if (this.logs.length === 0) {        Text('还没有导航记录')          .fontSize(13)          .fontColor('#94A3B8')          .width('100%')          .padding(14)          .backgroundColor('#F8FAFC')          .borderRadius(14)      } else {        ForEach(this.logs, (item: RouteLog) => {          Row({ space: 10 }) {            Text(item.action)              .fontSize(11)              .fontColor('#1D4ED8')              .padding({                left: 8,                right: 8,                top: 3,                bottom: 3              })              .backgroundColor('#DBEAFE')              .borderRadius(10)​            Text(item.detail)              .fontSize(13)              .fontColor('#334155')              .lineHeight(20)              .layoutWeight(1)          }          .width('100%')          .alignItems(VerticalAlign.Top)          .padding(12)          .backgroundColor('#F8FAFC')          .borderRadius(14)        }, (item: RouteLog) => item.id.toString())      }    }    .width('100%')    .padding(16)    .backgroundColor(Color.White)    .borderRadius(20)  }​  build() {    Scroll() {      Column({ space: 18 }) {        if (this.currentPage === DemoPage.List) {          this.BuildListPage()        } else if (this.currentPage === DemoPage.Detail) {          this.BuildDetailPage()        } else {          this.BuildEditPage()        }      }      .width('100%')      .padding(20)    }    .width('100%')    .height('100%')    .backgroundColor('#EEF2F7')  }}


©本站发布的所有内容,包括但不限于文字、图片、音频、视频、图表、标志、标识、广告、商标、商号、域名、软件、程序等,除特别标明外,均来源于网络或用户投稿,版权归原作者或原出处所有。我们致力于保护原作者版权,若涉及版权问题,请及时联系我们进行处理。
分类
HarmonyOS

暂无评论数据

加载中...

发布

头像

小雨同学

产品总监、独立开发者社群主理人、资深全栈工程师,HarmonyOS应用开发者高级认证,PMP认证,CSDN博客专家,鸿蒙极客,Trae Fellow,阿里云社区专家博主、51CTO 博客专家、OpenTiny 优秀布道师、科大讯飞荣誉讲师。

22

帖子

0

提问

145

粉丝

关注
热门推荐
地址:北京市朝阳区北三环东路三元桥曙光西里甲1号第三置业A座1508室 商务内容合作QQ:2291221 电话:13391790444或(010)62178877
版权所有:电脑商情信息服务集团 北京赢邦策略咨询有限责任公司
声明:本媒体部分图片、文章来源于网络,版权归原作者所有,我司致力于保护作者版权,如有侵权,请与我司联系删除

京ICP备:2022009079号-2

京公网安备:11010502051901号

ICP证:京B2-20230255