HarmonyOS NEXT AI 智能生活助手:首页设计与快捷入口实现

前言

在前两篇文章中,我们完成了 [项目规划与架构设计]和 [企业级工程创建]。本文是系列第 03 篇,将带你实现 HarmonyAI 的首页

首页是 APP 的"门面",用户打开 APP 第一眼看到的就是首页。一个优秀的首页需要兼顾美观性功能性引导性

HarmonyAI 首页包含以下核心模块:

  1. 快捷入口:AI 工具箱入口(聊天、翻译、OCR、花语等)
  2. 最近聊天:显示最近的对话历史
  3. AI 推荐:智能推荐功能卡片
  4. 每日一句:激励性名言警句
  5. 今日花语:每日随机花语展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

图1:HarmonyAI 首页模块布局示意图

一、首页整体设计

1.1 页面布局结构

首页采用 垂直滚动布局,从上到下依次排列:

┌─────────────────────┐
│  [搜索] 搜索/快捷入口栏 │  ← 顶部搜索栏
├─────────────────────┤
│  快捷入口 (Grid 2×4)  │  ← 8 个 AI 功能入口
│  ┌──┐ ┌──┐ ┌──┐ ┌──┐│
│  │聊│ │翻│ │OC│ │花││
│  │天│ │译│ │R │ │语││
│  └──┘ └──┘ └──┘ └──┘│
│  ┌──┐ ┌──┐ ┌──┐ ┌──┐│
│  │总│ │代│ │待│ │日││
│  │结│ │码│ │办│ │程││
│  └──┘ └──┘ └──┘ └──┘│
├─────────────────────┤
│  [星] AI 推荐         │  ← 横向滑动推荐
│  [推荐卡片] [推荐卡片]  │
├─────────────────────┤
│  [消息] 最近聊天       │  ← 最近对话列表
│  ────────────────    │
│  聊天的标题 1          │
│  聊天的标题 2          │
│  聊天的标题 3          │
├─────────────────────┤
│  [编辑] 每日一句       │  ← 名言警句
├─────────────────────┤
│  [花] 今日花语         │  ← 花语 Widget
└─────────────────────┘

1.2 交互设计

交互元素 手势/操作 反馈效果
快捷入口图标 点击 缩放动画 + 跳转
AI 推荐卡片 左右滑动 卡片滑动切换
最近聊天项 点击 跳转聊天页
搜索栏 点击 展开搜索页
下拉刷新 下拉手势 刷新所有数据

1.3 图标资源规范

HarmonyOS NEXT 设备上 emoji 会渲染为蓝色/紫色块,必须使用 SVG 矢量图标:

模块 图标资源名 说明
搜索栏 ic_search.svg 搜索图标
扫码 ic_scan.svg 扫码图标
AI 聊天 ic_chat.svg 聊天气泡
AI 翻译 ic_translate.svg 翻译图标
OCR 识别 ic_ocr.svg 文字识别
每日花语 ic_flower.svg 花朵图标
文章总结 ic_summary.svg 摘要图标
代码解释 ic_code.svg 代码括号
AI 待办 ic_todo.svg 待办清单
AI 日程 ic_schedule.svg 日历图标

提示:所有 SVG 图标统一放在 entry/src/main/resources/base/media 目录下,通过 $r('app.media.ic_xxx') 引用。


二、数据模型定义

2.1 快捷入口模型

// model/QuickEntry.ts
export interface QuickEntry {
  id: string;           // 唯一标识
  icon: Resource;       // 图标资源
  title: string;        // 标题
  description: string;  // 描述
  route: string;        // 路由路径
  color: ResourceColor; // 主题色
}

2.2 首页数据模型

// model/HomeData.ts
export interface HomeData {
  quickEntries: QuickEntry[];     // 快捷入口
  recentChats: Conversation[];   // 最近聊天
  aiRecommendations: AICard[];   // AI 推荐
  dailyQuote: string;             // 每日一句
  dailyFlower: FlowerInfo | null; // 今日花语
}

export interface AICard {
  id: string;
  title: string;
  description: string;
  icon: Resource;
  bgColor: ResourceColor;
  tip: string;
}

export interface FlowerInfo {
  name: string;
  language: string;      // 花语
  meaning: string;       // 寓意
  suggestion: string;    // 送花建议
  story: string;         // 历史故事
}

三、同步助手类实现

