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 })` -- 模态框标题新增 `
` 居中显示 -- i18n:`calendar.created_by` = "{name} 创建" / "Created by {name}" diff --git a/.workbuddy/memory/MEMORY.md b/.workbuddy/memory/MEMORY.md deleted file mode 100644 index 62fe6cb..0000000 --- a/.workbuddy/memory/MEMORY.md +++ /dev/null @@ -1,382 +0,0 @@ -# MEMORY.md - 长效记忆 - -> 最后更新: 2026-04-29 - ---- - -## 项目概览 - -**项目名称**: OPS 运营管理系统 -**技术栈**: Vue 3 + Vite (前端 Web) / uni-app (移动端) / Go + Gin + GORM (后端) - -``` -ops2/ -├── backend/my_work/ # Go 后端(端口由 config.yaml 决定,默认 8080) -├── frontend/ -│ ├── ops_vue_js/ # Vue 3 Web 前端 -│ └── ops2_uniapp/ # uni-app 移动端 -└── DOC/ -``` - ---- - -## 后端架构 - -### 启动流程 (`main.go`) -1. 检查 `./data/config.yaml`,不存在则复制 `./defConfig/configTemp.yaml` -2. `configed` 必须为 `true` 才能启动 -3. 初始化顺序:`ReturnInit` → `ApiUserInit` → `ApiFilesInit` → `ApiScheduleInit` → `ApiPurchaseInit` → `ApiWorkOrderInit` → `ApiWarehouseInit` → `BindsInit` -4. 静态文件服务 `./dist`,所有非 `/api` 请求转发给前端 HTML -5. 支持 TLS(证书路径在 config 中配置) -6. 版本信息通过 `-ldflags -X` 编译注入(`GitVersion / GitCommit / BuildTime`) - -### 路由根 (`api.go`) -``` -/api -├── /static → ApiStatic -├── /users → ApiUser -├── /files → ApiFiles -├── /purchase → ApiPurchase -├── /schedule → ApiSchedule -├── /work_order → ApiWorkOrder -├── /warehouse → ApiWarehouse -└── /admin → ApiSysAdmin -``` - -### 请求/响应格式 -```json -// 请求体 -{ "userCookieValue": "xxx", "data": { ...业务参数 } } - -// 响应体 -{ "err_code": 0, "err_msg": "apiOK", "return": { ... } } -``` -错误码从 `./defConfig/errorCodes.json` 读取,常用:`apiOK`=0,`userNoLogin`=-44,`permission_denied`,`parameErr`,`dbErr` - -### 认证机制 -- 登录成功返回 Cookie 对象(存 `TabUserCookie` 表) -- 后续请求通过 JSON body 中 `userCookieValue` 传递 -- Cookie 过期后端返回 err_code=-44,前端拦截器自动处理 -- `AuthenticationAuthority(ctx)` → 分离 cookie 和 data,验证返回 `(isAuth bool, user TabUser, data map)` - ---- - -## 用户认证模块 (`apiUsers.go`) - -### 数据表 -| 表 | 说明 | -|---|---| -| `TabUser` | 用户(Name 唯一索引) | -| `TabUserGroups` | 用户组 | -| `TabUserGroupBinds` | 用户-组绑定 | -| `TabUserInfo` | 用户详情(头像/昵称/性别/生日/语言等) | -| `TabUserCookie` | 登录 Cookie(ExpiresAt,Remember 字段) | -| `TabUserLoginFailLog` | 登录失败日志(24小时内聚合,累计 Count) | - -### 初始化 -- 自动创建 `admins` 组 + `admin` 用户(默认密码 `adminpassword`) -- 密码:加盐哈希,支持 `text` / `md5` / `md5salt`(config 指定) - -### 权限缓存(内存) -各模块维护各自的管理员 ID 列表: -| 变量 | 所属模块 | 刷新函数 | -|---|---|---| -| `sysAdmins []uint` | apiUsers.go | `updateSysAdminsCash()` | -| `scheduleAdmins []uint` | apiSchedule.go | `ScheduleUpdateAdminsCash()` | -| `purchaseAdmins []uint` | apiPurchase.go | `PurchaseUpdateAdminsCash()` | -| `workOrderAdmins []uint` | apiWorkOrder.go | `WorkOrderUpdateAdminsCash()` | -| `warehouseAdmins []uint` | apiWarehouse.go | `WarehouseUpdateAdminsCash()` | - -**修改用户组时自动刷新缓存**(`apiSysAdmin.go` 的 `add_group_member` 和 `remove_group_member`): -```go -switch group.Name { -case "admins": updateSysAdminsCash() -case "schedule_admin": ScheduleUpdateAdminsCash() -case "purchase_admin": PurchaseUpdateAdminsCash() -case "work_order_admin": WorkOrderUpdateAdminsCash() -case "warehouse_admin": WarehouseUpdateAdminsCash() -} -``` - -### API 路由 (`/api/users/*`) -| 路由 | 用途 | -|------|------| -| `POST /login` | 登录(返回 Cookie 对象,含 Remember) | -| `POST /register` | 注册 | -| `POST /getinfo` | 获取当前用户信息(含 isSysAdmin 字段) | -| `POST /changePassword` | 修改密码(oldpass/newpass) | -| `POST /changeEmail` | 修改邮箱 | -| `POST /updateAvatar` | 更新头像(FormData) | -| `POST /updateInfo` | 更新详情(firstName/username/birthdate/gender/region/language) | -| `GET /getuserinfo/:id` | 获取指定用户信息 | - ---- - -## 系统管理模块 (`apiSysAdmin.go`) - -**路由前缀**: `/api/admin/*`,全部要求 `SysAdminCheck` - -| 路由 | 用途 | -|------|------| -| `POST /sysadmins` | 获取系统管理员 ID 列表 | -| `POST /users` | 用户列表(分页+搜索,返回含头像路径) | -| `POST /groups` | 用户组列表(含成员数 + 前5个成员ID) | -| `POST /group_members` | 指定组的成员列表(分页) | -| `POST /user_detail` | 用户详情(基本信息 + userinfo) | -| `POST /reset_user_password` | 重置密码(同时注销该用户所有 cookie) | -| `POST /add_group_member` | 添加用户到组(含缓存刷新) | -| `POST /remove_group_member` | 从组移除用户(含缓存刷新) | -| `POST /login_fail_logs` | 登录失败日志(分页+搜索 username/IP) | - -`SysAdminCheck(userID)` 直接查内存 `sysAdmins` 列表。 - ---- - -## 文件管理模块 (`api_Files.go`) - -### 数据表 `TabFileInfo` -| 字段 | 说明 | -|---|---| -| `Sha256` | 唯一标识,去重键 | -| `Name` | 原始文件名 | -| `Path` | 存储路径 | -| `Mime` | MIME 类型 | -| `Type` | image/video/pdf 等 | -| `Const` | 引用计数 | - -### 存储结构 -``` -data/ -├── static/avatar/ # 用户头像(/api/static/avatar/:filename) -└── upload/ - ├── image/ # 以 SHA256 命名 - ├── video/ - ├── music/ - └── pdf/ -``` - -### API (`/api/files/*`) -| 路由 | 用途 | -|---|---| -| `POST /upload/image` | 上传图片(FormData + SHA256 去重) | -| `GET /:mode/:hash` | `get`=下载(带文件名),`download`=预览 | - ---- - -## 日程模块 (`apiSchedule.go`) - -### 数据表 -- `TabSchedule`(软删除):Title / StartDate / EndDate / BgColor(默认#3788d9) / Remark -- `TabScheduleLog`:操作日志 - -### API (`/api/schedule/*`) -| 路由 | 用途 | -|---|---| -| `POST /getevents` | 按日期范围查询(`start_date <= end AND end_date >= start`) | -| `POST /addevent` | 新增 | -| `POST /editevent` | 编辑 | -| `POST /deleevent` | 软删除 | - ---- - -## 采购模块 (`apiPurchase.go`) - -### 数据表 -| 表 | 用途 | -|---|---| -| `TabPurchaseOrder` | 采购订单(软删除) | -| `TabPurchaseCosts` | 费用明细(单价/运费,多币种) | -| `TabPurchaseFileBind` | 图片关联 | -| `TabPurchaseCommit` | 状态变更记录 | -| `TabPurchaseLog` | 操作日志 | - -### 状态流 -``` -pending → ordered → arrived → received - ↓ - lost / returned -``` - -### 货币:`1-CNY / 2-MOP / 3-HKD / 4-USD` - -### API (`/api/purchase/*`) -| 路由 | 用途 | -|---|---| -| `POST /getorder` | 订单详情(含费用/图片/状态记录/关联工单) | -| `POST /getorders` | 列表(搜索/分页/状态筛选) | -| `POST /addorder` | 新增 | -| `POST /updateorder` | 编辑(费用/图片重建) | -| `POST /deleteorder` | 删除 | -| `POST /updatestatus` | 更新状态(可附评论/图片) | -| `POST /delete_commit` | 删除状态记录 | -| `POST /getordercount` | 各状态数量统计 | -| `POST /search_work_orders` | 搜索工单(用于关联) | - ---- - -## 工单模块 (`apiWorkOrder.go`) - -### 数据表 -| 表 | 用途 | -|---|---| -| `TabWorkOrder` | 工单(软删除) | -| `TabWorkOrderFileBind` | 工单图片关联 | -| `TabWorkOrderCommit` | 进度记录 | -| `TabWorkOrderCommitFileBind` | 进度关联图片 | -| `TabWorkOrderPurchaseOrderBind` | 工单-采购订单关联(含 CommitID) | -| `TabWorkOrderLog` | 操作日志 | - -### 状态流 -``` -pending → checked → parts_ordered → repaired → returned - ↓ - unrepairable -``` - -### 特殊逻辑 -- 状态变为 `returned` 时,自动移除物品的容器绑定(`ContainerID = nil`) -- 工单 ↔ 物品:`TabWarehouseItemWorkOrderBind` -- 工单 ↔ 采购:`TabWorkOrderPurchaseOrderBind` - -### API (`/api/work_order/*`) -| 路由 | 用途 | -|---|---| -| `POST /add` | 新增(可关联物品) | -| `POST /update` | 编辑 | -| `POST /list` | 列表 | -| `POST /get` | 详情(含图片/进度/关联物品/采购) | -| `POST /commit` | 提交进度(更新状态,可关联采购) | -| `POST /delete` | 删除 | -| `POST /delete_commit` | 删除进度 | -| `POST /count` | 各状态统计 | -| `POST /search_purchase_orders` | 搜索采购订单(用于关联) | - ---- - -## 仓库模块 (`apiWarehouse.go`) - -### 数据表 -| 表 | 用途 | -|---|---| -| `TabWarehouseContainer` | 容器(树形,最多5层,ParentID=nil为顶级) | -| `TabWarehouseItem` | 物品(ContainerID=nil表示未入库) | -| `TabWarehouseItemCommit` | 物品移动记录 | -| `TabWarehouseLog` | 操作日志 | - -### 跨模块绑定表 (`binds.go`) -| 表 | 用途 | -|---|---| -| `TabWarehouseItemWorkOrderBind` | 物品-工单 | -| `TabWarehouseItemFileBind` | 物品-图片 | -| `TabWarehouseContainerFileBind` | 容器-图片 | -| `TabPurchaseFileBind` | 采购-图片 | -| `TabWorkOrderFileBind` | 工单-图片 | -| `TabWorkOrderCommitFileBind` | 工单进度-图片 | -| `TabWorkOrderPurchaseOrderBind` | 工单-采购 | - ---- - -## Web 前端架构 (`frontend/ops_vue_js/`) - -**技术栈**: Vue 3 + Vite 7 + Pinia + Vue Router (hash 模式) + Vue I18n + Tailwind CSS v4 + Tabler Icons - -### 目录结构 -``` -src/ -├── api/ -│ ├── index.js # Axios 实例,基础 URL /api,请求拦截注入 cookie,响应拦截处理 -44 -│ ├── auth.js # 认证 + sysadmin 管理 API -│ ├── purchase.js # 采购 API -│ ├── warehouse.js # 仓库 API -│ ├── work_order.js # 工单 API -│ ├── schedule.js # 日程 API -│ └── users.js # 其他用户信息 API(按需加载头像/用户名) -├── components/ -│ ├── AppHeader.vue # 导航栏(含系统管理入口,权限判断 isSysAdmin) -│ ├── AppFooter.vue -│ ├── AppToast.vue -│ ├── ConfirmDialog.vue -│ ├── PurchaseOrderForm.vue -│ ├── SettingNav.vue -│ ├── tagadder.vue -│ ├── useDropzone.vue # 文件拖拽上传 -│ ├── imageCropper.vue # 图片裁剪 -│ ├── datePicker.vue -│ ├── dateTimePicker.vue -│ └── datatimePickerForFullCalendar.vue -├── composables/ -├── i18n/ -│ ├── en.json # 英文翻译 -│ └── zh-CN.json # 中文翻译 -├── layouts/ -│ ├── DefaultLayout.vue # 需要登录的页面布局 -│ └── AuthLayout.vue # 认证页面全屏布局 -├── router/index.js -├── stores/ -│ ├── user.js # 当前用户(isLoggedIn / isSysAdmin / cookie / avatarUrl) -│ ├── users.js # 其他用户信息缓存(按需拉取,防重复请求) -│ └── toast.js # 全局 Toast -└── views/ - ├── HomeView.vue - ├── ScheduleView.vue # FullCalendar - ├── SysAdminView.vue # 系统管理(仅 sysAdmin 可访问,meta.requireSysAdmin) - ├── AdminView.vue - ├── purchase/ # PurchaseList / addorder / ShowOrder / editorder - ├── work_order/ # WorkOrderList / AddEditWorkOrder / ShowWorkOrder - ├── warehouse/ # WarehouseOverview / ContainerList / ContainerDetail / ItemList / ItemDetail / AddItem / ItemEdit - └── settings/ # AccountView / ContactView / SecurityView -``` - -### 路由说明 -- 公开页:`/` `/login` `/register` `/forgot_password` `/schedule` `/404` -- `/sysadmin` 需要 `meta.requireSysAdmin`,不满足跳回首页 -- 未登录跳转 `/login?redirect=原路径` - -### 状态管理 (`stores/user.js`) -- Cookie 持久化:`Remember=true` 存 localStorage,否则只存 sessionStorage -- `isSysAdmin` 由 `/users/getinfo` 返回的 `isSysAdmin` 字段驱动 -- `fetchUserInfo()` 在 `login()` 后自动调用 - -### i18n 翻译节点 -`week / errorpage / appname / tagadder / dropzone / cropper / purchase / work_order / warehouse / purchase_addorder / schedule / home / message / settings / button / footer / cost_type / order_status / sysadmin` - -### 构建配置 -- 输出目录: `../../backend/my_work/dist`(后端直接 serve) -- 开发代理: `/api` → `http://127.0.0.1:8080` -- 路径别名: `@` → `./src` - ---- - -## 移动端 (`frontend/ops2_uniapp/`) - -**技术栈**: uni-app + Vue 3 + Pinia - -**当前完成度**: ~40% - -| 页面 | 完成度 | -|---|---| -| login | 85%(表单/验证/请求/Toast) | -| settings | 90%(API 地址配置/连接测试) | -| index/order/message | 5%(占位) | -| user | 20%(登录入口) | - -**待完成**: -- 完善 `api/index.js`(Cookie 认证,参照 Web 前端) -- 实现各功能页面(仓库/工单/采购等) - ---- - -## 开发注意事项 - -1. **后端 JSON 字段命名**: 结构体字段为 PascalCase(如 `UserID / AvatarPath`),前端需对应 -2. **头像**: `usersStore.getAvatarUrlFromUserID(id)` 返回 `/api/static/avatar/{path}` 或默认 `/ava.svg` -3. **i18n 修改**: 同时修改 `en.json` 和 `zh-CN.json` 两个文件 -4. **同源部署**: 后端 serve `./dist` 静态资源,前端 build 直接输出到后端目录 -5. **API 封装**: `api/index.js` 统一返回 `{ errCode, data, raw }` - ---- - -## 更新记录 -- 2026-04-24: 首次梳理 -- 2026-04-28: 修复 SysAdminView 中 `` 误删 bug;添加 message.sysadmin 英文翻译 -- 2026-04-29: 全面重新分析代码,更新并精简记忆文档