HarmonyOS知识库——笔记编辑器的双模式与标签管理
知识库 App 的 NoteEditor 承担了新建和编辑两种完全不同的职责——新建时所有字段为空编辑时需要预填已有数据。这种一个页面两种模式的设计在工具类 App 里很常见但要做到两种模式切换流畅、数据不串需要在状态初始化和保存逻辑上下功夫。完整效果一、八个 State 的职责划分Statenote:Note|undefinedundefined// 原始笔记数据编辑模式StateisNew:booleantrue// 新建/编辑模式标记Statetitle:string// 标题输入Statecontent:string// 内容输入StateselectedNb:number1// 选中的笔记本 idStatefav:booleanfalse// 收藏状态StatetagInput:string// 标签输入框内容Statetags:string[][]// 已添加的标签列表八个 State 可以分为三组分组变量作用模式控制note, isNew区分新建和编辑内容输入title, content, selectedNb, tags笔记的四个核心字段交互状态fav, tagInput收藏切换和标签输入note存原始数据、title/content存编辑副本——为什么要分开因为编辑过程中用户可能修改了但没保存。如果直接修改note.title用户取消编辑后原始数据也被改了。用独立的title/content存编辑副本只有点保存才写回note。踩坑记录最初直接修改this.note.title v用户改了标题但点返回没保存首页列表里标题却变了——因为note是对象引用直接修改会影响原始数据。改成独立的title/content变量后解决。二、aboutToAppear 的模式判断aboutToAppear():void{constprouter.getParams()asRecordstring,Objectif(pp[noteId]){this.notegetNoteById(p[noteId]asnumber)if(this.note){this.isNewfalsethis.titlethis.note.titlethis.contentthis.note.contentthis.selectedNbthis.note.notebookIdthis.favthis.note.favoritedconstt:string[][]for(leti:number0;ithis.note.tags.length;i){t.push(this.note.tags[i])}this.tagst}}}有没有noteId参数决定了是新建还是编辑。从首页点新建时不传 noteId从笔记列表点某篇笔记进来时传 noteId。编辑模式需要复制 tags 数组。this.note.tags是原始数据的引用如果直接赋值this.tags this.note.tags用户在编辑器里添加/删除标签会直接影响原始数据。用 for 循环创建副本保证原始数据安全。为什么 tags 要复制而 title/content 不需要因为 tags 是数组——数组的引用复制会让两个变量指向同一内存修改任何一个都会影响另一个。title/content 是基本类型string赋值时直接复制值不存在引用问题。两种模式的初始化对比字段新建模式编辑模式isNewtruefalsetitle‘’空note.title预填content‘’空note.content预填selectedNb1默认第一个笔记本note.notebookId原笔记本favfalsenote.favorited原状态tags[]空数组note.tags 的副本导航栏标题“新建笔记”“编辑笔记”收藏/删除按钮不显示显示三、笔记本选择器Chip 样式的实现Row(){ForEach(NOTEBOOKS,(nb:Notebook){Text(nb.icon nb.name).fontSize(12).fontWeight(this.selectedNbnb.id?FontWeight.Bold:FontWeight.Regular).fontColor(this.selectedNbnb.id?#FFFFFF:T2).padding({left:12,right:12,top:6,bottom:6}).borderRadius(14).backgroundColor(this.selectedNbnb.id?nb.color:#F5F5FA).margin({right:6}).onClick((){this.selectedNbnb.id})})}选中和未选中的双重样式切换状态背景色文字颜色字重选中笔记本对应颜色如 #FF6B6B白色Bold未选中#F5F5FA浅灰T2灰色Regular选中状态用笔记本自己的颜色——让用户一眼知道这篇笔记属于哪个笔记本。4 个笔记本各有不同颜色红/紫/绿/橙选中时背景变成对应颜色视觉上非常直观。margin({ right: 6 })保证最后一个笔记本右侧也有间距。如果用Row({ space: 6 })最后一个元素右侧没有间距。手动加 margin 更可控。踩坑记录最初笔记本选择器没有fontWeight区分选中和未选中只有颜色差异。在小屏手机上颜色差异不明显用户反馈不知道选了哪个。加了 Bold 字重后视觉区分更清晰。四、标题和内容的输入设计标题单行 TextInputTextInput({placeholder:笔记标题,text:this.title}).fontSize(20).fontWeight(FontWeight.Bold).backgroundColor(transparent).width(100%).padding({left:16,right:16}).onChange((v:string){this.titlev})标题用 TextInput单行输入——因为标题通常只有一行。fontSize 20 Bold 让标题在视觉上最突出。backgroundColor(transparent)让输入框融入白色背景用户感知不到这是一个输入框只看到文字。内容全高 TextAreaTextArea({placeholder:开始记录你的想法...,text:this.content}).fontSize(15).backgroundColor(transparent).width(100%).padding({left:16,right:16}).layoutWeight(1).onChange((v:string){this.contentv})内容用 TextArea多行输入——因为笔记内容可能很长。layoutWeight(1)让 TextArea 占据标题和标签之间的所有剩余空间用户有最大的输入区域。TextArea 和 TextInput 的选择标准组件行数适用场景TextInput单行标题、搜索框、标签输入TextArea多行笔记内容、长文本描述分隔线用 Divider 而不是 Border。Divider().strokeWidth(0.5).color(#F0EEF4)在标题和内容之间画一条细线。比用 border-bottom 更简洁——不需要计算 padding 和宽度。五、标签管理添加与删除标签展示区Row({space:6}){ForEach(this.tags,(t:string,idx:number){Row(){Text(#t).fontSize(11).fontColor(A)Text( ×).fontSize(11).fontColor(T3)}.padding({left:8,right:8,top:3,bottom:3}).backgroundColor(A12).borderRadius(10).onClick((){this.removeTag(idx)})})}标签用#前缀——像社交媒体的 hashtag。紫色文字 浅紫背景和主色保持一致。点击整个标签行触发删除——不需要单独的删除按钮。idx是 ForEach 的第二个参数——当前项的索引。removeTag(idx)按索引删除不需要查找标签内容。标签输入区Row(){TextInput({placeholder:添加标签...,text:this.tagInput}).layoutWeight(1).height(32).fontSize(13).backgroundColor(#F5F5FA).borderRadius(16).padding({left:12}).onChange((v:string){this.tagInputv}).onSubmit((){this.addTag()})Text(添加).fontSize(12).fontWeight(FontWeight.Bold).fontColor(A).margin({left:8}).onClick((){this.addTag()})}两种添加方式点添加按钮或者按键盘确认键。onSubmit和onClick都调addTag()用户习惯哪种操作都能添加。踩坑记录最初只写了onClick没写onSubmit用户在输入标签后习惯按回车确认但按回车没反应。加了onSubmit后体验好很多。addTag 的防御性检查privateaddTag():void{if(this.tagInput.trim().length0)returnconstt:string[][]for(leti:number0;ithis.tags.length;i){t.push(this.tags[i])}t.push(this.tagInput.trim())this.tagstthis.tagInput}三个关键步骤空值检查—trim().length 0防止添加空白标签复制数组— 创建新数组t避免直接修改this.tags引用清空输入—this.tagInput 让输入框恢复空白为什么 addTag 和 removeTag 都要复制数组因为 State 要求替换引用才能触发刷新。直接this.tags.push(...)修改原数组不触发刷新。removeTag 的实现privateremoveTag(idx:number):void{constt:string[][]for(leti:number0;ithis.tags.length;i){if(i!idx)t.push(this.tags[i])}this.tagst}按索引过滤——遍历 tags跳过指定索引创建新数组。和菜谱 App 的 removeFromShop 逻辑完全一致。六、保存逻辑新建与编辑的分支处理privatesave():void{if(this.title.trim().length0)returnif(this.isNew){addNote(this.title.trim(),this.content,this.selectedNb,this.tags)}elseif(this.note){this.note.titlethis.title.trim()this.note.contentthis.contentthis.note.notebookIdthis.selectedNbthis.note.tagsthis.tagsthis.note.updatedAt2026-07-15}router.back()}新建和编辑走不同的保存路径模式保存方式数据变化新建addNote()创建新对象插入 _notes 数组新增一条笔记noteCount1编辑直接修改this.note的属性更新已有笔记updatedAt 改为当天新建用 addNote 函数数据层操作编辑直接修改对象引用操作。为什么编辑不用函数因为编辑是修改已有对象的属性——this.note是 _notes 数组里某个元素的引用修改它就等于修改了 _notes 里的数据。this.title.trim().length 0检查——标题不能为空。如果用户没输入标题就点保存save 直接 return不执行任何操作。内容可以为空用户可能只想记一个标题但标题不能为空。踩坑记录最初没检查标题为空用户保存了一篇标题为空的笔记首页列表里显示一个空标题卡片。加了检查后空标题保存时静默失败——没有错误提示但数据不会保存。更好的做法是弹一个 Toast 提示标题不能为空。七、删除与收藏的交互// 导航栏编辑模式显示收藏和删除按钮if(!this.isNew){Text(this.fav?❤️:).fontSize(20).onClick((){this.toggleFav()}).margin({right:8})Text().fontSize(18).onClick((){if(this.note){deleteNote(this.note.id);router.back()}}).margin({right:8})}// 收藏切换privatetoggleFav():void{if(this.note){this.favtoggleFav(this.note.id)}}新建模式不显示收藏和删除按钮——因为还没保存没有笔记可以收藏或删除。if (!this.isNew)控制按钮的显隐。删除后直接router.back()。删除完不需要等用户确认——因为这是一个轻量级的知识库 App不是重要数据管理工具。真实 App 应该加确认弹窗。收藏切换的返回值设计。toggleFav返回 boolean直接赋值this.fav toggleFav(...)。和菜谱 App 的收藏切换逻辑一致。八、整体嵌套结构Column 页面容器 ├→ Row (导航栏) 返回 标题 操作按钮 └→ Column (白色内容区) 白色背景包裹所有输入区 ├→ Row (笔记本选择器) 横向排列 4 个 Chip ├→ TextInput (标题) 单行输入20px 粗体 ├→ Divider 分隔线 ├→ TextArea (内容) 多行输入layoutWeight(1) 全高 └→ Column (标签区) 白色背景 顶部边框 ├→ Row (已有标签) 标签 Chip点击删除 └→ Row (标签输入) TextInput 添加按钮标签区为什么在底部而不是标题旁边因为标签数量不确定——0 个标签时不占空间5 个标签时占较多空间。放在底部避免了标签区的高度变化影响标题和内容的布局稳定性。标签区的白色背景 顶部边框。backgroundColor(#FFFFFF).border({ width: { top: 0.5 }, color: #F0EEF4 })让标签区和上方的 TextArea 视觉上分开——TextArea 在白色背景上标签区也在白色背景上但标签区多了一条顶部细线作为分隔。九、数据流向图用户输入 title/content/tags ↓ State 变量接收 ↓ 点击保存 ↓ ┌──────┴──────┐ 新建模式 编辑模式 addNote() this.note.xxx xxx (插入新对象) (修改引用属性) ↓ ↓ _notes 数组变化 ↓ router.back() ↓ 首页 onPageShow → refresh() ↓ 列表更新显示新笔记新建和编辑的数据流差异新建走 addNote → 修改 _notes → 返回首页 → refresh → 列表更新。编辑直接修改 note 引用 → 返回首页 → 列表显示修改后的内容。两种方式最终都能让首页看到最新数据。

