HarmonyOS 状态输入页实战第二篇:让 APP 根据心情、疲劳和需求生成个性化生活建议

摘要

本文继续拆解“知行生活小助手”的核心功能:状态输入与建议生成。一个生活助手类 APP 的关键不在于页面有多炫,而在于它是否能理解用户“此刻”的状态。项目通过心情选择、疲劳判断、需求分类和自由文本补充,构建了一个轻量但完整的用户状态模型,再通过本地建议池完成推荐。本文会从交互设计、数据模型、推荐策略、页面事件和质量优化几个角度展开。
在这里插入图片描述

目录

  1. 为什么状态输入是核心
  2. 用户状态模型设计
  3. 页面交互结构
  4. 推荐算法实现
  5. 生成后如何进入分析页
  6. 可优化方向
  7. 总结

一、为什么状态输入是核心

很多生活类 APP 会直接给用户一堆功能入口,例如记账、喝水、运动、番茄钟。但用户真正遇到的问题往往是:“我现在有点累,不知道该干什么。”

知行生活小助手的设计思路是:先让用户快速表达当前状态,再由系统给出一个具体建议。这样用户不需要在多个功能里犹豫,APP 直接帮助他完成一次小决策。

状态输入页主要采集四类信息:

输入项 作用
心情 判断用户当前情绪倾向
是否疲劳 判断建议强度是否要降低
当前需求 食物、运动、休息、其他
补充信息 通过关键词匹配更细粒度的意图

这种设计的优势是“轻输入、强反馈”。用户只需点几下,必要时补一句话,就能得到个性化建议。

二、页面效果与交互目标

状态输入页不是表单堆叠,而是一个“情绪问诊式”的轻交互页面。建议发布文章时配一张页面截图,截图位置可以使用:

doc/APP_05_UI/stitch_ai_life_assistant/status_input_final_polish/screen.png

页面交互目标如下:

目标 实现方式
快速表达情绪 心情 Chip / MoodSelector
降低输入压力 需求卡片和是否疲劳二选一
保留个性表达 补充信息 TextArea
强化 AI 感 底部“正在分析您的状态”脉冲提示
明确下一步 渐变 EnergyButton 触发生成建议

这类交互比普通表单更适合生活助手,因为用户打开应用时可能正处于疲劳、焦虑或选择困难状态,输入步骤越轻,完成率越高。

三、用户状态模型设计

项目中用 UserStatus 表示用户当前状态:

export interface UserStatus {
  mood: MoodType;
  isTired: boolean;
  needs: NeedType[];
  note: string;
}

枚举定义如下:

export enum MoodType {
  HAPPY = 'happy',
  NEUTRAL = 'neutral',
  TIRED = 'tired',
  SAD = 'sad'
}

export enum NeedType {
  FOOD = 'food',
  EXERCISE = 'exercise',
  REST = 'rest',
  OTHER = 'other'
}

默认状态通过工厂函数生成:

export function createDefaultUserStatus(): UserStatus {
  return {
    mood: MoodType.NEUTRAL,
    isTired: false,
    needs: [],
    note: ''
  };
}

这里推荐使用函数而不是直接写对象常量,原因是后续如果字段增多,例如加入睡眠时长、压力指数、天气影响等,只需要改默认构造函数即可。

四、页面交互结构

状态输入页由四个核心区域组成:

  1. 顶部导航:标题为“填写当前状态”,支持返回。
  2. 心情选择器:通过 MoodSelector 选择情绪。
  3. 疲劳开关:使用“是/否”两个 pill 按钮。
  4. 需求网格:通过 NeedGrid 选择当前需求。
  5. 补充信息输入框:允许用户输入一句自由描述。

页面状态定义如下:

@State status: UserStatus = createDefaultUserStatus();
@State isGenerating: boolean = false;
@State pulseOpacity: number = 0.4;
private service = SuggestionService.getInstance();

当用户点击不同选项时,页面会用不可变更新的方式刷新状态:

private handleMoodChange(mood: MoodType) {
  this.status = {
    mood: mood,
    isTired: this.status.isTired,
    needs: this.status.needs,
    note: this.status.note
  };
}

这种写法虽然比直接修改字段略长,但对 ArkUI 的响应式刷新更友好,也更容易排查状态变化。

五、推荐算法实现

推荐逻辑在 SuggestionService.generateMockSuggestion 中完成,整体策略可以拆成三步。

在这里插入图片描述

1. 优先匹配自由文本

如果用户在补充信息中写了“有点困”“想吃清淡的”“今天压力大”,系统会先从建议池中做关键词匹配:

const note = status.note ?? '';
if (note.length > 0) {
  const matches = matchByKeywords(note, LIFE_SUGGESTIONS);
  if (matches.length > 0) {
    const top = matches.slice(0, Math.min(8, matches.length));
    return toMock(top[Math.floor(Math.random() * top.length)]);
  }
}

这一步让推荐看起来更“懂用户”。即使目前不是大模型,也能通过标签和关键词实现可解释的推荐。

2. 再按需求分类筛选

如果没有文本命中,就根据用户选择的需求过滤建议池:

