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

目录

  1. 引言
  2. 产品定位与需求分析
  3. 数据模型设计与状态管理
  4. 首页场景:信息聚合与快速入口
  5. 列表场景:搜索、过滤与排序
  6. 统计场景:从数据到洞察
  7. 表单浮层:CRUD 交互设计
  8. 自定义组件体系与复用
  9. ArkTS 类型约束与常见陷阱
  10. 色彩、情感与品牌设计
  11. 六个应用的横向对比与能力图谱
  12. 扩展方向与产品化思考
  13. 总结

一、引言

1.1 「日冕手记」是什么

日冕手记是一款轻量级个人日记/记录管理应用。它不追求大而全,而是聚焦于「记录」这个核心场景,提供从数据录入、列表管理到统计分析的全链路体验。

「日冕」一词取「日光之冕」的意象——每天的生活如同太阳的光芒,而记录就是捕捉这些光芒的瞬间,让它们不至于被时间遗忘。

1.2 产品哲学

日冕手记的设计遵循三条核心理念:

记录即创造
每一次记录都是对当天生活的再创造。标题、内容、心情、分类、标签——五个维度的信息共同构成一个完整的「时间胶囊」。

数据服务于叙事
统计不是为了展示冰冷的数字,而是为了讲述「你的时间去了哪里」「你的心情如何变化」的故事。

简单但不简陋
6 个分类、5 级心情、全文搜索、分类统计——功能点到即止,但每一个都经过精心打磨。

1.3 技术栈

项目 内容
操作系统 HarmonyOS 4.0+
API 版本 24(6.1.1)
语言 ArkTS
布局 Column / Row / Scroll / GridRow/GridCol / Stack
输入 TextInput / TextArea / Button
数据流 @State 驱动 UI 重渲染
自定义组件 @Component × 3

二、产品定位与需求分析

2.1 用户画像

用户类型 核心需求 使用频率
学生 记录学习心得、日常感悟 每日
上班族 记录工作日志、情绪管理 工作日
旅行者 记录旅行见闻 行程中
自我管理爱好者 追踪习惯、分析时间分配 每日

2.2 功能拆解

日冕手记
│
├── 记录(录入层)
│   ├── 日期(默认当天,支持修改)
│   ├── 分类(6类单选)
│   ├── 标题(必填,单行)
│   ├── 内容(必填,多行)
│   ├── 心情(5级Emoji)
│   └── 标签(逗号分隔,可选)
│
├── 管理(操作层)
│   ├── 列表查看(时间倒序)
│   ├── 分类过滤(6类+全部)
│   ├── 全文搜索(标题/内容/标签)
│   ├── 排序切换(正序/倒序)
│   ├── 编辑(修改已有记录)
│   └── 删除(移除记录)
│
└── 分析(洞察层)
    ├── 数据概览(总记录/活跃天数/平均心情)
    ├── 分类分布(每类占比)
    ├── 心情分布(每级频次)
    └── 心情趋势(最近7天)

2.3 与同类应用的差异化

对比维度 传统日记App 日冕手记
信息维度 时间+内容 时间+分类+心情+标签
数据管理 按时间浏览 搜索+过滤+排序多维操作
统计分析 分类分布+心情分布+趋势
交互模式 列表为主 首页聚合+列表管理+统计洞察
目标用户 日记爱好者 轻量记录+数据分析爱好者

2.4 六个应用的能力演进

序号 应用 核心能力 新增技能点
1 栅格布局 布局展示 GridRow/GridCol
2 儿童闹钟 状态+定时器 @State / setInterval
3 豆豆计数器 动画+里程碑 scale动画 / 条件渲染
4 睡眠vs记忆力 数据可视化 Canvas / 贝塞尔曲线
5 教师课时表 管理型CRUD 表单 / 统计条
6 日冕手记 数据叙事 搜索/过滤/多维统计

三、数据模型设计与状态管理

3.1 数据模型

