diff --git a/.workbuddy/expert-history.json b/.workbuddy/expert-history.json deleted file mode 100644 index 2b429f0..0000000 --- a/.workbuddy/expert-history.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "version": 2, - "sessions": { - "ebf5e3bab92e4319aec2c39116541728": [ - { - "expertId": "BackendArchitect", - "name": "磐石石", - "profession": "后端架构师", - "avatarUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/avatars/02-Engineering/BackendArchitect/BackendArchitect.png", - "promptUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/experts/02-Engineering/BackendArchitect/BackendArchitect_zh.md", - "usedAt": 1776327849643, - "industryId": "all" - } - ] - }, - "lastUpdated": 1776336333635 -} \ No newline at end of file diff --git a/.workbuddy/memory/2026-04-16.md b/.workbuddy/memory/2026-04-16.md deleted file mode 100644 index ae05f9d..0000000 --- a/.workbuddy/memory/2026-04-16.md +++ /dev/null @@ -1,76 +0,0 @@ -# 2026-04-16 工作日志 - -## OPS2 系统工作流程分析 - -完成了对 OPS2 流程管理系统三端架构的全面分析,生成了完整的工作流程分析报告(ops2_workflow_analysis.md)。 - -### 系统技术栈 -- 后端:Go + Gin + GORM,支持 SQLite/MySQL/PostgreSQL 可配置切换 -- PC前端:Vue3 + Vite + Pinia + Axios -- 移动端:uni-app(Vue3+Vite),目标 H5 + Android - -### 核心功能模块 -1. **用户认证**:Cookie-based 认证,MD5+salt 密码哈希,支持 Remember Me -2. **日程管理**:日历事件 CRUD,权限分层(创建者/模块管理员/全局管理员) -3. **采购订单**:6种状态流转(pending→ordered→arrived→received/lost/returned),含完整审计日志 -4. **文件系统**:SHA256 内容去重,MIME 白名单校验,图片上传服务 - ---- - -## 移动端重构(对标 PC 前端) - -完成了 ops_uniapp 移动端的全面重构,参考 PC 前端架构对齐。 - -### 新增/重构文件 -- `api/request.js`:统一请求封装(自动注入 cookie、统一 err_code 解析、cookie 过期自动跳登录) -- `api/auth.js`、`api/schedule.js`、`api/purchase.js`:对标 PC 端 API 模块 -- `store/user.js`:轻量单例 Store(对标 PC 端 Pinia useUserStore,含 restoreSession) -- `pages/signin.vue`:重构登录页(表单验证、密码显示/隐藏、remember me) -- `pages/index/index.vue`:重构首页(今日日程卡片 + 待处理采购统计) -- `pages/setting/my_info.vue`:重构设置页(头像上传、信息修改、退出登录) -- `pages.json`:修正 API 路径前缀(/api/v1 → /api),更新页面路由 -- `App.vue`:接入 userStore.restoreSession() - -### 关键设计 -- API base 统一为 `/api`(修复旧版 /api/v1 错误) -- cookie 认证流程与 PC 端完全对齐 -- store/user.js 用模块单例替代 Pinia(uni-app 兼容) - ---- - -## 补建预留路由对应的 Vue 文件 - -修复了 `vite:import-analysis` 报错(pages.json 声明了路由但文件不存在)。 - -### 新建文件 -- `pages/schedule/schedule.vue`:移动端日程月份视图,支持按月浏览、添加/编辑/删除日程、颜色分类,30s 轮询刷新 -- `pages/purchase/list.vue`:移动端采购订单列表,支持搜索/状态过滤/分页加载更多;底部抽屉查看订单详情(费用明细/图片/变更记录);已登录可变更订单状态 - -### 重要发现 -- 移动端 `my_network_func.js` 中 `head_path = "/api/v1"` 与后端实际 `/api` 路径不匹配,需修复 -- 审计日志系统完善(TabScheduleLog、TabPurchaseLog、TabPurchaseCommit) -- 后端同时托管静态前端产物,单体部署架构 - ---- - -## @api 命名空间重构(修复 404 模块请求) - -### 问题 -`GET http://localhost:5173/api/auth.js net::ERR_ABORTED 404` - -manifest.json 配置了 `/api` 代理到后端 8080,导致所有 `/api` 开头的请求都被代理。 -即使加了 vite.config.js 的 resolve.alias,HBuilderX 的内置 dev server 行为也不完全符合预期。 - -### 解决 -将 API 模块目录从 `api/` 重命名为 `@api/`,彻底消除与 `/api` 代理前缀的冲突。 - -- `api/` → `@api/`,所有 import 路径相应更新 -- 真正的后端 API 请求走 `/api`(代理到 8080) -- `@api/*.js` 模块不走 `/api` 代理,不会冲突 -- `manifest.json` 的 h5.devServer 只保留 disableHostCheck,proxy 移至 vite.config.js - -### 更新文件 -- `@api/request.js`、`@api/auth.js`、`@api/schedule.js`、`@api/purchase.js`(新建) -- `store/user.js`、`pages/signin.vue`、`pages/setting/my_info.vue`、`pages/index/index.vue`、`pages/schedule/schedule.vue`、`pages/purchase/list.vue`(更新 import 路径) -- `vite.config.js`(简化 alias 配置) -- 删除旧 `api/` 目录 diff --git a/.workbuddy/memory/2026-04-23.md b/.workbuddy/memory/2026-04-23.md index 872c209..84d56d8 100644 --- a/.workbuddy/memory/2026-04-23.md +++ b/.workbuddy/memory/2026-04-23.md @@ -1,31 +1,13 @@ -# 2026-04-23 日志 +# 2026-04-23 -## 物品详情页编辑弹窗增加图片管理功能 +- 仓库容器详情页(WarehouseContainerDetail.vue):物品列表表格在序列号列后新增备注列,显示 `item.Remark` 字段,colspan 从 6 改为 7 +- 仓库总览页(WarehouseOverview.vue):物品列表表格同样在序列号列后新增备注列,显示 `item.Remark` 字段,colspan 从 6 改为 7 +- 工单详情页(ShowWorkOrder.vue):卡头时间显示从仅"创建时间"改为"创建时间 + 更新时间",使用 `order.UpdatedAt`,中英文 i18n 新增 `work_order.updated_at` -**涉及文件:** -- `frontend/ops_vue_js/src/views/warehouse/WarehouseItemDetail.vue` — 编辑弹窗增加 `useDropzone` 组件,支持加载已有图片、上传新图片、删除图片 -- `frontend/ops_vue_js/src/components/useDropzone.vue` — 导出 `loadInitialFiles` 方法供外部调用 +# 2026-04-24 -**实现方式:** -- 编辑弹窗中新增 `editDropzoneRef` ref,绑定 `useDropzone` 组件 -- `openEdit()` 时调用 `loadInitialFiles()` 刷新初始文件 -- `submitEdit()` 时从 dropzone 获取所有图片哈希(包含新上传和已存在的),一并传给 `updateItem` API -- 后端 `update_item` API 已支持 `photos` 字段,会重建图片绑定 - -**关键代码片段:** -```javascript -// 提交时获取所有图片哈希 -const photos = getEditPhotoHashes() -warehouseApi.updateItem({ id, name, serial_number, remark, quantity, photos }) -``` - -## 物品编辑改为独立页面 - -**涉及文件:** -- `frontend/ops_vue_js/src/views/warehouse/WarehouseItemEdit.vue` — 新建,物品编辑独立页面 -- `frontend/ops_vue_js/src/views/warehouse/WarehouseItemDetail.vue` — 编辑按钮改为 `router.push('/warehouse/item/edit/:id')`,移除弹窗代码 -- `frontend/ops_vue_js/src/router/index.js` — 新增 `/warehouse/item/edit/:id` 路由 - -**实现方式:** -- 创建 `WarehouseItemEdit.vue`,`onMounted` 获取物品数据(包含已有图片),通过 `setTimeout` 调用 `loadInitialFiles()` 加载到 dropzone -- 详情页编辑按钮改为跳转,移除弹窗及相关 state/function +- 后端 apiWarehouse.go:TabWarehouseContainer 和 TabWarehouseItem 的 CreatedAt/UpdatedAt 从 string 改为 *time.Time,加 gorm autoCreateTime/autoUpdateTime tag,清理了所有手动设置时间的代码(6 处),所有字段补齐 json tag +- 工单列表页(WorkOrderList.vue):创建时间列后新增更新时间列,显示 `order.UpdatedAt`,colspan 从 4 改为 5 +- 仓库总览页(WarehouseOverview.vue):容器列表和物品列表均新增更新时间列(创建时间后),colspan 从 7 改为 8,中英文 i18n 新增 `warehouse.updated_at` +- 容器详情页(WarehouseContainerDetail.vue):卡头元信息、子容器列表、物品列表三处均新增更新日期列/显示 +- 物品详情页(WarehouseItemDetail.vue):卡头新增更新日期显示;修复移动物品弹窗中目标容器搜索下拉框闪退问题(@mousedown.prevent + blur 延迟关闭) diff --git a/backend/my_work/routers/apiWarehouse.go b/backend/my_work/routers/apiWarehouse.go index 1822188..65edd95 100644 --- a/backend/my_work/routers/apiWarehouse.go +++ b/backend/my_work/routers/apiWarehouse.go @@ -13,25 +13,27 @@ import ( // ---------- 数据表结构 ---------- type TabWarehouseContainer struct { - ID uint `gorm:"primaryKey"` - Title string `gorm:"size:255;not null;comment:容器名"` - Remark string `gorm:"type:text;comment:描述"` - CreatedAt string `gorm:"size:20;comment:创建日期"` - CreatorID uint `gorm:"not null;index;comment:创建者id"` - ParentID *uint `gorm:"index;comment:父容器id,nil=顶级"` - ItemCount int `gorm:"default:0;comment:直接子物品数量"` - ChildCount int `gorm:"default:0;comment:子容器数量"` + ID uint `gorm:"primaryKey" json:"ID"` + Title string `gorm:"size:255;not null;comment:容器名" json:"Title"` + Remark string `gorm:"type:text;comment:描述" json:"Remark"` + CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime" json:"CreatedAt"` + UpdatedAt *time.Time `gorm:"type:datetime;autoUpdateTime" json:"UpdatedAt"` + CreatorID uint `gorm:"not null;index;comment:创建者id" json:"CreatorID"` + ParentID *uint `gorm:"index;comment:父容器id,nil=顶级" json:"ParentID"` + ItemCount int `gorm:"default:0;comment:直接子物品数量" json:"ItemCount"` + ChildCount int `gorm:"default:0;comment:子容器数量" json:"ChildCount"` } type TabWarehouseItem struct { - ID uint `gorm:"primaryKey"` - Name string `gorm:"size:255;not null;comment:物品名"` - SerialNumber string `gorm:"size:255;comment:序列号"` - Remark string `gorm:"type:text;comment:描述"` - Quantity int `gorm:"default:1;comment:数量"` - CreatedAt string `gorm:"size:20;comment:创建日期"` - CreatorID uint `gorm:"not null;index;comment:创建者id"` - ContainerID *uint `gorm:"index;comment:所属容器id,nil=未入库"` + ID uint `gorm:"primaryKey" json:"ID"` + Name string `gorm:"size:255;not null;comment:物品名" json:"Name"` + SerialNumber string `gorm:"size:255;comment:序列号" json:"SerialNumber"` + Remark string `gorm:"type:text;comment:描述" json:"Remark"` + Quantity int `gorm:"default:1;comment:数量" json:"Quantity"` + CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime" json:"CreatedAt"` + UpdatedAt *time.Time `gorm:"type:datetime;autoUpdateTime" json:"UpdatedAt"` + CreatorID uint `gorm:"not null;index;comment:创建者id" json:"CreatorID"` + ContainerID *uint `gorm:"index;comment:所属容器id,nil=未入库" json:"ContainerID"` } type TabWarehouseContainerFileBind struct { @@ -192,7 +194,6 @@ func ApiWarehouse(r *gin.RouterGroup) { c := TabWarehouseContainer{ Title: from.Title, Remark: from.Remark, - CreatedAt: strconv.FormatInt(time.Now().Unix(), 10), CreatorID: user.ID, ParentID: from.ParentID, } @@ -366,10 +367,11 @@ func ApiWarehouse(r *gin.RouterGroup) { } type FromList struct { - Search string `json:"search"` - ParentID *uint `json:"parent_id"` - Entries int `json:"entries"` - Page int `json:"page"` + Search string `json:"search"` + ParentID *uint `json:"parent_id"` + AllLevels bool `json:"all_levels"` + Entries int `json:"entries"` + Page int `json:"page"` } var from FromList if err := decodeJSON(data, &from); err != nil { @@ -390,8 +392,8 @@ func ApiWarehouse(r *gin.RouterGroup) { } if from.ParentID != nil { query = query.Where("parent_id = ?", *from.ParentID) - } else if from.Search == "" { - // 无搜索时默认只显示顶级容器 + } else if from.Search == "" && !from.AllLevels { + // 无搜索时默认只显示顶级容器(all_levels=true 时返回所有层级) query = query.Where("parent_id IS NULL") } query.Count(&count) @@ -568,7 +570,6 @@ func ApiWarehouse(r *gin.RouterGroup) { SerialNumber: from.SerialNumber, Remark: from.Remark, Quantity: quantity, - CreatedAt: strconv.FormatInt(time.Now().Unix(), 10), CreatorID: user.ID, ContainerID: from.ContainerID, } diff --git a/frontend/ops_vue_js/src/i18n/en.json b/frontend/ops_vue_js/src/i18n/en.json index b10c009..db57124 100644 --- a/frontend/ops_vue_js/src/i18n/en.json +++ b/frontend/ops_vue_js/src/i18n/en.json @@ -131,6 +131,7 @@ "no_photos": "No photos", "status": "Status", "created_at": "Created At", + "updated_at": "Updated At", "filter_all": "All", "status_pending": "Pending", "status_checked": "Checked", @@ -171,6 +172,7 @@ "remark": "Remark", "remark_placeholder": "Enter remark (optional)", "created_at": "Created At", + "updated_at": "Updated At", "created_by": "Created By", "child_containers": "Sub-Containers", "items": "Items", diff --git a/frontend/ops_vue_js/src/i18n/zh-CN.json b/frontend/ops_vue_js/src/i18n/zh-CN.json index 48e388a..476a3c9 100644 --- a/frontend/ops_vue_js/src/i18n/zh-CN.json +++ b/frontend/ops_vue_js/src/i18n/zh-CN.json @@ -131,6 +131,7 @@ "no_photos": "暂无图片", "status": "状态", "created_at": "创建时间", + "updated_at": "更新时间", "filter_all": "全部", "status_pending": "待处理", "status_checked": "已检查", @@ -171,6 +172,7 @@ "remark": "备注", "remark_placeholder": "输入备注(可选)", "created_at": "创建日期", + "updated_at": "更新日期", "created_by": "创建人", "child_containers": "子容器", "items": "物品", diff --git a/frontend/ops_vue_js/src/views/warehouse/WarehouseContainerDetail.vue b/frontend/ops_vue_js/src/views/warehouse/WarehouseContainerDetail.vue index d26b1e2..7005800 100644 --- a/frontend/ops_vue_js/src/views/warehouse/WarehouseContainerDetail.vue +++ b/frontend/ops_vue_js/src/views/warehouse/WarehouseContainerDetail.vue @@ -390,6 +390,7 @@ onMounted(async () => { {{ usersStore.getUsernameFromUserID(container.CreatorID) }} {{ t('warehouse.created_at') }}: {{ fmtTs(container.CreatedAt) }} + {{ t('warehouse.updated_at') }}: {{ fmtTs(container.UpdatedAt) }} {{ t('warehouse.child_containers') }}: {{ container.ChildCount }} {{ t('warehouse.items') }}: {{ container.ItemCount }} @@ -428,13 +429,14 @@ onMounted(async () => {