**项目:**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 → 浮起 → 拖到目标位置松手
六、教训
- **不动不相关的代码。**admin 页面、OperationContext 改了又还原,浪费时间。
- **同一台机器上的同源 API 比跨域 Python 后端可靠。**不依赖端口发现、不考虑后端是否启动。
- Inline style 优先级最高。
style={{ height: 344 }}不会被任何框架或 CSS 类覆盖。 - **HTML5 原生拖拽足够简单。**不需要 framer-motion Reorder 组件,几行 ref + state 就搞定。