interface RecordItem {
  id: string;       // 唯一标识
  date: string;     // 日期,格式 "2026-06-01"
  cat: string;      // 分类:工作/学习/生活/健康/旅行/其他
  title: string;    // 标题
  content: string;  // 正文内容
  mood: number;     // 心情等级 1~5
  tags: string;     // 标签,逗号分隔
}

7 个字段的设计考量

id:使用 Math.random().toString(36).substring(2, 9) 生成。8 位随机字母数字组合,冲突概率极低,足以满足单机应用需求。

date:使用 ISO 格式字符串 YYYY-MM-DD。这种格式可排序、可比较、可解析,是日期数据的最佳存储格式。

cat:字符串而非索引。虽然使用索引更节省空间,但字符串可读性更高,便于调试和序列化。

title / content:标题必填但较短,内容必填且可长。TextInput 和 TextArea 分别对应这两种输入需求。

mood:1~5 的整数。5 级量表是心理学中最常用的评分体系,既能区分情绪强度,又不会因选项过多而让用户选择困难。

tags:逗号分隔的字符串。为了简单性,没有使用数组,而是用一个字符串存储多个标签,搜索时用 includes() 匹配。

3.2 辅助数据类型

interface MoodDist {
  mood: number;    // 心情等级 1~5
  count: number;   // 该等级的记录数
  emoji: string;   // 对应 Emoji
}

MoodDist 用于统计 Tab 中展示心情分布。将其定义为独立接口而非内联对象类型,是为了满足 ArkTS 的「禁止内联对象类型作为类型声明」的编译约束。

3.3 状态变量设计

分组 变量 类型 用途
核心数据 records RecordItem[] 全部记录数据
列表状态 filterCat string 当前分类过滤
列表状态 searchText string 搜索关键词
列表状态 sortAsc boolean 排序方向
表单状态 showForm boolean 浮层显隐
表单状态 formMode string 添加/编辑
表单状态 editId string 编辑中的记录ID
表单状态 formDate/Cat/Title/Content/Mood/Tags 多类型 表单各字段

3.4 数据流

用户操作(点击/输入/滑动)
        ↓
方法调用(saveRecord / deleteRecord / getFiltered)
        ↓
@State 变量更新(records / filterCat / searchText)
        ↓
ArkUI 响应式引擎检测变化
        ↓
UI 自动重渲染(课表/列表/统计同步更新)

ArkUI 的响应式机制保证了数据的一致性:不管用户在哪一个 Tab 中修改数据,所有 Tab 都能立即感知到变化。


四、首页场景:信息聚合与快速入口

4.1 首页信息架构

首页(HomeTab)
│
├── 欢迎卡片
│   ├── ☀️ 大标题
│   ├── "记录每一天的精彩"
│   └── "用文字留住时光,让回忆有迹可循"
│
├── 快捷统计(3张卡片)
│   ├── 📝 总记录数
│   ├── 📂 分类数
│   └── 💫 平均心情
│
├── 分类浏览(GridRow 2列网格)
│   ├── 💼 工作 → 点击进入列表并过滤
│   ├── 📖 学习 → ...
│   ├── 🏠 生活
│   ├── 💪 健康
│   ├── ✈️ 旅行
│   └── 📌 其他
│
└── 最近记录(最多3条)
    ├── 😊 标题 + 日期 + 分类
    └── 点击可编辑

4.2 欢迎卡片

Column({ space: 8 }) {
  Text('☀️').fontSize(48)
  Text('记录每一天的精彩').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#333')
  Text('用文字留住时光,让回忆有迹可循').fontSize(13).fontColor('#999')
}
.width('100%').padding(24).backgroundColor('#FFFFFF').borderRadius(20)
.alignItems(HorizontalAlign.Center)

设计意图:欢迎卡片不仅是装饰,更是品牌传达的载体。大号 ☀️ Emoji + 两句slogan,在用户打开应用的第一秒就传递出「日冕手记」的产品调性。

4.3 分类网格

