实例:备忘录便签(Memo)|技术:六色便签种子、置顶与颜色覆盖
一、种子数据的设计目标
实例 11 的种子数据要撑起「多彩便签墙」,设计目标:
- 12 张便签:双列墙铺满两屏左右,内容丰富;
- 六色全覆盖:MEMO_COLORS 的六种颜色各 2 张——颜色筛选条每个色点都有数据;
- 3 张置顶:置顶区有内容(周会要点/超市采购/健身计划)——置顶排序可见;
- 内容叙事:每张便签是一个生活/工作场景(会议要点、购物清单、读书摘抄…),彩色墙有「生活剪影」感。
二、12 张便签种子数据全表
| # | 标题 | 内容摘要 | 颜色 | 置顶 | 时间偏移 |
|---|---|---|---|---|---|
| 1 | 周会要点 | 1. 进度汇报模板更新 2. 下周二上线灰度 3. 客户端 2.0 排期确认 | 米黄 | ✅ | 1 天前 |
| 2 | 超市采购清单 | 牛奶、鸡蛋、面包、香蕉、洗衣液、抽纸、垃圾袋 | 粉 | ✅ | 1 天前 |
| 3 | 健身计划 | 周一胸+三头 周三背+二头 周五腿 每次 30 分钟有氧收尾 | 绿 | ✅ | 2 天前 |
| 4 | 读书摘抄 | 「世界上只有一种真正的英雄主义…」——罗曼·罗兰 | 蓝 | — | 3 天前 |
| 5 | 项目灵感 | 给 App 加「每周回顾」入口:自动汇总本周新增数据… | 紫 | — | 4 天前 |
| 6 | 回家机票 | 国庆机票已订:10 月 1 日 09:30 出发,返程 10 月 6 日晚班 | 橙 | — | 5 天前 |
| 7 | 电影清单 | 待看:流浪地球 3、沙丘 2、奥本海默、宇宙探索编辑部 | 米黄 | — | 6 天前 |
| 8 | 生日提醒 | 妈妈 6 月 18 日生日,提前订蛋糕,当天记得视频通话 | 粉 | — | 7 天前 |
| 9 | 旅行攻略 | 大理 5 日游:洱海骑行、苍山索道、喜洲古镇、双廊看日落 | 绿 | — | 8 天前 |
| 10 | 代码小抄 | ArkTS 列表懒加载:onReachEnd 触发下一页,注意 loading 防抖 | 蓝 | — | 9 天前 |
| 11 | 待还账单 | 信用卡 6 月账单 2450 元,还款日 6 月 25 日 | 紫 | — | 10 天前 |
| 12 | 愿望清单 | ① 学会吉他 ② 跑一次半马 ③ 带爸妈去三亚 ④ 攒钱换相机 | 橙 | — | 11 天前 |
设计要点:
- 六色 × 2:米黄(工作/电影)、粉(购物/生日)、绿(健身/旅行)、蓝(摘抄/技术)、紫(灵感/账单)、橙(机票/愿望)——每色 2 张,筛选任一颜色都有结果;
- 3 张置顶:周会要点(工作)、超市采购(生活)、健身计划(健康)——置顶区三色并排,双键排序可见「置顶在前」;
- 内容有生活感:会议、购物、健身、读书、旅行、账单、愿望——普通人的真实便签墙。
三、代码实现
constnow=Date.now();constday=86400000;constseed:MemoSeed[]=[/* 12 张,见上表 */];for(constsofseed){constvalues:relationalStore.ValuesBucket={title:s.title,content:s.content,color:s.color,pinned:s.pinned,created_time:s.createdTime,updated_time:s.createdTime,};awaitstore.insert(MemoDao.TABLE,values);}hilog.info(DOMAIN,TAG,'已填充 12 张彩色便签种子数据');MemoSeed 接口:
interfaceMemoSeed{title:string;content:string;color:string;pinned:number;createdTime:number;}无 id(自增)、无 updatedTime(复用 createdTime——新便签没有编辑历史,两个时间戳一致合理)。
幂等保护:查 memo 表 COUNT,非空即返回(与其他实例同款模板)。
四、注入后的便签墙效果预测
| 区块 | 预期效果 |
|---|---|
| 便签墙 | 12 张彩色便签双列排列:前 3 张带 📌(周会/采购/健身),后 9 张无图钉 |
| 置顶排序 | 置顶区 3 张(米黄/粉/绿)排在最前,按更新时间倒序 |
| 颜色筛选 | 点粉色圆点 → 只剩 2 张(超市采购、生日提醒) |
| 标题栏 | 「共 12 张便签」 |
演示脚本:进入页面 → 指着彩色墙说「这是今天的便利贴墙」→ 点📌便签说「置顶的重要事项」→ 点粉色圆点 → 只剩粉色便签(采购+生日)→ 点「全部」恢复 → 点一张便签编辑换颜色 → 便签变色并上浮到同组顶部。
五、验证方法
- hilog 日志:搜
MemoDao,看到「已填充 12 张彩色便签种子数据」; - 页面显示:12 张彩签、3 张置顶、六色齐全;
- 数据库直查:
SELECTCOUNT(*)FROMmemo;-- 12SELECTcolor,COUNT(*)FROMmemoGROUPBYcolor;-- 每色 2SELECTCOUNT(*)FROMmemoWHEREpinned=1;-- 3SELECTtitle,color,pinnedFROMmemoORDERBYpinnedDESC,updated_timeDESC;-- 前 3 行 pinned=1,后 9 行 pinned=0 - 交互验证:点某便签 ⋯ → 「置顶」→ 便签跳到最前 + 📌;再点「取消置顶」→ 回到普通区。
六、FAQ
Q1:为什么置顶便签只有 3 张?
A:置顶区需要「有内容但不拥挤」——3 张正好展示「多张置顶共存」的形态(如果只 1 张,看不出置顶区的组概念)。普通区 9 张保证滚动可见。置顶与普通 3:9 的比例是演示的最优解。
Q2:为什么每色 2 张而不是 1 张?
A:筛选某色时「至少 2 条结果」让筛选后的墙面不至于太孤单,也验证「筛选不破坏排序」(2 张中置顶的排前面)。每色至少 2 条是颜色筛选类种子的基本要求。
Q3:内容为什么有的短(购物清单)有的长(健身计划)?
A:便签内容的天然差异——购物清单是罗列、健身计划是分段。长短混搭让便签墙的卡片高度参差,双列瀑布流的「错落感」才真实。内容长度服务于布局真实感。
Q4:便签的 updated_time 全部等于 created_time,排序会乱吗?
A:不会——排序按 updated_time 降序,种子数据的时间偏移(1~11 天)保证顺序:最新的(周会 1 天前)排最前。同组内 updated_time 不同,顺序确定。时间偏移的梯度让排序有明确顺序。
Q5:新增便签的默认颜色是什么?
A:页面 openAdd 里this.fColor = MEMO_COLORS[0](米黄),数据库 DEFAULT ‘#FFE8A3’ 兜底——两层默认值一致。默认色的双保险:UI 层预填 + 数据层兜底。
七、文章小结
本篇文章展示了实例 11 的种子数据:12 张六色便签(每色 2 张)+ 3 张置顶 + 生活化内容。核心是「颜色全覆盖」与「置顶可见」两个设计——六色齐全让筛选条每点有结果,置顶便签让双键排序一开屏就可见。便签墙的彩色效果和置顶排序在真实数据下得到完整验证。
下一篇(11-5)是实例 11 的收官文章,展示 MemoPage 全量代码与运行效果。
八、完整 initSeedData 实现:12 张便签的落库流水线
initSeedData把「幂等检查 → 种子定义 → 逐条插入 → 日志确认」串成一条流水线,先看全貌代码:
staticasyncinitSeedData(context:common.Context):Promise<void>{conststore=awaitMemoDao.getStore(context);// 幂等保护:表内已有数据则跳过,防止重复灌入constcountPredicates=newrelationalStore.RdbPredicates(MemoDao.TABLE);constcount=awaitstore.count(countPredicates);if(count>0){hilog.info(DOMAIN,TAG,'memo 表已有 %d 条数据,跳过种子填充',count);return;}constday=86400000;constnow=Date.now();constseed:MemoSeed[]=[{title:'周会要点',content:'1. 进度汇报模板更新\n2. 下周二上线灰度\n3. 客户端 2.0 排期确认',color:'#FFE8A3',pinned:1,createdTime:now-1*day},{title:'超市采购清单',content:'牛奶、鸡蛋、面包、香蕉、洗衣液、抽纸、垃圾袋',color:'#FFC4D6',pinned:1,createdTime:now-1*day},{title:'健身计划',content:'周一胸+三头\n周三背+二头\n周五腿\n每次 30 分钟有氧收尾',color:'#D5F5E3',pinned:1,createdTime:now-2*day},{title:'读书摘抄',content:'「世界上只有一种真正的英雄主义…」——罗曼·罗兰',color:'#CDE8FF',pinned:0,createdTime:now-3*day},{title:'项目灵感',content:'给 App 加「每周回顾」入口:自动汇总本周新增数据…',color:'#E8D5F5',pinned:0,createdTime:now-4*day},{title:'回家机票',content:'国庆机票已订:10 月 1 日 09:30 出发,返程 10 月 6 日晚班',color:'#FFD8B8',pinned:0,createdTime:now-5*day},{title:'电影清单',content:'待看:流浪地球 3、沙丘 2、奥本海默、宇宙探索编辑部',color:'#FFE8A3',pinned:0,createdTime:now-6*day},{title:'生日提醒',content:'妈妈 6 月 18 日生日,提前订蛋糕,当天记得视频通话',color:'#FFC4D6',pinned:0,createdTime:now-7*day},{title:'旅行攻略',content:'大理 5 日游:洱海骑行、苍山索道、喜洲古镇、双廊看日落',color:'#D5F5E3',pinned:0,createdTime:now-8*day},{title:'代码小抄',content:'ArkTS 列表懒加载:onReachEnd 触发下一页,注意 loading 防抖',color:'#CDE8FF',pinned:0,createdTime:now-9*day},{title:'待还账单',content:'信用卡 6 月账单 2450 元,还款日 6 月 25 日',color:'#E8D5F5',pinned:0,createdTime:now-10*day},{title:'愿望清单',content:'① 学会吉他 ② 跑一次半马 ③ 带爸妈去三亚 ④ 攒钱换相机',color:'#FFD8B8',pinned:0,createdTime:now-11*day},];for(constsofseed){constvalues:relationalStore.ValuesBucket={title:s.title,content:s.content,color:s.color,pinned:s.pinned,created_time:s.createdTime,updated_time:s.createdTime,// 新便签无编辑历史,两个时间戳一致};awaitstore.insert(MemoDao.TABLE,values);}hilog.info(DOMAIN,TAG,'已填充 12 张彩色便签种子数据');}流水线四步拆解:
| 步骤 | 代码 | 作用 |
|---|---|---|
| 1. 幂等检查 | store.count+if (count > 0) return | 空表才灌入,防止重复 |
| 2. 种子定义 | seed数组(12 条) | 数据源,一眼看全 |
| 3. 逐条插入 | for+store.insert | 落库,await 保证顺序 |
| 4. 日志确认 | hilog.info | 可观测,验证执行 |
两个值得注意的实现细节:
- count 用 RdbPredicates 而非裸 SQL——与查询/更新同款 API 风格,不拼字符串,类型安全;
- 时间戳相对偏移:
now - n * day让 12 张便签的 created_time 呈 1~11 天梯度——不写死日期,任何时候首启都能得到「最新在前」的排序效果。
九、种子设计复盘:颜色分布与置顶示例
9.1 颜色分布矩阵
六色各 2 张,把同色两张摆在一起看语义分组:
| 颜色 | 色值 | 两张便签 | 语义 |
|---|---|---|---|
| 米黄 | #FFE8A3 | 周会要点 / 电影清单 | 工作 + 休闲 |
| 粉 | #FFC4D6 | 超市采购 / 生日提醒 | 家庭采购 |
| 绿 | #D5F5E3 | 健身计划 / 旅行攻略 | 健康 + 出行 |
| 蓝 | #CDE8FF | 读书摘抄 / 代码小抄 | 学习 + 技术 |
| 紫 | #E8D5F5 | 项目灵感 / 待还账单 | 创意 + 财务 |
| 橙 | #FFD8B8 | 回家机票 / 愿望清单 | 出行 + 梦想 |
复盘结论:每色内部是「务实 + 浪漫」的搭配——米黄的会议与电影、紫的灵感与账单。点任一色点,筛选结果都呈现两种生活切面,不单调。
9.2 置顶示例的三种角色
3 张置顶便签对应三个场景角色:
| 置顶便签 | 角色 | 演示价值 |
|---|---|---|
| 周会要点 | 工作 | 置顶区展示工作重心 |
| 超市采购 | 生活 | 置顶区展示家庭待办 |
| 健身计划 | 健康 | 置顶区展示长期目标 |
三张连起来是「工作—生活—健康」的叙事线,一屏讲完普通人的三个重心——比 3 张同类便签更有代入感,也验证置顶区支持「多张异色并存」。
9.3 内容多样性盘点
12 条内容覆盖 8 种文体:会议纪要(周会要点)、清单罗列(超市采购)、分段计划(健身计划)、书摘语录(读书摘抄)、灵感记录(项目灵感)、票务信息(回家机票)、观影清单(电影清单)、提醒事项(生日提醒)……文体差异直接带来卡片高度差——购物清单两行、健身计划四行,双列瀑布墙的错落感由此而来。
十、注入后的便签墙效果预测
initSeedData在aboutToAppear首启时执行,注入后的第一屏可逐项预测:
| 观测点 | 预测结果 |
|---|---|
| 便签墙 | 12 张彩签双列排布,前 3 张带 📌 图钉 |
| 置顶区 | 米黄(周会)/ 粉(采购)/ 绿(健身)三色并排 |
| 时间显示 | 从「1 天前」到「11 天前」梯度,最新在前 |
| 粉色筛选 | 2 张:超市采购(置顶)、生日提醒 |
| 标题栏计数 | 「共 12 张便签」 |
筛选与排序的联动验证:点粉色圆点 → 置顶的「超市采购」仍排在筛选结果最前——证明颜色筛选没有破坏双键排序;点「全部」恢复 12 张。这个联动是「筛选收窄集合、排序保持顺序」两件事正交的正确性证明。
十一、验证 SQL 与自查清单
数据库直查(DevEco Studio 的 RDB 工具):
-- 1. 总数:应为 12SELECTCOUNT(*)FROMmemo;-- 2. 颜色分布:应返回 6 行,每行 COUNT = 2SELECTcolor,COUNT(*)AScntFROMmemoGROUPBYcolor;-- 3. 置顶数:应为 3SELECTCOUNT(*)FROMmemoWHEREpinned=1;-- 4. 排序正确性:前 3 行 pinned = 1,且 updated_time 递减SELECTtitle,color,pinned,updated_timeFROMmemoORDERBYpinnedDESC,updated_timeDESC;| 自查项 | 预期 | 失败排查 |
|---|---|---|
| 总数 | 12 | 幂等误判?表里已有旧数据 |
| 每色 2 张 | GROUP BY 每行 2 | 种子数组漏写颜色 |
| 置顶 3 张 | pinned = 1 共 3 行 | pinned 写成了字符串 ‘1’ |
| 前 3 行置顶 | 前 3 行 pinned = 1 | ORDER BY 漏了 pinned |
常见坑:pinned 传字符串'1'时 SQLite 隐式转换仍能命中,但语义混乱;更隐蔽的坑是 seed 里漏写updated_time导致全表同值,排序退化为本地顺序——时间戳梯度是排序可见性的前提。
十二、FAQ 补充
Q6:反复启动应用,种子会被重复插入吗?
A:不会——initSeedData第一步就是 COUNT 幂等检查,非空直接 return。只有清空表后再进页面才会重新填充,日志会分别打出「跳过」与「已填充」两种分支。
Q7:为什么不写死日期,而用 now - n * day?
A:写死日期(如 2025-06-01)会让「1 天前」的显示几天后失真——种子越来越旧。相对时间戳保证任何时候首启,墙上的时间梯度都像「刚贴上去的便利贴」。种子的时间要保鲜。
Q8:新增便签会排在置顶便签前面吗?
A:不会——新增便签 pinned = 0、updated_time 最新,它排在普通组最前,但置顶组(3 张)整体仍压在前面。双键排序下「置顶永远优先于时间」。
Q9:颜色筛选后置顶便签还在最前吗?
A:在——筛选是 WHERE color = ?,排序是 ORDER BY pinned DESC, updated_time DESC,筛选只收窄集合不改顺序。点粉色:置顶的「超市采购」第一、「生日提醒」第二。筛选与排序正交。