style: 对商品管理页面进行名称样式修改
Build and Deploy Vue3 / build (push) Successful in 1m15s
Build and Deploy Vue3 / deploy (push) Successful in 54s

This commit is contained in:
2026-03-13 14:07:09 +08:00
parent 3e751d4c42
commit d650bfeb61
10 changed files with 1934 additions and 439 deletions
-79
View File
@@ -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()
}
+55
View File
@@ -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
View File
@@ -40,12 +40,8 @@ export const menus = [
icon: 'Goods',
children: [
{
path: '/product/list',
title: '商品列表'
},
{
path: '/product/group',
title: '商品分组'
path: '/product/manage',
title: '商品管理'
}
]
},
+12 -11
View File
@@ -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
View File
@@ -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();
File diff suppressed because it is too large Load Diff
+9 -11
View File
@@ -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;
}
}