GridRow({ columns: 12, gutter: { x: 8, y: 8 } }) {
  ForEach(this.categories.filter(c => c !== '全部'), (cat: string) => {
    GridCol({ span: 6 }) {
      Row({ space: 10 }) {
        Text(this.getCatEmoji(cat)).fontSize(24)
        Column({ space: 2 }) {
          Text(cat).fontSize(14).fontWeight(FontWeight.Medium)
          Text(`${this.getCatCount(cat)}`).fontSize(11).fontColor('#999')
        }
        .alignItems(HorizontalAlign.Start)
      }
      .width('100%').padding(14).backgroundColor('#FFFFFF').borderRadius(14)
      .onClick(() => { this.filterCat = cat; this.tabIdx = 1; })
    }
  }, (cat: string) => cat)
}

使用 GridRow(columns: 12) + GridCol(span: 6) 实现两列等宽网格。每个分类卡片包含 Emoji 图标、分类名称和记录数,点击后跳转到列表页并自动应用该分类的过滤。

注意categories.filter(c => c !== '全部') 过滤掉「全部」选项,因为首页只展示具体分类。

4.4 最近记录

ForEach(this.records.slice(0, 3), (r: RecordItem) => {
  Row({ space: 12 }) {
    Text(this.moods[r.mood - 1]).fontSize(24)
    Column({ space: 2 }) {
      Text(r.title).fontSize(15).fontWeight(FontWeight.Medium).fontColor('#333')
      Text(`${this.formatDate(r.date)}  ·  ${r.cat}`).fontSize(11).fontColor('#999')
    }
    .layoutWeight(1)
  }
  .onClick(() => { this.openEdit(r); })
})

slice(0, 3) 取前 3 条记录(默认倒序排列是最后 3 条),为用户提供快速进入编辑的入口。

4.5 首页的设计哲学

好的首页应该回答用户的三个问题:

  1. 「我现在有什么?」 → 快捷统计:总记录/分类数/平均心情
  2. 「我能做什么?」 → 分类网格:点击进入某个分类
  3. 「我最近做了什么?」 → 最近记录:快速回顾

三个模块从上到下,从抽象到具体,构成了完整的信息层级。


五、列表场景:搜索、过滤与排序

5.1 搜索栏

TextInput({ placeholder: '🔍 搜索标题、内容或标签...' })
  .height(38).padding({ left: 12 }).backgroundColor('#FFFFFF').borderRadius(19)
  .layoutWeight(1).fontSize(13)
  .onChange((v: string) => { this.searchText = v; })

if (this.searchText !== '') {
  Text('✕').fontSize(16).fontColor('#999').onClick(() => { this.searchText = ''; })
}

设计细节

  • 搜索框使用 19px 大圆角,视觉柔和
  • 输入内容后显示 ✕ 清空按钮
  • onChange 实时更新搜索文本,搜索结果即时变化

5.2 全文搜索实现

getFiltered(): RecordItem[] {
  let list = [...this.records];
  if (this.filterCat !== '全部') {
    list = list.filter(r => r.cat === this.filterCat);
  }
  if (this.searchText !== '') {
    const q = this.searchText.toLowerCase();
    list = list.filter(r =>
      r.title.toLowerCase().includes(q) ||
      r.content.toLowerCase().includes(q) ||
      r.tags.toLowerCase().includes(q)
    );
  }
  list.sort((a, b) => {
    const cmp = a.date.localeCompare(b.date);
    return this.sortAsc ? cmp : -cmp;
  });
  return list;
}

搜索覆盖三个字段:标题、内容、标签。用户可以在一个搜索框中找到所有相关内容,无需选择搜索范围。

大小写不敏感:使用 toLowerCase() 转换后比较。

排序:按日期字符串比较,sortAsc 控制正序/倒序。

5.3 分类过滤

Scroll() {
  Row({ space: 8 }) {
    ForEach(this.categories, (cat: string) => {
      Text(`${cat} (${this.getCatCount(cat)})`)
        .fontColor(this.filterCat === cat ? '#FFFFFF' : '#666')
        .backgroundColor(this.filterCat === cat ? '#FF6F00' : '#F0F0F0')
        .borderRadius(16)
        .onClick(() => { this.filterCat = cat; })
    })
  }
}
.height(42)

