style: 对商品管理页面进行名称样式修改
This commit is contained in:
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+2
-6
@@ -40,12 +40,8 @@ export const menus = [
|
||||
icon: 'Goods',
|
||||
children: [
|
||||
{
|
||||
path: '/product/list',
|
||||
title: '商品列表'
|
||||
},
|
||||
{
|
||||
path: '/product/group',
|
||||
title: '商品分组'
|
||||
path: '/product/manage',
|
||||
title: '商品管理'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
+12
-11
@@ -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'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
+21
-27
@@ -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();
|
||||
|
||||
+1783
-68
File diff suppressed because it is too large
Load Diff
@@ -45,14 +45,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- 统一的用户数据概览区域 -->
|
||||
<div class="profile-stats-unified">
|
||||
<div class="profile-stats">
|
||||
<!-- 余额数据 -->
|
||||
<div class="stat-item" v-for="balance in userBalanceList" :key="balance.id">
|
||||
<div class="stat-label">{{ balance.typeName }}</div>
|
||||
<div class="stat-value" :style="{ color: balance.typeColor }">{{ formatBalance(balance.price) }}</div>
|
||||
<div class="stat-value">{{ formatBalance(balance.price) }}</div>
|
||||
</div>
|
||||
<!-- 分隔线 -->
|
||||
<el-divider direction="vertical" v-if="userBalanceList.length > 0" class="stats-divider" />
|
||||
<!-- 其他状态数据 -->
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">实名状态</div>
|
||||
@@ -1478,7 +1476,7 @@ onActivated(() => {
|
||||
}
|
||||
|
||||
/* 统一的用户数据概览区域 */
|
||||
.profile-stats-unified {
|
||||
.profile-stats {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
@@ -1486,18 +1484,18 @@ onActivated(() => {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.profile-stats-unified .stat-item {
|
||||
.profile-stats .stat-item {
|
||||
text-align: center;
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
.profile-stats-unified .stat-label {
|
||||
.profile-stats .stat-label {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.profile-stats-unified .stat-value {
|
||||
.profile-stats .stat-value {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
@@ -1710,7 +1708,7 @@ onActivated(() => {
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.profile-stats-unified {
|
||||
.profile-stats {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
background: #f9fafc;
|
||||
@@ -1733,11 +1731,11 @@ onActivated(() => {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.profile-stats-unified {
|
||||
.profile-stats {
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.profile-stats-unified .stat-item {
|
||||
.profile-stats .stat-item {
|
||||
min-width: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user