Avatar

CaiCai

INITIALIZING SYSTEM

封面

新增功能:记事本任务管理

写作时间:2026-07-13 10:22:11
# 新增功能
👁 次阅读

**项目:**XinghuisamaBlogs(个人博客 + 管理面板)
**功能:**左下角悬浮工具箱中的记事本(任务清单),支持增删改、完成切换、时间戳、长按拖拽排序
**环境:**测试环境 + 正式环境,两端同步

一、涉及文件

项目文件作用管理端components/toolbox/NotepadTool.tsx可编辑记事本(增删改拖)管理端app/api/toolbox/route.tsNext.js API(POST 写 / GET 读)管理端components/GlobalToolbox.tsx工具箱面板(高度修复)管理端components/toolbox/CalculatorTool.tsx计算器(高度适配)管理端cms_core/api/sync.py同步脚本(加 toolbox 目录)博客端components/toolbox/NotepadTool.tsx只读展示记事本博客端app/api/toolbox/route.ts只读 API博客端components/GlobalToolbox.tsx高度修复博客端components/toolbox/CalculatorTool.tsx高度适配

二、数据流

管理面板操作
  ├→ localStorage(即时)
  └→ POST /api/toolbox → data_storage/toolbox/tasks.json(磁盘)

点同步(设置页 → 项目仓库)
  └→ Python sync.py → 复制 toolbox/ → 博客 data_storage/toolbox/tasks.json

博客端展示
  └→ GET /api/toolbox → 读取 tasks.json → 渲染

三、核心数据结构

interface Task {
  id: string;           // 唯一ID
  text: string;         // 任务文字
  done: boolean;        // 是否完成
  createdAt: string;    // 创建时间 ISO
  completedAt?: string; // 完成时间 ISO(取消完成时清空)
}

四、关键技术点

1. 高度一致性

坑:AnimatePresence mode="wait" 切换工具时先卸载旧 DOM 再挂载新 DOM,中间真空导致面板高度塌缩。

**解:**外层用 style={{ height: 344 }} inline style 锁定高度,内部用简单条件渲染,彻底弃用 AnimatePresence 做工具切换。

2. 保存机制

**坑:**Python 后端 /api/toolbox/tasks 返回 404(路由未注册),保存失败。

**解:**改用 Next.js 同源 API(/api/toolbox),不跨域、不需要端口发现。

3. 数据防丢

**坑:**启动时从 API 读取磁盘数据,setTasks(data) 无条件覆盖 localStorage,可能用旧数据覆盖新数据。

**解:**仅当 localStorage 为空时才从磁盘恢复,否则保持 localStorage 并反向同步到磁盘。

4. JSON 损坏保护

API 读取时加 Array.isArray 校验,损坏文件自动重置为空数组,不再静默吞错。

5. 长按拖拽排序

用 HTML5 原生 Drag & Drop:

onPointerDown → 500ms timer → dragEnabled = true
onDragStart → 检查 dragEnabled,不满足则 e.preventDefault()
onDragOver  → 实时 setTasks 重排数组 → framer-motion 自动避让动画
onDragEnd   → 保存最终顺序

防误触:longPressFired 标记长按已触发,toggleTask 检查后阻止短按切换。

五、操作一览

操作方式新增底部输入框 + 回车 / ➕按钮完成/取消点击文字 或 点击左侧圆圈编辑文字双击文字 → 输入框 → 回车保存 / Esc 取消删除点击右侧红色圆形按钮拖拽排序长按 500ms → 浮起 → 拖到目标位置松手

六、教训

  1. **不动不相关的代码。**admin 页面、OperationContext 改了又还原,浪费时间。
  2. **同一台机器上的同源 API 比跨域 Python 后端可靠。**不依赖端口发现、不考虑后端是否启动。
  3. Inline style 优先级最高。style={{ height: 344 }} 不会被任何框架或 CSS 类覆盖。
  4. **HTML5 原生拖拽足够简单。**不需要 framer-motion Reorder 组件,几行 ref + state 就搞定。