3.1 SyncHelper 数据同步

在 HarmonyOS NEXT 中,@Observed 装饰器用于观察嵌套对象的属性变化。但有时我们需要更灵活的同步机制。

// utils/SyncHelper.ts
export class SyncHelper {
  static instance: SyncHelper;
  private observers: Map<string, Function[]> = new Map();

  static getInstance(): SyncHelper {
    if (!SyncHelper.instance) {
      SyncHelper.instance = new SyncHelper();
    }
    return SyncHelper.instance;
  }

  // 注册观察者
  observe(key: string, callback: Function): void {
    const callbacks = this.observers.get(key) || [];
    callbacks.push(callback);
    this.observers.set(key, callbacks);
  }

  // 通知变更
  notify(key: string, data?: any): void {
    const callbacks = this.observers.get(key);
    if (callbacks) {
      callbacks.forEach(cb => cb(data));
    }
  }

  // 移除观察者
  removeObserve(key: string, callback: Function): void {
    const callbacks = this.observers.get(key) || [];
    const index = callbacks.indexOf(callback);
    if (index > -1) {
      callbacks.splice(index, 1);
    }
  }
}

使用场景:当 AIService 返回数据后,通过 SyncHelper 通知首页更新每日一句或今日花语。


四、首页组件实现

4.1 顶部搜索栏

// components/SearchBar.ets
@Component
struct SearchBar {
  @State searchText: string = '';

  build() {
    Row() {
      // 搜索图标
      Image($r('app.media.ic_search'))
        .width(20).height(20)
        .margin({ left: 12 })
      // 输入框
      TextInput({ placeholder: '搜索AI能力...', text: this.searchText })
        .layoutWeight(1)
        .backgroundColor(Color.Transparent)
        .fontSize(16)
        .margin({ left: 8, right: 12 })
        .onChange((value: string) => {
          this.searchText = value;
        })
      // 扫码图标(预留)
      Image($r('app.media.ic_scan'))
        .width(20).height(20)
        .margin({ right: 12 })
    }
    .width('100%')
    .height(48)
    .backgroundColor(Color.White)
    .borderRadius(24)
    .shadow({ radius: 4, color: 'rgba(0,0,0,0.08)' })
    .margin({ left: 16, right: 16, top: 12 });
  }
}

4.2 快捷入口网格

// pages/HomePage.ets (快捷入口部分)
@Component
struct QuickEntryGrid {
  private entries: QuickEntry[] = [
    { id: 'chat', icon: $r('app.media.ic_chat'), title: 'AI 聊天',
      description: '智能对话助手', route: 'pages/ChatPage', color: '#6C5CE7' },
    { id: 'translate', icon: $r('app.media.ic_translate'), title: 'AI 翻译',
      description: '多语言互译', route: 'pages/TranslatePage', color: '#00B894' },
    { id: 'ocr', icon: $r('app.media.ic_ocr'), title: 'OCR 识别',
      description: '文字提取', route: 'pages/OCRPage', color: '#0984E3' },
    { id: 'flower', icon: $r('app.media.ic_flower'), title: '每日花语',
      description: '花语查询', route: 'pages/FlowerPage', color: '#E17055' },
    { id: 'summary', icon: $r('app.media.ic_summary'), title: '文章总结',
      description: 'AI 摘要', route: 'pages/SummaryPage', color: '#FDCB6E' },
    { id: 'code', icon: $r('app.media.ic_code'), title: '代码解释',
      description: '代码分析', route: 'pages/CodePage', color: '#74B9FF' },
    { id: 'todo', icon: $r('app.media.ic_todo'), title: 'AI 待办',
      description: '智能待办', route: 'pages/TodoPage', color: '#A29BFE' },
    { id: 'schedule', icon: $r('app.media.ic_schedule'), title: 'AI 日程',
      description: '日程规划', route: 'pages/SchedulePage', color: '#55EFC4' }
  ];

