From 5146e9847983ee661214e3f4a1ee0fb4947508f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E6=96=87=E5=B3=B0?= Date: Thu, 16 Apr 2026 18:55:11 +0800 Subject: [PATCH] up --- .workbuddy/expert-history.json | 17 + .workbuddy/memory/2026-04-16.md | 76 ++ .workbuddy/memory/MEMORY.md | 0 frontend/ops_uniapp/@api/auth.js | 42 + frontend/ops_uniapp/@api/purchase.js | 42 + frontend/ops_uniapp/@api/request.js | 97 ++ frontend/ops_uniapp/@api/schedule.js | 27 + frontend/ops_uniapp/App.vue | 15 +- frontend/ops_uniapp/manifest.json | 13 +- frontend/ops_uniapp/pages.json | 89 +- frontend/ops_uniapp/pages/index/index.vue | 523 ++++++++++- frontend/ops_uniapp/pages/purchase/list.vue | 827 ++++++++++++++++++ .../ops_uniapp/pages/schedule/schedule.vue | 627 +++++++++++++ frontend/ops_uniapp/pages/setting/my_info.vue | 806 +++++++++-------- frontend/ops_uniapp/pages/signin.vue | 471 ++++++---- frontend/ops_uniapp/store/user.js | 136 +++ frontend/ops_uniapp/vite.config.js | 37 + 17 files changed, 3171 insertions(+), 674 deletions(-) create mode 100644 .workbuddy/expert-history.json create mode 100644 .workbuddy/memory/2026-04-16.md create mode 100644 .workbuddy/memory/MEMORY.md create mode 100644 frontend/ops_uniapp/@api/auth.js create mode 100644 frontend/ops_uniapp/@api/purchase.js create mode 100644 frontend/ops_uniapp/@api/request.js create mode 100644 frontend/ops_uniapp/@api/schedule.js create mode 100644 frontend/ops_uniapp/pages/purchase/list.vue create mode 100644 frontend/ops_uniapp/pages/schedule/schedule.vue create mode 100644 frontend/ops_uniapp/store/user.js create mode 100644 frontend/ops_uniapp/vite.config.js diff --git a/.workbuddy/expert-history.json b/.workbuddy/expert-history.json new file mode 100644 index 0000000..2b429f0 --- /dev/null +++ b/.workbuddy/expert-history.json @@ -0,0 +1,17 @@ +{ + "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 new file mode 100644 index 0000000..ae05f9d --- /dev/null +++ b/.workbuddy/memory/2026-04-16.md @@ -0,0 +1,76 @@ +# 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/MEMORY.md b/.workbuddy/memory/MEMORY.md new file mode 100644 index 0000000..e69de29 diff --git a/frontend/ops_uniapp/@api/auth.js b/frontend/ops_uniapp/@api/auth.js new file mode 100644 index 0000000..0ec05dd --- /dev/null +++ b/frontend/ops_uniapp/@api/auth.js @@ -0,0 +1,42 @@ +/** + * 认证相关 API + * 对标 PC 前端 src/api/auth.js + */ +import { request } from './request.js' + +export const authApi = { + /** 登录 */ + login(username, password, remember = false) { + return request.post('/users/login', { username, password, remember }) + }, + + /** 注册 */ + register(username, email, password) { + return request.post('/users/register', { username, useremail: email, userpass: password }) + }, + + /** 通过 cookie 获取用户信息 */ + getUserInfo() { + return request.post('/users/getinfo', {}) + }, + + /** 修改密码 */ + changePassword(oldPass, newPass) { + return request.post('/users/changePassword', { oldpass: oldPass, newpass: newPass }) + }, + + /** 修改邮箱 */ + changeEmail(newEmail) { + return request.post('/users/changeEmail', { newemail: newEmail }) + }, + + /** 修改用户信息 */ + updateInfo(data) { + return request.post('/users/updateInfo', data) + }, + + /** 更新头像(文件上传) */ + updateAvatar(filePath) { + return request.upload('/users/updateAvatar', filePath) + }, +} diff --git a/frontend/ops_uniapp/@api/purchase.js b/frontend/ops_uniapp/@api/purchase.js new file mode 100644 index 0000000..68de33e --- /dev/null +++ b/frontend/ops_uniapp/@api/purchase.js @@ -0,0 +1,42 @@ +/** + * 采购订单相关 API + * 对标 PC 前端 src/api/purchase.js + */ +import { request } from './request.js' + +export const purchaseApi = { + /** 获取订单列表(分页+搜索+状态过滤) */ + getOrders(params = {}) { + return request.post('/purchase/getorders', params) + }, + + /** 获取单个订单详情 */ + getOrder(id) { + return request.post('/purchase/getorder', { id }) + }, + + /** 获取各状态订单数量统计 */ + getOrderCount() { + return request.post('/purchase/getordercount', {}) + }, + + /** 新增订单 */ + addOrder(data) { + return request.post('/purchase/addorder', data) + }, + + /** 更新订单 */ + updateOrder(data) { + return request.post('/purchase/updateorder', data) + }, + + /** 更新订单状态 */ + updateStatus(data) { + return request.post('/purchase/updatestatus', data) + }, + + /** 删除订单 */ + deleteOrder(id) { + return request.post('/purchase/deleteorder', { id }) + }, +} diff --git a/frontend/ops_uniapp/@api/request.js b/frontend/ops_uniapp/@api/request.js new file mode 100644 index 0000000..587db9f --- /dev/null +++ b/frontend/ops_uniapp/@api/request.js @@ -0,0 +1,97 @@ +/** + * 统一网络请求封装 + * 对标 PC 前端 src/api/index.js 的设计 + * - 自动注入 cookie + * - 统一解析 err_code / return 字段 + * - Cookie 过期自动清理并跳转登录 + */ + +import { userStore } from '../store/user.js' + +const API_BASE = '/api' + +/** + * 底层 POST 请求 + * @param {string} path - 接口路径,如 /users/login + * @param {object} data - 业务数据(会被包在 data 字段下) + * @returns {Promise<{errCode, data, raw}>} + */ +function post(path, data = {}) { + const body = { data } + + // 自动注入 cookie + const cookieValue = userStore.getCookieValue() + if (cookieValue) { + body.userCookieValue = cookieValue + } + + return new Promise((resolve, reject) => { + uni.request({ + url: API_BASE + path, + method: 'POST', + header: { 'Content-Type': 'application/json' }, + data: body, + timeout: 15000, + success(res) { + const raw = res.data + const errCode = raw?.err_code ?? -1 + + // Cookie 过期(err_code === -44),自动登出并跳转登录 + if (errCode === -44) { + userStore.logout() + uni.reLaunch({ url: '/pages/signin' }) + reject(new Error('Cookie expired')) + return + } + + resolve({ + errCode, + data: raw?.return ?? null, + raw, + }) + }, + fail(err) { + uni.showToast({ title: '网络连接失败', icon: 'none' }) + reject(err) + }, + }) + }) +} + +/** + * 上传文件(FormData) + * @param {string} path - 接口路径 + * @param {string} filePath - 本地文件路径(uni.chooseImage 返回的 tempFilePaths[0]) + * @param {string} name - 文件字段名,默认 'file' + */ +function upload(path, filePath, name = 'file') { + const cookieValue = userStore.getCookieValue() + + return new Promise((resolve, reject) => { + uni.uploadFile({ + url: API_BASE + path, + filePath, + name, + formData: cookieValue ? { cookie: cookieValue } : {}, + timeout: 30000, + success(res) { + try { + const raw = JSON.parse(res.data) + resolve({ + errCode: raw?.err_code ?? -1, + data: raw?.return ?? null, + raw, + }) + } catch { + reject(new Error('JSON parse error')) + } + }, + fail(err) { + uni.showToast({ title: '上传失败', icon: 'none' }) + reject(err) + }, + }) + }) +} + +export const request = { post, upload } diff --git a/frontend/ops_uniapp/@api/schedule.js b/frontend/ops_uniapp/@api/schedule.js new file mode 100644 index 0000000..89bbff0 --- /dev/null +++ b/frontend/ops_uniapp/@api/schedule.js @@ -0,0 +1,27 @@ +/** + * 日程相关 API + * 对标 PC 前端 src/api/schedule.js + */ +import { request } from './request.js' + +export const scheduleApi = { + /** 获取日程列表 */ + getEvents(params = {}) { + return request.post('/schedule/getevents', params) + }, + + /** 新增日程 */ + addEvent(data) { + return request.post('/schedule/addevent', data) + }, + + /** 编辑日程 */ + editEvent(data) { + return request.post('/schedule/editevent', data) + }, + + /** 删除日程 */ + deleEvent(data) { + return request.post('/schedule/deleevent', data) + }, +} diff --git a/frontend/ops_uniapp/App.vue b/frontend/ops_uniapp/App.vue index 6fc6d96..f61f9c5 100644 --- a/frontend/ops_uniapp/App.vue +++ b/frontend/ops_uniapp/App.vue @@ -1,17 +1,14 @@ diff --git a/frontend/ops_uniapp/manifest.json b/frontend/ops_uniapp/manifest.json index 771779a..49fc04e 100644 --- a/frontend/ops_uniapp/manifest.json +++ b/frontend/ops_uniapp/manifest.json @@ -71,18 +71,7 @@ "vueVersion" : "3", "h5" : { "devServer" : { - "disableHostCheck" : true, - "proxy" : { - "/api" : { - "target" : "http://127.0.0.1:8080", - "changeOrigin" : true, - "secure" : false, - "ws": false, - "pathRewrite" : { - "^/api" : "" - } - } - } + "disableHostCheck" : true } } diff --git a/frontend/ops_uniapp/pages.json b/frontend/ops_uniapp/pages.json index ed73c64..a597c7d 100644 --- a/frontend/ops_uniapp/pages.json +++ b/frontend/ops_uniapp/pages.json @@ -1,46 +1,47 @@ { - "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages - { - "path": "pages/index/index", - "style": { - "navigationBarTitleText": "uni-app" - } - }, - { - "path" : "pages/test/test", - "style" : - { - "navigationBarTitleText" : "" - } - }, - { - "path" : "pages/setting/my_info", - "style" : - { - "navigationBarTitleText" : "" - } - }, - { - "path" : "pages/signin", - "style" : - { - "navigationBarTitleText" : "" - } - }, - { - "path" : "components/setting-menu/setting-menu", - "style" : - { - "navigationBarTitleText" : "" - } - } - ], - "globalStyle": { - "navigationBarTextStyle": "black", - "navigationBarTitleText": "uni-app", - "navigationBarBackgroundColor": "#F8F8F8", - "backgroundColor": "#F8F8F8", - "navigationStyle": "custom" - }, - "uniIdRouter": {} + "pages": [ + { + "path": "pages/index/index", + "style": { + "navigationBarTitleText": "Operations", + "navigationStyle": "custom" + } + }, + { + "path": "pages/signin", + "style": { + "navigationBarTitleText": "登录", + "navigationStyle": "custom" + } + }, + { + "path": "pages/setting/my_info", + "style": { + "navigationBarTitleText": "个人设置", + "navigationStyle": "custom" + } + }, + { + "path": "pages/schedule/schedule", + "style": { + "navigationBarTitleText": "日程", + "navigationStyle": "custom" + } + }, + { + "path": "pages/purchase/list", + "style": { + "navigationBarTitleText": "采购订单", + "navigationStyle": "custom" + } + } + ], + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "Operations", + "navigationBarBackgroundColor": "#FFFFFF", + "backgroundColor": "#F3F4F6", + "navigationStyle": "custom" + }, + "uniIdRouter": {} } diff --git a/frontend/ops_uniapp/pages/index/index.vue b/frontend/ops_uniapp/pages/index/index.vue index 134d8ca..078ccc2 100644 --- a/frontend/ops_uniapp/pages/index/index.vue +++ b/frontend/ops_uniapp/pages/index/index.vue @@ -1,55 +1,484 @@ - - - \ No newline at end of file + + + + {{ isLoggedIn ? `你好,${displayName}` : '欢迎使用' }} + + {{ todayDisplay }} + + + + + + + + 📅 + + 日程 + + 今日 {{ todaySchedules.length }} 项 + + + + + 加载中… + + + + + + + + {{ getColorLabel(item.BgColor) }} + + + + {{ item.Title }} + + {{ formatDateRange(item.StartDate, item.EndDate) }} + + + + + + + + 今日暂无日程 + + + + 查看全部 › + + + + + + + + + 🛒 + + 采购订单 + + + + + + + {{ loadingOrders ? '…' : pendingOrderCount || '—' }} + + 待处理 + + + + 登录后查看 + + + + 查看全部 › + + + + + + + + diff --git a/frontend/ops_uniapp/pages/purchase/list.vue b/frontend/ops_uniapp/pages/purchase/list.vue new file mode 100644 index 0000000..06b663c --- /dev/null +++ b/frontend/ops_uniapp/pages/purchase/list.vue @@ -0,0 +1,827 @@ + + +