心情日记的安全编辑流程:输入校验、删除确认与状态回收

记录类页面除了保存,还需要认真处理"不该保存"和"不该误删"。心情日记在输入为空时阻止创建记录,删除旧记录则要求同一条目连续点击两次,避免一次误触直接移除内容。

心情和颜色通过函数映射。保存时只记录心情名称与最终颜色,列表渲染不再重复判断当前主题。

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: '' }
}

保存方法只接收通过校验的数据,失败时保留输入并把错误显示在编辑区附近。

相关推荐
数字新视界5 小时前
一体化机柜在数据中心运维中的创新应用与优势分析
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
隔窗听雨眠6 小时前
KingbaseES平替全链路实践:从迁移评估到可持续运维
运维
天外飞雨道沧桑6 小时前
我在浏览器里练了一个 792 参数的大模型,它学会了补全 JavaScript
开发语言·javascript·ai·ecmascript·ai编程
hhhjhl6 小时前
Yup 跨端三方库仓颉适配全流程
java·开发语言
安教安全员通关宝6 小时前
2026年广东安全员C3证考试题库+答案+解析
安全
skywalk81636 小时前
登到192.168.1.5那台机器,看看今天白天,大约从早晨到晚上,都卡住了。看看因为什么卡住,为什么卡住而没有杀死那个导致卡死的进程,为什么
运维·系统·freebsd
运维行者_6 小时前
网络监控工具怎么选?从设备监控到智能运维的能力清单
运维·服务器·开发语言·网络·人工智能·支持向量机·php
2401_868534786 小时前
陈冲2026年上海纽约大学全英文毕业演讲
服务器·网络
“AI国潮设计-小江”7 小时前
【AIGC实战】Python+SDXL打造“潮汕英歌舞IP”万能立牌插件,批量生成“发财元宝蛋糕”视觉营销方案(附源码)
开发语言·人工智能·python·prompt·aigc
lhbweilai7 小时前
分析FreeRTOS内核对象API的源码实现(八)
c语言·开发语言·stm32·单片机