过滤栏使用 Scroll 包裹,当分类标签过多时支持横向滑动。每个标签显示分类名和记录数,选中态为橙底白字。

5.4 记录卡片

Column({ space: 6 }) {
  Row({ space: 10 }) {
    Text(this.moods[r.mood - 1]).fontSize(22)
    Column({ space: 2 }) {
      Text(r.title).fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333')
      Text(r.content).fontSize(13).fontColor('#666').lineHeight(20)
        .maxLines(2).textOverflow({ overflow: TextOverflow.Ellipsis })
    }
    .layoutWeight(1)
  }
  Row({ space: 8 }) {
    Text(`${this.formatDate(r.date)}`).fontSize(11).fontColor('#BBB')
    Text(`#${r.cat}`).fontSize(11).fontColor('#FF6F00').fontWeight(FontWeight.Medium)
    if (r.tags !== '') {
      Text(r.tags.split(',')[0]).fontSize(10).fontColor('#4CAF50')
    }
    Blank()
    Text('✏️').fontSize(16).onClick(() => { this.openEdit(r); })
  }
}

双层布局

  • 上层(Row):心情 Emoji + 标题(粗体)+ 内容预览(最多 2 行)
  • 下层(Row):日期、分类、第一个标签、编辑按钮

maxLines(2) + textOverflow(Ellipsis) 保证内容不会撑高卡片,保持列表统一高度。

5.5 空状态

if (this.getFiltered().length === 0) {
  Column() {
    Text('📭 暂无匹配记录').fontSize(16).fontColor('#CCC').margin({ top: 60 })
    Text('试试其他关键词或分类').fontSize(13).fontColor('#DDD')
  }
  .width('100%').alignItems(HorizontalAlign.Center).layoutWeight(1)
}

当搜索结果为空时,显示友好的空状态提示而不是空白页面。


六、统计场景:从数据到洞察

6.1 概览卡片

Row({ space: 12 }) {
  StatCard({ icon: '📝', value: `${this.records.length}`, label: '总记录', color: '#FF6F00' })
  StatCard({ icon: '📅', value: `${this.getActiveDays()}`, label: '活跃天数', color: '#4CAF50' })
  StatCard({ icon: '💫', value: `${this.getAvgMood()}`, label: '平均心情', color: '#2196F3' })
}

三张卡片分别回答:「写了多少」「坚持了多久」「心情如何」。

活跃天数通过 Set 去重计算:

getActiveDays(): number {
  const set = new Set<string>();
  this.records.forEach(r => set.add(r.date));
  return set.size;
}

平均心情保留一位小数:

getAvgMood(): number {
  if (this.records.length === 0) return 0;
  const sum = this.records.reduce((s, r) => s + r.mood, 0);
  return Math.round(sum / this.records.length * 10) / 10;
}

6.2 分类分布

ForEach(this.categories.filter(c => c !== '全部'), (cat: string) => {
  Row({ space: 12 }) {
    Text(this.getCatEmoji(cat)).fontSize(18)
    Text(cat).fontSize(14).fontColor('#333').width(40)
    Stack() {
      Row().width('100%').height(22).backgroundColor('#F0F0F0').borderRadius(11)
      Row().width(`${(this.getCatCount(cat) / Math.max(this.records.length, 1)) * 100}%`).height(22)
        .backgroundColor(this.getCatColor(cat)).borderRadius(11)
      Text(`${this.getCatCount(cat)}`).fontSize(11).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
    }
    .layoutWeight(1).height(22)
  }
})

每个分类一行,包含 Emoji、名称和进度条。进度条宽度 = 该分类记录数 / 总记录数。

6.3 心情分布

ForEach(this.getMoodDist(), (m: MoodDist) => {
  Row({ space: 12 }) {
    Text(m.emoji).fontSize(20)
    Text(`Level ${m.mood}`).fontSize(13).fontColor('#666').width(64)
    Stack() {
      Row().width('100%').height(24).backgroundColor('#F0F0F0').borderRadius(12)
      Row().width(`${(m.count / Math.max(this.records.length, 1)) * 100}%`).height(24)
        .backgroundColor(this.getMoodColor(m.mood)).borderRadius(12)
      Text(`${m.count}`).fontSize(11).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
    }
    .layoutWeight(1).height(24)
  }
})

