
前言
空状态是应用中不可或缺的 UI 设计。当用户还没有任何数据时,空状态页面需要传达"这里没有内容"的信息,同时引导用户进行下一步操作。
"海风日记"在上首页、回顾页、标签管理页、搜索页等多个地方都设计了空状态引导,通过品牌元素(emoji 波浪)、提示文字和行动按钮的组合,引导用户创建第一篇日记。本文将从源码出发,深入讲解空状态的设计原则和实现方式。
一、空状态设计原则
1.1 空状态的三个要素
一个优秀的空状态应该包含以下三个要素:
| 要素 | 作用 | 示例 |
|---|---|---|
| 视觉元素 | 吸引注意力,缓解"空"的突兀感 | 🌊、📖、📊 |
| 提示文字 | 说明当前状态 | "还没有日记哦" |
| 行动按钮 | 引导用户下一步操作 | "写第一篇日记" |
1.2 海风日记的空状态场景
| 页面 | 视觉元素 | 提示文字 | 行动按钮 |
|---|---|---|---|
| 首页 | 🌊 | "还没有日记哦" | "写第一篇日记" |
| 回顾页 | 📊 | "还没有数据哦" | "去写日记" |
| 日历页 | 📖 | "这个月还没有日记" | 无(点击下方 +) |
| 标签页 | 🏷️ | "还没有标签" | 输入框添加 |
| 搜索页 | 🌊 | "没有找到相关内容" | 无(换关键词) |
二、首页空状态实现
2.1 源码
typescript
if (this.diaryList.length === 0) {
// 空状态:引导写第一篇日记
Column({ space: 0 }) {
// 视觉元素:波浪 emoji
Text('🌊').fontSize(64).margin({ bottom: 20 })
// 提示文字
Text('还没有日记哦')
.fontSize(18).fontColor(COLOR_TEXT_MAIN).fontWeight(FontWeight.Bold)
.margin({ bottom: 8 })
// 补充说明
Text('记录今天的心情,让每一天都值得回忆')
.fontSize(14).fontColor(COLOR_TEXT_HINT)
.textAlign(TextAlign.Center)
.padding({ left: 40, right: 40 })
.margin({ bottom: 32 })
// 行动按钮
Button('写第一篇日记')
.height(48).fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
.backgroundColor(COLOR_PRIMARY).borderRadius(24)
.padding({ left: 32, right: 32 })
.shadow({ radius: 12, color: 'rgba(245,166,35,0.30)', offsetY: 4 })
.onClick(() => {
router.pushUrl({ url: 'pages/diary/WriteDiaryPage' })
})
}
.width('100%').layoutWeight(1)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
2.2 布局分析
┌──────────────────────────────┐
│ │
│ 🌊 │ ← 视觉元素(emoji)
│ │
│ 还没有日记哦 │ ← 主标题
│ │
│ 记录今天的心情,让每一天 │
│ 都值得回忆 │ ← 副标题
│ │
│ ┌──────────────────────┐ │
│ │ 写第一篇日记 │ │ ← 行动按钮
│ └──────────────────────┘ │
│ │
└──────────────────────────────┘
三、回顾页空状态实现
3.1 源码
typescript
if (MOCK_DIARIES.length === 0) {
Column({ space: 12 }) {
Text('📊').fontSize(48).margin({ top: 60 })
Text('还没有数据哦')
.fontSize(17).fontColor(COLOR_TEXT_MAIN).fontWeight(FontWeight.Bold)
Text('写几篇日记后,这里会展示你的\n写作统计和情绪趋势')
.fontSize(14).fontColor(COLOR_TEXT_HINT)
.textAlign(TextAlign.Center).lineHeight(22)
Button('去写日记')
.height(44).fontSize(15).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
.backgroundColor(COLOR_PRIMARY).borderRadius(22)
.padding({ left: 28, right: 28 }).margin({ top: 24 })
.onClick(() => router.pushUrl({ url: 'pages/diary/WriteDiaryPage' }))
}
.width('100%').alignItems(HorizontalAlign.Center).layoutWeight(1)
}
四、空状态的设计对比
4.1 不同场景的空状态
| 场景 | emoji | 标题 | 描述 | 按钮 |
|---|---|---|---|---|
| 首页空日记 | 🌊 | "还没有日记哦" | "记录今天的心情..." | "写第一篇日记" |
| 回顾空数据 | 📊 | "还没有数据哦" | "写几篇日记后..." | "去写日记" |
| 日历空月份 | 📖 | "这个月还没有日记" | "点击下方 + 记录..." | 无 |
| 标签空列表 | 🏷️ | "还没有标签" | "添加标签可以帮你..." | 输入框 |
| 搜索无结果 | 🌊 | "没有找到相关内容" | "换个关键词试试吧" | 无 |
4.2 空状态与品牌一致性
所有空状态都遵循以下品牌规范:
- 使用 emoji 作为视觉元素:保持品牌调性一致
- 暖色系文字 :主标题使用
COLOR_TEXT_MAIN,副标题使用COLOR_TEXT_HINT - 橙色按钮 :CTA 按钮使用
COLOR_PRIMARY品牌色 - 居中布局:所有空状态使用居中对齐
五、空状态的交互反馈
5.1 按钮点击反馈
typescript
Button('写第一篇日记')
.shadow({
radius: 12,
color: 'rgba(245,166,35,0.30)', // 橙色阴影
offsetY: 4
})
.onClick(() => {
router.pushUrl({ url: 'pages/diary/WriteDiaryPage' })
})
5.2 跳转后的行为
点击空状态按钮后,用户跳转到写日记页面,完成后返回首页,首页应该显示新的日记内容:
typescript
// 在 onPageShow 中刷新数据
onPageShow() {
this.diaryList = [...MOCK_DIARIES]
}
六、空状态与真实数据的过渡
6.1 条件渲染
typescript
if (this.diaryList.length === 0) {
// 空状态
this.emptyState()
} else {
// 数据列表
this.diaryListContent()
}
6.2 过渡动画
typescript
// 空状态到数据列表的过渡
Column()
.transition({ type: TransitionType.Insert, opacity: 1 })
.transition({ type: TransitionType.Delete, opacity: 0 })
七、常见问题与排查
7.1 空状态不显示
问题:数据为空时空状态没有显示。
原因:条件判断逻辑错误,或数据初始值不为空。
解决方案 :检查 @State 初始值和判断逻辑:
typescript
@State diaryList: DiaryEntry[] = [] // 初始值为空数组
7.2 空状态和列表同时显示
问题:空状态和日记列表同时显示在页面上。
原因:条件渲染的 else 分支缺失。
解决方案:使用 if/else 确保互斥显示:
typescript
if (data.length === 0) {
// 空状态
} else {
// 列表
}
总结
本文通过"海风日记"的多个空状态场景,深入讲解了空状态引导的设计和实现:
- 设计原则:视觉元素 + 提示文字 + 行动按钮
- 首页实现:emoji + 标题 + 描述 + 按钮的完整布局
- 多场景空状态:首页、回顾、日历、标签、搜索的差异化设计
- 品牌一致性:所有空状态遵循统一的品牌规范
- 交互反馈:按钮阴影、跳转后的数据刷新
下一篇文章将深入讲解 WaveHeaderBand 组件的品牌视觉头部封装,敬请期待。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源: