# HarmonyOS 状态输入页实战第二篇:让 APP 根据心情、疲劳和需求生成个性化生活建议
本文介绍了生活助手类APP的核心功能——状态输入与个性化建议生成。通过心情选择、疲劳判断、需求分类和自由文本补充四类信息,构建轻量完整的用户状态模型。文章详细阐述了页面交互设计、数据模型构建、推荐算法实现三个关键环节:1)采用"情绪问诊式"轻交互降低用户输入压力;2)定义UserStatus接口封装用户状态;3)实现三级推荐策略(关键词匹配→需求分类→心情调权)。最后说明了状态到建议的完整流转路径
HarmonyOS 状态输入页实战第二篇:让 APP 根据心情、疲劳和需求生成个性化生活建议
摘要
本文继续拆解“知行生活小助手”的核心功能:状态输入与建议生成。一个生活助手类 APP 的关键不在于页面有多炫,而在于它是否能理解用户“此刻”的状态。项目通过心情选择、疲劳判断、需求分类和自由文本补充,构建了一个轻量但完整的用户状态模型,再通过本地建议池完成推荐。本文会从交互设计、数据模型、推荐策略、页面事件和质量优化几个角度展开。
目录
- 为什么状态输入是核心
- 用户状态模型设计
- 页面交互结构
- 推荐算法实现
- 生成后如何进入分析页
- 可优化方向
- 总结
一、为什么状态输入是核心
很多生活类 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: ''
};
}
这里推荐使用函数而不是直接写对象常量,原因是后续如果字段增多,例如加入睡眠时长、压力指数、天气影响等,只需要改默认构造函数即可。
四、页面交互结构
状态输入页由四个核心区域组成:
- 顶部导航:标题为“填写当前状态”,支持返回。
- 心情选择器:通过
MoodSelector选择情绪。 - 疲劳开关:使用“是/否”两个 pill 按钮。
- 需求网格:通过
NeedGrid选择当前需求。 - 补充信息输入框:允许用户输入一句自由描述。
页面状态定义如下:
@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 作为云端或端侧大模型入口,但在比赛和课程项目中,本地策略更容易演示和调试。
九、可优化方向
当前推荐逻辑已经能支撑完整演示,但如果要进一步提升产品体验,可以从以下方向扩展:
- 增加权重评分:关键词命中、需求分类、心情标签分别给分,而不是简单随机。
- 引入用户历史偏好:用户经常采纳哪类建议,就提高该类建议权重。
- 避免短时间重复:最近出现过的建议暂时降权。
- 接入端侧或云端大模型:将
callHAGAgent作为后续真实 AI 能力入口。 - 结合天气和时间:下雨天少推荐外出,晚上少推荐高强度运动。
十、参考资料
- HarmonyOS ArkTS 状态管理与声明式 UI。
- HarmonyOS 页面路由与
AppStorage使用方式。 - CSDN 质量分规则:建议提升正文长度、结构层次、代码格式、链接质量和内容实用性。
- 项目文件:
entry/src/main/ets/pages/StatusInputPage.ets、entry/src/main/ets/services/SuggestionService.ets。
十一、互动问题
如果继续优化这个推荐模块,你会优先做哪一步?
- 引入真实大模型。
- 增加用户历史偏好。
- 接入天气和时间。
- 增加更丰富的情绪输入。
十二、总结
状态输入页是知行生活小助手的“理解入口”。它通过心情、疲劳、需求和补充文本构建用户当前状态,再由服务层进行关键词匹配、分类筛选和心情调权,最终生成一条可执行的生活建议。对于 HarmonyOS 项目来说,这个模块很好地体现了 ArkUI 页面状态管理、服务层封装和轻量推荐算法的结合。
更多推荐



所有评论(0)