getMoodColor 为 5 级心情分配从红到绿的渐变色:

getMoodColor(m: number): string {
  const colors = ['#F44336', '#FF9800', '#FFC107', '#8BC34A', '#4CAF50'];
  return colors[m - 1] || '#999';
}

6.4 心情趋势

Row({ space: 4 }) {
  ForEach(this.records.slice(0, 7), (r: RecordItem) => {
    Column({ space: 2 }) {
      Text(this.moods[r.mood - 1]).fontSize(18)
      Text(r.date.substring(5)).fontSize(9).fontColor('#999')
    }
    .layoutWeight(1).alignItems(HorizontalAlign.Center)
  })
}

最近 7 条记录的心情 Emoji 排列成趋势图。layoutWeight(1) 让每个 Emoji 均匀分布。简洁的「微趋势」视图,让用户一眼看出最近心情的起伏。

6.5 统计模块的设计原则

从数据到洞察的三层递进

  1. 数值概览(总记录/活跃天数/平均心情)→ 用户知道「多少」
  2. 分布构成(分类占比/心情占比)→ 用户知道「什么」
  3. 时间趋势(最近心情序列)→ 用户知道「变化」

好的数据可视化不是展示数据,而是帮助用户发现数据中的故事。


七、表单浮层:CRUD 交互设计

7.1 浮层架构

@Builder
FormOverlay() {
  Column() {
    // 遮罩层 — 点击关闭
    Column().width('100%').height('100%').backgroundColor('#00000044')
      .onClick(() => { this.showForm = false; })

    // 表单卡片
    Column({ space: 14 }) {
      // ... 表单字段 ...
    }
    .width('90%').padding(20).backgroundColor('#FFFFFF').borderRadius(20)
    .shadow({ radius: 12, color: '#30000000', offsetY: -4 })
    .alignSelf(ItemAlign.Center)
  }
  .width('100%').height('100%').justifyContent(FlexAlign.Center)
}

设计模式:一个半透明遮罩 + 一个居中白色卡片。遮罩点击关闭,符合 Material Design 的 Dialog 设计规范。

7.2 心情选择器

Row({ space: 6 }) {
  Text('💫').fontSize(18)
  ForEach([1, 2, 3, 4, 5], (m: number) => {
    Text(this.moods[m - 1]).fontSize(24)
      .opacity(this.formMood === m ? 1 : 0.35)
      .scale({ x: this.formMood === m ? 1.2 : 1 })
      .onClick(() => { this.formMood = m; })
  })
}

交互反馈

  • 未选中:35% 透明度
  • 选中:100% 透明度 + 1.2 倍缩放

scale 链式调用在选中时放大 Emoji,给予清晰的视觉反馈。注意 .scale() 放在 .opacity() 之后。

7.3 保存按钮的禁用态

Button('💾 保存')
  .backgroundColor(this.formTitle && this.formContent ? '#FF6F00' : '#CCC')
  .enabled(this.formTitle !== '' && this.formContent !== '')
  .onClick(() => { this.saveRecord(); })

标题和内容都填写后才可保存。按钮颜色从灰色 #CCC 变为橙色 #FF6F00。双重反馈(颜色 + enabled 属性)确保用户知道何时可以点击。

7.4 与教师课时表表单的对比

对比项 课时表表单 日冕手记表单
字段数 6 7
复杂字段 星期+节次选择器(按钮组) 心情选择器(Emoji)
输入类型 TextInput TextInput + TextArea
校验规则 科目+班级必填 标题+内容必填
删除按钮 编辑模式显示 编辑模式显示

日冕手记的表单增加了多行文本输入(TextArea)和 Emoji 心情选择器,信息维度更丰富。


八、自定义组件体系与复用

8.1 组件一览

组件名 用途 参数 复用次数
TabBtn 底部导航项 icon / label / active / act 3
QuickCard 首页快捷统计 icon / value / label / color 3
StatCard 统计页概览卡 icon / value / label / color 3

