- 移除 EventDetail.vue 未用的 computed 导入 - 移除 HeaderBar.vue 未用的 .btn-secondary CSS - store (updateEvent/splitEvent/toggleOccurrence): uid 缺失改抛错 (fail-fast), 不再静默 return - EventDetail onSplit: 捕获 splitEvent 抛错并提示用户 - DropZone/HeaderBar 解析失败: catch 内补 console.error - ical-io getZone: 时区未注册回退 UTC 时 console.warn - WeekPreview 周标签改用 i18n (weekPreview.rangeFmt), 与 MonthPreview 一致 - docs: 新增 error-handling.md; 修正 auto-save-design state 快照补 timezone - README: 补充部署说明 (经 zMainPage apache2 托管)
75 lines
3.0 KiB
Markdown
75 lines
3.0 KiB
Markdown
# 自动保存设计
|
||
|
||
本文档记录 `src/composables/useCalendar.js` 与
|
||
`src/components/HeaderBar.vue` 的保存模型重构。
|
||
|
||
## 背景
|
||
|
||
原设计有「dirty 标记 + 下载前确认弹窗 + 快照式 undo/redo(最多 50 步)」。
|
||
EventDetail 的表单已改为自动应用(`watch(form)` 即时写回 store,
|
||
commit `1c6ecf9`),dirty 仅用于「下载前提示未保存」与红点显示。撤销是
|
||
独立的历史栈。
|
||
|
||
## 重构:移除 dirty / undo
|
||
|
||
纯前端工具,无持久化需求,所有编辑本就即时生效(自动应用 watch)。
|
||
dirty 标记与确认弹窗是冗余的仪式感;undo 栈在自动应用场景下也意义
|
||
有限(每次 keystroke 都会 push 历史,栈迅速填满噪声)。移除以降低
|
||
复杂度、减少状态字段。
|
||
|
||
### 删除的死代码(`useCalendar.js`)
|
||
|
||
- `state.history`(快照栈)、`state.dirty`
|
||
- `MAX_HISTORY` 常量
|
||
- `snapshot()`、`pushHistory()`、`restore()`
|
||
- `undo()` action、`canUndo` computed
|
||
- `markSaved()` action
|
||
- 各 action(`organize`/`updateEvent`/`deleteEvent`/`addEvent`/
|
||
`splitEvent`/`toggleOccurrence`)内的 `pushHistory()` 与
|
||
`state.dirty = true` 调用
|
||
- `loadFile` 内的 `state.history = []`、`state.dirty = false`
|
||
- 不再使用的 `cloneEvent` 导入(snapshot 曾用)
|
||
|
||
### `HeaderBar.vue` 改动
|
||
|
||
- 移除「撤销」按钮
|
||
- 移除下载按钮后的红点 `●`(`<span class="dirty-dot">`)与
|
||
`.dirty-dot` 样式
|
||
- `onDownload()`:移除 `if (state.dirty && !confirm(...))` 确认弹窗与
|
||
`markSaved()` 调用;直接 `serialize()` 下载
|
||
|
||
## 自动保存语义
|
||
|
||
- **所有编辑即时生效**:`updateEvent`/`deleteEvent`/`addEvent`/
|
||
`splitEvent`/`toggleOccurrence` 直接 mutate 响应式 `state.events`,
|
||
无中间缓冲。EventDetail 的 `watch(form, { deep: true })` 把表单变化
|
||
立即 patch 回 store。
|
||
- **下载兜底**:`onDownload` 调 `serialize()` 序列化**当前最新状态**。
|
||
即便有任何未触发渲染的边界改动,下载时也会被序列化包含。文件名取
|
||
原文件名去 `.ics` 加 `.edited.ics`。
|
||
- **occurrences 不持久化**:EventModel 不存 occurrences,`OccurrenceGrid`
|
||
渲染时实时调 `expandOccurrences(ev)` 计算,`toggleOccurrence` 直接改
|
||
`exdates`。无缓存,无不同步风险。
|
||
|
||
## 状态字段(重构后)
|
||
|
||
```js
|
||
const state = reactive({
|
||
meta: null,
|
||
events: [],
|
||
selectedUid: null,
|
||
fileName: null,
|
||
viewMode: 'edit', // 'edit' | 'month' | 'week'
|
||
previewDate: todayISO(),
|
||
timezone: detectTimezone(), // 日历级默认时区 (新建事件 tzid / X-WR-TIMEZONE)
|
||
})
|
||
```
|
||
|
||
无 `history`、无 `dirty`、无 `redoStack`。store 仅承载当前状态与视图锚点。
|
||
|
||
## 取消确认弹窗的理由
|
||
|
||
原 `onDownload` 在 `dirty` 时弹「有未保存的更改,仍要下载?」。但自动
|
||
应用下,所有更改已在 store 中,下载即序列化 store,不存在「未保存」。
|
||
弹窗只会误导用户以为有数据丢失风险。移除后下载是一次性操作。
|