diff --git a/README.md b/README.md index 9d77b64..c622bd6 100644 --- a/README.md +++ b/README.md @@ -1,103 +1,146 @@ -# timeTable2 - 纯前端 ICS 日历整理器 +# timeTableFix - 纯前端 ICS 日历整理器 -浏览器内解析、去重、编辑 ICS 日历文件,全程无需 Python 或后端服务。拖入 `.ics` → 整理去重 → 编辑 → 下载。 +浏览器内解析、去重、编辑 ICS 日历文件,全程无需后端服务。拖入 `.ics` -> 整理去重 -> 编辑 -> 下载。 ## 功能 -- 📥 **拖入即用**:拖拽或选择 `.ics` 文件,浏览器内直接解析 -- 🔁 **智能去重**:自动检测重复模式,将 61 个扁平事件合并为 5 个 RRULE 重复事件 + 3 个独立事件 -- ✏️ **可视化编辑**:编辑标题/地点/时间/重复规则,点击网格切换排除日期 -- ✂️ **拆分事件**:把一个重复事件按日期拆成前后两段(如后半学期换教室) -- ➕ **增删事件**:新增单次/重复事件,删除任意事件 -- ↶ **撤销重做**:快照式 undo/redo,最多 50 步 -- ⬇ **导出 ICS**:编辑完成下载整理后的 `.ics`,可直接导入日历应用 - -## 快速开始 - -```bash -# 安装依赖(使用 npmmirror 镜像加速,已配在 .npmrc) -npm install - -# 启动开发服务器 -npm run dev - -# 构建生产版本 -npm run build - -# 运行单元测试 -npm test -``` - -浏览器打开 `http://localhost:5173`,拖入 `.ics` 文件即可。 - -## 使用流程 - -1. **导入**:拖入 `.ics` 文件(或点击选择),左侧显示所有原始事件 -2. **去重**:点击顶栏「🔁 整理去重」,自动合并重复事件为 RRULE + EXDATE -3. **编辑**:点击左侧事件,右侧展开编辑表单 - - 修改标题/地点/描述/时间 - - 调整重复规则(频率/间隔/星期/重复到) - - 点击 occurrence 网格切换排除日期(绿色=包含,红色删除线=排除) -4. **拆分**(可选):点击「✂ 拆分此日程」,选日期,把重复事件拆成前后两段 -5. **新增/删除**:点「➕ 新增日程」或「删除此日程」 -6. **撤销**:「↶ 撤销」/「↷ 重做」最多 50 步 -7. **下载**:点「⬇ 下载 ICS」,导出整理后的 `.ics`(有未保存更改时会提示确认) +- 📥 拖入即用:拖拽或选择 `.ics` 文件,浏览器内直接解析 +- 🔁 智能去重:自动检测重复模式,将扁平事件合并为 RRULE 重复事件 + EXDATE +- ✏️ 可视化编辑:编辑标题/地点/时间/重复规则,点击网格切换排除日期 +- ✂️ 拆分事件:把一个重复事件按日期拆成前后两段 +- ➕ 增删事件:新增单次/重复事件,删除任意事件 +- ↶ 撤销重做:快照式 undo/redo,最多 50 步 +- ⬇️ 导出 ICS:下载整理后的 `.ics`,可直接导入日历应用 ## 项目结构 ``` -timeTable2/ -├── index.html Vite 入口 +timeTableFix/ +├── index.html Vite 入口 ├── package.json -├── .npmrc npmmirror 镜像加速 -├── vite.config.js +├── vite.config.js 构建配置(含 preview.allowedHosts) ├── vitest.config.js +├── .npmrc npmmirror 镜像加速 ├── src/ -│ ├── main.js 应用挂载 -│ ├── App.vue 布局装配 +│ ├── main.js 应用挂载 +│ ├── App.vue 布局装配 │ ├── composables/ -│ │ └── useCalendar.js 中心 store + 快照 undo/redo +│ │ └── useCalendar.js 中心 store + 快照 undo/redo │ ├── lib/ -│ │ ├── weekday.js 星期常量与工具(统一 getDay 索引) -│ │ ├── ical-io.js ICS 文本 <-> plain model(ical.js 封装) -│ │ ├── recur.js RRULE 网格展开 -│ │ └── organize.js GCD 去重算法(移植自 Python 版) +│ │ ├── weekday.js 星期常量与工具 +│ │ ├── date.js 日期工具 +│ │ ├── ical-io.js ICS 文本 <-> plain model +│ │ ├── recur.js RRULE 网格展开 +│ │ ├── organize.js GCD 去重算法 +│ │ ├── event-factory.js 事件构造工厂 +│ │ └── split.js 事件拆分逻辑 │ └── components/ -│ ├── HeaderBar.vue 顶栏:整理/撤销/重做/导入/下载 -│ ├── DropZone.vue 拖拽/选择文件 -│ ├── EventList.vue 侧栏事件列表 -│ ├── EventDetail.vue 编辑表单 + 拆分 -│ └── OccurrenceGrid.vue 排除日期网格 -└── test/ Vitest 单元测试(34 个) - ├── golden-org.ics 测试用例(61 事件的真实课表) - ├── weekday.test.js - ├── ical-io.test.js - ├── recur.test.js - └── organize.test.js +│ ├── HeaderBar.vue 顶栏:整理/撤销/重做/导入/下载 +│ ├── DropZone.vue 拖拽/选择文件 +│ ├── EventList.vue 侧栏事件列表 +│ ├── EventDetail.vue 编辑表单 + 拆分 +│ └── OccurrenceGrid.vue 排除日期网格 +├── test/ Vitest 单元测试 +│ ├── golden-org.ics 测试用例(61 事件的真实课表) +│ ├── weekday.test.js +│ ├── ical-io.test.js +│ ├── recur.test.js +│ └── organize.test.js +└── docs/ 设计文档 ``` -### 三个核心 lib 模块 +## Ubuntu 从零安装 -| 文件 | 作用 | -|------|------| -| `ical-io.js` | ICS 文本解析为普通对象、序列化回 ICS 文本(ical.js 封装) | -| `organize.js` | GCD 间隔检测 + 系列分组 + EXDATE 生成(移植自 `ical_organizer.py`) | -| `recur.js` | 从 RRULE + EXDATE 按需展开 occurrence 网格 | +### 1. 安装 Node.js 18+ -## 算法 +```bash +curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - +sudo apt-get install -y nodejs +node --version # 确认 v18.x +``` -去重算法从 `timeTable/ical_organizer.py` 1:1 移植: +### 2. 克隆并安装依赖 -1. 按 (标题 + 地点 + 开始时间 + 时长 + 星期) 将事件分组 -2. 计算组内相邻日期间隔的 **GCD**,识别: - - GCD = 7 → `FREQ=WEEKLY`(每周,含 `BYDAY`) - - GCD = 1 → `FREQ=DAILY`(每天) - - GCD = 14 → `FREQ=WEEKLY;INTERVAL=2`(每两周) -3. 构建期望网格,网格上缺失的日期写入 `EXDATE`(如假期) -4. 不符合规律的组(跳过次数 ≥ 事件数)保留为独立事件 -5. 合并后保留首个事件的原始 UID(避免重新导入冲突) +```bash +git clone https://git.zikai.wang/zikai/timeTableFix.git +cd timeTableFix +npm install # 已配 npmmirror 镜像加速 +``` -**验证**:对 `org.ics`(61 事件)整理后输出 8 个事件(5 重复 + 3 独立),与 Python 版 `ical_organizer.py --verify` 结果一致。下载的 ICS 用 Python `recurring-ical-events` 展开,61 个 occurrence 与原始文件完全匹配。 +### 3. 构建生产版本 + +```bash +npm run build # 产物输出到 dist/ +npm test # (可选)运行单元测试 +``` + +### 4. 部署为系统服务(systemd) + +创建服务文件 `/etc/systemd/system/timetable-fix.service`: + +```ini +[Unit] +Description=timeTableFix ICS Calendar Organizer +After=network.target + +[Service] +Type=simple +WorkingDirectory=/path/to/timeTableFix +ExecStart=/path/to/timeTableFix/node_modules/.bin/vite preview --host 0.0.0.0 --port 8888 +Restart=on-failure +RestartSec=3 +Environment=NODE_ENV=production +StandardOutput=append:/var/log/timetable-preview.log +StandardError=append:/var/log/timetable-preview.log + +[Install] +WantedBy=multi-user.target +``` + +> 将上述 `WorkingDirectory` 和 `ExecStart` 中的路径替换为实际的 `timeTableFix` 绝对路径。 + +加载并启动: + +```bash +sudo systemctl daemon-reload +sudo systemctl enable --now timetable-fix +``` + +### 5. 放行防火墙端口 + +```bash +sudo ufw allow 8888/tcp +``` + +浏览器访问 `http://<服务器IP>:8888` 即可。若通过域名访问(如 `f.zikai.wang`),需在 `vite.config.js` 的 `preview.allowedHosts` 中添加该域名。 + +## 使用(启动 / 关闭) + +服务由 systemd 管理,开机自启、崩溃自动恢复。 + +```bash +# 启动 +sudo systemctl start timetable-fix + +# 关闭 +sudo systemctl stop timetable-fix + +# 重启 +sudo systemctl restart timetable-fix + +# 查看状态 +sudo systemctl status timetable-fix + +# 查看日志 +tail -f /var/log/timetable-preview.log +``` + +开发模式(无需 systemd,前台运行): + +```bash +cd timeTableFix +npm run dev # http://localhost:5173 +``` ## 技术栈 @@ -105,21 +148,7 @@ timeTable2/ |----|------| | 框架 | Vue 3.5(SFC,`