8.2 TabBtn

@Component
struct TabBtn {
  private icon: string = '';
  private label: string = '';
  private active: boolean = false;
  private act: () => void = () => {};

  build() {
    Column({ space: 2 }) {
      Text(this.icon).fontSize(20)
      Text(this.label).fontSize(11)
        .fontColor(this.active ? '#FF6F00' : '#999')
        .fontWeight(this.active ? FontWeight.Bold : FontWeight.Normal)
    }
    .layoutWeight(1).height('100%').justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
    .onClick(() => { this.act(); })
  }
}

作为跨应用复用的典范,TabBtn 从儿童闹钟到教师课时表到日冕手记,结构和逻辑几乎不变,只有选中态颜色发生了变化。

8.3 QuickCard 与 StatCard

两个卡片组件的结构和逻辑高度相似:

// QuickCard - 首页使用
Column({ space: 4 }) {
  Text(this.icon).fontSize(20)
  Text(this.value).fontSize(22).fontWeight(FontWeight.Bold).fontColor(this.color)
  Text(this.label).fontSize(11).fontColor('#999')
}

// StatCard - 统计页使用
Column({ space: 6 }) {
  Text(this.icon).fontSize(24)
  Text(this.value).fontSize(26).fontWeight(FontWeight.Bold).fontColor(this.color)
  Text(this.label).fontSize(12).fontColor('#999')
}

区别仅在于字号和间距的微调。理论上可以合并为一个组件,但分开维护的好处是修改一个不影响另一个。

8.4 组件设计原则

  1. 单一职责:每个组件只做一件事
  2. 参数驱动:通过参数控制外观和行为
  3. 回调通信:通过 act: () => void 回调与父组件通信
  4. 无内部状态:所有数据由父组件通过参数传入

九、ArkTS 类型约束与常见陷阱

9.1 内联对象类型禁止

错误信息

Object literals cannot be used as type declarations (arkts-no-obj-literals-as-types)

错误示例

// ❌ 方法返回内联对象类型
getMoodDist(): { mood: number; count: number; emoji: string }[] { ... }

// ❌ ForEach 参数使用内联对象类型
ForEach(list, (item: { mood: number; count: number; emoji: string }) => { ... })

解决方案:定义显式接口

interface MoodDist {
  mood: number;
  count: number;
  emoji: string;
}

这是 ArkTS 与标准 TypeScript 的重要区别之一。ArkTS 要求所有对象类型必须有显式声明,不能使用内联类型标注。

9.2 数组修改不触发更新

// ❌ 不触发 UI 更新
this.records[idx].title = '新标题';

// ✅ 触发 UI 更新
this.records[idx].title = '新标题';
this.records = [...this.records];  // 创建新引用

// ✅ push / filter / unshift 也触发
this.records.unshift({ ... });
this.records = this.records.filter(r => r.id !== id);

ArkUI 通过引用比较检测数组变化。直接修改数组元素不会创建新引用,需要显式赋值或使用数组方法。

9.3 分类方法返回值

本应用中多个方法返回统计数据:

// ✅ 正确:方法中声明变量
getMoodDist(): MoodDist[] {
  const result: MoodDist[] = [];
  for (let i = 1; i <= 5; i++) {
    result.push({ ... });
  }
  return result;
}

// ✅ 正确:显式的 forEach 循环
getDayStats(): DayStat[] {
  const arr: DayStat[] = [];
  this.weekDays.forEach((day, i) => {
    arr.push({ day, count: ... });
  });
  return arr;
}

注意这里使用了 forEach + push 的 Imperative 风格,而不是 map 的 Functional 风格。这是因为 ArkTS 对某些函数式编程模式的类型推断有限。

9.4 @Builder 内禁止变量声明

@Builder
MyBuilder() {
  // ❌ 不允许
  const x = this.someMethod();
  if (x > 0) { Text(x.toString()) }

  // ✅ 允许
  if (this.someMethod() > 0) {
    Text(this.someMethod().toString())
  }
}

