Selaa lähdekoodia

管理后台前端的重构调整

lingmin_package@163.com 2 kuukautta sitten
vanhempi
sitoutus
04d0959c0d

+ 23 - 0
.env.dev

@@ -0,0 +1,23 @@
+# 开发环境配置
+VITE_APP_TITLE=管理平台 - 开发环境
+VITE_API_BASE_URL=http://192.168.92.61
+VITE_APP_ENV=development
+VITE_APP_DEBUG=true
+
+# 后端服务地址
+VITE_BACKEND_HOST=localhost
+VITE_BACKEND_PORT=8000
+VITE_BACKEND_PROTOCOL=http
+
+# OAuth 配置
+VITE_OAUTH_CLIENT_ID=eqhoIdAyAWbA8MsYHsNqQqNLJbCayTjY
+VITE_OAUTH_REDIRECT_URI=http://localhost:3000/callback
+
+# 功能开关
+VITE_ENABLE_MOCK=false
+VITE_ENABLE_CONSOLE_LOG=true
+VITE_ENABLE_ERROR_REPORT=false
+
+# 其他配置
+VITE_REQUEST_TIMEOUT=10000
+VITE_UPLOAD_MAX_SIZE=5242880

+ 0 - 2
.env.development

@@ -1,2 +0,0 @@
-VITE_API_BASE_URL=http://localhost:8000
-VITE_APP_TITLE=SSO认证中心

+ 30 - 0
.env.prod

@@ -0,0 +1,30 @@
+# 生产环境配置
+NODE_ENV=production
+
+# 应用配置
+VITE_APP_TITLE=SSO认证中心
+VITE_APP_ENV=production
+VITE_APP_DEBUG=false
+
+# API 配置
+VITE_API_BASE_URL=https://api.yourdomain.com
+VITE_BACKEND_HOST=api.yourdomain.com
+VITE_BACKEND_PORT=443
+VITE_BACKEND_PROTOCOL=https
+
+# OAuth 配置
+VITE_OAUTH_CLIENT_ID=prod_client_id_replace_me
+VITE_OAUTH_REDIRECT_URI=https://sso.yourdomain.com/callback
+
+# 功能开关
+VITE_ENABLE_MOCK=false
+VITE_ENABLE_CONSOLE_LOG=false
+VITE_ENABLE_ERROR_REPORT=true
+
+# 其他配置
+VITE_REQUEST_TIMEOUT=30000
+VITE_UPLOAD_MAX_SIZE=10485760
+
+# 生产环境特有配置
+VITE_ENABLE_ANALYTICS=true
+VITE_SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id

+ 30 - 0
.env.test

@@ -0,0 +1,30 @@
+# 测试环境配置
+NODE_ENV=test
+
+# 应用配置
+VITE_APP_TITLE=管理平台 - 测试环境
+VITE_APP_ENV=test
+VITE_APP_DEBUG=true
+
+# API 配置
+VITE_API_BASE_URL=http://test-api.example.com
+VITE_BACKEND_HOST=test-api.example.com
+VITE_BACKEND_PORT=80
+VITE_BACKEND_PROTOCOL=http
+
+# OAuth 配置
+VITE_OAUTH_CLIENT_ID=test_client_id_12345
+VITE_OAUTH_REDIRECT_URI=http://test-frontend.example.com/callback
+
+# 功能开关
+VITE_ENABLE_MOCK=false
+VITE_ENABLE_CONSOLE_LOG=true
+VITE_ENABLE_ERROR_REPORT=true
+
+# 其他配置
+VITE_REQUEST_TIMEOUT=15000
+VITE_UPLOAD_MAX_SIZE=5242880
+
+# 测试环境特有配置
+VITE_TEST_USER_TOKEN=test_token_12345
+VITE_ENABLE_TEST_TOOLS=true

+ 7 - 0
.gitignore

@@ -12,6 +12,13 @@ dist
 dist-ssr
 *.local
 
