import { defineConfig, loadEnv } from 'vite' import vue from '@vitejs/plugin-vue' import { fileURLToPath, URL } from 'node:url' import dns from 'node:dns' // 解决 Node.js 17+ 默认使用 IPv6 导致无法连接 localhost 的问题 try { dns.setDefaultResultOrder('ipv4first') } catch (e) { console.warn('Failed to set DNS result order to ipv4first', e) } export default defineConfig(({ mode }) => { // 加载环境变量 const env = loadEnv(mode, process.cwd(), '') // 开发环境必须配置代理目标 if (mode === 'development') { if (!env.VITE_API_PROXY_TARGET) { throw new Error( '❌ 错误: 开发环境必须配置 VITE_API_PROXY_TARGET 环境变量\n' + '请在 .env.development 文件中添加:\n' + 'VITE_API_PROXY_TARGET=http://czemc.localhost\n' + '\n' + '示例:\n' + 'VITE_API_PROXY_TARGET=http://czemc.localhost\n' + '或\n' + 'VITE_API_PROXY_TARGET=http://localhost:8000' ) } // 打印代理配置信息 console.log('🔧 Vite 代理配置:', { target: env.VITE_API_PROXY_TARGET, mode: mode }) } return { // 配置基础路径,使用相对路径以支持子路径部署 // 如果部署在根路径,可以设置为 '/' // 如果部署在子路径(如 /budget/),使用相对路径 './' 更灵活 base: env.VITE_BASE_PATH || './', plugins: [vue()], resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } }, server: { port: 3000, open: true, proxy: mode === 'development' ? { // 代理所有 /api 请求到后端服务器 '/api': { target: env.VITE_API_PROXY_TARGET, changeOrigin: true, secure: false, ws: true, // 添加 X-Forwarded-* 头 xfwd: true, // 不重写路径 rewrite: (path) => path, // 添加代理日志和错误处理 configure: (proxy, options) => { // 如果配置了 VITE_API_PROXY_HOST_HEADER,则强制设置 Host 头 // 这对于后端配置了虚拟主机(Virtual Host)的情况非常有用 if (env.VITE_API_PROXY_HOST_HEADER) { proxy.on('proxyReq', (proxyReq) => { proxyReq.setHeader('Host', env.VITE_API_PROXY_HOST_HEADER) }) } console.log('✅ 代理中间件已配置:', { target: options.target, path: '/api' }) proxy.on('proxyReq', (proxyReq, req, res) => { const targetUrl = `${options.target}${req.url}` console.log(`➡️ [代理转发] ${req.method} ${req.url}`) console.log(` → 目标地址: ${targetUrl}`) console.log(` → Host 头: ${proxyReq.getHeader('host')}`) console.log(` → 请求头:`, JSON.stringify(proxyReq.getHeaders(), null, 2)) }) proxy.on('proxyRes', (proxyRes, req, res) => { console.log(`⬅️ [代理响应] ${req.url} -> ${proxyRes.statusCode}`) console.log(` → 响应头:`, JSON.stringify(proxyRes.headers, null, 2)) }) proxy.on('error', (err, req, res) => { console.error(`❌ [代理错误] ${req.url}:`, err.message) console.error(` 错误代码: ${err.code}`) console.error(` 错误详情:`, { code: err.code, errno: err.errno, syscall: err.syscall, address: err.address, port: err.port, stack: err.stack }) // 尝试发送错误响应 if (res && !res.headersSent) { try { res.writeHead(500, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }) res.end(JSON.stringify({ code: 500, msg: '代理服务器错误', error: err.message, errorCode: err.code })) } catch (e) { console.error('无法发送错误响应:', e) } } }) }, // 保持请求路径不变,直接转发到目标服务器 // 例如: /api/budget/xxx -> http://czemc.localhost/api/budget/xxx } } : undefined }, // 定义全局常量替换 define: { // 确保环境变量在构建时被正确替换 __APP_ENV__: JSON.stringify(env.VITE_APP_ENV || mode) }, // 构建配置 build: { // 从环境变量读取构建输出目录,如果没有则使用默认值 outDir: env.VITE_BUILD_OUT_DIR || 'dist', sourcemap: mode === 'development', // 根据环境输出不同的构建产物名称 rollupOptions: { output: { // 可以根据环境自定义输出文件名 entryFileNames: `assets/[name].${mode}.js`, chunkFileNames: `assets/[name].${mode}.js`, assetFileNames: `assets/[name].${mode}.[ext]` } } } } })