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
+1
View File
@@ -0,0 +1 @@
VITE_API_BASE_URL='https://apiservertest.s1f.ren'
-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;
}
}
+52 -17
View File
@@ -1,24 +1,59 @@
import { defineConfig } from 'vite'
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': resolve(__dirname, 'src')
}
},
optimizeDeps: {
include: ['monaco-editor']
},
// 清除系统代理环境变量,避免本地开发时系统代理(VPN/Clash等)干扰 vite proxy
delete process.env.HTTP_PROXY
delete process.env.HTTPS_PROXY
delete process.env.http_proxy
delete process.env.https_proxy
delete process.env.ALL_PROXY
delete process.env.all_proxy
// 设置 NO_PROXY 确保本地连接不走代理
process.env.NO_PROXY = 'localhost,127.0.0.1,::1'
process.env.no_proxy = 'localhost,127.0.0.1,::1'
build: {
rollupOptions: {
output: {
manualChunks: {
'monaco-editor': ['monaco-editor']
// https://vite.dev/config/
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
// 开发环境代理目标,默认测试服务器
const proxyTarget = env.VITE_API_BASE_URL || 'https://apiservertest.s1f.ren'
return {
plugins: [vue()],
resolve: {
alias: {
'@': resolve(__dirname, 'src')
}
},
optimizeDeps: {
include: ['monaco-editor']
},
server: {
// 强制绑定 IPv4 回环地址,避免 TUN/VPN 代理模式拦截 IPv6 或通配地址
host: '127.0.0.1',
port: 5174,
strictPort: false,
proxy: {
'/api': {
target: proxyTarget,
changeOrigin: true,
secure: false
},
'/acs': {
target: proxyTarget,
changeOrigin: true,
secure: false
}
}
},
build: {
rollupOptions: {
output: {
manualChunks: {
'monaco-editor': ['monaco-editor']
}
}
}
}
-221
View File
@@ -1,221 +0,0 @@
✅已完成、⚠️部分完成、❌未完成这样显示
-----------------------------------------------------------------------------------------------需要解决
1.管理员后台在配置管理展示的类型,有两行都是相同的,只需要展示一行 ✅已完成
2.,需要做相近字排序,比如前缀都是轮播图的展示在一起这样方便后续修改理解意思,还有是运用在同一处地方的可以展示在一起 ✅已完成
3.配置管理后台:表格table名称列的图标向下与文字保持平行 ✅已完成
4.table的值字段浮动不展示tip,并且点击可以打开对应的编辑弹窗 ✅已完成
5.将编辑配置的设置值的上传文件按钮进行删除,并且点击编辑修改文件图标打开的还得是imageSelector组件 ✅已完成
6.点击编辑配置的的修改图片的按钮,应该打开imageSelector.vue组件进行替换图片 ✅已完成
7.配置管理后台:表格table名称列的图标向下与文字保持平行,需要保持在同一行,只需要图标向下移动一点与文字保持水平 ✅已完成
8.现在这种不好看,需要图标在左边,文字在右边 ✅已完成
9.中间这个值列表,当是文字的时候,只展示一行,在配置管理的table表格中 ✅已完成
-----------------------------------------------------------------------------------------------需要解决
规则限制:
1.在-------需要解决的中间进行写是否解决问题的判断也可以列todolist便于查看是否完成
2.不要将我原本的问题进行删除了,只需要在范围的最后面添加todolist判断对应问题是否完成就可以了
3.在以后只要有添加时间选择器就要遵循以下规则
时间选择器接受的是毫秒级时间戳,获取时的时间转换,将ISO格式转换为毫秒级时间戳
- 列表展示使用formatDate函数
- 时间选择器正确显示毫秒级时间戳
- 提交时转换为秒级时间戳
4.后续每次写的页面,组件都需要兼容移动端
5.只要是关于选择用户,文件,优惠卷,代金卷的都使用组件
6.只要是弹窗需要使用选择组件的都参照用户列表的编辑用户的推介人的选择样式在弹窗中
7.如果给出需要访问的地址文档信息,自行访问下面的接口地址内容
## 一、项目结构 ✅
```
src/
├── api/ # API接口层
│ ├── admin/ # 管理后台API (12个文件)
│ │ ├── activity.js ├── cdn.js ├── discount.js
│ │ ├── file.js ├── group.js ├── order.js
│ │ ├── Permission.js ├── product.js ├── product-test.js
│ │ ├── router.js ├── setting.js └── user.js
│ ├── domain.js ├── groupBuy.js
│ ├── login.js └── ticket.js
├── components/ # 公共组件 (14个)
│ ├── admin/
│ │ ├── AvatarSelector.vue # 头像选择组件
│ │ └── FileSelector.vue # 文件/封面选择组件 ✅新增
│ ├── Charts/ # 图表组件 (4个)
│ ├── layout/ # 布局组件 (4个)
│ ├── marketing/DiscountDetailDialog.vue
│ ├── UserSelector/index.vue # 用户选择组件(推荐人选择)
│ ├── Container.vue ├── MonacoEditor.vue
│ ├── Qrcode.vue └── TextTruncate.vue
├── views/ # 页面视图 (67个)
├── store/ # 状态管理 (2个)
├── router/index.js # 路由配置
├── utils/ # 工具函数
│ └── acs/ # ACS云服务工具 (12个)
├── config/menus.js # 菜单配置
├── assets/ # 静态资源
├── App.vue # 应用入口
├── main.js # 主入口
└── style.css # 全局样式
```
## 二、API接口统计 ✅ (约240+个)
| 模块 | 文件 | 接口数 |
|------|------|--------|
| 登录认证 | login.js | 2 |
| 工单管理 | ticket.js | 12 |
| 域名白名单 | domain.js | 3 |
| 拼团管理 | groupBuy.js | 18 |
| 用户管理 | admin/user.js | 28 |
| 商品管理 | admin/product.js | 19 |
| 订单管理 | admin/order.js | 5 |
| 优惠码/代金券 | admin/discount.js | 21 |
| 权限管理 | admin/Permission.js | 8 |
| 管理员组 | admin/group.js | 6 |
| 配置管理 | admin/setting.js | 11 |
| 文件管理 | admin/file.js | 7 |
| 活动管理 | admin/activity.js | 8 |
| 路由管理 | admin/router.js | 2 |
| CDN管理 | admin/cdn.js | 1 |
| ACS服务器 | utils/acs/server.js | 60+ |
| ACS镜像 | utils/acs/mirror.js | 11 |
| ACS消息 | utils/acs/message.js | 8 |
| ACS审计 | utils/acs/audit.js | 4 |
| ACS远程桌面 | utils/acs/guacamole.js | 5 |
## 三、页面视图统计 ✅ (67个)
| 分类 | 页面 | 数量 |
|------|------|------|
| 基础页面 | Login, Dashboard, NotFound, Redirect, About, Home | 6 |
| 用户管理 | UserList, UserDetail, UserBalance, UserGroup, AdminGroup | 5 |
| 商品管理 | ProductList, ProductGroup | 2 |
| 订单管理 | OrderList | 1 |
| 优惠营销 | DiscountCode, Voucher, VoucherManagement + 6个子页面 | 9 |
| 活动管理 | SigninActivity, GroupBuyActivity, GroupBuyType | 3 |
| 工单管理 | TicketList, TicketDetail, TicketChat | 3 |
| 系统管理 | PermissionRoute/Admin, SystemFile, DomainWhitelist, SettingGroup, Setting, OperationLog, Users | 8 |
| 站点审计 | AllSites, ViolationSites | 2 |
| 全局设置 | GlobalSetting | 1 |
| 个人中心 | UserInfo, ChangePassword | 2 |
| ACS消息 | Announcements, Policies, News | 3 |
| ACS镜像 | VmImages, ContainerImages, ImageCategories, ImageForm, ImageRequests | 5 |
| ACS节点 | Nodes, server, ServerForm, VmDetail, containDetail, containerConsole, containFile + 2组件 | 9 |
| ACS远程桌面 | Guacamole | 1 |
## 四、CSS样式统计 ✅
### style.css 全局样式
| 类型 | 内容 |
|------|------|
| 重置样式 | `* { margin:0; padding:0; box-sizing:border-box; }` |
| 文字颜色 | `.text-primary` `.text-success` `.text-warning` `.text-danger` `.text-info` |
| 文字对齐 | `.text-center` `.text-left` `.text-right` |
| Flex布局 | `.flex` `.flex-column` `.justify-center` `.justify-between` `.items-center` |
| 尺寸 | `.w-full` `.h-full` |
| 间距 | `.mb-10` `.mt-10` `.mr-10` `.ml-10` `.p-10` `.py-10` `.px-10` |
| 响应式 | `.hidden-xs` `.hidden-sm` `.hidden-md` `.hidden-lg` |
### App.vue 全局样式
| 类型 | 内容 |
|------|------|
| 过渡动画 | `.fade-enter-active` `.fade-leave-active` |
| 滚动条 | `::-webkit-scrollbar` 自定义滚动条 |
| 按钮扁平化 | `.el-button--primary/success/danger/default` 无圆角深蓝灰主题 |
| 输入框扁平化 | `.el-input__wrapper` 无圆角边框 |
| 标签扁平化 | `.el-tag--success/danger/info` 无圆角彩色背景 |
| 卡片扁平化 | `.el-card` 无圆角1px边框 |
| 表格扁平化 | `.el-table` 深蓝灰表头 |
| 分页扁平化 | `.el-pagination` 无圆角深蓝灰选中 |
| 下拉菜单 | `.el-dropdown-menu` 无圆角阴影边框 |
| 对话框 | `.el-dialog` 无圆角扁平化头部尾部 |
## 五、全局主题配置 ✅
### 主色调
| 颜色 | 值 | 用途 |
|------|------|------|
| 主色 | `#1890ff` | Element Plus主题色 |
| 深蓝灰 | `#2c3e50` | 按钮主色、表头、激活状态 |
| 次深蓝灰 | `#34495e` | 悬浮状态、文字 |
| 绿色 | `#27ae60` / `#52c41a` | 成功状态 |
| 红色 | `#e74c3c` / `#f5222d` | 危险/错误状态 |
| 橙色 | `#faad14` | 警告状态 |
| 蓝色 | `#3498db` | 链接、信息 |
| 紫色 | `#722ed1` | 转化率图表 |
| 背景灰 | `#f5f7fa` / `#f0f2f5` | 页面背景 |
| 边框灰 | `#e1e8ed` / `#d5d9e0` | 边框分割线 |
### 设计风格
| 特性 | 说明 |
|------|------|
| 圆角 | 全局0圆角扁平化设计 |
| 阴影 | 轻阴影 `0 2px 8px rgba(44,62,80,0.1)` |
| 字体 | Helvetica Neue, PingFang SC, Microsoft YaHei |
| 字号 | 基础14px |
## 六、Store状态管理 ✅
| Store | 功能 |
|-------|------|
| userStore.js | 用户信息存储 `userInfo` `setUserInfo()` |
| tagsViewStore.js | 多标签页管理 `visitedViews` `addVisitedView()` `delVisitedView()` `delOthersViews()` `delAllViews()` `delLeftViews()` `delRightViews()` |
## 七、技术栈 ✅
| 技术 | 版本 |
|------|------|
| Vue | ^3.5.13 |
| Vite | ^6.0.3 |
| Element Plus | ^2.9.1 |
| Vue Router | ^4.5.0 |
| Pinia | ^3.0.2 |
| Axios | ^1.7.9 |
| ECharts | ^5.6.0 |
| VueUse | ^13.1.0 |
| Monaco Editor | ^0.52.2 |
| Xterm.js | ^5.3.0 |
| qrcode | ^1.5.4 |
| dayjs | ^1.11.13 |
## 表单组件优化 ✅已完成
### 1. FileSelector 文件选择组件 ✅
位置: `src/components/admin/FileSelector.vue`
功能:
- 通用文件/封面选择器
- 支持文件列表分页、搜索
- 支持文件上传
- 支持图片预览
- 支持按文件类型筛选(image/document/video/audio)
- 已选文件信息显示
### 2. UserSelector 用户选择组件 ✅
位置: `src/components/UserSelector/index.vue`
功能:
- 用户列表搜索选择
- 用于推荐人ID等需要选择用户的场景
- 显示用户ID、用户名、邮箱
### 3. UserDetail 编辑表单优化 ✅
- 推荐人ID: 改为使用UserSelector组件,显示用户头像、用户名、ID
- 用户封面: 新增FileSelector组件,支持预览和选择图片
-----------------------------------------------------------------------------------------------需要解决
每次解决后的内容写在-需要解决之间,不要写在外面
我不要你解释,不是我主动告诉你解释需求,那么你就根据问题开始直接编写代码解决问题或者完善功能,问题都是用-需要解决隔开的
对应完成的部分在当前文档记录并且进行标记✅已完成、⚠️部分完成、❌未完成这样显示