相关新闻

DRA75x时钟系统硬件设计与软件配置实战指南

DRA75x时钟系统硬件设计与软件配置实战指南

1. 项目概述与核心价值时钟系统,对于任何一位嵌入式硬件工程师而言,都是设计工作的起点和基石。它就像一座城市的心脏和脉搏,为芯片内部数以亿计的晶体管提供精准的节拍,确保CPU、内存、总线、外设等各个“城区”能够协调一致地高…

2026/7/15 18:09:51阅读更多 →
如何在5分钟内上手uilang?从引入到实现第一个交互效果的完整指南

如何在5分钟内上手uilang?从引入到实现第一个交互效果的完整指南

如何在5分钟内上手uilang?从引入到实现第一个交互效果的完整指南 【免费下载链接】uilang A minimal, UI-focused programming language for web designers. 项目地址: https://gitcode.com/gh_mirrors/ui/uilang 想要快速为网页添加交互效果却不想学习复杂的…

2026/7/15 18:04:50阅读更多 →
Alpha 主题系统完全指南:为你的iOS调试工具创建个性化界面的方法

Alpha 主题系统完全指南:为你的iOS调试工具创建个性化界面的方法

Alpha 主题系统完全指南:为你的iOS调试工具创建个性化界面的方法 【免费下载链接】Alpha Next generation debugging framework for iOS 项目地址: https://gitcode.com/gh_mirrors/alp/Alpha Alpha是一款功能强大的iOS下一代调试框架,它提供了一…

