记录类页面除了保存,还需要认真处理"不该保存"和"不该误删"。心情日记在输入为空时阻止创建记录,删除旧记录则要求同一条目连续点击两次,避免一次误触直接移除内容。
心情和颜色通过函数映射。保存时只记录心情名称与最终颜色,列表渲染不再重复判断当前主题。
typescript
private moodColor(mood: string): string {
if (mood === '开心') { return '#F0A24F' }
if (mood === '平静') { return '#4E9B86' }
if (mood === '疲惫') { return '#6F7FC2' }
return '#8A6C91'
}

保存前对正文 trim()。只有空白字符时同样被拦截;成功记录放在顶部,随后清空输入框。
typescript
private save(): void {
const text: string = this.diaryText.trim()
if (text.length === 0) {
this.message = '写下一点内容再保存'
return
}
const entry: MoodEntry = {
id: Date.now(),
mood: this.selectedMood,
text,
date: '今天',
color: this.moodColor(this.selectedMood)
}
this.entries = [entry, ...this.entries]
this.diaryText = ''
this.message = '已经留在今天的日记里'
}
删除确认通过 pendingDeleteId 完成。第一次点击只保存目标ID并更改提示;第二次点击同一ID才过滤数组。点击另一条记录时,待确认目标会转移,不会误删上一条。
typescript
private requestDelete(id: number): void {
if (this.pendingDeleteId === id) {
this.entries = this.entries.filter(
(item: MoodEntry) => item.id !== id
)
this.pendingDeleteId = 0
this.message = '旧记录已删除'
} else {
this.pendingDeleteId = id
this.message = '再点一次删除,确认移除这条记录'
}
}
四种心情按钮共用一组数据,图标、文字色和背景色都根据 selectedMood 派生。保存按钮也使用当前心情颜色,选择变化后不需要额外同步主题状态。
typescript
ForEach(this.moods, (mood: string) => {
Column({ space: 5 }) {
Text(mood)
.fontColor(
this.selectedMood === mood
? Color.White
: this.moodColor(mood)
)
}
.backgroundColor(
this.selectedMood === mood
? this.moodColor(mood)
: '#EEF1F5'
)
.onClick(() => {
this.selectedMood = mood
})
})
当前二次删除确认没有超时,也没有在其他操作后清除。例如第一次点击删除后去保存新日记,原记录仍处于待确认状态。
更完整的交互应在选择心情、保存或经过一定时间后重置 pendingDeleteId,避免很久以后一次点击就直接删除。

当前记录只在内存中,二次确认也没有超时。正式保存个人内容时需要接入本地持久化,并评估加密与应用锁;在这些能力完成前,页面只能作为交互原型,不能宣称已经具备隐私保护。
typescript
private entryById(id: number): MoodEntry | undefined {
return this.entries.find((entry: MoodEntry) =>
entry.id === id
)
}
private requireEntry(id: number): MoodEntry {
const target: MoodEntry | undefined = this.entryById(id)
if (!target) {
throw new Error('Entry not found: ' + id)
}
return target
}
最近记录列表把待删除ID转换成按钮文案。只有目标条目显示"确认删除",其他记录仍显示普通"删除"。
typescript
Text(this.pendingDeleteId === item.id ? '确认删除' : '删除')
.fontColor(
this.pendingDeleteId === item.id ? '#C05052' : '#9AA2B0'
)
.onClick(() => {
this.requestDelete(item.id)
})
这个反馈位于被操作的卡片内部,比只在页面顶部显示提示更容易确认目标。若加入撤销条,应在真正删除后暂存被移除对象和原索引,超时后再清理,而不是立即丢失恢复信息。
情绪选择和正文输入组成一份草稿
页面用 selectedMood 保存当前情绪,content 保存文字。选择按钮只改变情绪,不提前创建日记;保存时统一校验并生成记录:
typescript
private moodColor(mood: string): string {
const target: MoodOption | undefined = this.moods.find(
(item: MoodOption) => item.name === mood
)
return target ? target.color : '#E8ECF4'
}
private save(): void {
const text: string = this.content.trim()
if (text.length === 0) {
this.message = '写下一点内容后再保存'
return
}
this.entries = [{
id: Date.now(),
mood: this.selectedMood,
content: text,
date: '今天'
}, ...this.entries]
this.content = ''
this.message = '日记已保存'
}
保存失败时不清空正文,用户可以继续编辑;成功后才清空草稿。使用头部插入让最新记录置顶。真实时间应保存时间戳,"今天"由展示层计算,这样第二天重新打开时旧记录会自动显示实际日期。
情绪颜色由配置数组统一映射,按钮、卡片标记和详情页可以共用同一来源。如果将颜色分别写死在多个组件中,新增情绪时容易遗漏某处。情绪名称是展示文字,持久化时更适合保存稳定枚举值,避免改文案后历史记录无法匹配颜色。
两次点击确认删除的状态边界
pendingDeleteId 指向等待确认的唯一卡片。第一次点击只进入确认态,第二次点击同一 ID 才删除;点击另一条会把确认目标切过去。这比全局"是否确认"布尔值更安全,因为布尔值无法说明待删除的是哪条记录。
typescript
private requestDelete(id: number): void {
if (this.pendingDeleteId !== id) {
this.pendingDeleteId = id
this.message = '再次点击确认删除'
return
}
this.entries = this.entries.filter((item: MoodEntry) => item.id !== id)
this.pendingDeleteId = -1
this.message = '记录已删除'
}
删除完成后必须复位待确认 ID。若列表支持搜索和排序,确认目标仍然依靠 ID,而不能保存可变索引。还可以在几秒后自动取消确认,定时器在组件销毁时清理;当前双击流程不依赖定时器,行为更直接。
撤销删除与确认删除可以同时存在:先确认目标,再把对象从数组移出并暂存在撤销槽,用户点击恢复时按原索引插回。只有撤销时限结束后才真正从持久化层删除。这样能防止误触,又不会让一次确认对话打断记录浏览。
长文本、隐私和持久化
正文增加后,列表只显示摘要,点击卡片再进入完整内容。摘要应在字符边界截取,不能简单按字节切割;标签、图片等富内容则需要独立数据结构。输入长度上限和剩余字数也应在编辑区即时展示,保存时再做最终校验。
typescript
private replaceEntry(
id: number,
updater: (source: MoodEntry) => MoodEntry
): void {
this.entries = this.entries.map((entry: MoodEntry) =>
entry.id === id ? updater(entry) : entry
)
}
private updateEntryMessage(id: number): void {
const target: MoodEntry | undefined = this.entryById(id)
this.message = target ? '状态已更新' : '目标记录不存在'
}
日期筛选、情绪统计都应从记录数组派生。统计某类情绪时按稳定枚举聚合,不能依赖颜色;颜色属于主题表现,用户更换配色不应改变历史统计结果。
保存前返回结构化校验结果
错误信息和有效草稿通过同一个函数产生:
typescript
interface MoodDraftResult {
valid: boolean
content: string
error: string
}
private validateDraft(): MoodDraftResult {
const content: string = this.content.trim()
if (content.length === 0) {
return { valid: false, content: '', error: '内容不能为空' }
}
if (content.length > 2000) {
return { valid: false, content, error: '内容超过 2000 字' }
}
return { valid: true, content, error: '' }
}
保存方法只接收通过校验的数据,失败时保留输入并把错误显示在编辑区附近。