diff --git a/.env b/.env new file mode 100644 index 0000000..7a30cbc --- /dev/null +++ b/.env @@ -0,0 +1 @@ +VITE_API_BASE_URL='https://apiservertest.s1f.ren' \ No newline at end of file diff --git a/src/api/admin/product-test.js b/src/api/admin/product-test.js deleted file mode 100644 index 1d1c49b..0000000 --- a/src/api/admin/product-test.js +++ /dev/null @@ -1,79 +0,0 @@ -// 商品管理 API 接口测试文件 -// 此文件用于验证所有接口是否正确对接 OpenAPI 文档 - -import { - // 商品分组管理 - getProductGroupList, - createProductGroup, - updateProductGroup, - hideProductGroup, - startProductGroup, - deleteProductGroup, - - // 商品管理 - getProductList, - getProductTagList, - createProduct, - updateProduct, - deleteProduct, - - // 商品参数管理 - getProductParameterList, - createProductParameter, - getProductParameterDetail, - updateProductParameter, - deleteProductParameter, - addProductParameterValue, - deleteProductParameterValue, - updateProductParameterValue -} from './product' - -/** - * 商品管理 API 接口对接验证 - * - * 根据 OpenAPI 文档,所有接口已完整对接: - * - * 1. 商品分组管理 (6个接口) - * ✅ GET /api/v1/admin/good/group/list - 获取商品分组列表 - * ✅ POST /api/v1/admin/good/group/create - 创建商品分组 - * ✅ POST /api/v1/admin/good/group/update - 更新商品分组 - * ✅ POST /api/v1/admin/good/group/disable - 隐藏商品组 - * ✅ POST /api/v1/admin/good/group/enable - 启用商品组 - * ✅ DELETE /api/v1/admin/good/group/delete - 删除商品分组 - * - * 2. 商品管理 (4个接口) - * ✅ GET /api/v1/admin/good/goods/list - 获取商品列表 - * ✅ GET /api/v1/admin/good/goods/tag_list - 获取商品标签列表 - * ✅ POST /api/v1/admin/good/goods/create - 创建商品 - * ✅ POST /api/v1/admin/good/goods/update - 更新商品 - * ✅ DELETE /api/v1/admin/good/goods/delete - 删除商品 - * - * 3. 商品参数管理 (8个接口) - * ✅ GET /api/v1/admin/good/spec/list - 获取商品参数列表 - * ✅ POST /api/v1/admin/good/spec/create - 创建商品参数 - * ✅ GET /api/v1/admin/good/spec/detail - 获取商品参数详情 - * ✅ POST /api/v1/admin/good/spec/update - 更新商品参数 - * ✅ DELETE /api/v1/admin/good/spec/delete - 删除商品参数 - * ✅ POST /api/v1/admin/good/spec/add_value - 增加商品参数值 - * ✅ DELETE /api/v1/admin/good/spec/delete_value - 删除商品参数值 - * ✅ POST /api/v1/admin/good/spec/update_value - 更新商品参数值 - * - * 总计:18个接口全部对接完成 - * - * 页面实现状态: - * ✅ ProductList.vue - 商品列表管理页面(包含商品参数管理) - * ✅ ProductGroup.vue - 商品分组管理页面 - * - * 注意事项: - * 1. 所有 POST/DELETE 接口使用 multipart/form-data 格式 - * 2. 更新商品参数接口使用 query 参数而非 body - * 3. 价格字段以分为单位存储 - * 4. 商品标签从 tag_list 接口获取 - */ - -export const API_STATUS = { - totalApis: 18, - implementedApis: 18, - completionRate: '100%', - lastUpdated: new Date().toISOString() -} \ No newline at end of file diff --git a/src/config/env.js b/src/config/env.js new file mode 100644 index 0000000..b91a842 --- /dev/null +++ b/src/config/env.js @@ -0,0 +1,55 @@ +/** + * 环境配置文件 + * 所有硬编码的 URL / 域名 / 环境变量统一在此管理 + */ + +// 当前环境 +const isDevelopment = import.meta.env.MODE === 'development' + +// API 基础地址 +// 开发环境使用 vite 代理 (baseUrl 为空),生产环境使用实际地址 +const API_BASE_MAP = { + development: '', // 开发环境通过 vite proxy 代理 + production: import.meta.env.VITE_API_BASE_URL || 'https://cloudapi.007yjs.com', + staging: import.meta.env.VITE_API_BASE_URL || 'https://apiservertest.s1f.ren' +} + +// 获取当前环境的 API 基础地址 +const currentEnv = import.meta.env.VITE_APP_ENV || import.meta.env.MODE || 'development' +export const baseUrl = API_BASE_MAP[currentEnv] || API_BASE_MAP.development + +// ACS 服务基础地址 +export const acsBaseUrl = isDevelopment ? '' : baseUrl + +// 网站标题 +export const siteTitle = '007UI管理系统' + +// 请求超时时间(毫秒) +export const requestTimeout = 50000 +export const acsRequestTimeout = 30000 + +// Token 存储键名 +export const TOKEN_KEY = 'token' +export const TOKEN_EXPIRE_KEY = 'tokenExpire' +export const USER_INFO_KEY = 'userInfo' + +// 不需要 token 认证的 URL 前缀 +export const noAuthUrls = [ + '/v1/user/login', + '/v1/user/check/get_code_img', + '/v1/user/register', + '/v1/user/refresh_token' +] + +export default { + isDevelopment, + baseUrl, + acsBaseUrl, + siteTitle, + requestTimeout, + acsRequestTimeout, + TOKEN_KEY, + TOKEN_EXPIRE_KEY, + USER_INFO_KEY, + noAuthUrls +} diff --git a/src/config/menus.js b/src/config/menus.js index dbb2875..87c1f83 100644 --- a/src/config/menus.js +++ b/src/config/menus.js @@ -40,12 +40,8 @@ export const menus = [ icon: 'Goods', children: [ { - path: '/product/list', - title: '商品列表' - }, - { - path: '/product/group', - title: '商品分组' + path: '/product/manage', + title: '商品管理' } ] }, diff --git a/src/router/index.js b/src/router/index.js index c4c0daa..0f8db57 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -224,7 +224,7 @@ const routes = [ } ] }, - // 商品管理路由 + // 商品管理路由(已合并商品列表和商品分组到统一树形视图) { path: 'product', name: 'Product', @@ -232,23 +232,24 @@ const routes = [ title: '商品管理', icon: 'Goods' }, - redirect: '/product/list', + redirect: '/product/manage', children: [ { - path: 'list', - name: 'ProductList', - component: () => import('../views/product/ProductList.vue'), + path: 'manage', + name: 'ProductManage', + component: () => import('../views/product/ProductGroup.vue'), meta: { - title: '商品列表' + title: '商品管理' } }, + { + // 保留旧路由兼容,重定向到新路由 + path: 'list', + redirect: '/product/manage' + }, { path: 'group', - name: 'ProductGroup', - component: () => import('../views/product/ProductGroup.vue'), - meta: { - title: '商品分组' - } + redirect: '/product/manage' } ] }, diff --git a/src/utils/request.js b/src/utils/request.js index 7a39598..2ea6943 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -2,23 +2,17 @@ import axios from 'axios' import { ElMessage } from 'element-plus' import router from '@/router' import {getRefreshToken,refreshAccessToken} from "@/api/login.js"; - -// 基础URL -const baseUrl = 'https://apiservertest.s1f.ren' // SSL证书有问题 -// const baseUrl = 'http://apiservertest.s1f.ren' // HTTP版本 -// const baseUrl = 'https://cloudapi.007yjs.com' // 尝试备用地址 +import { baseUrl, acsBaseUrl, noAuthUrls as noAuthUrlList, requestTimeout, acsRequestTimeout, TOKEN_KEY, TOKEN_EXPIRE_KEY, USER_INFO_KEY } from '@/config/env.js' // 检查URL是否需要认证 const urlNeedAuth = (url) => { - // 这里可以添加不需要认证的URL列表 - const noAuthUrls = ['/v1/user/login', '/v1/user/check/get_code_img', '/v1/user/register', '/v1/user/refresh_token'] - return !noAuthUrls.some(noAuthUrl => url.includes(noAuthUrl)) + return !noAuthUrlList.some(noAuthUrl => url.includes(noAuthUrl)) } // 检查token是否过期 const isTokenExpired = () => { - const token = localStorage.getItem('token') - const expire = localStorage.getItem('tokenExpire') + const token = localStorage.getItem(TOKEN_KEY) + const expire = localStorage.getItem(TOKEN_EXPIRE_KEY) if (!token) return true // 检查过期时间 @@ -34,7 +28,7 @@ const isTokenExpired = () => { // 检查token是否即将过期(5分钟内) const isTokenExpiringSoon = () => { - const expire = localStorage.getItem('tokenExpire') + const expire = localStorage.getItem(TOKEN_EXPIRE_KEY) if (!expire) return false const expireTime = parseInt(expire) * 1000 // 转换为毫秒 @@ -81,26 +75,26 @@ const doRefreshToken = async () => { if (newTokenRes.data?.code === 200 && newTokenRes.data?.data?.token) { const { token, expire } = newTokenRes.data.data - localStorage.setItem('token', token) + localStorage.setItem(TOKEN_KEY, token) if (expire) { - localStorage.setItem('tokenExpire', expire.toString()) + localStorage.setItem(TOKEN_EXPIRE_KEY, expire.toString()) } return token } } // 刷新失败,触发登出逻辑 - localStorage.removeItem('token') - localStorage.removeItem('tokenExpire') - localStorage.removeItem('userInfo') + localStorage.removeItem(TOKEN_KEY) + localStorage.removeItem(TOKEN_EXPIRE_KEY) + localStorage.removeItem(USER_INFO_KEY) ElMessage.warning('登录过期,请重新登录') router.push('/login') return null } catch (error) { console.error('Token刷新失败:', error) // 刷新失败,触发登出逻辑 - localStorage.removeItem('token') - localStorage.removeItem('tokenExpire') - localStorage.removeItem('userInfo') + localStorage.removeItem(TOKEN_KEY) + localStorage.removeItem(TOKEN_EXPIRE_KEY) + localStorage.removeItem(USER_INFO_KEY) ElMessage.warning('登录过期,请重新登录') router.push('/login') return null @@ -121,7 +115,7 @@ class Request { (config) => { // 在发送请求之前做些什么 // 例如:添加 token - const token = localStorage.getItem('token') + const token = localStorage.getItem(TOKEN_KEY) if (token) { config.headers.Authorization = `Bearer ${token}` } @@ -190,7 +184,7 @@ class Request { // 创建默认实例 const request = new Request({ baseURL: baseUrl, - timeout: 50000, + timeout: requestTimeout, headers: { 'Content-Type': 'multipart/form-data' } @@ -201,21 +195,21 @@ export const baseURL = baseUrl export const http2 = axios.create({ baseURL: baseUrl, - timeout: 30000, + timeout: acsRequestTimeout, headers: {}, }); http2.interceptors.request.use(async config => { - const token = localStorage.getItem('token') + const token = localStorage.getItem(TOKEN_KEY) // 检查是否需要认证 if (urlNeedAuth(config.url)) { // 检查token是否已过期 if (isTokenExpired()) { if (token) { - localStorage.removeItem('token') - localStorage.removeItem('tokenExpire') - localStorage.removeItem('userInfo') + localStorage.removeItem(TOKEN_KEY) + localStorage.removeItem(TOKEN_EXPIRE_KEY) + localStorage.removeItem(USER_INFO_KEY) ElMessage.warning('登录过期,请重新登录') } router.push('/login') @@ -275,7 +269,7 @@ http2.interceptors.response.use( } const { status } = error.response; if (status === 401) { - localStorage.removeItem('token'); + localStorage.removeItem(TOKEN_KEY); ElMessage.warning('登陆过期,请重新登陆') router.push('/login') return Promise.reject(); diff --git a/src/views/product/ProductGroup.vue b/src/views/product/ProductGroup.vue index b8dd4e1..456f675 100644 --- a/src/views/product/ProductGroup.vue +++ b/src/views/product/ProductGroup.vue @@ -44,6 +44,9 @@ 新增顶级分组 + + 新增商品 + 刷新 @@ -59,7 +62,6 @@ - @@ -87,12 +89,12 @@ row-key="id" :header-cell-style="{ background: '#fafafa', color: '#333', fontWeight: 600 }" > - + - + + + + + + + + + -