2026/7/15 18:04:50阅读更多 →
【法律人AI提效秘籍】:3步用ChatGPT自动提取判例核心要件,准确率提升92%(实测司法文书库)

【法律人AI提效秘籍】:3步用ChatGPT自动提取判例核心要件,准确率提升92%(实测司法文书库)

更多请点击: https://codechina.net 第一章:ChatGPT整理判例要点的底层逻辑与司法语义适配原理 ChatGPT在判例分析任务中并非简单地执行关键词匹配或模板填充,而是依托三层协同机制实现法律文本的深度理解:领域感知的预训练语义空…

2026/7/15 21:55:20阅读更多 →
需求失焦=成本暴增!用ChatGPT做需求提炼前,必须完成这7项前置可信度校验

需求失焦=成本暴增!用ChatGPT做需求提炼前,必须完成这7项前置可信度校验

更多请点击: https://intelliparadigm.com 第一章:需求失焦的本质与成本失控的底层逻辑 需求失焦并非源于沟通不畅或文档缺失,而是系统性反馈机制失效的结果——当产品演进脱离真实用户行为数据,仅依赖主观假设迭代时&#xff0c…

2026/7/15 21:55:20阅读更多 →
Cesium OGC- wms服务教程

Cesium OGC- wms服务教程

OGC- wms服务 OGC- wms服务 ▶ 在线运行案例 案例合集: 三维可视化功能案例(threehub.cn)开源仓库github地址: https://github.com/z2586300277/three-cesium-examples400个案例代码: 网盘链接 你将学到什么 Scene / Camera…