  build() {
    Column() {
      // 标题
      Text('AI 工具箱')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .width('100%')
        .margin({ left: 16, bottom: 12 });

      // 2x4 网格布局
      Grid() {
        ForEach(this.entries, (item: QuickEntry) => {
          GridItem() {
            // 快捷入口卡片
            Column() {
              // 图标
              Image(item.icon)
                .width(36).height(36)
                .margin({ bottom: 6 });
              // 标题
              Text(item.title)
                .fontSize(14)
                .fontWeight(FontWeight.Medium);
              // 描述
              Text(item.description)
                .fontSize(11)
                .fontColor(Color.Gray);
            }
            .width('100%')
            .padding(12)
            .backgroundColor(Color.White)
            .borderRadius(16)
            .shadow({ radius: 4, color: 'rgba(0,0,0,0.06)' })
            .onClick(() => {
              RouterUtil.navigateTo(item.route);
            });
          }
        }, (item: QuickEntry) => item.id)
      }
      .columnsTemplate('1fr 1fr 1fr 1fr')
      .rowsTemplate('1fr 1fr')
      .columnsGap(10)
      .rowsGap(10)
      .padding({ left: 16, right: 16 });
    }
  }
}

4.3 AI 推荐卡片(横向滑动)

@Component
struct AIRecommendation {
  private cards: AICard[] = [
    { id: '1', title: '今日翻译', description: '英语 → 中文',
      icon: $r('app.media.ic_translate_big'), bgColor: '#6C5CE7',
      tip: '试试翻译 "Hello World"' },
    { id: '2', title: '代码助手', description: '代码解释与优化',
      icon: $r('app.media.ic_code_big'), bgColor: '#00B894',
      tip: '粘贴代码开始分析' },
    { id: '3', title: '文章总结', description: '长文快速摘要',
      icon: $r('app.media.ic_summary_big'), bgColor: '#0984E3',
      tip: '输入文章或链接' }
  ];
  @State currentIndex: number = 0;

  build() {
    Column() {
      // 标题栏
      Row() {
        Image($r('app.media.ic_star')).width(20).height(20).margin({ right: 6 });
        Text('AI 推荐')
          .fontSize(18)
          .fontWeight(FontWeight.Bold);
        Blank();
        Text('更多')
          .fontSize(14)
          .fontColor($r('app.color.primary'));
      }
      .width('100%')
      .padding({ left: 16, right: 16, bottom: 12 });

      // 横向滑动列表
      Scroll() {
        Row() {
          ForEach(this.cards, (card: AICard) => {
            // 推荐卡片
            Column() {
              Image(card.icon)
                .width(48).height(48);
              Text(card.title)
                .fontSize(16)
                .fontWeight(FontWeight.Bold)
                .fontColor(Color.White)
                .margin({ top: 12 });
              Text(card.description)
                .fontSize(13)
                .fontColor('rgba(255,255,255,0.8)')
                .margin({ top: 4 });
              Text(card.tip)
                .fontSize(12)
                .fontColor('rgba(255,255,255,0.6)')
                .margin({ top: 8 });
            }
            .width(220)
            .height(160)
            .padding(16)
            .backgroundColor(card.bgColor)
            .borderRadius(20)
            .margin({ left: 16 })
            .alignItems(HorizontalAlign.Start);
          })
        }
      }
      .scrollable(ScrollDirection.Horizontal)
      .edgeEffect(EdgeEffect.Spring);
    }
  }
}

4.4 最近聊天列表

@Component
struct RecentChatList {
  @State conversations: Conversation[] = [];
  private syncHelper = SyncHelper.getInstance();

  aboutToAppear() {
    this.loadRecentChats();
    // 监听新消息
    this.syncHelper.observe('new_message', () => {
      this.loadRecentChats();
    });
  }

  loadRecentChats() {
    // 从 ConversationRepository 加载最近 5 条
    ConversationRepository.getInstance()
      .getRecentConversations(5)
      .then((chats: Conversation[]) => {
        this.conversations = chats;
      });
  }

  build() {
    Column() {
      // 标题
      Row() {
        Image($r('app.media.ic_chat')).width(20).height(20).margin({ right: 6 });
        Text('最近聊天')
          .fontSize(18)
          .fontWeight(FontWeight.Bold);
        Blank();
        Text('查看全部 >')
          .fontSize(14)
          .fontColor(Color.Gray)
          .onClick(() => {
            RouterUtil.navigateTo('pages/HistoryPage');
          });
      }
      .width('100%')
      .padding({ left: 16, right: 16, bottom: 12 });

      // 聊天列表
      ForEach(this.conversations, (conv: Conversation) => {
        HistoryCard({ conversation: conv })
          .margin({ bottom: 8 })
          .onClick(() => {
            RouterUtil.navigateTo('pages/ChatPage', { convId: conv.id });
          });
      }, (conv: Conversation) => conv.id);
    }
  }
}

