HarmonyOS应用实战-启示散页-08-提问历史别无限长:用去重、截断和 Sheet 入口做轻量记忆

HarmonyOS应用实战-启示散页-08-提问历史别无限长:用去重、截断和 Sheet 入口做轻量记忆

提问历史的价值不是把用户每一次点击都永久保存,而是帮助用户找回最近问过什么、当时抽到了什么。答案之书如果无限追加历史,Preferences 会越来越大,列表越来越慢,重复问题也会淹没有用记录。轻量应用更适合做"最近记忆":去重、截断、可清理,并用 Sheet 承接查看入口。

这篇文章会把问题拆成四个可落地的点:

  1. 历史记录保存问题、答案、题库和时间,不只保存问题文本。
  2. 用最近上限控制 Preferences 体积。
  3. 相同问题可合并或前移,避免列表噪声。
  4. Sheet 入口只展示轻量历史,完整规则留在 Service。

1. 历史不是日志仓库

日志追求完整,历史追求可用。用户在答案之书里可能连续问同一个问题,也可能只是试动画。把每次抽取都永久保存,会让列表快速失去阅读价值。历史服务应该定义上限,比如只保留最近 50 条。

ts 复制代码
const MAX_HISTORY_COUNT: number = 50;

interface QuestionHistoryRecord {
  id: string;
  question: string;
  answerText: string;
  deckId: string;
  answerId: string;
  createdAt: number;
}

历史记录仍要保存来源字段,因为它可能被用于收藏、复盘或再次提问。

2. 空问题不要污染历史

答案之书允许用户不输入问题直接抽答案,但这类行为不一定值得进提问历史。可以只记录非空问题,或者把空问题标记为"随便问问"。关键是规则要在 Service 里统一,而不是由页面临时判断。

ts 复制代码
function normalizeQuestion(question?: string): string {
  return (question ?? '').trim().replace(/\s+/g, ' ');
}

if (!normalizeQuestion(result.question)) {
  return;
}

输入规范化能合并多余空格,也能避免空字符串成为历史列表里的噪声。

3. 重复问题可以前移

用户重复问同一个问题时,历史列表里出现十条相同问题没有意义。答案之书可以按规范化问题去重:新结果覆盖旧结果,并移动到最前面。这样保留最近答案,也保持列表清爽。

ts 复制代码
const key: string = normalizeQuestion(result.question).toLocaleLowerCase();
const rest: QuestionHistoryRecord[] = records.filter((item: QuestionHistoryRecord): boolean => {
  return normalizeQuestion(item.question).toLocaleLowerCase() !== key;
});
const next: QuestionHistoryRecord[] = [createHistoryRecord(result), ...rest]
  .slice(0, MAX_HISTORY_COUNT);

这个策略不是丢数据,而是把历史定位成最近记忆。用户最需要看到的是最后一次问这个问题得到的答案。

4. 追加历史要复用抽取结果

历史来源应该是 DrawAnswerResult,不要让 DrawingPage 再拼一遍字段。只要抽取结果已经包含 deckId、answerId、answerText、question 和 drawnAt,历史服务就能稳定生成记录。

ts 复制代码
function createHistoryRecord(result: DrawAnswerResult): QuestionHistoryRecord {
  return {
    id: newId('history'),
    question: normalizeQuestion(result.question),
    answerText: result.answer.text,
    deckId: result.deckId,
    answerId: result.answer.id,
    createdAt: result.drawnAt
  };
}

历史是抽取链路的下游,字段必须从同一个结果对象来,避免收藏和历史对不上。

5. Preferences 里只放必要字段

历史记录可能增长,字段越多越容易拖慢读写。不要把整份 Deck、完整 Answer 对象或 UI 展示字段写进历史。展示需要的题库名、时间文案可以在 Service 读取时组装。

ts 复制代码
await PreferencesStore.setJson<QuestionHistoryRecord[]>(
  PrefStoreName.App,
  AppPrefKey.QuestionHistory,
  next
);
AppStorage.setOrCreate(AppStorageKey.LastHistoryUpdateAt, Date.now());

持久化字段越稳定,后续迁移越轻。展示字段属于 ViewItem,不属于历史原始记录。

6. Sheet 适合承接轻量历史

提问历史不是主流程页面,放在 Sheet 里更符合轻量工具体验。首页保留抽取入口,Sheet 展示最近问题、答案和时间,并提供再次提问、收藏、清空。

ts 复制代码
@Builder HistorySheet() {
  Column({ space: 12 }) {
    ForEach(this.historyItems, (item: HistoryViewItem) => {
      this.historyRow(item);
    }, (item: HistoryViewItem): string => item.id)
  }
}

Sheet 只承载展示和操作入口,不直接读写 Preferences。列表数据仍从 HistoryService 来。

7. 清空历史要明确范围

清空历史不是清空收藏,也不是清空题库。按钮文案和 Service 方法都要表达范围,避免误删。对于本地离线应用,用户数据边界越清楚,隐私和发布说明越好写。

