diff --git a/api/index.js b/api/index.js index e8bab20..fb4d81b 100644 --- a/api/index.js +++ b/api/index.js @@ -1,31 +1,162 @@ /** - * API 接口汇总 - * 按模块分组管理接口 + * API 请求封装 + * 基于 uni.request,统一处理 Cookie 认证和错误 */ -// 用户相关 +import { useConfigStore } from '../stores/config' +// Storage Keys +const STORAGE_KEY_COOKIE_SESSION = 'userCookieSession' + +// 错误码定义 +const ERR_COOKIE_EXPIRED = 'userCookieError' + +/** + * 获取 API 基础地址 + */ +function getBaseUrl() { + const configStore = useConfigStore() + return configStore.getApiBaseUrl() +} + +/** + * 获取保存的 Cookie 值 + */ +function getCookie() { + try { + // 优先从 storage 读取(避免 Pinia 初始化时序问题) + const cookieStr = uni.getStorageSync(STORAGE_KEY_COOKIE_SESSION) + if (cookieStr) { + const cookie = JSON.parse(cookieStr) + return cookie?.Value ?? '' + } + } catch { + // ignore + } + return '' +} + +/** + * 请求封装 - 统一错误处理 + */ +function request(options) { + return new Promise((resolve, reject) => { + const baseUrl = getBaseUrl() + if (!baseUrl) { + reject({ errCode: -1, message: 'API地址未配置' }) + return + } + + // GET 请求不注入 cookie,POST 请求注入 cookie + let data = options.data || {} + if (options.method === 'POST') { + const cookie = getCookie() + // 后端期望格式: { data: {...业务数据}, userCookieValue: "xxx" } + data = { + data: data, // 业务数据包装在 data 字段 + userCookieValue: cookie || undefined + } + } + + uni.request({ + url: baseUrl + options.path, + method: options.method || 'GET', + data, + header: { + 'Content-Type': 'application/json' + }, + timeout: options.timeout || 10000, + success: (res) => { + const data = res.data + + // 检查 Cookie 过期 + if (data?.err_code === ERR_COOKIE_EXPIRED || data?.err_code === -44) { + // 清除存储的 Cookie + uni.removeStorageSync(STORAGE_KEY_COOKIE_SESSION) + uni.showToast({ title: '登录已过期,请重新登录', icon: 'none' }) + reject({ errCode: -44, message: '登录已过期' }) + return + } + + if (res.statusCode === 200) { + resolve({ + errCode: data?.err_code === 0 ? 0 : -1, + data: data?.return || null, + raw: data + }) + } else { + uni.showToast({ title: '服务异常', icon: 'none' }) + reject({ errCode: -1, message: '服务异常' }) + } + }, + fail: (err) => { + uni.showToast({ title: '网络错误', icon: 'none' }) + reject({ errCode: -2, message: '网络错误', detail: err }) + } + }) + }) +} + +/** + * API 接口汇总 + */ export const api = { /** - * GET 请求(一般不需要认证) + * GET 请求(不需要认证) */ - get(path) { - - }, + get(path, data = {}) { + return request({ + path, + method: 'GET', + data + }) + }, /** - * POST JSON + * POST JSON 请求(需要认证) */ - post(path, data = {},callback) { - console.log("post") - }, + post(path, data = {}) { + return request({ + path, + method: 'POST', + data // 业务数据,会被包装成 { data, userCookieValue } + }) + }, /** * POST FormData(文件上传) */ - upload(path, file) { - - }, + upload(path, fileData) { + return new Promise((resolve, reject) => { + const baseUrl = getBaseUrl() + const cookie = getCookie() + + uni.uploadFile({ + url: baseUrl + path, + filePath: fileData.uri, + name: fileData.name || 'file', + formData: { + cookie: cookie || '' + }, + success: (res) => { + try { + const data = JSON.parse(res.data) + resolve({ + errCode: data?.err_code === 0 ? 0 : -1, + data: data?.return || null, + raw: data + }) + } catch { + reject({ errCode: -1, message: '解析响应失败' }) + } + }, + fail: (err) => { + uni.showToast({ title: '上传失败', icon: 'none' }) + reject({ errCode: -2, message: '上传失败', detail: err }) + } + }) + }) + } } -export default api \ No newline at end of file +export default api diff --git a/api/purchase.js b/api/purchase.js new file mode 100644 index 0000000..2e2ab21 --- /dev/null +++ b/api/purchase.js @@ -0,0 +1,33 @@ +/** + * 采购订单 API + */ +import api from './index.js' + +export const purchaseApi = { + // 获取订单列表 + getOrders(data = {}) { + return api.post('/purchase/getorders', data) + }, + + // 获取订单详情 + getOrder(data = {}) { + return api.post('/purchase/getorder', data) + }, + + // 获取订单数量统计 + getOrderCount() { + return api.post('/purchase/getordercount', {}) + }, + + // 更新订单状态 + updateOrderStatus(data = {}) { + return api.post('/purchase/updatestatus', data) + }, + + // 新增订单 + addOrder(data = {}) { + return api.post('/purchase/addorder', data) + } +} + +export default purchaseApi diff --git a/api/schedule.js b/api/schedule.js new file mode 100644 index 0000000..b49ecb6 --- /dev/null +++ b/api/schedule.js @@ -0,0 +1,28 @@ +/** + * 日程管理 API + */ +import api from './index.js' + +export const scheduleApi = { + // 获取日程列表 + getEvents(data = {}) { + return api.post('/schedule/getevents', data) + }, + + // 新增日程 + addEvent(data = {}) { + return api.post('/schedule/addevent', data) + }, + + // 编辑日程 + editEvent(data = {}) { + return api.post('/schedule/editevent', data) + }, + + // 删除日程 + deleteEvent(data = {}) { + return api.post('/schedule/deleevent', data) + } +} + +export default scheduleApi diff --git a/api/user.js b/api/user.js index 532c7f6..a9656ba 100644 --- a/api/user.js +++ b/api/user.js @@ -1,9 +1,86 @@ -import api from "."; +/** + * 用户相关 API + */ +import api from './index' export const userApi = { - - login(username, password, remember) { - return api.post('/users/login', { username, password, remember }) - }, - -} \ No newline at end of file + /** + * 用户登录 + * @param {string} username - 用户名 + * @param {string} password - 密码 + * @param {boolean} remember - 是否记住登录 + * @returns {Promise<{errCode, data: {cookie}}>} + */ + login(username, password, remember = true) { + return api.post('/users/login', { + username, + password, + remember + }) + }, + + /** + * 用户注册 + * @param {string} username - 用户名 + * @param {string} email - 邮箱 + * @param {string} password - 密码 + */ + register(username, email, password) { + return api.post('/users/register', { + username, + useremail: email, + userpass: password + }) + }, + + /** + * 获取当前用户信息 + * @returns {Promise<{errCode, data: {user, userInfo}}>} + */ + getUserInfo() { + return api.post('/users/getinfo', {}) + }, + + /** + * 修改密码 + * @param {string} oldPass - 旧密码 + * @param {string} newPass - 新密码 + */ + changePassword(oldPass, newPass) { + return api.post('/users/changePassword', { + oldpass: oldPass, + newpass: newPass + }) + }, + + /** + * 修改邮箱 + * @param {string} newEmail - 新邮箱 + */ + changeEmail(newEmail) { + return api.post('/users/changeEmail', { + newemail: newEmail + }) + }, + + /** + * 更新用户信息 + * @param {Object} data - 用户信息 { username, remark, birthday } + */ + updateInfo(data) { + return api.post('/users/updateInfo', data) + }, + + /** + * 上传头像 + * @param {string} fileUri - 文件本地路径 + */ + updateAvatar(fileUri) { + return api.upload('/users/updateAvatar', { + name: 'file', + uri: fileUri + }) + } +} + +export default userApi diff --git a/api/users.js b/api/users.js new file mode 100644 index 0000000..19dcd18 --- /dev/null +++ b/api/users.js @@ -0,0 +1,13 @@ +/** + * 用户信息 API + */ +import api from './index.js' + +export const usersApi = { + // 通过用户ID获取用户信息(GET 请求) + getUserInfoFromUserID(userID) { + return api.get('/users/getuserinfo/' + userID) + } +} + +export default usersApi diff --git a/api/warehouse.js b/api/warehouse.js new file mode 100644 index 0000000..b2a3926 --- /dev/null +++ b/api/warehouse.js @@ -0,0 +1,66 @@ +import api from './index.js' + +export const warehouseApi = { + // 获取容器列表 + listContainer(params = {}) { + return api.post('/warehouse/list_container', params) + }, + + // 获取容器详情 + getContainer(id) { + return api.post('/warehouse/get_container', { id }) + }, + + // 新增容器 + addContainer(data) { + return api.post('/warehouse/add_container', data) + }, + + // 更新容器 + updateContainer(data) { + return api.post('/warehouse/update_container', data) + }, + + // 删除容器 + deleteContainer(id) { + return api.post('/warehouse/delete_container', { id }) + }, + + // 获取物品列表 + listItem(params = {}) { + return api.post('/warehouse/list_item', params) + }, + + // 获取物品详情 + getItem(id) { + return api.post('/warehouse/get_item', { id }) + }, + + // 新增物品 + addItem(data) { + return api.post('/warehouse/add_item', data) + }, + + // 更新物品 + updateItem(data) { + return api.post('/warehouse/update_item', data) + }, + + // 删除物品 + deleteItem(id) { + return api.post('/warehouse/delete_item', { id }) + }, + + // 移动物品 + moveItem(id, containerId) { + return api.post('/warehouse/move_item', { + item_id: id, + new_container: containerId + }) + }, + + // 获取仓库统计 + getStats() { + return api.post('/warehouse/get_stats', {}) + } +} diff --git a/api/work_order.js b/api/work_order.js new file mode 100644 index 0000000..f3b1664 --- /dev/null +++ b/api/work_order.js @@ -0,0 +1,43 @@ +import api from './index.js' + +export const workOrderApi = { + // 获取工单列表 + list(params = {}) { + return api.post('/work_order/list', params) + }, + + // 获取工单详情 + get(id) { + return api.post('/work_order/get', { id }) + }, + + // 获取工单统计 + getCount() { + return api.post('/work_order/count', {}) + }, + + // 新增工单 + add(data) { + return api.post('/work_order/add', data) + }, + + // 更新工单 + update(data) { + return api.post('/work_order/update', data) + }, + + // 删除工单 + delete(id) { + return api.post('/work_order/delete', { id }) + }, + + // 提交进度 + commit(data) { + return api.post('/work_order/commit', data) + }, + + // 搜索采购订单 + searchPurchaseOrders(query, limit = 10) { + return api.post('/work_order/search_purchase_orders', { search: query, limit }) + } +} diff --git a/manifest.json b/manifest.json index f5a5576..5e4e73f 100644 --- a/manifest.json +++ b/manifest.json @@ -68,5 +68,10 @@ "uniStatistics" : { "enable" : false }, - "vueVersion" : "3" + "vueVersion" : "3", + "h5" : { + "devServer" : { + "port" : 5174 + } + } } diff --git a/pages.json b/pages.json index 988f9b9..c1c62eb 100644 --- a/pages.json +++ b/pages.json @@ -13,9 +13,63 @@ } }, { - "path": "pages/message/message", + "path": "pages/order/order-detail", "style": { - "navigationBarTitleText": "消息" + "navigationBarTitleText": "订单详情" + } + }, + { + "path": "pages/order/order-add", + "style": { + "navigationBarTitleText": "新增订单" + } + }, + { + "path": "pages/workorder/workorder", + "style": { + "navigationBarTitleText": "工单" + } + }, + { + "path": "pages/workorder/add-workorder", + "style": { + "navigationBarTitleText": "新建工单" + } + }, + { + "path": "pages/workorder/show-workorder", + "style": { + "navigationBarTitleText": "工单详情" + } + }, + { + "path": "pages/workorder/edit-workorder", + "style": { + "navigationBarTitleText": "编辑工单" + } + }, + { + "path": "pages/warehouse/warehouse", + "style": { + "navigationBarTitleText": "仓库" + } + }, + { + "path": "pages/warehouse/item-detail", + "style": { + "navigationBarTitleText": "物品详情" + } + }, + { + "path": "pages/warehouse/add-item", + "style": { + "navigationBarTitleText": "新增物品" + } + }, + { + "path": "pages/warehouse/item-edit", + "style": { + "navigationBarTitleText": "编辑物品" } }, { @@ -35,6 +89,12 @@ "style": { "navigationBarTitleText": "设置" } + }, + { + "path": "pages/message/message", + "style": { + "navigationBarTitleText": "消息" + } } ], "globalStyle": { @@ -65,10 +125,16 @@ "text": "订单" }, { - "pagePath": "pages/message/message", - "iconPath": "static/tabbar/message.png", - "selectedIconPath": "static/tabbar/message-active.png", - "text": "消息" + "pagePath": "pages/workorder/workorder", + "iconPath": "static/tabbar/workorder.png", + "selectedIconPath": "static/tabbar/workorder-active.png", + "text": "工单" + }, + { + "pagePath": "pages/warehouse/warehouse", + "iconPath": "static/tabbar/warehouse.png", + "selectedIconPath": "static/tabbar/warehouse-active.png", + "text": "仓库" }, { "pagePath": "pages/user/user", diff --git a/pages/index/index.vue b/pages/index/index.vue index 9d520a7..82ca69b 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -1,23 +1,414 @@ diff --git a/pages/login/login.vue b/pages/login/login.vue index b5b9331..badd15f 100644 --- a/pages/login/login.vue +++ b/pages/login/login.vue @@ -1,67 +1,148 @@ @@ -69,14 +150,41 @@ const handleLogin = () => { .container { flex: 1; display: flex; - justify-content: center; + flex-direction: column; align-items: center; - background-color: #f5f5f5; - padding: 40rpx; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100vh; + padding: 100rpx 40rpx 40rpx; } +/* Logo 区域 */ +.logo-section { + display: flex; + flex-direction: column; + align-items: center; + margin-bottom: 80rpx; +} + +.logo-img { + width: 160rpx; + height: 160rpx; + margin-bottom: 20rpx; +} + +.app-name { + font-size: 40rpx; + font-weight: bold; + color: #FFFFFF; + letter-spacing: 4rpx; +} + +/* 表单区域 */ .form { width: 100%; + background-color: #FFFFFF; + border-radius: 20rpx; + padding: 40rpx; + box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.1); } .input { @@ -84,19 +192,56 @@ const handleLogin = () => { line-height: 90rpx; padding: 0 30rpx; margin-bottom: 30rpx; - background-color: #FFFFFF; + background-color: #F8F8F8; border-radius: 10rpx; font-size: 28rpx; } +.input:last-of-type { + margin-bottom: 20rpx; +} + +/* 记住登录 */ +.remember-row { + margin-bottom: 30rpx; +} + +.remember-label { + display: flex; + align-items: center; + font-size: 26rpx; + color: #666; +} + +.remember-label checkbox { + margin-right: 10rpx; +} + +/* 登录按钮 */ .submit-btn { width: 100%; height: 90rpx; line-height: 90rpx; - background-color: #007AFF; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #FFFFFF; font-size: 32rpx; + border-radius: 45rpx; + border: none; +} + +.submit-btn[disabled] { + background: #CCCCCC; +} + +/* 错误提示 */ +.error-tip { + width: 100%; + margin-top: 30rpx; + padding: 20rpx; + background-color: rgba(255, 255, 255, 0.9); border-radius: 10rpx; - margin-top: 20rpx; + text-align: center; + color: #FF3B30; + font-size: 26rpx; } diff --git a/pages/order/order-add.vue b/pages/order/order-add.vue new file mode 100644 index 0000000..2ad2362 --- /dev/null +++ b/pages/order/order-add.vue @@ -0,0 +1,679 @@ +