五、首页主页面组合

5.1 HomePage 完整实现

// pages/HomePage.ets
@Entry
@Component
struct HomePage {
  @State dailyQuote: string = '';
  @State dailyFlower: FlowerInfo | null = null;
  @StorageLink('statusBarHeight') statusBarHeight: number = 32;
  @StorageLink('navBarHeight') navBarHeight: number = 24;
  private syncHelper = SyncHelper.getInstance();

  aboutToAppear() {
    this.loadDailyData();
  }

  loadDailyData() {
    // 加载每日一句
    this.dailyQuote = '每一个不曾起舞的日子,都是对生命的辜负。 —— 尼采';
    // 从 AIService 加载今日花语
    AIService.getInstance()
      .getDailyFlower()
      .then((flower: FlowerInfo) => {
        this.dailyFlower = flower;
      })
      .catch(() => {
        // 兜底数据
        this.dailyFlower = {
          name: '向日葵',
          language: '沉默的爱',
          meaning: '忠诚、希望、光明',
          suggestion: '适合送给正在奋斗的朋友',
          story: '向日葵的花语是沉默的爱,代表着勇敢追求幸福。'
        };
      });
  }

  build() {
    Column() {
      // 顶部安全区占位
      Row().width('100%').height(this.statusBarHeight);

      // 可滚动区域
      Scroll() {
        Column() {
          // 搜索栏
          SearchBar();

          // 快捷入口
          QuickEntryGrid()
            .margin({ top: 20 });

          // AI 推荐
          AIRecommendation()
            .margin({ top: 24 });

          // 最近聊天
          RecentChatList()
            .margin({ top: 24 });

          // 每日一句
          DailyQuoteCard({ quote: this.dailyQuote })
            .margin({ top: 24 });

          // 今日花语
          if (this.dailyFlower) {
            DailyFlowerCard({ flower: this.dailyFlower! })
              .margin({ top: 16, bottom: 32 });
          }
        }
        .width('100%');
      }
      .scrollable(ScrollDirection.Vertical)
      .edgeEffect(EdgeEffect.Spring)
      .layoutWeight(1);

      // 底部安全区占位
      Row().width('100%').height(this.navBarHeight);
    }
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.page_background'));
  }
}

5.2 每日一句卡片

@Component
struct DailyQuoteCard {
  @Prop quote: string;

  build() {
    Column() {
      Row() {
        Image($r('app.media.ic_edit')).width(18).height(18).margin({ right: 6 });
        Text('每日一句')
          .fontSize(16)
          .fontWeight(FontWeight.Bold);
      }
      .width('100%')
      .margin({ bottom: 12 });

      Column() {
        Text(this.quote)
          .fontSize(15)
          .fontColor('#2D3436')
          .lineHeight(24)
          .textAlign(TextAlign.Center);
      }
      .padding(20)
      .backgroundColor(Color.White)
      .borderRadius(16)
      .shadow({ radius: 4, color: 'rgba(0,0,0,0.06)' });
    }
    .padding({ left: 16, right: 16 });
  }
}

六、路由与导航

6.1 RouterUtil 工具类

// utils/RouterUtil.ts
import router from '@ohos.router';

export class RouterUtil {
  // 跳转页面
  static navigateTo(uri: string, params?: Record<string, Object>): void {
    router.pushUrl({
      url: uri,
      params: params
    }, router.RouterMode.Standard, (err) => {
      if (err) {
        hilog.error(0x0000, 'RouterUtil',
          'Failed to navigate. Code: %{public}d, message: %{public}s',
          err.code, err.message);
      }
    });
  }

  // 返回上一页
  static back(page?: number): void {
    router.back(page);
  }

  // 替换页面
  static replaceTo(uri: string, params?: Record<string, Object>): void {
    router.replaceUrl({
      url: uri,
      params: params
    });
  }

  // 清除栈并跳转
  static clearAndNavigate(uri: string): void {
    router.clear();
    router.pushUrl({ url: uri });
  }
}

6.2 路由配置

// 在 EntryAbility 中配置路由表
onWindowStageCreate(windowStage: window.WindowStage) {
  // 配置路由
  windowStage.loadContent('pages/HomePage', (err) => {
    if (err.code) {
      hilog.error(0x0000, 'HarmonyAI',
        'Failed to load content. Cause: %{public}s',
        JSON.stringify(err));
    }
  });
}