2026/7/15 21:55:20阅读更多 →
C++/OpenCV 配置进阶:从“无法打开源文件”到正确引入opencv_contrib模块

C++/OpenCV 配置进阶:从“无法打开源文件”到正确引入opencv_contrib模块

1. 理解OpenCV核心库与contrib模块的关系第一次接触OpenCV时&#xff0c;很多人都会疑惑为什么有些功能能用而有些功能会报错。比如当你满怀信心地写下#include <opencv2/tracking.hpp>准备实现目标跟踪功能时&#xff0c;编译器却无情地抛出"无法打开源文件"的…

2026/7/15 21:55:20阅读更多 →
【数据挖掘实战】从购物篮到推荐系统:Apriori与FP-Growth算法Python全解析

【数据挖掘实战】从购物篮到推荐系统:Apriori与FP-Growth算法Python全解析

1. 从"啤酒与尿布"说起&#xff1a;频繁模式挖掘的商业价值 1990年代&#xff0c;沃尔玛的工程师在分析销售数据时发现一个有趣现象&#xff1a;啤酒和尿布经常出现在同一张购物小票上。经过调查发现&#xff0c;年轻父亲们常在购买婴儿用品时顺手带走几罐啤酒。这个…

2026/7/15 21:55:20阅读更多 →
遗传算法实战:N皇后问题的Python工程实现与调优