这是 ArkTS 最严格的约束之一。@Builder 函数体只能包含 UI 组件语法(创建组件、设置属性、条件/循环渲染)。

9.5 Record 类型的遍历

// ❌ For...in 不支持
for (const key in this.subjectColors) { ... }

// ✅ Object.keys() 可行
ForEach(Object.keys(this.subjectColors), (key: string) => { ... })

Record<string, string> 类型使用 Object.keys() 获取键数组,然后通过 ForEach 遍历。


十、色彩、情感与品牌设计

10.1 主色调的选择

日冕手记采用 暖橙色 #FF6F00 作为主色调。

为什么是橙色?

色彩 情感联想 适用场景
蓝色 冷静、专业、理性 教师课时表
绿色 健康、自然、成长 豆豆计数器
橙色 温暖、活力、记录 日冕手记

橙色介于红色(激情)和黄色(愉悦)之间,既有活力又不过于热烈。配合「日冕」的品牌名,橙色让人联想到「阳光」「温暖」「记录生活」。这正是日记类应用想要传达的情感。

10.2 页面底色

.backgroundColor('#FFF8E1')  // 浅橙黄

#FFF8E1 是一种极淡的橙黄色,类似米纸或旧书页的颜色,让人联想起纸笔记录的怀旧感。同时,浅色底色让白色的卡片和内容更醒目。

10.3 分类配色

分类 色值 Emoji 情感联想
工作 #3F51B5 靛蓝 💼 专业、严谨
学习 #4CAF50 绿 📖 成长、知识
生活 #FF9800 🏠 温馨、日常
健康 #F44336 💪 活力、关注
旅行 #9C27B0 ✈️ 自由、探索
其他 #607D8B 灰蓝 📌 中性、通用

每种颜色与分类语义对应,帮助用户快速识别。

10.4 心情色彩

getMoodColor(m: number): string {
  const colors = ['#F44336', '#FF9800', '#FFC107', '#8BC34A', '#4CAF50'];
  return colors[m - 1];
}
心情 Emoji 色值 含义
1 😞 红色 #F44336 很差
2 😐 橙黄 #FF9800 一般
3 🙂 金黄 #FFC107 还行
4 😊 草绿 #8BC34A 不错
5 🤩 翠绿 #4CAF50 很棒

从红到绿的自然过渡,符合「从差到好」的直觉认知。

10.5 品牌一致性

日冕手记的品牌元素贯穿整个应用:

  • 名称:「☀️ 日冕手记」出现在标题栏
  • Slogan:「记录每一天的精彩」出现在首页欢迎卡片
  • 色调:橙色主色 + 浅橙底色 + 白色卡片
  • Emoji:☀️ 太阳作为品牌符号

十一、六个应用的横向对比与能力图谱

11.1 复杂度雷达图

维度 栅格布局 儿童闹钟 豆豆计数器 睡眠曲线 课时表 日冕手记
布局复杂度 ★★★★ ★★ ★★ ★★★ ★★★★ ★★★
状态管理 ★★★ ★★★ ★★ ★★★ ★★★★
交互复杂度 ★★ ★★ ★★★ ★★★ ★★★★
数据复杂度 ★★ ★★★ ★★★★
视觉设计 ★★★ ★★★ ★★★ ★★★ ★★★ ★★★★
代码行数 ~420 ~650 ~380 ~730 ~450 ~615

11.2 技能覆盖

技能点 栅格布局 儿童闹钟 豆豆计数器 睡眠曲线 课时表 日冕手记
GridRow/GridCol
Canvas 绘图
@State 管理
@Builder
@Component
setInterval
动画 (scale)
定时器
条件渲染
ForEach
TextInput
TextArea
搜索/过滤
数据统计
Scroll
CRUD

11.3 学习路径建议

入门 → 栅格布局(布局基础)
  ↓
基础 → 儿童闹钟(状态管理 + 生命周期)
  ↓
进阶 → 豆豆计数器(动画 + 里程碑系统)
  ↓
可视化 → 睡眠曲线(Canvas 绘图 + 数据可视化)
  ↓