注意:HarmonyOS NEXT 路由需要在 module.json5 中配置 page 的 srcEntry,或者使用 router.pushUrl 动态跳转。


七、主题与样式

7.1 首页主题色

// theme/LightTheme.ts
export const LightTheme = {
  // 背景色
  pageBackground: '#F5F6FA',
  cardBackground: '#FFFFFF',
  // 主色调
  primary: '#6C5CE7',
  primaryLight: '#A29BFE',
  // 快捷入口颜色
  entryColors: {
    chat: '#6C5CE7',
    translate: '#00B894',
    ocr: '#0984E3',
    flower: '#E17055',
    summary: '#FDCB6E',
    code: '#74B9FF',
    todo: '#A29BFE',
    schedule: '#55EFC4'
  },
  // 文字颜色
  textPrimary: '#2D3436',
  textSecondary: '#636E72',
  textTertiary: '#B2BEC3',
  // 阴影
  shadow: 'rgba(0,0,0,0.06)'
};

7.2 玻璃拟态效果

// 玻璃拟态组件
@Component
struct GlassCard {
  @Prop blur: number = 10;
  @Prop opacity: number = 0.15;

  build() {
    Column() {
      // 内容插槽
    }
    .width('100%')
    .backgroundColor('rgba(255,255,255,0.7)')
    .borderRadius(20)
    .shadow({ radius: 10, color: 'rgba(0,0,0,0.08)' })
    .backdropBlur(this.blur);
  }
}

八、完整的首页效果

8.1 页面功能矩阵

模块 数据来源 交互方式 视觉效果
搜索栏 本地输入 点击展开 圆角搜索框
快捷入口 本地数据 点击跳转 2×4 网格,图标 + 文字
AI 推荐 AIService 横向滑动 渐变卡片,圆角
最近聊天 Repository 点击跳转 列表项,阴影
每日一句 本地/远程 自动刷新 白色卡片
今日花语 AIService 点击查看更多 花语卡片

8.2 性能优化要点

  1. 延迟加载:首页按模块延迟渲染,优先显示快捷入口
  2. 图片缓存:使用 Image Kit 的缓存机制
  3. 数据预加载:在 Splash 页预加载首页数据
  4. 状态管理:使用 @State 最小化更新范围

九、常见问题

9.1 Grid 布局不整齐

// 错误:columnsTemplate 设置错误
Grid() {
  // ...
}
.columnsTemplate('1fr 1fr 1fr') // 3列,不符合2×4需求

// 正确:4列布局
.columnsTemplate('1fr 1fr 1fr 1fr')
.rowsTemplate('1fr 1fr')

9.2 路由跳转失败

// 错误:路由路径不正确
router.pushUrl({ url: 'pages/ChatPage' }); // 缺少 .ets 后缀

// 正确
router.pushUrl({ url: 'pages/ChatPage' }); // 不需要后缀
// 需要注意的是,路由必须在 module.json5 中注册

提示:HarmonyOS NEXT 路由路径不需要文件后缀,但是必须在 module.json5abilities 中配置 pages 数组。


十、Git 提交

# 添加首页相关文件
git add .

# 提交
git commit -m "feat(home): 完成首页设计与快捷入口

- 实现 2x4 快捷入口网格
- 实现 AI 推荐横向滑动卡片
- 实现最近聊天列表
- 实现每日一句卡片
- 实现今日花语卡片
- 封装 RouterUtil 路由工具
- 封装 SyncHelper 同步助手
- 定义首页数据模型

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>"

# 创建 Tag
git tag v0.0.2

总结

本文实现了 HarmonyAI 的首页,包含快捷入口网格、AI 推荐卡片、最近聊天列表、每日一句和今日花语五大模块。核心要点:

  1. 2×4 快捷入口网格:8 个 AI 功能入口,点击跳转对应页面
  2. 横向滑动推荐卡片:智能推荐,滑动切换
  3. 最近聊天列表:展示最近对话,快速进入聊天
  4. 每日一句 + 今日花语:首页内容丰富度提升
  5. 完整主题配色:统一视觉风格

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


上一篇: [创建企业级 AI 工程与目录结构]

下一篇: [ChatGPT 风格 AI 聊天界面]

相关资源:

Logo

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

更多推荐