ts 复制代码
async clearQuestionHistory(): Promise<void> {
  await PreferencesStore.setJson<QuestionHistoryRecord[]>(
    PrefStoreName.App,
    AppPrefKey.QuestionHistory,
    []
  );
  AppStorage.setOrCreate(AppStorageKey.LastHistoryUpdateAt, Date.now());
}

清空动作只触碰历史 key。收藏、题库、当前题库都不应该被顺手改掉。

8. 历史刷新独立于题库刷新

历史变化不一定意味着题库变化。答案之书用独立的 LastHistoryUpdateAt,Sheet 打开或刷新信号变化时重新读取。这样历史列表不会依赖首页重建,也不会因为收藏变化而误刷新。

ts 复制代码
@StorageLink('lastHistoryUpdateAt')
@Watch('reloadHistory')
lastHistoryUpdateAt: number = 0;

private async reloadHistory(): Promise<void> {
  this.historyItems = await HistoryService.listViewItems();
}

刷新信号按数据域拆分,是让小应用长期可维护的基础习惯。

9. 验证与排障

历史验证要连续抽取、重复提问、清空、重启、删除来源题库。尤其要看 MAX_HISTORY_COUNT 是否生效,以及重复问题是否前移而不是新增一条。

text 复制代码
验证点:
1. 空问题不进入历史或按约定显示
2. 同一问题重复提问只保留最近一条
3. 超过 50 条后只保留最近 50 条
4. 清空历史不影响收藏和题库
5. 重启后历史仍按时间倒序显示

如果 Sheet 打开很慢,先看历史是否无限增长,再看是否把展示字段或整份题库写进了历史记录。

历史里的"再次提问"也要重新走抽取链路,而不是把旧答案重新展示一遍。历史记录提供的是 question 上下文,新的答案仍由 AnswerDrawService 从当前题库里生成。这样用户能区分"查看过去结果"和"基于同一个问题再抽一次",历史功能才不会变成隐藏的结果缓存。

这个边界对收藏也有影响:从历史里收藏旧答案,应该收藏历史记录里的 answerId 和 answerText;从历史里再次提问后收藏新答案,则使用新的 DrawAnswerResult。两个动作看起来都从 Sheet 发起,但数据来源不同,Service 方法也应该分开。

验证清单

  • 清应用数据后从冷启动进入,确认默认数据、页面状态和日志分支符合预期。
  • 对本文涉及的写路径准备正常、空值、重复、越界四类输入,确认错误停在 Service 或 Repository。
  • 页面返回、重新进入、切换题库、收藏、历史或删除后,确认对应刷新信号触发重新读取。
  • 修改资源或模块归属后重新构建,确认 HAP、HAR、HSP 的依赖方向没有反转。
  • 涉及真机体验、备份恢复、发布素材的内容,单独记录是否已经在设备或平台侧验证。

常见问题与处理

现象 先看哪里 处理方式
历史列表越来越慢 MAX_HISTORY_COUNT 是否生效 写入时截断最近记录
重复问题刷屏 是否按规范化问题去重 重复问题前移并覆盖旧结果
清空后收藏没了 清理范围是否过大 只清 QuestionHistory key
Sheet 数据不刷新 LastHistoryUpdateAt 是否变化 历史写入和清空后发刷新信号

小结

提问历史应该是轻量记忆,不是无限日志。规范化问题、合并重复、限制上限、独立刷新,能让历史功能长期保持有用,也不会拖累本地存储。

相关推荐
世人万千丶8 小时前
鸿蒙Flutter Flex嵌套布局技巧
学习·flutter·华为·harmonyos·鸿蒙·鸿蒙系统
绝世番茄8 小时前
HarmonyOS NEXT 实战:SideBar + Navigation 侧边导航布局完全指南
华为·harmonyos·鸿蒙
xd1855785558 小时前
家电选购参谋 —— 鸿蒙AI智能助手开发全流程解析
人工智能·华为·harmonyos·鸿蒙
FrameNotWork9 小时前
HarmonyOS 6.0 相机开发——拍照与录像
数码相机·华为·harmonyos
FrameNotWork9 小时前
HarmonyOS 6.0 自定义TabBar——从基础到动效
华为·harmonyos
世人万千丶9 小时前
鸿蒙Flutter Flex布局性能优化
学习·flutter·性能优化·harmonyos·鸿蒙·鸿蒙系统
●VON9 小时前
鸿蒙 PC Markdown 编辑器质量流水线:Web 构建、回归与 Release 门禁
前端·华为·编辑器·harmonyos·鸿蒙
ZZZMMM.zip9 小时前
断舍离清单 —— 鸿蒙AI智能助手开发全流程解析
人工智能·华为·harmonyos·鸿蒙·鸿蒙系统
listening7779 小时前
HarmonyOS 6.1 跨设备数据库实战:分布式账本的落地与一致性校验
数据库·harmonyos·分布式账本