遗传算法实战:N皇后问题的Python工程实现与调优

1. 这不是教科书&#xff0c;而是一次真实的GA工程复盘&#xff1a;从Matlab到Python的N皇后实战手记你点开这篇文章&#xff0c;大概率不是为了背诵“遗传算法是模拟生物进化过程的优化方法”这种定义。你真正想搞懂的是&#xff1a;当一个真实问题——比如100个皇后怎么在100…

2026/7/15 21:50:20阅读更多 →
VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比&#xff1a;全局安装 vs 项目本地安装的4个关键差异当你在VSCode中启动一个新的TypeScript项目时&#xff0c;第一个技术决策往往从安装方式开始。这个看似简单的选择——全局安装还是项目本地安装——实际上会深刻影响你的开发流程、团队协作和…

2026/7/15 6:42:19阅读更多 →
智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件&#xff1a;5分钟实现自动化学习的智能助手 【免费下载链接】zhihuishu 智慧树刷课插件&#xff0c;自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 智慧树刷课插件是一款专为智慧树在线教育平台设计的Chrome浏…

2026/7/15 6:12:45阅读更多 →
Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL&#xff1a;跨平台游戏模组获取的终极解决方案 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 你是否在GOG或Epic Games Store购买了心仪的游戏…

2026/7/15 10:54:00阅读更多 →
AI框架决定企业AI能走多远

AI框架决定企业AI能走多远

企业AI建设的第一性原理 企业搞AI&#xff0c;最关键的决定是什么&#xff1f;不是选哪家大模型&#xff0c;不是先做哪个场景&#xff0c;不是招多少AI人才——而是选哪个AI开发框架。 为什么&#xff1f;因为框架决定了企业AI能力的"天花板"。选对了框架&#xff0…

2026/7/15 0:01:30阅读更多 →
Java企业为什么需要AI框架

Java企业为什么需要AI框架

Java企业在AI时代的尴尬处境 Java是全球企业级应用开发的主流语言——全球超过一半的企业系统跑在Java上。但在AI浪潮面前&#xff0c;很多Java企业感到尴尬&#xff1a;大模型的接口是各种语言的&#xff0c;AI开发社区以其他语言为主流&#xff0c;似乎Java在AI时代"掉队…

2026/7/15 0:01:30阅读更多 →
CC3230x嵌入式开发实战:SD主机、定时器与低功耗模式深度解析

CC3230x嵌入式开发实战:SD主机、定时器与低功耗模式深度解析

1. 项目概述&#xff1a;为什么需要关注CC3230x的SD主机、定时器与低功耗&#xff1f;在物联网和嵌入式设备开发领域&#xff0c;我们常常面临一个核心矛盾&#xff1a;设备需要具备强大的连接能力、可靠的数据存储和实时控制功能&#xff0c;同时又必须严格控制功耗以延长电池…

2026/7/15 0:01:30阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时&#xff0c;发现推理速度只有可怜的 1-2 FPS&#xff0c;而别人的演示视频却能跑到 30 FPS 以上&#xff0c;那么问题很可能不在模型本身&#xff0c;而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后&#xff0c;会直接使用官方示例…

2026/7/15 15:50:47阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一&#xff1a;为什么你需要了解 Coze 和 Dify&#xff1f;如果你对 AI 应用开发感兴趣&#xff0c;但一看到“大模型”、“智能体”、“工作流”这些词就头疼&#xff0c;觉得门槛太高&#xff0c;那这篇文章就是为你准备的。很多开发者&#xff0c;包括我自己&#…

2026/7/15 8:52:38阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会&#xff1a;配图一直是个让人头疼的问题。2026年&#xff0c;AI生图工具已经非常成熟了&#xff0c;但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1&#xff1a;速度之王2026年6月11日&#xff0c…

2026/7/15 14:06:23阅读更多 →