You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

150 lines
5.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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]`
}
}
}
}
})