const NEED_CATEGORY: Record<string, SuggestionCategory> = {
  [NeedType.FOOD]: SuggestionCategory.FOOD,
  [NeedType.EXERCISE]: SuggestionCategory.EXERCISE,
  [NeedType.REST]: SuggestionCategory.REST
};

用户选择“吃饭”,就从食物类建议里抽取;选择“运动”,就从运动类建议里抽取。这样推荐不会偏离用户当前意图。

3. 最后用心情标签调权

项目中还维护了心情到标签的映射:

const MOOD_TAG_HINTS: Record<string, string[]> = {
  [MoodType.TIRED]: ['困', '疲劳', '疲惫', '没精神'],
  [MoodType.SAD]: ['难过', '低落', '沮丧', '伤心'],
  [MoodType.HAPPY]: ['开心', '兴奋', '奖励'],
  [MoodType.NEUTRAL]: ['专注', '正念', '日常']
};

这一步的价值在于让“同样是运动需求”也能生成不同建议:疲劳时推荐拉伸或散步,精力好时可以推荐高强度运动。

六、生成后如何进入分析页

点击“生成建议”时,页面会进入加载态,模拟分析过程,然后生成建议、保存记录,并把记录 id 写入 AppStorage

async handleGenerate() {
  this.isGenerating = true;
  await new Promise<void>((resolve) => setTimeout(resolve, 1500));
  const mock = this.service.generateMockSuggestion(this.status);
  const record = await this.service.saveSuggestion({
    content: mock.content,
    reasoning: mock.reasoning,
    category: mock.category,
    iconKey: mock.iconKey
  });
  this.isGenerating = false;
  AppStorage.setOrCreate<string>('pendingRecordId', record.id);
  AppRouter.push(RouteNames.ANALYSIS_RESULT);
}

这里的 pendingRecordId 是页面间传递数据的关键。分析页可以根据这个 id 查到刚刚生成的建议,避免通过路由参数传大对象。

七、调试验证清单

测试场景 预期结果
默认状态直接生成 能生成一条通用生活建议
输入“有点困” 优先匹配休息、补水、轻运动相关建议
选择“吃饭”需求 建议分类更偏向 FOOD
选择“运动”需求 建议分类更偏向 EXERCISE
生成过程中重复点击按钮 按钮禁用或显示分析中
生成成功后跳转 进入 AnalysisResultPage,并保存 pendingRecordId

可以用下面方式临时输出关键状态:

console.info('current mood=' + this.status.mood);
console.info('current needs=' + JSON.stringify(this.status.needs));
console.info('note=' + this.status.note);

如果生成结果不符合预期,优先检查 LIFE_SUGGESTIONS 中的 tags 是否覆盖了用户输入的关键词。

八、常见踩坑

1. TextArea 输入后推荐没有变化

检查 onChange 是否把输入值写回了 status.note

.onChange((val: string) => {
  this.handleNoteChange(val);
})

如果忘记更新 note,服务层永远拿不到自由文本,自然无法做关键词匹配。

2. 多选需求只取了第一个是否合理?

当前逻辑中使用 status.needs[0] 做分类筛选,这是 MVP 阶段的简化方案。如果要更准确,可以把多个需求转换成多个分类权重,例如 FOOD + REST 时优先推荐清淡饮食或休息相关内容。

3. 为什么没有直接接入大模型?

本地建议池的优势是稳定、快速、可解释、无需网络。后续可以把 callHAGAgent 作为云端或端侧大模型入口,但在比赛和课程项目中,本地策略更容易演示和调试。

九、可优化方向

当前推荐逻辑已经能支撑完整演示,但如果要进一步提升产品体验,可以从以下方向扩展:

  1. 增加权重评分:关键词命中、需求分类、心情标签分别给分,而不是简单随机。
  2. 引入用户历史偏好:用户经常采纳哪类建议,就提高该类建议权重。
  3. 避免短时间重复:最近出现过的建议暂时降权。
  4. 接入端侧或云端大模型:将 callHAGAgent 作为后续真实 AI 能力入口。
  5. 结合天气和时间:下雨天少推荐外出,晚上少推荐高强度运动。

十、参考资料

  1. HarmonyOS ArkTS 状态管理与声明式 UI。
  2. HarmonyOS 页面路由与 AppStorage 使用方式。
  3. CSDN 质量分规则:建议提升正文长度、结构层次、代码格式、链接质量和内容实用性。
  4. 项目文件:entry/src/main/ets/pages/StatusInputPage.etsentry/src/main/ets/services/SuggestionService.ets

十一、互动问题

如果继续优化这个推荐模块,你会优先做哪一步?

  1. 引入真实大模型。
  2. 增加用户历史偏好。
  3. 接入天气和时间。
  4. 增加更丰富的情绪输入。

十二、总结

状态输入页是知行生活小助手的“理解入口”。它通过心情、疲劳、需求和补充文本构建用户当前状态,再由服务层进行关键词匹配、分类筛选和心情调权,最终生成一条可执行的生活建议。对于 HarmonyOS 项目来说,这个模块很好地体现了 ArkUI 页面状态管理、服务层封装和轻量推荐算法的结合。

Logo

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

更多推荐