管理 → 教师课时表(CRUD + 表单 + 统计条)
  ↓
综合 → 日冕手记(搜索 + 过滤 + 多维统计 + 情感设计)

每个应用都在前一个的基础上引入新的技能点,逐步构建完整的鸿蒙 ArkUI 开发知识体系。


十二、扩展方向与产品化思考

12.1 功能扩展

1. 数据持久化
当前数据存储在内存中,应用重启后丢失。使用 @ohos.data.preferences@ohos.data.relationalStore 实现持久化:

import { preferences } from '@kit.ArkData';

async saveRecords(records: RecordItem[]): Promise<void> {
  const prefs = await preferences.getPreferences(this.context, 'records');
  await prefs.put('data', JSON.stringify(records));
  await prefs.flush();
}

2. 图片附件
支持为每条记录添加图片,使用 @ohos.multimedia.media API 拍照或从相册选择。

3. 日历视图
在首页增加日历视图,有记录的日子显示小圆点,点击查看当天记录。

4. 导出备份
支持将记录导出为 JSON 或 Markdown 格式,通过分享功能发送到其他应用。

5. 密码锁
设置应用密码或生物识别锁,保护隐私。

6. 云同步
通过华为云或第三方云服务,在多设备间同步记录。

12.2 产品化思考

从「应用」到「产品」,需要考虑的远不止功能:

用户 onboarding
首次使用时,展示引导页或示例数据,帮助用户快速上手。本应用在 aboutToAppear 中初始化了 5 条示例记录,用户打开即有内容可看。

数据安全感
日记/记录类应用最核心的用户需求是「数据不丢失」。需要明确的「已保存」提示和自动保存机制。

情感设计
奖励机制、连续记录提醒、里程碑祝贺——这些「情感化设计」能提升用户的长期使用意愿。

隐私保护
记录内容属于高度隐私数据。应用需要明确说明数据存储位置(本地/云端),并提供数据清除功能。

12.3 从开发者到产品思维

回顾六个应用的开发过程,我们经历了一条从「技术实现」到「产品设计」的进化路径:

阶段 关注点 问题
1~2 「这个功能怎么做」 技术实现
3~4 「怎么做才好看」 用户体验
5~6 「做什么才有用」 产品价值

日冕手记作为这个系列的收官之作,它的价值不在于技术难度,而在于它思考了「用户为什么要用这个应用」这个问题。


十三、总结

13.1 核心知识点

技术 关键代码
多维数据过滤 getFiltered() 同时处理搜索+分类+排序
全文搜索 toLowerCase().includes() 三字段匹配
过滤栏 Scroll + Row 横向滑动
心情选择器 opacity + scale 选中态反馈
统计分布 Stack + Row 构建进度条
TextArea 多行输入 TextArea({ height: 140 })
@State 数组更新 unshift() / filter() / 展开运算符

13.2 六个应用的共同模式

回顾六个应用,无论功能如何变化,都遵循着相同的架构模式:

@State 变量 → @Builder UI 组件 → 交互事件 → 方法调用 → @State 更新

这是 ArkUI 响应式编程的核心循环。掌握了这个循环,就掌握了鸿蒙应用开发的精髓。

13.3 写在最后

从栅格布局到日冕手记,六个应用覆盖了鸿蒙 ArkUI 开发的完整知识体系:

  • 布局系统:GridRow/GridCol、Column/Row、Stack、Scroll
  • 状态管理:@State、@Builder、@Component
  • 生命周期:aboutToAppear、aboutToDisappear
  • 交互事件:onClick、onChange
  • 动画效果:scale、transition
  • 数据可视化:Canvas、条形图、进度条
  • CRUD 操作:增删改查、表单校验
  • 搜索过滤:全文搜索、多维筛选
  • 数据统计:聚合、分布、趋势

这不仅是技术的积累,更是产品思维的进阶。希望这六篇博客能为你提供从入门到实战的完整指引。


本文所有代码基于 HarmonyOS API 24(6.1.1),使用 ArkTS 语言编写。完整源码请参考项目中的 Index.ets 文件。

Logo

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

更多推荐