HarmonyOs应用《日记本》开发第17篇 - 日记编辑页面 DiaryEdit 详解

📅 2026/7/27 4:27:53 ✍️ 编辑团队 👁️ 阅读次数
HarmonyOs应用《日记本》开发第17篇 - 日记编辑页面 DiaryEdit 详解
本篇详细分析日记编辑页面的实现包括表单输入、日期选择、天气心情标签选择以及数据保存逻辑。一、页面功能概述DiaryEdit 页面是日记应用的核心交互页面承担了新建和编辑两种模式新建模式初始化空表单保存时创建新日记编辑模式加载已有日记数据保存时更新内容日期选择通过 DatePicker 选择日记日期天气/心情标签从预设选项中选择内容输入多行文本输入区域二、页面状态设计2.1 状态变量EntryComponentstruct DiaryEdit{Statediary:DiaryInfonewDiaryInfo()StateisEditMode:booleanfalseStateselectedDate:DatenewDate()StateshowDatePicker:booleanfalseStateshowDeleteConfirm:booleanfalseStatecontentInput:stringStateselectedWeather:WeatherWeather.SUNNYStateselectedMood:MoodMood.HAPPYprivatestore:DiaryStoreDiaryStore.getInstance()privateeditId:string}状态变量说明变量类型用途diaryDiaryInfo当前编辑的日记对象isEditModeboolean区分新建/编辑模式selectedDateDate选中的日期showDatePickerboolean控制日期选择器显示contentInputstring绑定文本输入框内容selectedWeatherWeather当前选中的天气selectedMoodMood当前选中的心情2.2 模式判断aboutToAppear(){constparamsrouter.getParams()asRecordstring,stringif(paramsparams.modeeditparams.id){this.isEditModetruethis.editIdparams.idthis.loadDiary(params.id)}else{this.isEditModefalsethis.diary.datethis.formatDate(newDate())this.selectedDatenewDate()}}逻辑分析从路由参数获取mode和id若mode edit进入编辑模式加载已有数据否则进入新建模式设置默认日期为今天三、页面布局结构3.1 整体布局┌──────────────────────────────┐ │ ← 返回 保存 │ 顶部导航栏 ├──────────────────────────────┤ │ 2026-07-24 ▼ │ 日期选择行 ├──────────────────────────────┤ │ 天气: ☀️ ☁️ ️ ⛅ ️ │ 天气标签 ├──────────────────────────────┤ │ 心情: │ 心情标签 ├──────────────────────────────┤ │ │ │ 今天发生了什么... │ 内容输入区 │ │ │ │ │ │ ├──────────────────────────────┤ │ [删除日记] │ 仅编辑模式显示 └──────────────────────────────┘3.2 顶部导航栏Row(){// 返回按钮Text(返回).fontSize(16).onClick((){router.back()})// 标题Text(this.isEditMode?编辑日记:新建日记).fontSize(18).fontWeight(FontWeight.Bold).layoutWeight(1).textAlign(TextAlign.Center)// 保存按钮Text(保存).fontSize(16).fontColor(#007DFF).onClick((){this.saveDiary()})}.width(100%).height(56).padding({left:16,right:16})四、日期选择功能4.1 日期显示行Row(){Image($r(app.media.calendar)).width(20).height(20)Text(this.formatDate(this.selectedDate)).fontSize(16).layoutWeight(1)Image($r(app.media.arrow_down)).width(16).height(16)}.padding(16).onClick((){this.showDatePickertrue})4.2 DatePicker 弹窗if(this.showDatePicker){DatePickerDialog.show({start:newDate(2020-01-01),end:newDate(),selected:this.selectedDate,onAccept:(value:DatePickerResult){this.selectedDatenewDate(value.year,value.month,value.day)this.diary.datethis.formatDate(this.selectedDate)this.showDatePickerfalse},onCancel:(){this.showDatePickerfalse}})}DatePickerDialog 参数说明参数说明start可选最早日期end可选最晚日期selected默认选中日期onAccept确认选择回调onCancel取消选择回调4.3 日期格式化privateformatDate(date:Date):string{constyeardate.getFullYear()constmonth(date.getMonth()1).toString().padStart(2,0)constdaydate.getDate().toString().padStart(2,0)return${year}-${month}-${day}}格式化要点月份需要 1getMonth()返回 0-11使用padStart(2, 0)保证两位数格式最终输出YYYY-MM-DD格式五、天气与心情选择5.1 天气标签组Text(天气).fontSize(14).fontColor(#666).margin({top:16,left:16})Flex({wrap:FlexWrap.Wrap}){ForEach(WeatherList,(item:WeatherItem,index:number){Text(item.label).fontSize(16).padding({left:16,right:16,top:8,bottom:8}).margin(4).borderRadius(20).backgroundColor(this.selectedWeatheritem.value?#007DFF:#F5F5F5).fontColor(this.selectedWeatheritem.value?#FFFFFF:#333333).onClick((){this.selectedWeatheritem.valuethis.diary.weatheritem.label})})}.padding({left:12,right:12})5.2 心情标签组Text(心情).fontSize(14).fontColor(#666).margin({top:16,left:16})Flex({wrap:FlexWrap.Wrap}){ForEach(MoodList,(item:MoodItem,index:number){Text(item.label).fontSize(16).padding({left:16,right:16,top:8,bottom:8}).margin(4).borderRadius(20).backgroundColor(this.selectedMooditem.value?#007DFF:#F5F5F5).fontColor(this.selectedMooditem.value?#FFFFFF:#333333).onClick((){this.selectedMooditem.valuethis.diary.mooditem.label})})}标签选择设计模式使用Flex组件实现自动换行布局选中状态通过背景色和文字颜色区分圆角胶囊形状符合现代标签设计风格点击即选中支持切换六、内容输入区TextArea({text:this.contentInput,placeholder:今天发生了什么...}).width(100%).height(200).fontSize(16).padding(16).borderRadius(12).backgroundColor(#FAFAFA).onChange((value:string){this.contentInputvaluethis.diary.contentvalue})TextArea 关键属性属性说明text输入框当前文本placeholder占位提示文字onChange文本变化回调height输入区域高度七、数据保存逻辑7.1 表单验证privatevalidateInput():boolean{if(this.contentInput.trim().length0){promptAction.showToast({message:请输入日记内容,duration:2000})returnfalse}returntrue}7.2 保存方法privateasyncsaveDiary(){if(!this.validateInput()){return}// 组装日记数据this.diary.contentthis.contentInputthis.diary.datethis.formatDate(this.selectedDate)try{if(this.isEditMode){this.diary.idthis.editIdawaitthis.store.updateDiary(this.diary)promptAction.showToast({message:保存成功,duration:1500})}else{this.diary.idthis.generateId()this.diary.createTimeDate.now()awaitthis.store.addDiary(this.diary)promptAction.showToast({message:创建成功,duration:1500})}router.back()}catch(error){promptAction.showToast({message:保存失败请重试,duration:2000})console.error(保存日记失败: error)}}保存流程用户点击保存 │ ▼ 表单验证 ──失败──→ 显示错误提示 │ 通过 │ ▼ 判断模式 │ ├──新建──→ 生成ID → 添加日记 → Toast提示 → 返回 │ └──编辑──→ 设置ID → 更新日记 → Toast提示 → 返回7.3 ID 生成privategenerateId():string{returnDate.now().toString()Math.random().toString(36).substr(2,9)}ID 生成策略时间戳保证全局递增性随机字符串防止同一毫秒创建的日记冲突最终格式示例1721845678901a3b7c2d八、删除功能仅在编辑模式下显示删除按钮if(this.isEditMode){Button(删除日记).width(90%).height(48).fontSize(16).fontColor(#FF3B30).backgroundColor(#FFFFFF).borderRadius(12).margin({top:24}).onClick((){this.showDeleteConfirmtrue})}删除确认弹窗将在后续章节详细讨论。九、加载已有数据privateasyncloadDiary(id:string){try{constdiaryawaitthis.store.getDiaryById(id)if(diary){this.diarydiarythis.contentInputdiary.contentthis.selectedDatethis.parseDate(diary.date)this.selectedWeatherthis.getWeatherByLabel(diary.weather)this.selectedMoodthis.getMoodByLabel(diary.mood)}}catch(error){console.error(加载日记失败: error)promptAction.showToast({message:加载失败,duration:2000})}}十、总结DiaryEdit 页面的设计要点双模式复用新建和编辑共用同一页面通过状态变量区分表单数据绑定State变量与 UI 组件双向同步标签选择器Flex 条件样式实现标签组异步保存async/await 处理 Preferences 存储用户反馈Toast 提示操作结果数据验证前端验证防止空内容提交这些模式是鸿蒙表单页面的典型实现适用于各类编辑场景。