请添加图片描述


一、引言

逐一拆解了 HdsTabs 悬浮页签栏的各个特性——基础配置、自定义页签、视觉特效、迷你栏与动态控制。但在实际产品开发中,这些特性从来不会单独存在。它们在同一个界面中协同工作,才能真正发挥价值。

本文将以一个完整的音乐播放器应用——WaveFlow——为例,展示如何将 HdsTabs barFloatingStyle 的全部特性集成到一个真实的应用场景中。WaveFlow 不是一个简单的 Demo,它包含 4 个具有完整数据结构和交互逻辑的页签,以及一个与主内容区状态联动的"正在播放"迷你栏。

本文的代码经过精简,保留了核心架构和关键交互逻辑。

二、应用架构概述

2.1 页签结构

WaveFlow 包含四个页签:

  1. 浏览 — 音乐播放 Hero + 最近播放
  2. 发现 — 搜索 + 热门标签 + 排行榜
  3. 音乐库 — 统计数据 + 歌单列表
  4. 我的 — 个人中心 + 菜单列表

2.2 数据结构设计

在开始写 UI 之前,先定义好所有数据结构:

// 曲目数据
interface TrackItem {
  id: number;
  title: string;      // 曲目标题
  artist: string;     // 艺人名
  duration: string;   // 时长(如 "3:29")
  color1: string;     // 渐变色起始
  color2: string;     // 渐变色结束
}

// 歌单数据
interface PlaylistItem {
  id: number;
  name: string;       // 歌单名称
  count: number;      // 歌曲数量
  color: string;      // 主题色
}

// 统计数据
interface StatItem {
  label: string;      // 统计项名称
  count: string;      // 统计数值
}

// 菜单项
interface ProfileMenuItem {
  icon: Resource;     // 菜单图标
  label: string;      // 菜单标题
  desc: string;       // 菜单描述
}

2.3 初始化数据

private tracks: TrackItem[] = [
  { id: 1, title: 'Golden Hour', artist: 'JVKE', duration: '3:29',
    color1: '#FF9500', color2: '#FF3B30' },
  { id: 2, title: 'Midnight Rain', artist: 'Taylor Swift', duration: '2:54',
    color1: '#5856D6', color2: '#AF52DE' },
  { id: 3, title: 'Ocean Breeze', artist: 'Lofi Studio', duration: '4:12',
    color1: '#007AFF', color2: '#34C759' },
  { id: 4, title: 'Neon Lights', artist: 'The Weeknd', duration: '3:45',
    color1: '#FF3B30', color2: '#FF9500' },
  { id: 5, title: 'Morning Dew', artist: 'Chill Hop', duration: '3:18',
    color1: '#34C759', color2: '#007AFF' },
  { id: 6, title: 'Star Gazing', artist: 'Post Malone', duration: '3:52',
    color1: '#AF52DE', color2: '#5856D6' }
];

private playlists: PlaylistItem[] = [
  { id: 1, name: '今日推荐', count: 30, color: '#FF9500' },
  { id: 2, name: '收藏歌曲', count: 128, color: '#FF3B30' },
  { id: 3, name: '睡前放松', count: 22, color: '#5856D6' },
  { id: 4, name: '运动模式', count: 45, color: '#34C759' },
  { id: 5, name: '通勤路上', count: 56, color: '#007AFF' },
  { id: 6, name: '深度专注', count: 18, color: '#AF52DE' }
];

private stats: StatItem[] = [
  { label: '歌单', count: '12' },
  { label: '歌曲', count: '286' },
  { label: '艺人', count: '45' },
  { label: '专辑', count: '23' }
];

private menuItems: ProfileMenuItem[] = [
  { icon: $r('sys.media.ohos_ic_public_clock'), label: '我的收藏',
    desc: '收藏的歌曲和歌单' },
  { icon: $r('sys.media.ohos_ic_public_clock'), label: '播放历史',
    desc: '最近播放的 50 首歌曲' },
  { icon: $r('sys.media.ohos_ic_public_phone'), label: '下载管理',
    desc: '离线歌曲 32 首' },
  { icon: $r('sys.media.ohos_ic_public_clock'), label: '设置',
    desc: '音质、通知与隐私' },
  { icon: $r('sys.media.ohos_ic_public_phone'), label: '关于',
    desc: 'WaveFlow v2.0' }
];