+# Environment files
+.env.local
+.env.*.local
+
+# Docker
+.dockerignore
+
 # Editor directories and files
 .vscode/*
 !.vscode/extensions.json

+ 220 - 0
BUILD_GUIDE.md

@@ -0,0 +1,220 @@
+# LQAdminFront 本地构建指南
+
+## 概述
+
+本项目支持多环境构建,包括开发环境(development)、测试环境(test)和生产环境(production)。每个环境都有独立的配置文件和构建输出目录。
+
+## 环境配置文件
+
+| 环境 | 配置文件 | 说明 |
+|------|----------|------|
+| 开发环境 | `.env.development` | 本地开发使用 |
+| 测试环境 | `.env.test` | 测试服务器使用 |
+| 生产环境 | `.env.prod` / `.env.production` | 生产服务器使用 |
+
+## 快速开始
+
+
+# 使用 nvm 管理 Node.js 版本
+# 安装并切换到 LTS 版本
+nvm install 18.18.0  # 或 20.9.0
+nvm use 18.18.0
+
+# 验证 Node.js 版本
+node --version  # 应该显示 v18.18.0 或类似
+
+### 安装依赖
+
+```bash
+npm install
+```
+
+### 开发模式
+
+```bash
+# 开发环境
+npm run dev
+
+# 测试环境
+npm run dev:test
+
+# 生产环境
+npm run dev:prod
+```
+
+## 构建命令
+
+### 基础构建命令
+
+```bash
+# 开发环境构建 "build:dev": "vue-tsc && vite build --mode development",
+
+npm run build:dev
+
+# 测试环境构建
+npm run build:test
+
+# 生产环境构建
+npm run build:prod
+
+# 构建所有环境
+npm run build:all
+```
+
+### 使用构建脚本(推荐)
+
+#### Linux/Mac 用户
+
+```bash
+# 开发环境
+./scripts/build-dev.sh
+
+# 测试环境
+./scripts/build-test.sh
+
+# 生产环境
+./scripts/build-prod.sh
+```
+
+#### Windows 用户
+
+```cmd
+# 开发环境
+scripts\build-dev.bat
+
+# 测试环境
+scripts\build-test.bat
+
+# 生产环境
+scripts\build-prod.bat
+```
+
+## 构建输出
+
+每个环境的构建文件会输出到不同的目录:
+
+- 开发环境:`dist-development/`
+- 测试环境:`dist-test/`
+- 生产环境:`dist-production/`
+
+## 预览构建结果
+
+```bash
+# 预览开发环境构建
+npm run preview:dev
+
+# 预览测试环境构建
+npm run preview:test
+
+# 预览生产环境构建
+npm run preview:prod
+```
+
+## 代码质量检查
+
+```bash
+# 代码格式化
+npm run format
+
+# ESLint 检查和修复
+npm run lint
+
+# TypeScript 类型检查
+npm run type-check
+
+# 清理构建文件
+npm run clean
+```
+
+## 构建优化
+
+### 开发环境
+- 包含 sourcemap
+- 不压缩代码
+- 保留 console 和 debugger
+
+### 测试环境
+- 包含 sourcemap
+- 轻度压缩
+- 保留 console 用于调试
+
+### 生产环境
+- 不包含 sourcemap
+- 完全压缩和混淆
+- 移除 console 和 debugger
+- 启用代码分割和优化
+
+## 环境变量说明
+
+### 通用变量
+
+- `VITE_APP_TITLE`: 应用标题
+- `VITE_APP_ENV`: 当前环境
+- `VITE_APP_DEBUG`: 是否启用调试模式
+- `VITE_API_BASE_URL`: API 基础地址
+- `VITE_OAUTH_CLIENT_ID`: OAuth 客户端 ID
+- `VITE_OAUTH_REDIRECT_URI`: OAuth 重定向地址
+
+### 功能开关
+
+- `VITE_ENABLE_MOCK`: 是否启用 Mock 数据
+- `VITE_ENABLE_CONSOLE_LOG`: 是否启用控制台日志
+- `VITE_ENABLE_ERROR_REPORT`: 是否启用错误报告
+
+## 部署说明
+
+### 开发环境部署
+
+1. 执行构建:`npm run build:dev`
+2. 将 `dist-development/` 目录内容部署到开发服务器
+
+### 测试环境部署
+
+1. 执行构建:`npm run build:test`
+2. 将 `dist-test/` 目录内容部署到测试服务器
+
+### 生产环境部署
+
+1. 执行构建:`npm run build:prod`
+2. 将 `dist-production/` 目录内容部署到生产服务器
+
+## Docker 构建
+
+如果需要使用 Docker 构建,请参考 `DOCKER_DEPLOYMENT.md` 文档。
+
+## 故障排除
+
+### 构建失败
+
+1. 检查 Node.js 版本(推荐 16+)
+2. 清理依赖:`rm -rf node_modules package-lock.json && npm install`
+3. 清理构建缓存:`npm run clean`
+
+### 类型检查错误
+
+```bash
+# 单独运行类型检查
+npm run type-check
+```
+
+### 代码格式问题
+
+```bash
+# 自动修复格式问题
+npm run format
+npm run lint
+```
+
+## 性能优化建议
+
+1. 使用 `npm run build:analyze` 分析构建包大小
+2. 合理配置代码分割
+3. 优化图片和静态资源
+4. 启用 gzip 压缩(服务器配置)
+
+## 注意事项
+
+1. 不同环境的配置文件请根据实际情况修改
+2. 生产环境的敏感信息(如 API 密钥)请通过环境变量或配置中心管理
+3. 构建前请确保代码已通过所有检查(lint、type-check)
+4. 建议在 CI/CD 流程中集成构建和部署流程

+ 132 - 0
README_DOCKER.md

@@ -0,0 +1,132 @@
+# LQAdminFront Docker 快速启动指南
+
+## 🚀 快速开始
+
+### 1. 构建镜像
+
+```bash
+# 进入前端项目目录
+cd LQAdminFront
+
+# 给构建脚本执行权限(Linux/Mac)
+chmod +x build.sh
+
+# 构建开发环境镜像
+./build.sh -e dev
+
+# 构建生产环境镜像
+./build.sh -e prod -t v1.0.0
+```
+
+### 2. 运行容器
+
+```bash
+# 开发环境 (端口 3000)
+docker-compose --profile dev up -d
+
+# 测试环境 (端口 3001)
+docker-compose --profile test up -d
+
+# 生产环境 (端口 80)
+docker-compose --profile prod up -d
+```
+
+### 3. 访问应用
+
+- **开发环境**: http://localhost:3000
+- **测试环境**: http://localhost:3001  
+- **生产环境**: http://localhost
+
+## 📁 文件说明
+
+| 文件 | 说明 |
+|------|------|
+| `Dockerfile` | Docker 镜像构建文件 |
+| `docker-compose.yml` | Docker Compose 配置 |
+| `nginx.conf` | Nginx 服务器配置 |
+| `docker-entrypoint.sh` | 容器启动脚本 |
+| `build.sh` | 自动化构建脚本 |
+| `.env.dev` | 开发环境配置 |
+| `.env.test` | 测试环境配置 |
+| `.env.prod` | 生产环境配置 |
+
+## 🔧 环境配置
+
+### 开发环境
+- API地址: `http://localhost:8000`
+- 调试模式: 开启
+- 热重载: 支持
+
+### 测试环境  
+- API地址: `http://test-api.example.com`
+- 调试模式: 开启
+- 错误报告: 开启
+
+### 生产环境
+- API地址: `https://api.yourdomain.com`
+- 调试模式: 关闭
+- 性能优化: 开启
+
+## 🛠 自定义配置
+
+### 修改后端地址
+
+```bash
+# 方式1: 修改环境文件
+vim .env.prod
+
+# 方式2: 运行时指定环境变量
+docker run -p 80:80 \
+  -e VITE_API_BASE_URL=https://your-api.com \
+  lqadmin-frontend:prod
+```
+
+### 修改应用标题
+
+```bash
+docker run -p 80:80 \
+  -e VITE_APP_TITLE="您的应用名称" \
+  lqadmin-frontend:prod
+```
+
+## 📊 监控和调试
+
+```bash
+# 查看容器状态
+docker ps
+
+# 查看容器日志
+docker logs lqadmin-frontend-prod
+
+# 进入容器调试
+docker exec -it lqadmin-frontend-prod sh
+
+# 健康检查
+curl http://localhost/health
+```
+
+## 🔄 更新部署
+
+```bash
+# 1. 重新构建镜像
+./build.sh -e prod -t v1.0.1
+
+# 2. 停止旧容器
+docker-compose --profile prod down
+
+# 3. 启动新容器
+docker-compose --profile prod up -d
+```
+
+## ❓ 常见问题
+
+### Q: 容器启动失败?
+A: 检查端口是否被占用,查看容器日志排查问题
+
+### Q: API 请求失败?
+A: 确认后端服务是否正常,检查 CORS 配置
+
+### Q: 配置未生效?
+A: 重新构建镜像,确保环境变量正确设置
+
+更多详细信息请参考 [DOCKER_DEPLOYMENT.md](./DOCKER_DEPLOYMENT.md)

+ 28 - 0
docker/docker-compose.yml

@@ -0,0 +1,28 @@
+version: '3.8'
+
+services:
+  nginx:
+    image: nginx:alpine  # 使用轻量级 alpine 版本
+    container_name: nginx-web
+    restart: always
+    ports:
+      - "80:80"
+      - "443:443"
+    volumes:
+      # 挂载配置文件
+      - /home/lq/nginx/conf/nginx.conf:/etc/nginx/nginx.conf:ro
+      - /home/lq/nginx//conf.d:/etc/nginx/conf.d:ro
+      # 挂载网站文件
+      - /home/lq/nginx/html:/usr/share/nginx/html:ro
+      # 挂载 SSL 证书
+      - /home/lq/nginx/ssl:/etc/nginx/ssl:ro
+      # 挂载日志
+      - /home/lq/nginx/logs:/var/log/nginx
+    environment:
+      - TZ=Asia/Shanghai
+    networks:
+      - lq_network 
+
+networks:
+  lq_network:
+    external: true     

+ 114 - 0
docker/nginx.conf

@@ -0,0 +1,114 @@
+user nginx;
+worker_processes auto;
+error_log /var/log/nginx/error.log notice;
+pid /var/run/nginx.pid;
+
+events {
+    worker_connections 1024;
+    use epoll;
+    multi_accept on;
+}
+
+http {
+    include /etc/nginx/mime.types;
+    default_type application/octet-stream;
+
+    # 日志格式
+    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+                    '$status $body_bytes_sent "$http_referer" '
+                    '"$http_user_agent" "$http_x_forwarded_for"';
+
+    access_log /var/log/nginx/access.log main;
+
+    # 基本配置
+    sendfile on;
+    tcp_nopush on;
+    tcp_nodelay on;
+    keepalive_timeout 65;
+    types_hash_max_size 2048;
+    client_max_body_size 10M;
+
+    # Gzip 压缩
+    gzip on;
+    gzip_vary on;
+    gzip_min_length 1024;
+    gzip_proxied any;
+    gzip_comp_level 6;
+    gzip_types
+        text/plain
+        text/css
+        text/xml
+        text/javascript
+        application/json
+        application/javascript
+        application/xml+rss
+        application/atom+xml
+        image/svg+xml;
+
+    # 安全头
+    add_header X-Frame-Options "SAMEORIGIN" always;
+    add_header X-Content-Type-Options "nosniff" always;
+    add_header X-XSS-Protection "1; mode=block" always;
+    add_header Referrer-Policy "no-referrer-when-downgrade" always;
+
+    server {
+        listen 80;
+        server_name _;
+        root /usr/share/nginx/html;
+        index index.html;
+
+        # 静态资源缓存
+        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
+            expires 1y;
+            add_header Cache-Control "public, immutable";
+            access_log off;
+        }
+
+        # HTML 文件不缓存
+        location ~* \.html$ {
+            expires -1;
+            add_header Cache-Control "no-cache, no-store, must-revalidate";
+            add_header Pragma "no-cache";
+        }
+
+        # API 代理(可选,如果需要代理到后端)
+        location /api/ {
+            proxy_pass ${BACKEND_URL}/api/;
+            proxy_set_header Host $host;
+            proxy_set_header X-Real-IP $remote_addr;
+            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+            proxy_set_header X-Forwarded-Proto $scheme;
+            proxy_connect_timeout 30s;
+            proxy_send_timeout 30s;
+            proxy_read_timeout 30s;
+        }
+
+        # OAuth 代理
+        location /oauth/ {
+            proxy_pass ${BACKEND_URL}/oauth/;
+            proxy_set_header Host $host;
+            proxy_set_header X-Real-IP $remote_addr;
+            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+            proxy_set_header X-Forwarded-Proto $scheme;
+        }
+
+        # SPA 路由支持
+        location / {
+            try_files $uri $uri/ /index.html;
+        }
+
+        # 健康检查
+        location /health {
+            access_log off;
+            return 200 "healthy\n";
+            add_header Content-Type text/plain;
+        }
+
+        # 安全配置
+        location ~ /\. {
+            deny all;
+            access_log off;
+            log_not_found off;
+        }
+    }
+}

+ 18 - 0
env-template.js

@@ -0,0 +1,18 @@
+// 运行时环境变量配置
+// 这个文件会在容器启动时被动态替换
+window.__ENV__ = {
+  VITE_APP_TITLE: '${VITE_APP_TITLE}',
+  VITE_API_BASE_URL: '${VITE_API_BASE_URL}',
+  VITE_APP_ENV: '${VITE_APP_ENV}',
+  VITE_APP_DEBUG: '${VITE_APP_DEBUG}',
+  VITE_BACKEND_HOST: '${VITE_BACKEND_HOST}',
+  VITE_BACKEND_PORT: '${VITE_BACKEND_PORT}',
+  VITE_BACKEND_PROTOCOL: '${VITE_BACKEND_PROTOCOL}',
+  VITE_OAUTH_CLIENT_ID: '${VITE_OAUTH_CLIENT_ID}',
+  VITE_OAUTH_REDIRECT_URI: '${VITE_OAUTH_REDIRECT_URI}',
+  VITE_ENABLE_MOCK: '${VITE_ENABLE_MOCK}',
+  VITE_ENABLE_CONSOLE_LOG: '${VITE_ENABLE_CONSOLE_LOG}',
+  VITE_ENABLE_ERROR_REPORT: '${VITE_ENABLE_ERROR_REPORT}',
+  VITE_REQUEST_TIMEOUT: '${VITE_REQUEST_TIMEOUT}',
+  VITE_UPLOAD_MAX_SIZE: '${VITE_UPLOAD_MAX_SIZE}'
+};

+ 19 - 3
package.json

@@ -4,11 +4,26 @@
   "description": "SSO认证中心前端应用",
   "type": "module",
   "scripts": {
-    "dev": "vite",
+    "dev": "vite --mode dev",
+    "dev:test": "vite --mode test",
+    "dev:prod": "vite --mode prod",
     "build": "vue-tsc && vite build",
+    "build:dev": "vite build --mode dev",
+    "build:test": "vue-tsc && vite build --mode test",
+    "build:prod": "vue-tsc && vite build --mode prod",
     "preview": "vite preview",
+    "preview:dev": "vite preview --mode dev",
+    "preview:test": "vite preview --mode test",
+    "preview:prod": "vite preview --mode prod",
     "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
-    "format": "prettier --write src/"
+    "format": "prettier --write src/",
+    "clean": "rimraf dist dist-* build-*",
+    "build:analyze": "vue-tsc && vite build --mode prod --report",
+    "type-check": "vue-tsc --noEmit",
+    "script:build-dev": "node scripts/build-dev.sh",
+    "script:build-test": "node scripts/build-test.sh",
+    "script:build-prod": "node scripts/build-prod.sh",
+    "build:all": "npm run build:dev && npm run build:test && npm run build:prod"
   },
   "dependencies": {
     "vue": "^3.3.8",
@@ -32,6 +47,7 @@
     "prettier": "^3.0.3",
     "typescript": "~5.2.0",
     "vite": "^4.5.0",
-    "vue-tsc": "^1.8.22"
+    "vue-tsc": "^1.8.22",
+    "rimraf": "^5.0.5"
   }
 }

+ 39 - 0
scripts/build-dev.bat

@@ -0,0 +1,39 @@
+@echo off
+chcp 65001 >nul
+
+echo 🚀 开始构建开发环境...
+
+REM 清理之前的构建文件
+echo 🧹 清理构建目录...
+call npm run clean
+
+REM 类型检查
+echo 🔍 执行类型检查...
+call npm run type-check
+if %errorlevel% neq 0 (
+    echo ❌ 类型检查失败!
+    pause
+    exit /b 1
+)
+
+REM 构建开发环境
+echo 📦 构建开发环境包...
+call npm run build:dev
+if %errorlevel% neq 0 (
+    echo ❌ 构建失败!
+    pause
+    exit /b 1
+)
+
+REM 检查构建结果
+if exist "dist-development" (
+    echo ✅ 开发环境构建完成!
+    echo 📁 构建文件位置: dist-development/
+    echo 🌐 可以使用 'npm run preview:dev' 预览构建结果
+) else (
+    echo ❌ 构建失败!
+    pause
+    exit /b 1
+)
+
+pause

+ 26 - 0
scripts/build-dev.sh

@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# 开发环境本地打包脚本
+echo "🚀 开始构建开发环境..."
+
+# 清理之前的构建文件
+echo "🧹 清理构建目录..."
+npm run clean
+
+# 类型检查
+echo "🔍 执行类型检查..."
+npm run type-check
+
+# 构建开发环境
+echo "📦 构建开发环境包..."
+npm run build:dev
+
+# 检查构建结果
+if [ -d "dist-development" ]; then
+    echo "✅ 开发环境构建完成!"
+    echo "📁 构建文件位置: dist-development/"
+    echo "🌐 可以使用 'npm run preview:dev' 预览构建结果"
+else
+    echo "❌ 构建失败!"
+    exit 1
+fi

+ 51 - 0
scripts/build-prod.bat

@@ -0,0 +1,51 @@
+@echo off
+chcp 65001 >nul
+
+echo 🚀 开始构建生产环境...
+
+REM 清理之前的构建文件
+echo 🧹 清理构建目录...
+call npm run clean
+
+REM 代码检查
+echo 🔍 执行代码检查...
+call npm run lint
+if %errorlevel% neq 0 (
+    echo ❌ 代码检查失败!
+    pause
+    exit /b 1
+)
+
+REM 类型检查
+echo 🔍 执行类型检查...
+call npm run type-check
+if %errorlevel% neq 0 (
+    echo ❌ 类型检查失败!
+    pause
+    exit /b 1
+)
+
+REM 构建生产环境
+echo 📦 构建生产环境包...
+call npm run build:prod
+if %errorlevel% neq 0 (
+    echo ❌ 构建失败!
+    pause
+    exit /b 1
+)
+
+REM 检查构建结果
+if exist "dist-production" (
+    echo ✅ 生产环境构建完成!
+    echo 📁 构建文件位置: dist-production/
+    echo 🌐 可以使用 'npm run preview:prod' 预览构建结果
+    echo.
+    echo 📊 构建文件大小:
+    dir /s dist-production
+) else (
+    echo ❌ 构建失败!
+    pause
+    exit /b 1
+)
+
+pause

+ 35 - 0
scripts/build-prod.sh

@@ -0,0 +1,35 @@
+#!/bin/bash
+
+# 生产环境本地打包脚本
+echo "🚀 开始构建生产环境..."
+
+# 清理之前的构建文件
+echo "🧹 清理构建目录..."
+npm run clean
+
+# 代码检查
+echo "🔍 执行代码检查..."
+npm run lint
+
+# 类型检查
+echo "🔍 执行类型检查..."
+npm run type-check
+
+# 构建生产环境
+echo "📦 构建生产环境包..."
+npm run build:prod
+
+# 检查构建结果
+if [ -d "dist-production" ]; then
+    echo "✅ 生产环境构建完成!"
+    echo "📁 构建文件位置: dist-production/"
+    echo "🌐 可以使用 'npm run preview:prod' 预览构建结果"
+    
+    # 显示构建文件大小
+    echo ""
+    echo "📊 构建文件大小:"
+    du -sh dist-production/*
+else
+    echo "❌ 构建失败!"
+    exit 1
+fi

+ 48 - 0
scripts/build-test.bat

@@ -0,0 +1,48 @@
+@echo off
+chcp 65001 >nul
+
+echo 🚀 开始构建测试环境...
+
+REM 清理之前的构建文件
+echo 🧹 清理构建目录...
+call npm run clean
+
+REM 代码检查
+echo 🔍 执行代码检查...
+call npm run lint
+if %errorlevel% neq 0 (
+    echo ❌ 代码检查失败!
+    pause
+    exit /b 1
+)
+
+REM 类型检查
+echo 🔍 执行类型检查...
+call npm run type-check
+if %errorlevel% neq 0 (
+    echo ❌ 类型检查失败!
+    pause
+    exit /b 1
+)
+
+REM 构建测试环境
+echo 📦 构建测试环境包...
+call npm run build:test
+if %errorlevel% neq 0 (
+    echo ❌ 构建失败!
+    pause
+    exit /b 1
+)
+
+REM 检查构建结果
+if exist "dist-test" (
+    echo ✅ 测试环境构建完成!
+    echo 📁 构建文件位置: dist-test/
+    echo 🌐 可以使用 'npm run preview:test' 预览构建结果
+) else (
+    echo ❌ 构建失败!
+    pause
+    exit /b 1
+)
+
+pause

+ 30 - 0
scripts/build-test.sh

@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# 测试环境本地打包脚本
+echo "🚀 开始构建测试环境..."
+
+# 清理之前的构建文件
+echo "🧹 清理构建目录..."
+npm run clean
+
+# 代码检查
+echo "🔍 执行代码检查..."
+npm run lint
+
+# 类型检查
+echo "🔍 执行类型检查..."
+npm run type-check
+
+# 构建测试环境
+echo "📦 构建测试环境包..."
+npm run build:test
+
+# 检查构建结果
+if [ -d "dist-test" ]; then
+    echo "✅ 测试环境构建完成!"
+    echo "📁 构建文件位置: dist-test/"
+    echo "🌐 可以使用 'npm run preview:test' 预览构建结果"
+else
+    echo "❌ 构建失败!"
+    exit 1
+fi

+ 19 - 19
src/api/admin.ts

@@ -106,7 +106,7 @@ export interface SystemConfig {
 export const adminApi = {
   // 获取系统统计
   getSystemStats(): Promise<ApiResponse<SystemStats>> {
-    return request.get('/api/v1/admin/stats')
+    return request.get('/api/v1/system/admin/stats')
   },
 
   // 获取用户列表(管理员视图)
@@ -124,7 +124,7 @@ export const adminApi = {
     page: number
     page_size: number
   }>> {
-    return request.get('/api/v1/admin/users', { params })
+    return request.get('/api/v1/system/admin/users', { params })
   },
 
   // 创建用户
@@ -135,7 +135,7 @@ export const adminApi = {
     phone?: string
     is_superuser?: boolean
   }): Promise<ApiResponse<UserManagement>> {
-    return request.post('/api/v1/admin/users', data)
+    return request.post('/api/v1/system/admin/users', data)
   },
 
   // 更新用户
@@ -145,24 +145,24 @@ export const adminApi = {
     is_active?: boolean
     is_superuser?: boolean
   }): Promise<ApiResponse<UserManagement>> {
-    return request.put(`/api/v1/admin/users/${userId}`, data)
+    return request.put(`/api/v1/system/admin/users/${userId}`, data)
   },
 
   // 删除用户
   deleteUser(userId: string): Promise<ApiResponse> {
-    return request.delete(`/api/v1/admin/users/${userId}`)
+    return request.delete(`/api/v1/system/admin/users/${userId}`)
   },
 
   // 重置用户密码
   resetUserPassword(userId: string, newPassword: string): Promise<ApiResponse> {
-    return request.put(`/api/v1/admin/users/${userId}/password`, {
+    return request.put(`/api/v1/system/admin/users/${userId}/password`, {
       new_password: newPassword
     })
   },
 
   // 强制用户下线
   forceUserLogout(userId: string): Promise<ApiResponse> {
-    return request.post(`/api/v1/admin/users/${userId}/force-logout`)
+    return request.post(`/api/v1/system/admin/users/${userId}/force-logout`)
   },
 
   // 获取应用列表(管理员视图)
@@ -180,12 +180,12 @@ export const adminApi = {
     page: number
     page_size: number
   }>> {
-    return request.get('/api/v1/admin/apps', { params })
+    return request.get('/api/v1/system/admin/apps', { params })
   },
 
   // 审核应用
   approveApp(appId: string, approved: boolean, reason?: string): Promise<ApiResponse> {
-    return request.put(`/api/v1/admin/apps/${appId}/approve`, {
+    return request.put(`/api/v1/system/admin/apps/${appId}/approve`, {
       approved,
       reason
     })
@@ -206,7 +206,7 @@ export const adminApi = {
     page: number
     page_size: number
   }>> {
-    return request.get('/api/v1/admin/logs', { params })
+    return request.get('/api/v1/system/admin/logs', { params })
   },
 
   // 获取安全事件
@@ -224,12 +224,12 @@ export const adminApi = {
     page: number
     page_size: number
   }>> {
-    return request.get('/api/v1/admin/security/events', { params })
+    return request.get('/api/v1/system/admin/security/events', { params })
   },
 
   // 处理安全事件
   handleSecurityEvent(eventId: string, action: string, note?: string): Promise<ApiResponse> {
-    return request.put(`/api/v1/admin/security/events/${eventId}`, {
+    return request.put(`/api/v1/system/admin/security/events/${eventId}`, {
       action,
       note
     })
@@ -237,17 +237,17 @@ export const adminApi = {
 
   // 获取系统配置
   getSystemConfig(): Promise<ApiResponse<SystemConfig>> {
-    return request.get('/api/v1/admin/config')
+    return request.get('/api/v1/system/admin/config')
   },
 
   // 更新系统配置
   updateSystemConfig(config: Partial<SystemConfig>): Promise<ApiResponse> {
-    return request.put('/api/v1/admin/config', config)
+    return request.put('/api/v1/system/admin/config', config)
   },
 
   // 系统备份
   createBackup(description?: string): Promise<ApiResponse<{ backup_id: string }>> {
-    return request.post('/api/v1/admin/backup', { description })
+    return request.post('/api/v1/system/admin/backup', { description })
   },
 
   // 获取备份列表
@@ -257,19 +257,19 @@ export const adminApi = {
     size: number
     created_at: string
   }>>> {
-    return request.get('/api/v1/admin/backups')
+    return request.get('/api/v1/system/admin/backups')
   },
 
   // 下载备份
   downloadBackup(backupId: string): Promise<Blob> {
-    return request.get(`/api/v1/admin/backups/${backupId}/download`, {
+    return request.get(`/api/v1/system/admin/backups/${backupId}/download`, {
       responseType: 'blob'
     })
   },
 
   // 数据导出
   exportData(type: 'users' | 'apps' | 'logs', format: 'csv' | 'json', filters?: Record<string, any>): Promise<Blob> {
-    return request.post(`/api/v1/admin/export/${type}`, {
+    return request.post(`/api/v1/system/admin/export/${type}`, {
       format,
       filters
     }, {
@@ -282,6 +282,6 @@ export const adminApi = {
     cleaned_count: number
     types: string[]
   }>> {
-    return request.post('/api/v1/admin/cleanup', { types })
+    return request.post('/api/v1/system/admin/cleanup', { types })
   }
 }

+ 10 - 10
src/api/app.ts

@@ -80,42 +80,42 @@ export const appApi = {
     page: number
     page_size: number
   }>> {
-    return request.get('/api/v1/apps', { params })
+    return request.get('/api/v1/system/apps', { params })
   },
 
   // 获取应用详情
   getApp(appId: string): Promise<ApiResponse<App>> {
-    return request.get(`/api/v1/apps/${appId}`)
+    return request.get(`/api/v1/system/apps/${appId}`)
   },
 
   // 创建应用
   createApp(data: CreateAppRequest): Promise<ApiResponse<App>> {
-    return request.post('/api/v1/apps', data)
+    return request.post('/api/v1/system/apps', data)
   },
 
   // 更新应用
   updateApp(appId: string, data: UpdateAppRequest): Promise<ApiResponse<App>> {
-    return request.put(`/api/v1/apps/${appId}`, data)
+    return request.put(`/api/v1/system/apps/${appId}`, data)
   },
 
   // 删除应用
   deleteApp(appId: string): Promise<ApiResponse> {
-    return request.delete(`/api/v1/apps/${appId}`)
+    return request.delete(`/api/v1/system/apps/${appId}`)
   },
 
   // 启用/禁用应用
   toggleAppStatus(appId: string, isActive: boolean): Promise<ApiResponse> {
-    return request.put(`/api/v1/apps/${appId}/status`, { is_active: isActive })
+    return request.put(`/api/v1/system/apps/${appId}/status`, { is_active: isActive })
   },
 
   // 重置应用密钥
   resetAppSecret(appId: string): Promise<ApiResponse<{ app_secret: string }>> {
-    return request.post(`/api/v1/apps/${appId}/reset-secret`)
+    return request.post(`/api/v1/system/apps/${appId}/reset-secret`)
   },
 
   // 获取应用密钥
   getAppSecret(appId: string): Promise<ApiResponse<{ app_secret: string }>> {
-    return request.get(`/api/v1/apps/${appId}/secret`)
+    return request.get(`/api/v1/system/apps/${appId}/secret`)
   },
 
   // 上传应用图标
@@ -134,7 +134,7 @@ export const appApi = {
     start_date?: string
     end_date?: string
   }): Promise<ApiResponse<AppStats>> {
-    return request.get(`/api/v1/apps/${appId}/stats`, { params })
+    return request.get(`/api/v1/system/apps/${appId}/stats`, { params })
   },
 
   // 获取应用日志
@@ -151,6 +151,6 @@ export const appApi = {
     page: number
     page_size: number
   }>> {
-    return request.get(`/api/v1/apps/${appId}/logs`, { params })
+    return request.get(`/api/v1/system/apps/${appId}/logs`, { params })
   }
 }

+ 9 - 9
src/api/user.ts

@@ -55,26 +55,26 @@ export interface UserToken {
 }
 
 export const userApi = {
-  // 获取用户信息
+  // 获取用户信息 - 使用auth模块的userinfo端点
   getProfile(): Promise<ApiResponse<UserProfile>> {
-    return request.get('/api/v1/users/profile')
+    return request.get('/api/v1/auth/userinfo')
   },
 
   // 更新用户信息
   updateProfile(data: UpdateProfileRequest): Promise<ApiResponse> {
-    return request.put('/api/v1/users/profile', data)
+    return request.put('/api/v1/system/users/profile', data)
   },
 
   // 修改密码
   changePassword(data: ChangePasswordRequest): Promise<ApiResponse> {
-    return request.put('/api/v1/users/password', data)
+    return request.put('/api/v1/system/users/password', data)
   },
 
   // 上传头像
   uploadAvatar(file: File): Promise<ApiResponse<{ url: string }>> {
     const formData = new FormData()
     formData.append('avatar', file)
-    return request.post('/api/v1/users/avatar', formData, {
+    return request.post('/api/v1/system/users/avatar', formData, {
       headers: {
         'Content-Type': 'multipart/form-data'
       }
@@ -93,7 +93,7 @@ export const userApi = {
     page: number
     page_size: number
   }>> {
-    return request.get('/api/v1/users/login-logs', { params })
+    return request.get('/api/v1/system/users/login-logs', { params })
   },
 
   // 获取用户令牌
@@ -106,16 +106,16 @@ export const userApi = {
     page: number
     page_size: number
   }>> {
-    return request.get('/api/v1/users/tokens', { params })
+    return request.get('/api/v1/system/users/tokens', { params })
   },
 
   // 撤销令牌
   revokeToken(tokenId: string): Promise<ApiResponse> {
-    return request.delete(`/api/v1/users/tokens/${tokenId}`)
+    return request.delete(`/api/v1/system/users/tokens/${tokenId}`)
   },
 
   // 强制下线(撤销所有令牌)
   forceLogout(): Promise<ApiResponse> {
-    return request.post('/api/v1/users/force-logout')
+    return request.post('/api/v1/system/users/force-logout')
   }
 }

+ 1 - 1
src/layouts/MainLayout.vue

@@ -162,7 +162,7 @@ const getMenuChildren = (menu: MenuItem) => {
 const loadUserMenus = async () => {
   menuLoading.value = true
   try {
-    const result = await request.get<any, ApiResponse<MenuItem[]>>('/api/v1/user/menus')
+    const result = await request.get<any, ApiResponse<MenuItem[]>>('/api/v1/system/user/menus')
     if (result.code === 0) {
       userMenus.value = result.data
       console.log('用户菜单加载成功:', result.data)

+ 1 - 1
src/stores/auth.ts

@@ -100,7 +100,7 @@ export const useAuthStore = defineStore('auth', () => {
     if (!token.value) return
     
     try {
-      const response = await request.get('/api/v1/user/menus')
+      const response = await request.get('/api/v1/system/user/menus')
       if (response.code === 0) {
         userMenus.value = response.data
       }

+ 7 - 7
src/views/admin/Apps.vue

@@ -649,7 +649,7 @@ const loadApps = async () => {
       }
     })
     
-    const result = await request.get('/api/v1/apps', { params })
+    const result = await request.get('/api/v1/system/apps', { params })
     
     if (result.code === 0) {
       apps.value = result.data.items || []
@@ -715,7 +715,7 @@ const editApp = (app: any) => {
 const showSecret = async (app: any) => {
   try {
     // 获取包含密钥的完整应用信息
-    const result = await request.get(`/api/v1/apps/${app.id}`)
+    const result = await request.get(`/api/v1/system/apps/${app.id}`)
     
     if (result.code === 0) {
       selectedApp.value = result.data
@@ -759,7 +759,7 @@ const toggleAppStatus = async (app: any) => {
     })
     
     // 调用API切换状态
-    const result = await request.put(`/api/v1/apps/${app.id}/status`, {
+    const result = await request.put(`/api/v1/system/apps/${app.id}/status`, {
       is_active: !app.is_active
     })
     
@@ -792,7 +792,7 @@ const resetAppSecret = async (app: any) => {
     )
     
     // 调用API重置密钥
-    const result = await request.post(`/api/v1/apps/${app.id}/reset-secret`)
+    const result = await request.post(`/api/v1/system/apps/${app.id}/reset-secret`)
     
     if (result.code === 0) {
       // 更新应用的密钥
@@ -830,7 +830,7 @@ const deleteApp = async (app: any) => {
     )
     
     // 调用API删除应用
-    const result = await request.delete(`/api/v1/apps/${app.id}`)
+    const result = await request.delete(`/api/v1/system/apps/${app.id}`)
     
     if (result.code === 0) {
       ElMessage.success('应用已删除')
@@ -943,7 +943,7 @@ const submitCreateForm = async () => {
       refresh_token_expires: createForm.refresh_token_expires
     }
     
-    const result = await request.post('/api/v1/apps', createData)
+    const result = await request.post('/api/v1/system/apps', createData)
     
     if (result.code === 0) {
       ElMessage.success('应用创建成功')
@@ -984,7 +984,7 @@ const submitEditForm = async () => {
       refresh_token_expires: editForm.refresh_token_expires
     }
     
-    const result = await request.put(`/api/v1/apps/${selectedApp.value.id}`, updateData)
+    const result = await request.put(`/api/v1/system/apps/${selectedApp.value.id}`, updateData)
     
     if (result.code === 0) {
       ElMessage.success('应用更新成功')

+ 1 - 1
src/views/admin/Logs.vue

@@ -364,7 +364,7 @@ const loadLogs = async () => {
     total.value = mockLogs.length
     
     // TODO: 实际API调用
-    // const result = await request.get('/api/v1/admin/logs', {
+    // const result = await request.get('/api/v1/system/admin/logs', {
     //   params: {
     //     page: currentPage.value,
     //     page_size: pageSize.value,

+ 5 - 5
src/views/admin/Menus.vue

@@ -572,7 +572,7 @@ const buildParentOptions = (menuList: any[]) => {
 const loadMenus = async () => {
   loading.value = true
   try {
-    const result = await request.get('/api/v1/admin/menus', {
+    const result = await request.get('/api/v1/system/admin/menus', {
       params: {
         page: currentPage.value,
         page_size: pageSize.value,
@@ -664,7 +664,7 @@ const collapseAll = () => {
 // 状态切换
 const handleStatusChange = async (menu: any) => {
   try {
-    await request.put(`/api/v1/admin/menus/${menu.id}`, {
+    await request.put(`/api/v1/system/admin/menus/${menu.id}`, {
       is_active: menu.is_active
     })
     ElMessage.success('菜单状态更新成功')
@@ -715,7 +715,7 @@ const deleteMenu = async (menu: any) => {
       }
     )
     
-    await request.delete(`/api/v1/admin/menus/${menu.id}`)
+    await request.delete(`/api/v1/system/admin/menus/${menu.id}`)
     ElMessage.success('菜单删除成功')
     loadMenus()
   } catch (error) {
@@ -732,11 +732,11 @@ const saveMenu = async () => {
     
     if (editingMenu.value) {
       // 更新菜单
-      await request.put(`/api/v1/admin/menus/${editingMenu.value.id}`, menuForm)
+      await request.put(`/api/v1/system/admin/menus/${editingMenu.value.id}`, menuForm)
       ElMessage.success('菜单更新成功')
     } else {
       // 创建菜单
-      await request.post('/api/v1/admin/menus', menuForm)
+      await request.post('/api/v1/system/admin/menus', menuForm)
       ElMessage.success('菜单创建成功')
     }
     

+ 8 - 8
src/views/admin/Roles.vue

@@ -319,7 +319,7 @@ const permissionTreeRef = ref()
 const loadRoles = async () => {
   loading.value = true
   try {
-    const result = await request.get('/api/v1/admin/roles', {
+    const result = await request.get('/api/v1/system/admin/roles', {
       params: {
         page: currentPage.value,
         page_size: pageSize.value,
@@ -367,7 +367,7 @@ const handleSelectionChange = (selection: any[]) => {
 // 状态切换
 const handleStatusChange = async (role: any) => {
   try {
-    await request.put(`/api/v1/admin/roles/${role.id}`, {
+    await request.put(`/api/v1/system/admin/roles/${role.id}`, {
       is_active: role.is_active
     })
     ElMessage.success('角色状态更新成功')
@@ -401,7 +401,7 @@ const deleteRole = async (role: any) => {
       }
     )
     
-    await request.delete(`/api/v1/admin/roles/${role.id}`)
+    await request.delete(`/api/v1/system/admin/roles/${role.id}`)
     ElMessage.success('角色删除成功')
     loadRoles()
   } catch (error: any) {
@@ -446,7 +446,7 @@ const managePermissions = async (role: any) => {
 // 加载所有菜单
 const loadAllMenus = async () => {
   try {
-    const result = await request.get('/api/v1/admin/menus', {
+    const result = await request.get('/api/v1/system/admin/menus', {
       params: {
         page_size: 1000  // 获取所有菜单
       }
@@ -467,7 +467,7 @@ const loadAllMenus = async () => {
 const loadRolePermissions = async (roleId: string) => {
   try {
     console.log('🔍 Loading role permissions for roleId:', roleId)
-    const result = await request.get(`/api/v1/admin/roles/${roleId}/menus`)
+    const result = await request.get(`/api/v1/system/admin/roles/${roleId}/menus`)
     
     console.log('📡 Role permissions API response:', result)
     
@@ -513,14 +513,14 @@ const saveRole = async () => {
     
     if (editingRole.value) {
       // 更新角色
-      await request.put(`/api/v1/admin/roles/${editingRole.value.id}`, {
+      await request.put(`/api/v1/system/admin/roles/${editingRole.value.id}`, {
         display_name: roleForm.display_name,
         description: roleForm.description
       })
       ElMessage.success('角色更新成功')
     } else {
       // 创建角色
-      await request.post('/api/v1/admin/roles', roleForm)
+      await request.post('/api/v1/system/admin/roles', roleForm)
       ElMessage.success('角色创建成功')
     }
     
@@ -554,7 +554,7 @@ const savePermissions = async () => {
     const checkedKeys = permissionTreeRef.value?.getCheckedKeys() || []
     
     // 保存角色菜单权限
-    await request.put(`/api/v1/admin/roles/${currentRole.value.id}/menus`, {
+    await request.put(`/api/v1/system/admin/roles/${currentRole.value.id}/menus`, {
       menu_ids: checkedKeys
     })
     

+ 11 - 18
src/views/admin/Users.vue

@@ -64,20 +64,13 @@
         <template #default="{ row }">
           <div class="roles-container">
             <el-tag 
-              v-for="(role, index) in row.roles.slice(0, 1)" 
-              :key="role" 
+              v-if="row.roles" 
               size="small" 
               style="margin-right: 4px; margin-bottom: 2px;"
             >
-              {{ role }}
+              {{ row.roles }}
             </el-tag>
-            <el-tooltip 
-              v-if="row.roles.length > 1"
-              :content="row.roles.slice(1).join(', ')"
-              placement="top"
-            >
-              <el-tag size="small" type="info">+{{ row.roles.length - 1 }}</el-tag>
-            </el-tooltip>
+            <el-tag v-else size="small" type="info">未分配</el-tag>
           </div>
         </template>
       </el-table-column>
@@ -361,7 +354,7 @@ const isCurrentUser = (userId: string) => {
 const loadUsers = async () => {
   loading.value = true
   try {
-    const result = await request.get('/api/v1/admin/users', {
+    const result = await request.get('/api/v1/system/admin/users', {
       params: {
         page: currentPage.value,
         page_size: pageSize.value,
@@ -386,7 +379,7 @@ const loadUsers = async () => {
 // 加载所有角色
 const loadRoles = async () => {
   try {
-    const result = await request.get('/api/v1/roles/all')
+    const result = await request.get('/api/v1/system/roles/all')
     
     if (result.code === 0) {
       allRoles.value = result.data
@@ -422,7 +415,7 @@ const handleSelectionChange = (selection: any[]) => {
 // 状态切换
 const handleStatusChange = async (user: any) => {
   try {
-    await request.put(`/api/v1/admin/users/${user.id}`, {
+    await request.put(`/api/v1/system/admin/users/${user.id}`, {
       is_active: user.is_active
     })
     ElMessage.success('用户状态更新成功')
@@ -465,7 +458,7 @@ const deleteUser = async (user: any) => {
       }
     )
     
-    await request.delete(`/api/v1/admin/users/${user.id}`)
+    await request.delete(`/api/v1/system/admin/users/${user.id}`)
     ElMessage.success('用户删除成功')
     loadUsers()
   } catch (error: any) {
@@ -492,7 +485,7 @@ const batchDelete = async () => {
     
     // 逐个删除(可以优化为批量API)
     for (const user of selectedUsers.value) {
-      await request.delete(`/api/v1/admin/users/${user.id}`)
+      await request.delete(`/api/v1/system/admin/users/${user.id}`)
     }
     
     ElMessage.success('批量删除成功')
@@ -549,11 +542,11 @@ const saveUser = async () => {
         updateData.password = userForm.password
       }
       
-      await request.put(`/api/v1/admin/users/${editingUser.value.id}`, updateData)
+      await request.put(`/api/v1/system/admin/users/${editingUser.value.id}`, updateData)
       ElMessage.success('用户更新成功')
     } else {
       // 创建用户
-      await request.post('/api/v1/admin/users', userForm)
+      await request.post('/api/v1/system/admin/users', userForm)
       ElMessage.success('用户创建成功')
     }
     
@@ -576,7 +569,7 @@ const saveUserRoles = async () => {
   try {
     savingRoles.value = true
     
-    await request.put(`/api/v1/admin/users/${currentUser.value.id}`, {
+    await request.put(`/api/v1/system/admin/users/${currentUser.value.id}`, {
       role_ids: selectedRoleIds.value
     })
     

+ 1 - 1
src/views/apps/Create.vue

@@ -399,7 +399,7 @@ const createApp = async () => {
     console.log('创建应用:', formData)
     
     // 调用API创建应用
-    const response = await fetch('/api/v1/apps', {
+    const response = await fetch('/api/v1/system/apps', {
       method: 'POST',
       headers: {
         'Authorization': `Bearer ${getToken()}`,

+ 6 - 6
src/views/apps/Index.vue

@@ -505,7 +505,7 @@ const loadApps = async () => {
     
     console.log('加载应用列表,参数:', params)
     
-    const result = await request.get('/api/v1/apps', { params })
+    const result = await request.get('/api/v1/system/apps', { params })
     console.log('API响应:', result)
     
     if (result.code === 0) {
@@ -573,7 +573,7 @@ const editApp = (app: any) => {
 const showSecret = async (app: any) => {
   try {
     // 获取包含密钥的完整应用信息
-    const result = await request.get(`/api/v1/apps/${app.id}`)
+    const result = await request.get(`/api/v1/system/apps/${app.id}`)
     
     if (result.code === 0) {
       selectedApp.value = result.data
@@ -617,7 +617,7 @@ const toggleAppStatus = async (app: any) => {
     })
     
     // 调用API切换状态
-    const result = await request.put(`/api/v1/apps/${app.id}/status`, {
+    const result = await request.put(`/api/v1/system/apps/${app.id}/status`, {
       is_active: !app.is_active
     })
     
@@ -650,7 +650,7 @@ const resetAppSecret = async (app: any) => {
     )
     
     // 调用API重置密钥
-    const result = await request.post(`/api/v1/apps/${app.id}/reset-secret`)
+    const result = await request.post(`/api/v1/system/apps/${app.id}/reset-secret`)
     
     if (result.code === 0) {
       // 更新应用的密钥
@@ -688,7 +688,7 @@ const deleteApp = async (app: any) => {
     )
     
     // 调用API删除应用
-    const result = await request.delete(`/api/v1/apps/${app.id}`)
+    const result = await request.delete(`/api/v1/system/apps/${app.id}`)
     
     if (result.code === 0) {
       ElMessage.success('应用已删除')
@@ -772,7 +772,7 @@ const submitEditForm = async () => {
       refresh_token_expires: editForm.refresh_token_expires
     }
     
-    const result = await request.put(`/api/v1/apps/${selectedApp.value.id}`, updateData)
+    const result = await request.put(`/api/v1/system/apps/${selectedApp.value.id}`, updateData)
     
     if (result.code === 0) {
       ElMessage.success('应用更新成功')

+ 292 - 0
test_frontend.html

@@ -0,0 +1,292 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>前端API测试</title>
+    <style>
+        body {
+            font-family: Arial, sans-serif;
+            max-width: 1200px;
+            margin: 0 auto;
+            padding: 20px;
+            background: #f5f5f5;
+        }
+        .container {
+            background: white;
+            padding: 20px;
+            border-radius: 8px;
+            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+            margin-bottom: 20px;
+        }
+        h1 {
+            color: #333;
+            border-bottom: 2px solid #409eff;
+            padding-bottom: 10px;
+        }
+        h2 {
+            color: #666;
+            margin-top: 20px;
+        }
+        button {
+            background: #409eff;
+            color: white;
+            border: none;
+            padding: 10px 20px;
+            border-radius: 4px;
+            cursor: pointer;
+            margin: 5px;
+        }
+        button:hover {
+            background: #66b1ff;
+        }
+        .success {
+            background: #d4edda;
+            border-left: 4px solid #28a745;
+            padding: 15px;
+            margin: 10px 0;
+        }
+        .error {
+            background: #f8d7da;
+            border-left: 4px solid #dc3545;
+            padding: 15px;
+            margin: 10px 0;
+        }
+        #output {
+            background: #f9f9f9;
+            padding: 15px;
+            border-radius: 4px;
+            min-height: 100px;
+            white-space: pre-wrap;
+            font-family: monospace;
+            max-height: 600px;
+            overflow-y: auto;
+        }
+        table {
+            width: 100%;
+            border-collapse: collapse;
+            margin-top: 10px;
+        }
+        th, td {
+            border: 1px solid #ddd;
+            padding: 8px;
+            text-align: left;
+        }
+        th {
+            background-color: #409eff;
+            color: white;
+        }
+        tr:nth-child(even) {
+            background-color: #f2f2f2;
+        }
+    </style>
+</head>
+<body>
+    <div class="container">
+        <h1>🔍 前端API测试工具</h1>
+        
+        <h2>步骤 1: 登录</h2>
+        <div>
+            <input type="text" id="username" placeholder="用户名" value="admin" style="padding: 8px; margin-right: 10px;">
+            <input type="password" id="password" placeholder="密码" value="admin123" style="padding: 8px; margin-right: 10px;">
+            <button onclick="testLogin()">登录</button>
+        </div>
+        
+        <h2>步骤 2: 测试API</h2>
+        <div>
+            <button onclick="testGetUsers()">获取用户列表</button>
+            <button onclick="testGetMenus()">获取菜单</button>
+            <button onclick="testGetRoles()">获取角色列表</button>
+        </div>
+        
+        <h2>输出:</h2>
+        <div id="output">等待操作...</div>
+        
+        <div id="userTable"></div>
+    </div>
+
+    <script>
+        let accessToken = localStorage.getItem('access_token');
+        
+        function log(message, type = 'info') {
+            const output = document.getElementById('output');
+            const timestamp = new Date().toLocaleTimeString();
+            const prefix = type === 'error' ? '❌' : type === 'success' ? '✅' : 'ℹ️';
+            output.textContent += `[${timestamp}] ${prefix} ${message}\n`;
+            output.scrollTop = output.scrollHeight;
+        }
+        
+        function clearOutput() {
+            document.getElementById('output').textContent = '';
+            document.getElementById('userTable').innerHTML = '';
+        }
+        
+        async function testLogin() {
+            clearOutput();
+            log('开始登录测试...');
+            
+            const username = document.getElementById('username').value;
+            const password = document.getElementById('password').value;
+            
+            try {
+                const response = await fetch('http://localhost:8000/api/v1/auth/login', {
+                    method: 'POST',
+                    headers: {
+                        'Content-Type': 'application/json'
+                    },
+                    body: JSON.stringify({ username, password })
+                });
+                
+                log(`状态码: ${response.status}`);
+                
+                const data = await response.json();
+                log(`响应: ${JSON.stringify(data, null, 2)}`);
+                
+                if (data.code === 0) {
+                    accessToken = data.data.access_token;
+                    localStorage.setItem('access_token', accessToken);
+                    log('登录成功!Token已保存', 'success');
+                    log(`Token: ${accessToken.substring(0, 50)}...`);
+                } else {
+                    log(`登录失败: ${data.message}`, 'error');
+                }
+            } catch (error) {
+                log(`请求失败: ${error.message}`, 'error');
+            }
+        }
+        
+        async function testGetUsers() {
+            if (!accessToken) {
+                log('请先登录!', 'error');
+                return;
+            }
+            
+            clearOutput();
+            log('开始获取用户列表...');
+            
+            try {
+                const response = await fetch('http://localhost:8000/api/v1/system/admin/users?page=1&page_size=20', {
+                    method: 'GET',
+                    headers: {
+                        'Authorization': `Bearer ${accessToken}`,
+                        'Content-Type': 'application/json'
+                    }
+                });
+                
+                log(`状态码: ${response.status}`);
+                
+                const data = await response.json();
+                
+                if (data.code === 0) {
+                    log(`获取成功!总数: ${data.data.total}`, 'success');
+                    log(`用户数据: ${JSON.stringify(data.data, null, 2)}`);
+                    
+                    // 显示用户表格
+                    displayUserTable(data.data.items);
+                } else {
+                    log(`获取失败: ${data.message}`, 'error');
+                }
+            } catch (error) {
+                log(`请求失败: ${error.message}`, 'error');
+            }
+        }
+        
+        async function testGetMenus() {
+            if (!accessToken) {
+                log('请先登录!', 'error');
+                return;
+            }
+            
+            clearOutput();
+            log('开始获取菜单...');
+            
+            try {
+                const response = await fetch('http://localhost:8000/api/v1/system/user/menus', {
+                    method: 'GET',
+                    headers: {
+                        'Authorization': `Bearer ${accessToken}`,
+                        'Content-Type': 'application/json'
+                    }
+                });
+                
+                log(`状态码: ${response.status}`);
+                
+                const data = await response.json();
+                
+                if (data.code === 0) {
+                    log(`获取成功!菜单数: ${data.data.length}`, 'success');
+                    log(`菜单数据: ${JSON.stringify(data.data, null, 2)}`);
+                } else {
+                    log(`获取失败: ${data.message}`, 'error');
+                }
+            } catch (error) {
+                log(`请求失败: ${error.message}`, 'error');
+            }
+        }
+        
+        async function testGetRoles() {
+            if (!accessToken) {
+                log('请先登录!', 'error');
+                return;
+            }
+            
+            clearOutput();
+            log('开始获取角色列表...');
+            
+            try {
+                const response = await fetch('http://localhost:8000/api/v1/system/roles/all', {
+                    method: 'GET',
+                    headers: {
+                        'Authorization': `Bearer ${accessToken}`,
+                        'Content-Type': 'application/json'
+                    }
+                });
+                
+                log(`状态码: ${response.status}`);
+                
+                const data = await response.json();
+                
+                if (data.code === 0) {
+                    log(`获取成功!角色数: ${data.data.length}`, 'success');
+                    log(`角色数据: ${JSON.stringify(data.data, null, 2)}`);
+                } else {
+                    log(`获取失败: ${data.message}`, 'error');
+                }
+            } catch (error) {
+                log(`请求失败: ${error.message}`, 'error');
+            }
+        }
+        
+        function displayUserTable(users) {
+            const tableDiv = document.getElementById('userTable');
+            
+            let html = '<h2>用户列表</h2><table>';
+            html += '<tr><th>用户名</th><th>邮箱</th><th>手机</th><th>角色</th><th>状态</th><th>管理员</th></tr>';
+            
+            users.forEach(user => {
+                html += '<tr>';
+                html += `<td>${user.username}</td>`;
+                html += `<td>${user.email}</td>`;
+                html += `<td>${user.phone || '-'}</td>`;
+                html += `<td>${user.roles || '未分配'}</td>`;
+                html += `<td>${user.is_active ? '激活' : '禁用'}</td>`;
+                html += `<td>${user.is_superuser ? '是' : '否'}</td>`;
+                html += '</tr>';
+            });
+            
+            html += '</table>';
+            tableDiv.innerHTML = html;
+        }
+        
+        // 页面加载时检查token
+        window.onload = function() {
+            if (accessToken) {
+                log('检测到已保存的Token', 'success');
+                log(`Token: ${accessToken.substring(0, 50)}...`);
+            } else {
+                log('未检测到Token,请先登录');
+            }
+        };
+    </script>
+</body>
+</html>

+ 65 - 27
vite.config.ts

@@ -1,39 +1,77 @@
-import { defineConfig } from 'vite'
+import { defineConfig, loadEnv } from 'vite'
 import vue from '@vitejs/plugin-vue'
 import { resolve } from 'path'
 
 // https://vitejs.dev/config/
-export default defineConfig({
-  plugins: [vue()],
-  resolve: {
-    alias: {
-      '@': resolve(__dirname, 'src'),
+export default defineConfig(({ command, mode }) => {
+  // 加载环境变量
+  const env = loadEnv(mode, process.cwd(), '')
+  
+  // 根据模式设置不同的配置
+  const isDev = mode === 'dev'
+  const isTest = mode === 'test'
+  const isProd = mode === 'prod'
+  
+  return {
+    plugins: [vue()],
+    resolve: {
+      alias: {
+        '@': resolve(__dirname, 'src'),
+      },
     },
-  },
-  server: {
-    port: 3000,
-    host: true,
-    proxy: {
-      '/api': {
-        target: 'http://localhost:8000',
-        changeOrigin: true,
+    server: {
+      port: isDev ? 3000 : isTest ? 3001 : 3002,
+      host: true,
+      open: true, // 自动打开浏览器
+      proxy: {
+        '/api': {
+          target: env.VITE_API_BASE_URL || 'http://localhost:8000',
+          changeOrigin: true,
+        },
+        '/oauth': {
+          target: env.VITE_API_BASE_URL || 'http://localhost:8000',
+          changeOrigin: true,
+        },
       },
-      '/oauth': {
-        target: 'http://localhost:8000',
-        changeOrigin: true,
+    },
+    build: {
+      outDir: `dist-${mode}`, // 不同环境输出到不同目录
+      sourcemap: isDev || isTest, // 开发和测试环境生成 sourcemap
+      minify: isProd ? 'terser' : false, // 生产环境压缩
+      rollupOptions: {
+        output: {
+          // 根据环境设置不同的文件名
+          entryFileNames: `assets/[name]-${mode}.[hash].js`,
+          chunkFileNames: `assets/[name]-${mode}.[hash].js`,
+          assetFileNames: `assets/[name]-${mode}.[hash].[ext]`,
+          manualChunks: {
+            vue: ['vue', 'vue-router', 'pinia'],
+            element: ['element-plus', '@element-plus/icons-vue'],
+            utils: ['axios', 'js-cookie', 'dayjs'],
+          },
+        },
       },
+      // 生产环境优化
+      ...(isProd && {
+        terserOptions: {
+          compress: {
+            drop_console: true, // 移除 console
+            drop_debugger: true, // 移除 debugger
+          },
+        },
+      }),
+    },
+    define: {
+      // 注入环境变量到代码中
+      __APP_ENV__: JSON.stringify(mode),
+      __BUILD_TIME__: JSON.stringify(new Date().toISOString()),
     },
-  },
-  build: {
-    outDir: 'dist',
-    sourcemap: false,
-    rollupOptions: {
-      output: {
-        manualChunks: {
-          vue: ['vue', 'vue-router', 'pinia'],
-          element: ['element-plus', '@element-plus/icons-vue'],
+    css: {
+      preprocessorOptions: {
+        scss: {
+          additionalData: `@import "@/styles/variables.scss";`,
         },
       },
     },
-  },
+  }
 })