diff --git a/.workbuddy/memory/2026-04-24.md b/.workbuddy/memory/2026-04-24.md deleted file mode 100644 index 6b41e0c..0000000 --- a/.workbuddy/memory/2026-04-24.md +++ /dev/null @@ -1,156 +0,0 @@ -# 2026-04-24 日志 - -## 项目梳理 - -- 完整梳理了 ops2 项目的运行逻辑 -- 涉及:后端 Go + Gin 架构、Web 前端 Vue 3 + Vite、移动端 uni-app 基础框架 - -## 核心模块梳理 - -### 用户认证 (apiUsers.go) -- Cookie 认证机制,密码加盐哈希 -- 各模块独立管理员组(purchase_admin / work_order_admin / schedule_admin / warehouse_admin) -- 默认创建 admin 用户 - -### 文件管理 (apiFiles.go) -- SHA256 哈希去重存储(同一文件只存一份) -- 支持图片/视频/音频/PDF 等类型 -- 头像单独存储 - -### 日程排班 (apiSchedule.go) -- 日程表(软删除)+ 操作日志 -- 日期范围查询 - -### 仓库模块 (apiWarehouse.go) -- 容器(树形,最多5层嵌套)+ 物品管理 -- 支持工单关联、物品移动记录、操作日志 - -### 采购模块 (apiPurchase.go) -- 订单状态:pending → ordered → arrived → received / lost / returned -- 费用明细(多币种)、图片上传、状态记录 -- 可关联工单 - -### 工单模块 (apiWorkOrder.go) -- 工单状态:pending → checked → parts_ordered → repaired → returned / unrepairable -- 可关联仓库物品和采购订单 -- 特殊逻辑:送还时自动移除物品容器绑定 - -## 移动端登录功能开发 - -### 完成的功能 - -1. **api/index.js** - API 封装层 - - uni.request 统一封装 - - 自动注入 userCookieValue - - Cookie 过期自动处理(-44 错误码) - - 支持文件上传 - -2. **stores/user.js** - 用户状态管理 - - userCookie / user / userInfo 状态 - - isLoggedIn / cookieValue / username / avatarUrl 计算属性 - - login(cookie) - 登录保存 - - logout() - 清除状态 - - restoreSession() - 启动时恢复会话 - - fetchUserInfo() - 获取用户信息 - -3. **api/user.js** - 用户 API - - login / register / getUserInfo / changePassword / changeEmail / updateInfo / updateAvatar - -4. **pages/login/login.vue** - 登录页面(已完成 100%) - - 表单验证 - - 调用登录 API - - 记住登录选项 - - 登录成功跳转用户中心 - - 错误提示(密码错误/用户名不存在) - -5. **pages/user/user.vue** - 用户中心(已完成 100%) - - 未登录状态:显示登录入口 - - 已登录状态:显示用户信息卡片 + 功能菜单 + 退出登录 - - 快捷入口:订单/工单/仓库/日程 - - 退出登录确认对话框 - -## 移动端工程分析 (ops2_uniapp) - -**技术栈**: uni-app + Vue 3 + Pinia + HBuilderX - -**已完成**: -- 基础框架搭建 -- Pinia 状态管理(config.js, user.js) -- 自定义 Toast 组件 -- 登录页(✅ 100%)+ 设置页(90%) -- 用户中心(✅ 100%) - -**API 封装**: 已完善 Cookie 认证机制 - -**当前总完成度**: ~50% - -**待完成**: -- 4个 TabBar 页面功能实现(index/order/message/work-order/schedule/warehouse) -- 后端各模块对接 - -## 下一步 - -移动端登录和用户中心已完成,可以: -1. ~~开发主页(index)仪表盘~~ ✅ -2. 开发订单列表(order) -3. 继续完善其他功能页面 - ---- - -## 下午开发记录 - -### 1. TabBar 导航修改 -- 改为:主页 → 订单 → 仓库 → 用户 -- 新增 `pages/warehouse/warehouse.vue` -- 新增 `static/tabbar/warehouse*.png` - -### 2. API 层完善 -- **api/schedule.js** - 日程管理(getEvents/addEvent/editEvent/deleteEvent) -- **api/purchase.js** - 采购订单(getOrders/getOrder/getOrderCount) - -### 3. 主页功能开发 (pages/index/index.vue) -- 欢迎语 + 今日日期显示 -- 今日日程列表(调用 scheduleApi.getEvents) -- 订单统计(待处理/已到达/已收件,调用 purchaseApi.getOrderCount) -- 快捷入口卡片 - -### 4. 主页页面交互 -- 未登录时显示"欢迎使用 OPS 系统" -- 登录后显示"用户名,您好!" -- 日程按颜色标签显示 -- 订单数量有状态提示(待处理>0时橙色高亮) -- 点击统计和快捷入口跳转到对应 TabBar 页面 - -### 5. TabBar 导航再次修改(增加工单) -- 改为:主页 → 订单 → 工单 → 仓库 → 用户 -- 新增 `pages/workorder/workorder.vue` - 工单列表页 - - 顶部统计:待处理/已检查/已下单零件/已维修 - - 状态下拉筛选 - - 工单卡片列表(ID/状态/标题/描述/创建时间) - - 状态颜色标签 - - 分页加载 -- 新增 `api/work_order.js` - 工单 API(list/get/getCount/add/update/delete/commit) -- 新增 `static/tabbar/workorder*.png` - 工单图标(复制订单图标) - -### 6. 仓库页面完善 (pages/warehouse/warehouse.vue) - -**新增功能**: -- 容器卡片长按菜单(编辑/删除) -- 新增容器弹窗完善: - - 父容器选择(可选将容器添加到其他目录) - - 颜色标签选择(8种预设颜色) - - 图片上传(最多3张) - - 备注输入 -- 容器卡片显示颜色边框和图标背景色 -- 删除容器前检查是否有子容器/物品 - -**后端更新** (apiWarehouse.go): -- TabWarehouseContainer 添加 Color 字段 -- add_container / update_container 接口支持 color 参数 - -**前端关键函数**: -- onContainerLongPress() - 长按显示操作菜单 -- showEditModal() / confirmDeleteContainer() - 编辑/删除 -- openParentPicker() / selectParent() - 父容器选择 -- selectColor() - 颜色选择 -- chooseImage() / uploadImage() / removePhoto() - 图片管理 diff --git a/.workbuddy/memory/2026-04-27.md b/.workbuddy/memory/2026-04-27.md deleted file mode 100644 index db31063..0000000 --- a/.workbuddy/memory/2026-04-27.md +++ /dev/null @@ -1,66 +0,0 @@ -# 2026-04-27 工作记录 - -## 移动端仓库 - 物品列表显示数量 - -- `pages/warehouse/warehouse.vue`:两处物品卡片(全部 Tab / 仅物品 Tab)均加入 `数量: {{ item.Quantity ?? 1 }}` 显示 - -## 后端 - 编译时注入 Git 版本信息 - -- `main.go`:添加 `GitVersion`、`GitCommit`、`BuildTime` 三个变量(由 `-ldflags -X` 注入) -- `main.go`:添加 `GET /api/version` 接口(无需认证,返回三个版本字段) -- `install.sh`:编译前读取 `git describe --tags`、`git rev-parse --short HEAD`、当前时间,组装 `LDFLAGS` 传入 `go build` -- 编译命令示例:`CGO_ENABLED=0 GOOS=linux go build -o OPSYS -ldflags="-s -w -X main.GitVersion=... -X main.GitCommit=... -X main.BuildTime=..." main.go` - -## 移动端设置页 - 显示版本号 + 自动递增脚本 - -- 创建 `frontend/ops2_uniapp/bump-version.js`: - - 读取 `manifest.json`(支持去除 JS 注释后再 `JSON.parse`) - - `versionCode` +1,`versionName` 自动递增 patch 位 - - 用法:`node bump-version.js`(打包前手动执行) -- `pages/settings/settings.vue`: - - 底部新增"关于"分组,显示版本号 - - 真机端:`plus.runtime.version` - - H5 端:`fetch('/manifest.json')` + 去注释 + `JSON.parse` - - 样式补全 `cell-label` - -## 移动端仓库 - 物品编辑页补充 quantity 字段 - -- `pages/warehouse/item-edit.vue`:参考 Web 端 `WarehouseItemEdit.vue` - - form 增加 `quantity: 1` - - 编号和备注之间添加 +/− 步进器 UI - - `fetchDetail` 回填 `quantity: item.Quantity ?? 1` - - 提交数据加入 `quantity: form.value.quantity > 0 ? form.value.quantity : 1` - - 删除调试 log,补步进器样式 - -## 移动端仓库 - 新增物品页面补充 quantity 字段 - -- `pages/warehouse/add-item.vue`:参考 Web 端 `WarehouseAddItem.vue`,补充 `quantity` 字段 - - form 增加 `quantity: 1`(默认值 1) - - 模板增加 +/− 步进器 UI - - 提交数据中加入 `quantity: form.value.quantity > 0 ? form.value.quantity : 1` - -## 后端 - 根路由增加版本字段 - -- `routers/api.go`: - - 添加包级变量 `GitVersion`、`GitCommit`、`BuildTime` - - `GET /api/` 响应新增 `version`、`gitCommit`、`buildTime` 三个字段 -- `main.go`:`main()` 启动时将变量赋值给 `routers` 包 -- `install.sh` 修复:`BUILD_TIME` 格式改 `YYYYMMDD_HHMMSS`(去空格,避免 ldflags 解析报错) - -## Web 前端 - 版本定义 + Footer 显示 - -- `vite.config.js`:读取 `package.json` 的 `version`,通过 `define: { __APP_VERSION__ }` 注入全局常量 -- `components/AppFooter.vue`: - - script 中 `const version = __APP_VERSION__` - - 模板中版权行末尾显示 `v{{ version }}`(如 `v0.1.0`) - -## 移动端 - 全局搜索功能 - -- `pages/index/index.vue`:欢迎区域右上角添加 🔍 搜索按钮,点击跳转到 `/pages/search/search` -- `pages/search/search.vue`:新建全局搜索页面 - - 顶部蓝色搜索栏(输入框 + 返回按钮 + 清空按钮) - - 分类 Tab:全部 / 订单 / 工单 / 物品 / 容器 - - 输入防抖 500ms,自动触发搜索 - - 结果卡片显示图标、标题、描述,点击跳转详情 - - 支持跳转到:订单详情、工单详情、物品详情、容器页 -- `pages.json`:注册搜索页面路由 diff --git a/.workbuddy/memory/2026-04-28.md b/.workbuddy/memory/2026-04-28.md deleted file mode 100644 index 980f88a..0000000 --- a/.workbuddy/memory/2026-04-28.md +++ /dev/null @@ -1,61 +0,0 @@ -# 2026-04-28 工作记录 - -## 搜索页面条码识别 bug 修复 - -- 修复搜索页面 `res.data.orders` → `res.data.all_orders`(工单和采购订单列表API返回字段名均为 `all_orders`) -- 搜索页添加条码格式识别:`wo:ID`→工单、`item:ID`→物品、`warehouse:ID`→容器、`po:ID`→采购订单 -- 容器跳转用 `uni.$emit('barcode-navigate-container')` + `switchTab`(tabBar页面不能 navigateTo) -- 搜索页添加扫码按钮(📷) -- App端扫码:`uni.getSetting` 仅小程序支持,App端需用 `plus.android` 检查权限 -- 条件编译:`#ifdef APP-PLUS`、`#ifdef MP-WEIXIN`、`#ifdef H5` 分平台处理 - -## download_app 改造 - -- `download.php`:改为扫描 `__DIR__/../` 根目录下的所有 `.apk`,用 `filemtime()` 取最新,不再依赖文件名格式 -- `index.php`:版本信息同时显示文件名和修改时间 - -## 订单详情页打印功能 - -- `order-detail.vue` 右上角添加打印按钮 🖨 -- 点击后用 LcPrinter 插件打印:标题(加粗大字)/ 备注 / 状态 / 创建日期 / 条形码 `po:ID`(height=4,barcodeType=73) -- 条件编译 `#ifdef APP-PLUS`,非 App 端 toast 提示不支持 -- 订单详情页补充 `order.Styles`(样式)字段显示,插在"链接"和"备注"之间,与 web 端对齐 -- 同步修正打印函数第三行:原打印"状态"→改为打印 `Styles`(样式) - -## 打印初始化代码补全 - -- 在以下四个页面的打印函数中统一加入初始化代码(`initPrinter` + `setConcentration` + `setLineSpacing`),参照 `printer-test.vue` 写法: - - `order-detail.vue` → `printOrder()` - - `show-workorder.vue` → `printWorkOrder()` - - `item-detail.vue` → `printItem()` - - `warehouse.vue` → `printContainer()` - -## 用户登录失败日志表 - -- `apiUsers.go` 新增 `TabUserLoginFailLog` 表结构:记录 `username`、`userID`、`IP`、`userAgent`、`reason`(password_error / user_not_found)、`count`(连续失败次数)、`created_at`、`updated_at` -- `InitUsersRouter` 中新增 `AutoMigrate(&TabUserLoginFailLog{})` -- 登录成功时清除该用户失败记录;密码错误和用户不存在时调用 `recordLoginFail()` 记录/更新失败日志(24小时内累计次数) -- `recordLoginFail()` 在 `TabUserLoginFailLog` 结构体后定义 - -## apiWarehouse.go 查重完善 - -- `add_container` 接口:添加同层级容器 Title 查重(`parent_id` + `title` + `deleted_at IS NULL`),重复返回 `container_title_exist` -- `add_item` 接口:已有查重逻辑(`Name` + `SerialNumber`)保持不变 - -## 完善 updateSysAdminsCash 函数 - -- 原函数为空,现实现: - 1. 查询 `admins` 用户组 ID - 2. 查询该组所有成员的 `TabUserGroupBinds` 记录 - 3. 提取所有 `UserID` 更新到 `sysAdmins` 缓存切片 - 4. 组不存在或查询失败时清空缓存 - -## Web 前端系统管理员入口 - -- 后端 `getinfo` 接口返回 `isSysAdmin` 布尔值(不再暴露完整管理员列表) -- 新建 `apiSysAdmin.go` 文件,专门处理系统管理员接口 -- 后端新增 `POST /admin/sysadmins` 接口(位于 apiSysAdmin.go),仅系统管理员可访问,返回完整 `sysAdmins` 数组 -- 前端 `userStore`:`isSysAdmin` 改为 ref,直接从后端获取 -- `AppHeader.vue` 用户菜单:当 `isSysAdmin` 为 true 时显示「系统管理」入口(琥珀色盾牌图标) -- 新建 `SysAdminView.vue`:4 个标签页占位符(用户管理、用户组、登录日志、系统配置),页面内调用 `authApi.sysAdmins()` 获取管理员列表 -- 路由 `/admin`:添加 `requireSysAdmin` 元信息,路由守卫拦截非管理员访问 diff --git a/.workbuddy/memory/2026-04-29.md b/.workbuddy/memory/2026-04-29.md deleted file mode 100644 index 98cba7e..0000000 --- a/.workbuddy/memory/2026-04-29.md +++ /dev/null @@ -1,89 +0,0 @@ -# 2026-04-29 工作记录 - -## 完成的任务 - -### 1. Web 前端优化 -- **WorkOrderList.vue** - 工单列表页 - - 增宽工单标题列:w-64 → w-80 - - 增宽描述列:w-32 → w-48 - - 状态气泡添加 `whitespace-nowrap` 防止换行 - -- **WarehouseOverview.vue** - 仓库概览页 - - 操作列按钮添加 `whitespace-nowrap` - - 删除创建日期列 - -- **WarehouseContainerDetail.vue** - 容器详情页 - - 操作列按钮添加 `whitespace-nowrap` - - 删除创建日期列 - -### 2. 部署脚本 -- 创建 `install.sh`(根目录) - - 执行 git pull - - 构建前端:cd frontend/ops_vue_js && npm run build - - 安装后端:cd backend/my_work && sudo bash install.sh - -### 3. 移动端开发 -- **add-workorder.vue** - 新建工单页 - - 添加客户搜索功能(参考 Web 前端实现) - - 修改关联物品为多选模式 - - 添加 `selectedItems`、`linkedItemIds` 数组 - - 修改 `submitForm()` 提交 `item_ids` 数组 - -- **edit-workorder.vue** - 编辑工单页 - - 添加关联物品多选功能 - - 添加关联客户多选功能 - - 加载时已关联的物品和客户 - - 修改 `fetchOrder()` 加载 `res.data.items` 和 `res.data.customers` - - 修改 `submitForm()` 提交 `item_ids` 和 `customer_ids` - -### 4. API 文件创建 -- **customer.js** (uni-app api) - - 创建客户 API 模块 - - 支持 list、get、add、update、delete 操作 - -## 技术要点 -- 使用防抖搜索(setTimeout 300ms) -- 多选标签式 UI(参考关联客户实现) -- Vue 3 Composition API (ref, reactive, onMounted) -- uni-app 开发规范 - -### 5. 样式修改 -- **show-workorder.vue** - 工单详情页 - - 修改关联客户标签颜色:绿色 → 蓝色 - - 背景色: #f6ffed → #e6f7ff - - 边框色: #b7eb8f → #91d5ff - - 文字色: #52c41a → #1890ff - - 与关联物品样式保持一致 - -### 6. 物品页面添加关联客户 -- **add-item.vue** - 新增物品页 - - 添加客户搜索功能(防抖 300ms) - - 添加 `selectedCustomers` 数组存储已选客户 - - 添加 `customerSearchQuery`、`customerSearchResults` 等搜索数据 - - 修改 `submitForm()` 提交 `customer_ids` 数组 - - 添加客户标签样式(蓝色主题) - -- **item-edit.vue** - 编辑物品页 - - 添加客户搜索功能(防抖 300ms) - - 修改 `fetchDetail()` 加载已关联客户 `res.data.customers`(非 `linkedCustomers`) - - 修改 `submitForm()` 提交 `customer_ids` 数组 - - 添加客户标签样式(蓝色主题) - -- **item-detail.vue** - 物品详情页 - - 添加加载 `res.data.customers` - - 添加显示关联客户 UI - - 添加关联客户样式 - -### 7. 修复关联客户显示问题 -- **问题1**: item-edit.vue 没回填关联客户 - - 原因:使用了错误的字段名 `linkedCustomers` - - 修复:改为 `res.data.customers` - -- **问题2**: item-detail.vue 没显示关联客户 - - 原因:`fetchDetail()` 没有加载 `customers` 字段 - - 修复:添加 `linkedCustomers.value = res.data.customers || []` - - 添加模板代码显示关联客户 - -- **后端修复**: apiWarehouse.go - - `CustomerInfo` 结构体添加 `PrimaryPhone` 字段 - - 返回客户信息时包含 `primary_phone` diff --git a/.workbuddy/memory/2026-04-30.md b/.workbuddy/memory/2026-04-30.md deleted file mode 100644 index 3916ab7..0000000 --- a/.workbuddy/memory/2026-04-30.md +++ /dev/null @@ -1,7 +0,0 @@ -# 2026-04-30 工作记录 - -## 修复编译错误:TabCustomer 无 PrimaryPhone 字段 - -- **问题**: `apiWarehouse.go:1177` 中 `c.PrimaryPhone undefined` -- **原因**: `TabCustomer` 结构体没有 `PrimaryPhone` 字段,电话存储在独立的 `TabCustomerPhone` 表(`is_primary=true` 标记主号码) -- **修复**: 改为从 `TabCustomerPhone` 表查询主号码,拼接格式 `+{Prefix} {Phone}`,文件:`routers/apiWarehouse.go` diff --git a/.workbuddy/memory/2026-05-06.md b/.workbuddy/memory/2026-05-06.md deleted file mode 100644 index dc4afb5..0000000 --- a/.workbuddy/memory/2026-05-06.md +++ /dev/null @@ -1,147 +0,0 @@ -# 2026-05-06 日志 - -## 日历事件日程类型功能 - -### 修改内容 -- **后端** `routers/apiCalendar.go`: - - `TabCalendarEvent` 结构体新增 `ScheduleType string` 字段(默认值 work) - - `addevent`/`updateevent` 接口解析并保存 `schedule_type` 参数 - -- **前端** `CalendarDetail.vue`: - - `eventData` 新增 `scheduleType` 字段 - - `openEventModal`/`editEvent` 传递 `scheduleType` - - `selectColor` 函数联动更新 `scheduleType` - - `saveEvent`/`eventDrop` 提交 `schedule_type` - - `getEvents` 返回数据附加 `extendedProps.scheduleType` - - 模态框显示日程类型标签 - -- **i18n**: - - `zh-CN.json`: 新增 `event_type: "日程类型"` - - `en.json`: 新增 `event_type: "Event Type"` - -### 日程类型选项 -- work - 工作 -- duty - 值班 -- exam - 考试 -- standby - 备用 -- personal_holiday - 个人假期 -- public_holiday - 公众假期 - -### 注意事项 -- GORM AutoMigrate 会自动添加新字段 -- 前端颜色选择与日程类型联动 - -## 修复:calendar/events jsonErr - -**问题**:`fromGetCalendarEvents` 的 `start/end` 是 `*time.Time` 类型,无法直接解析字符串格式的日期。 - -**修复**:改为直接用类型断言解析字符串,用 `time.Parse("2006-01-02", ...)` 解析。 - -## 优化:BgColor 弃用,前端根据 ScheduleType 渲染颜色 - -**前端**:添加 `getColorByScheduleType()` 函数,`getEvents` 中使用 scheduleType 映射颜色。 - -**后端**:只存储 ScheduleType,不处理颜色逻辑。颜色完全由前端 `colorOptions` 控制。 - -## CalendarDetail 滚动标题快照对比 - -**功能**:每次 getEvents 存快照,数据变化或宽度变化时重新计算标题滚动。 - -**实现**: -- `pageData.lastEventsSnapshot`:存储上一次的 JSON 快照 -- `getEvents()` 末尾对比快照,变化则 `setTimeout(recalcScrollTitles, 150)` -- `ResizeObserver` 监听日历容器宽度变化,防抖 150ms 后重算 -- `applyScrollToTitle()`:清除旧状态 → 测量 overflow → 设置 `--scroll-distance` 和 `data-truncated` 属性 - -## CalendarList 编辑/删除按钮改用 canEdit - -**改动**: -- `CalendarList.vue`:编辑/删除按钮 `v-if` 条件从 `calendar.UserID === userStore.userInfo?.ID` 改为 `calendar.canEdit` -- 移除废弃的 `useUserStore` 导入 - -## CalendarList 删除改用 ConfirmDialog 组件 - -**改动**: -- `CalendarList.vue` 导入并使用 `ConfirmDialog` 组件 -- 新增 `showDeleteModal` + `deletingCalendar` 状态 -- `deleteCalendar()` 改为打开确认弹窗,`confirmDelete()` 执行实际删除 API -- i18n 新增 `calendar.confirm_delete_message`:zh-CN「确定要删除日历「{name}」吗?此操作不可撤销。」,en 英文版 - -## 新增日历管理页面 /calendars/admin - -**功能**:系统管理员查看所有日历列表,包含日程数量、创建者、创建时间。 - -**新增文件**: -- `src/views/calendar/CalendarAdminList.vue` - 日历管理列表组件 - -**路由修改** `src/router/index.js`: -- 新增 `/calendars/admin` 路由,指向 `CalendarAdminList.vue` -- 设置 `meta: { requireSysAdmin: true }` 要求管理员权限 - -**SysAdminView.vue**: -- `tabs` 数组新增 `{ id: 'calendar', label: t('calendar.admin_title'), to: '/calendars/admin' }` - -**i18n 新增**: -- `zh-CN.json`: `calendar.admin_title = "日历管理"`, `calendar.event_count = "日程数量"` -- `en.json`: `calendar.admin_title = "Calendar Admin"`, `calendar.event_count = "Event Count"` - -## 修复:CalendarAdminList eventCounts 未定义 - -**问题**:模板访问 `eventCounts[calendar.ID]` 但 `eventCounts` 从未声明,且 `fetchEventCounts` 未被调用。 - -**修复**:后端已直接返回 `event_count` 字段,改为模板直接用 `calendar.event_count ?? 0`,删除多余的 `eventCounts` ref 和 `fetchEventCounts` 函数。 - -## 新增:后端 TabCalendarEventUserBind 绑定表 - -**文件**:`routers/binds.go` -- 新增 `TabCalendarEventUserBind` 结构体(EventID/UserID/CreatorID/CreatedAt) -- 在 `BindsInit` AutoMigrate 中注册 - -## 新增:日历右键菜单(复制/粘贴日程) - -**文件**:`CalendarDetail.vue` - -**功能**: -- 右键日程事件弹出上下文菜单,显示「复制日程」「粘贴日程」 -- 复制:将日程数据存入 `clipboard` ref -- 粘贴:以 clipboard 数据调用 `addEvent` API 创建新日程 -- 点击任意位置关闭菜单 - -**实现**: -- `contextMenu` ref 管理菜单显示/位置/事件数据 -- `clipboard` ref 存储复制的日程 -- `eventDidMount` 中为每个事件绑定 `contextmenu` 事件 -- `onMounted` 注册全局 click 关闭菜单,`onBeforeUnmount` 移除 -- i18n 新增 `calendar.copy_event/paste_event/copy_success/paste_success/no_event_to_paste` - -## 修复:粘贴多天日程时结束日期少1天 - -**问题**:Ctrl+V 粘贴大于1天的日程时,目标结束日期少1天。 - -**原因**:`pasteToDate` 中用 `new Date(targetEndMs).toISOString().split('T')[0]` 计算目标结束日期,`toISOString()` 输出 UTC 时间,在 UTC+8 时区下日期会往前偏移1天。 - -**修复**(`CalendarDetail.vue` `pasteToDate` 函数): -- 改用天数差 `Math.round((origEndDate - origStartDate) / 86400000)` 替代毫秒差 -- 用本地日期方法 `targetEndDate.setDate(targetEndDate.getDate() + diffDays)` + 手动拼接 `YYYY-MM-DD`,替代 `toISOString().split('T')[0]` - -## 修复:Ctrl+C 无法复制大于1天的日程 - -**问题**:Ctrl+C 复制多天日程后粘贴无效果。 - -**根因**:`calendarOptions.value.events` 中多天事件的 `end` 是 `DateUtils.toCalendarEnd()` 返回的 **Date 对象**(非字符串)。Ctrl+C 直接 `selectedEvent.end || selectedEvent.start` 存入 clipboard,导致 `pasteToDate` 调用 `clipboard.value.end.split('T')[0]` 时 Date 对象没有 `split` 方法而报错。单天事件因走 `isSameDay` 分支不解析 `end`,所以不受影响。 - -**修复**(`CalendarDetail.vue` `handleKeyDown` Ctrl+C 分支): -- 检测 `end` 是否为 Date 实例,如果是则用本地日期方法转为 `YYYY-MM-DD` 字符串再存入 clipboard - -## 新增:编辑事件窗口显示创建者 - -**功能**:编辑事件模态框标题居中显示「xxx 创建」 - -**实现**(参考 ScheduleView.vue): -- 导入 `useUsersStore`,添加 `usersStore` -- `pageData.eventBindUserID`:数组,每项 `{ eventID, userID }` -- `getUserIdFromEventID(eventID)`:通过事件ID查创建者用户ID -- `getUsernameFromUserID(userID)`:调用 `usersStore.getUsernameFromUserID(userID)`(有缓存) -- `getEvents` 中遍历事件时 `pageData.value.eventBindUserID.push({ eventID: item.ID, userID: item.UserID })` -- 模态框标题新增 `