三、浏览页签 — 沉浸式 Hero 播放区

浏览页是应用的主入口,它的核心是一个占据顶部大半屏幕的 Hero 播放区。这个区域的设计目标是让用户一眼就能看到当前正在播放的内容,并且可以直接从顶部的控制按钮组进行播放操作。

3.1 Hero 播放区代码

// 浏览页签的 Hero 播放区
Column() {
  // 专辑封面(带播放状态叠加层)
  Stack() {
    // 主封面
    Column()
      .width(160).height(160)
      .borderRadius(20)
      .linearGradient({
        direction: GradientDirection.Top,
        colors: [
          [this.tracks[this.currentTrackIndex].color1, 0.0],
          [this.tracks[this.currentTrackIndex].color2, 1.0]
        ]
      })
      .shadow({
        radius: 30,
        color: this.tracks[this.currentTrackIndex].color1 + '40',
        offsetX: 0,
        offsetY: 12
      })

    // 播放中动画圈(播放时显示)
    if (this.isPlaying) {
      Row()
        .width(60).height(60)
        .borderRadius(30)
        .border({ width: 2, color: '#FFFFFF30' })
        .position({ x: 50, y: 50 })
    }
  }
  .width(160).height(160)
  .margin({ top: 24 })

  // 曲目标题与艺人
  Text(this.tracks[this.currentTrackIndex].title)
    .fontSize(22)
    .fontWeight(FontWeight.Bold)
    .fontColor('#FFFFFF')
    .margin({ top: 16 })

  Text(this.tracks[this.currentTrackIndex].artist)
    .fontSize(14)
    .fontColor('#99FFFFFF')
    .margin({ top: 4 })

  // 播放进度条
  Row() {
    Text('1:24')
      .fontSize(10).fontColor('#99FFFFFF')
    Column()
      .height(2)
      .layoutWeight(1)
      .backgroundColor('#FFFFFF30')
      .borderRadius(1)
      .margin({ left: 8, right: 8 })
    Text(this.tracks[this.currentTrackIndex].duration)
      .fontSize(10).fontColor('#99FFFFFF')
  }
  .width('100%')
  .padding({ left: 40, right: 40, top: 12 })

  // 播放控制按钮组
  Row({ space: 24 }) {
    // 随机播放
    Row() {
      Image($r('sys.media.ohos_ic_public_arrow_left'))
        .width(16).height(16)
        .fillColor(this.isShuffled ? '#007AFF' : '#FFFFFF')
    }
    .width(36).height(36).borderRadius(18)
    .backgroundColor('#FFFFFF20')
    .justifyContent(FlexAlign.Center)
    .onClick(() => { this.isShuffled = !this.isShuffled })

    // 上一首
    Row() {
      Image($r('sys.media.ohos_ic_public_arrow_left'))
        .width(16).height(16).fillColor('#FFFFFF')
    }
    .width(40).height(40).borderRadius(20)
    .backgroundColor('#FFFFFF20')
    .justifyContent(FlexAlign.Center)
    .onClick(() => {
      const len = this.tracks.length;
      this.currentTrackIndex = (this.currentTrackIndex + len - 1) % len;
    })

    // 播放/暂停
    Row() {
      Image(this.isPlaying ?
        $r('sys.media.ohos_ic_public_pause') :
        $r('sys.media.ohos_ic_public_play'))
        .width(24).height(24).fillColor('#FFFFFF')
    }
    .width(56).height(56).borderRadius(28)
    .backgroundColor('#FFFFFF30')
    .justifyContent(FlexAlign.Center)
    .onClick(() => { this.isPlaying = !this.isPlaying })

    // 下一首
    Row() {
      Image($r('sys.media.ohos_ic_public_arrow_right'))
        .width(16).height(16).fillColor('#FFFFFF')
    }
    .width(40).height(40).borderRadius(20)
    .backgroundColor('#FFFFFF20')
    .justifyContent(FlexAlign.Center)
    .onClick(() => {
      this.currentTrackIndex = (this.currentTrackIndex + 1) % this.tracks.length;
    })

    // 循环模式
    Row() {
      Text(this.repeatMode === 2 ? '1' : this.repeatMode === 1 ? 'All' : '')
        .fontSize(8).fontColor('#FFFFFF')
    }
    .width(36).height(36).borderRadius(18)
    .backgroundColor('#FFFFFF20')
    .justifyContent(FlexAlign.Center)
    .onClick(() => { this.repeatMode = (this.repeatMode + 1) % 3 })
  }
  .margin({ top: 20, bottom: 12 })
}
.width('100%')
.borderRadius(16)
.linearGradient({
  direction: GradientDirection.Top,
  colors: [['#182431', 0.0], ['#2C3E50', 1.0]]
})
.margin({ top: 16, left: 16, right: 16 })

Hero 播放区使用了深色渐变背景(#182431#2C3E50),配合半透明白色控件,营造出高端音响设备般的视觉质感。封面使用渐变色模拟专辑封面,并通过阴影增强立体感。五个控制按钮(随机/上一首/播放暂停/下一首/循环模式)以等间距排列,主播放按钮置于视觉中心并放大至 56vp。

3.2 最近播放宫格

Hero 区下方是最近播放的曲目,以四宫格形式展示:

// 最近播放
Column() {
  Row() {
    Text('最近播放')
      .fontSize(16)
      .fontWeight(FontWeight.Medium)
      .fontColor('#182431')
  }
  .width('100%')
  .padding({ left: 4, top: 4, bottom: 8 })

  Row({ space: 10 }) {
    ForEach(this.tracks.slice(0, 4), (track: TrackItem, idx: number) => {
      Column() {
        // 小封面
        Column()
          .width(70).height(70)
          .borderRadius(12)
          .linearGradient({
            direction: GradientDirection.Top,
            colors: [[track.color1, 0.0], [track.color2, 1.0]]
          })

        // 曲目标题
        Text(track.title)
          .fontSize(11).fontColor('#182431')
          .maxLines(1)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .margin({ top: 4 })
          .width(70)

        // 艺人
        Text(track.artist)
          .fontSize(9).fontColor('#99182431')
          .width(70)
      }
      .onClick(() => { this.currentTrackIndex = idx })
    })
  }
  .width('100%')
}
.width('100%')
.padding(16)

每个宫格包含渐变色封面、曲目名和艺人名。点击任意宫格会更新 currentTrackIndex,状态变更会联动到 Hero 区的封面颜色、曲目信息以及底部的迷你栏。

四、发现页签 — 搜索与排行榜

发现页签以内容发现为核心,包含搜索框、热门标签和排行榜三个模块。

4.1 搜索框

Row() {
  Image($r('sys.media.ohos_ic_public_clock'))
    .width(16).height(16)
    .fillColor('#8E8E93')
    .margin({ left: 12 })

  Text('搜索歌曲、艺人或专辑')
    .fontSize(14)
    .fontColor('#8E8E93')
    .layoutWeight(1)
    .margin({ left: 8 })
}
.width('100%')
.height(40)
.backgroundColor('#F2F2F7')
.borderRadius(20)
.margin({ top: 16, left: 16, right: 16 })

搜索框采用圆角胶囊形态(borderRadius: 20),背景为浅灰色,内部放置搜索图标和占位文字,视觉风格与 iOS 搜索栏一致。

4.2 热门标签

Row({ space: 8 }) {
  ForEach(['#流行', '#摇滚', '#电子', '#爵士', '#古典', '#R&B', '#嘻哈'],
    (tag: string) => {
    Text(tag)
      .fontSize(11)
      .fontColor('#AF52DE')
      .padding({ left: 12, right: 12, top: 6, bottom: 6 })
      .backgroundColor('#AF52DE10')
      .borderRadius(14)
  })
}
.width('100%')
.padding({ left: 16, right: 16, top: 12 })

标签使用品牌紫色文字配 10% 透明度的紫色背景,圆角标签形态。每个标签是独立的 Text 组件,未来可以扩展为可点击的筛选入口。

4.3 排行榜列表

排行榜是发现页的核心内容,每条记录包含排名序号、封面、曲目信息、时长和播放按钮:

Column({ space: 8 }) {
  ForEach(this.tracks, (track: TrackItem, idx: number) => {
    Row() {
      // 排名序号
      Text(String(idx + 1))
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .fontColor(idx < 3 ? '#FF3B30' : '#8E8E93')
        .width(24)

      // 封面
      Column()
        .width(44).height(44)
        .borderRadius(8)
        .linearGradient({
          direction: GradientDirection.Top,
          colors: [[track.color1, 0.0], [track.color2, 1.0]]
        })
        .margin({ left: 8 })

      // 曲目信息
      Column() {
        Text(track.title).fontSize(14).fontColor('#182431')
        Text(track.artist).fontSize(11).fontColor('#99182431')
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
      .margin({ left: 10 })

      // 时长
      Text(track.duration).fontSize(12).fontColor('#8E8E93')

      // 播放按钮
      Row() {
        Image($r('sys.media.ohos_ic_public_play'))
          .width(14).height(14).fillColor('#007AFF')
      }
      .width(28).height(28).borderRadius(14)
      .backgroundColor('#007AFF14')
      .justifyContent(FlexAlign.Center)
      .margin({ left: 8 })
      .onClick(() => { this.currentTrackIndex = idx })
    }
    .width('100%')
    .padding(12)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
  })
}
.width('100%')
.padding({ left: 16, right: 16 })

排名序号的前三名使用红色高亮(#FF3B30),其余使用灰色。点击播放按钮将当前曲目索引设置为对应条目,与 Hero 区和迷你栏联动。

五、音乐库页签 — 数据概览与歌单

音乐库页签以数据展示为主,上半部分是四个统计指标卡片,下半部分是歌单列表。

5.1 统计卡片

Row({ space: 12 }) {
  ForEach(this.stats, (stat: StatItem) => {
    Column() {
      Text(stat.count)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor('#182431')
      Text(stat.label)
        .fontSize(10)
        .fontColor('#99182431')
    }
    .layoutWeight(1)
    .padding(12)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
  })
}
.width('100%')
.padding({ top: 16, left: 16, right: 16 })

四个卡片通过 layoutWeight(1) 等宽分布,每个卡片包含大号数值和小号标签文字。这种布局适合在有限空间内呈现汇总数据。

5.2 歌单列表

Column({ space: 8 }) {
  ForEach(this.playlists, (pl: PlaylistItem) => {
    Row() {
      // 左侧色块图标
      Column()
        .width(48).height(48)
        .borderRadius(10)
        .backgroundColor(pl.color + '30')
        .justifyContent(FlexAlign.Center)

      // 歌单信息
      Column() {
        Text(pl.name)
          .fontSize(14)
          .fontWeight(FontWeight.Medium)
          .fontColor('#182431')
        Text(pl.count + ' 首歌曲')
          .fontSize(11)
          .fontColor('#99182431')
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
      .margin({ left: 10 })

      // 箭头
      Image($r('sys.media.ohos_ic_public_arrow_right'))
        .width(16).height(16)
        .fillColor('#C7C7CC')
    }
    .width('100%')
    .padding(12)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
  })
}
.width('100%')
.padding({ left: 16, right: 16 })

每个歌单行包含三个元素:30% 透明度的主题色方块、歌单名称和歌曲数量、右侧导航箭头。这种行布局是移动端列表页的经典范式。

六、我的页签 — 个人中心

6.1 个人资料卡片

Column() {
  // 头像
  Column()
    .width(72).height(72)
    .borderRadius(36)
    .backgroundColor('#007AFF20')
    .justifyContent(FlexAlign.Center)
    .margin({ top: 20 })

  // 用户名
  Text('音乐爱好者')
    .fontSize(18)
    .fontWeight(FontWeight.Bold)
    .fontColor('#FFFFFF')
    .margin({ top: 10 })

  // 副标题
  Text('HarmonyOS NEXT · WaveFlow')
    .fontSize(12)
    .fontColor('#99FFFFFF')
    .margin({ top: 4 })
}
.width('100%')
.height(180)
.borderRadius(16)
.linearGradient({
  direction: GradientDirection.Top,
  colors: [['#007AFF', 0.0], ['#5856D6', 1.0]]
})
.margin({ top: 16, left: 16, right: 16 })

使用蓝色到紫色的渐变背景,白色文字居中对齐。头像区域使用半透明白色圆形替代真实头像图片。

6.2 菜单列表

Column({ space: 4 }) {
  ForEach(this.menuItems, (item: ProfileMenuItem) => {
    Row() {
      // 圆形图标背景
      Row() {
        Image(item.icon)
          .width(20).height(20)
          .fillColor('#007AFF')
      }
      .width(36).height(36)
      .borderRadius(18)
      .backgroundColor('#007AFF14')
      .justifyContent(FlexAlign.Center)
      .margin({ right: 12 })

      // 菜单文字
      Column() {
        Text(item.label)
          .fontSize(14)
          .fontWeight(FontWeight.Medium)
          .fontColor('#182431')
        Text(item.desc)
          .fontSize(11)
          .fontColor('#99182431')
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)

      // 右箭头
      Image($r('sys.media.ohos_ic_public_arrow_right'))
        .width(16).height(16)
        .fillColor('#C7C7CC')
    }
    .width('100%')
    .padding(14)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
  })
}
.width('100%')
.padding({ left: 16, right: 16, top: 16 })

每个菜单行结构为:圆形蓝色图标背景(36vp)+ 标题/描述文字 + 右箭头。Column 的间距设为 4vp 使各行紧密排列。

七、NowPlayingMiniBar — 播放中迷你栏

迷你栏是 WaveFlow 的点睛之笔。它在页签栏的同行右侧显示当前正在播放的曲目信息和控制按钮,无论用户切换到哪个页签,都能看到和操作播放状态。

迷你栏的状态与 Hero 区完全联动:currentTrackIndexisPlaying 是共享的 @State 变量,任何一处的修改都会同时反映在 Hero 区和迷你栏中。

@Builder
NowPlayingMiniBar() {
  Row() {
    // 封面缩略图(36vp 小方块)
    Stack() {
      Column()
        .width(36).height(36)
        .borderRadius(6)
        .linearGradient({
          direction: GradientDirection.Top,
          colors: [
            [this.tracks[this.currentTrackIndex].color1, 0.0],
            [this.tracks[this.currentTrackIndex].color2, 1.0]
          ]
        })
    }
    .margin({ left: 4 })

    // 曲目信息(布局权重自动填满剩余空间)
    Column() {
      Text(this.tracks[this.currentTrackIndex].title)
        .fontSize(12)
        .fontWeight(FontWeight.Medium)
        .fontColor('#182431')
        .maxLines(1)
        .textOverflow({ overflow: TextOverflow.Ellipsis })

      Text(this.tracks[this.currentTrackIndex].artist
        + (this.isPlaying ? ' · 播放中' : ' · 已暂停'))
        .fontSize(9)
        .fontColor('#99182431')
        .maxLines(1)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
    }
    .alignItems(HorizontalAlign.Start)
    .layoutWeight(1)
    .margin({ left: 8 })

    // 播放/暂停按钮(蓝色实心圆形)
    Row() {
      Image(this.isPlaying ?
        $r('sys.media.ohos_ic_public_pause') :
        $r('sys.media.ohos_ic_public_play'))
        .width(14).height(14)
        .fillColor('#FFFFFF')
    }
    .width(30).height(30)
    .borderRadius(15)
    .backgroundColor('#007AFF')
    .justifyContent(FlexAlign.Center)
    .margin({ right: 4 })
    .onClick(() => { this.isPlaying = !this.isPlaying })

    // 下一首按钮(蓝色透明圆形)
    Row() {
      Image($r('sys.media.ohos_ic_public_arrow_right'))
        .width(12).height(12)
        .fillColor('#007AFF')
    }
    .width(28).height(28)
    .borderRadius(14)
    .backgroundColor('#007AFF14')
    .justifyContent(FlexAlign.Center)
    .margin({ right: 4 })
    .onClick(() => {
      this.currentTrackIndex = (this.currentTrackIndex + 1)
        % this.tracks.length;
    })
  }
}

迷你栏使用了紧凑布局风格:左侧 36vp 封面缩略图、中间曲目信息(单行省略、状态副标题)、右侧两个控制按钮(播放/暂停为实心蓝底、下一首为透明蓝底)。所有数据源(currentTrackIndexisPlayingtracks 数组)均与主内容区共享,保证状态一致性。

八、barFloatingStyle 完整配置

整合所有特性的 barFloatingStyle 配置如下:

HdsTabs({ controller: this.controller }) {
  // ... 四个 TabContent
}
.barOverlap(true)         // 悬浮模式
.barPosition(BarPosition.End)  // 置于底部
.vertical(false)           // 水平排列
.barFloatingStyle({
  // 响应式宽度
  barWidth: {
    smallWidth: 200,
    mediumWidth: 300,
    largeWidth: 400
  },
  // 底部间距
  barBottomMargin: 28,
  // 渐变遮罩
  gradientMask: {
    maskColor: '#66F1F3F5',
    maskHeight: 92
  },
  // 系统材质
  systemMaterialEffect: {
    materialType: hdsMaterial.MaterialType.IMMERSIVE,
    materialLevel: hdsMaterial.MaterialLevel.ADAPTIVE
  },
  // 播放中迷你栏
  miniBar: {
    miniBarBuilder: () => this.NowPlayingMiniBar()
  }
})

这个配置选择了 IMMERSIVE 沉浸材质 + 92vp 渐变遮罩,在音乐播放器的沉浸式体验和实用性之间取得了良好平衡。迷你栏随状态变量响应式更新,页签栏宽度和间距使用推荐值。

九、页面间的状态联动机制

WaveFlow 的关键设计是多个页面共享同一组播放状态。无论是浏览页的 Hero 控件、发现页排行榜的播放按钮、还是迷你栏的控制按钮,它们都操作相同的 currentTrackIndexisPlaying 状态变量:

@State currentTrackIndex: number = 0;  // 当前播放曲目的索引
@State isPlaying: boolean = true;       // 播放/暂停状态
@State isShuffled: boolean = false;     // 随机播放状态
@State repeatMode: number = 0;          // 循环模式 0=顺序 1=循环 2=单曲

状态变更的传播路径:

  1. 用户点击 Hero 区的"播放暂停"按钮 → isPlaying 反转 → Hero 区的图标和迷你栏图标同时更新
  2. 用户点击迷你栏的"下一首" → currentTrackIndex +1 → Hero 区的封面颜色、曲目信息、迷你栏的封面和信息全部联动更新
  3. 用户在发现页排行榜点击播放 → currentTrackIndex 被设为该曲目索引 → 所有相关 UI 同步刷新

这种基于 @State 的响应式联动是 ArkUI 的核心优势——开发者只需维护一份状态,框架自动完成所有依赖该状态的 UI 刷新。

十、手动管理 currentTabIndex 同步

在 WaveFlow 中,由于没有使用 CustomBuilder 自定义页签,而是使用 BottomTabBarStyle,所以不需要维护选中态。但如果未来迁移到 CustomBuilder,需要注意在 HdsTabs 的 onChange 回调中同步 currentTabIndex

HdsTabs({
  controller: this.controller,
  onTabChanged: (index: number) => {
    this.currentTabIndex = index;
  }
}) {
  // ...
}

十一、小结

本文通过 WaveFlow 音乐播放器的完整案例,展示了 HdsTabs barFloatingStyle 在实际项目中的综合运用:

  • 浏览页:深色 Hero 区 + 完整播放控件组 + 最近播放宫格,构建应用的主视觉焦点
  • 发现页:搜索框 + 热门标签 + 排行榜列表,经典内容发现模式
  • 音乐库页:统计卡片 + 歌单列表,数据概览型页面范式
  • 我的页:渐变资料卡片 + 菜单列表,个人中心标准布局
  • 迷你栏:与所有页面共享播放状态,提供跨页签的播放控制
  • 视觉配置:IMMERSIVE 材质 + 92vp 遮罩 + 28vp 间距,打造沉浸式音乐体验

这个案例的代码量约为 570 行,涵盖了 HdsTabs 悬浮页签栏的全部核心特性。你可以将其作为起点,根据实际产品的设计需求进行调整——比如替换模拟数据为真实 API、接入系统音频播放能力、或扩展更多页签和交互细节。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