328 lines
13 KiB
TypeScript
328 lines
13 KiB
TypeScript
import { defineConfig, type Plugin } from 'vite'
|
||
import vue from '@vitejs/plugin-vue'
|
||
import { resolve } from 'path'
|
||
import fs from 'fs'
|
||
import Components from 'unplugin-vue-components/vite'
|
||
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
|
||
|
||
// https://vite.dev/config/
|
||
// 生成 public/models/manifest.json,自动列举 .onnx 文件
|
||
function modelsManifestPlugin(): Plugin {
|
||
const writeManifest = () => {
|
||
try {
|
||
const modelsDir = resolve(__dirname, 'public/models')
|
||
if (!fs.existsSync(modelsDir)) return
|
||
const files = fs
|
||
.readdirSync(modelsDir)
|
||
.filter((f) => f.toLowerCase().endsWith('.onnx'))
|
||
const manifestPath = resolve(modelsDir, 'manifest.json')
|
||
fs.writeFileSync(manifestPath, JSON.stringify(files, null, 2))
|
||
console.log(`📦 models manifest updated (${files.length}):`, files)
|
||
} catch (e) {
|
||
console.warn('⚠️ update models manifest failed:', (e as any)?.message)
|
||
}
|
||
}
|
||
|
||
return {
|
||
name: 'models-manifest',
|
||
apply: 'serve',
|
||
configureServer(server) {
|
||
writeManifest()
|
||
const dir = resolve(__dirname, 'public/models')
|
||
try {
|
||
if (fs.existsSync(dir)) {
|
||
fs.watch(dir, { persistent: true }, (_event, filename) => {
|
||
if (!filename) return
|
||
if (filename.toLowerCase().endsWith('.onnx')) writeManifest()
|
||
})
|
||
}
|
||
} catch {}
|
||
},
|
||
buildStart() {
|
||
writeManifest()
|
||
},
|
||
closeBundle() {
|
||
writeManifest()
|
||
},
|
||
}
|
||
}
|
||
|
||
export default defineConfig({
|
||
plugins: [
|
||
vue(),
|
||
modelsManifestPlugin(),
|
||
Components({
|
||
resolvers: [
|
||
AntDesignVueResolver({
|
||
importStyle: false, // css in js
|
||
}),
|
||
],
|
||
}),
|
||
],
|
||
resolve: {
|
||
alias: {
|
||
'@': resolve(__dirname, 'src'),
|
||
'~': resolve(__dirname, 'src'),
|
||
},
|
||
},
|
||
server: {
|
||
host: '0.0.0.0', // 新增:允许所有网络接口访问
|
||
port: 3001, // 明确设置为3001端口
|
||
open: true,
|
||
cors: true,
|
||
proxy: {
|
||
// RSS新闻代理转发到百度新闻(需要放在/api之前,优先匹配)
|
||
'/api/rss': {
|
||
target: 'https://news.baidu.com',
|
||
changeOrigin: true,
|
||
secure: true,
|
||
timeout: 10000, // 设置10秒超时
|
||
rewrite: (path) => {
|
||
// 百度新闻RSS格式: /n?cmd=1&class=类别&tn=rss
|
||
// 支持多种RSS路径
|
||
if (path.includes('/world')) {
|
||
return '/n?cmd=1&class=internet&tn=rss' // 国际新闻
|
||
} else if (path.includes('/tech')) {
|
||
return '/n?cmd=1&class=technic&tn=rss' // 科技新闻
|
||
} else if (path.includes('/domestic')) {
|
||
return '/n?cmd=1&class=civilnews&tn=rss' // 国内新闻
|
||
} else if (path.includes('/finance')) {
|
||
return '/n?cmd=1&class=finance&tn=rss' // 财经新闻
|
||
}
|
||
// 默认使用国内新闻
|
||
return '/n?cmd=1&class=civilnews&tn=rss'
|
||
},
|
||
configure: (proxy, options) => {
|
||
proxy.on('proxyReq', (proxyReq, req, res) => {
|
||
// 添加必要的请求头,模拟浏览器请求
|
||
proxyReq.setHeader('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36')
|
||
proxyReq.setHeader('Accept', 'application/xml, text/xml, */*')
|
||
proxyReq.setHeader('Accept-Language', 'zh-CN,zh;q=0.9,en;q=0.8')
|
||
proxyReq.setHeader('Referer', 'https://news.baidu.com/')
|
||
proxyReq.setHeader('Host', 'news.baidu.com')
|
||
// 移除Origin,避免CORS问题
|
||
proxyReq.removeHeader('Origin')
|
||
if (process.env.NODE_ENV === 'development') {
|
||
console.log(`📰 RSS代理请求: ${req.method} ${req.url} -> ${proxyReq.path}`)
|
||
}
|
||
})
|
||
|
||
proxy.on('proxyRes', (proxyRes, req, res) => {
|
||
// 添加CORS头部
|
||
res.setHeader('Access-Control-Allow-Origin', '*')
|
||
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS')
|
||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
|
||
res.setHeader('Access-Control-Expose-Headers', 'Content-Type')
|
||
|
||
// 确保Content-Type正确
|
||
if (proxyRes.headers['content-type']) {
|
||
res.setHeader('Content-Type', proxyRes.headers['content-type'])
|
||
} else {
|
||
res.setHeader('Content-Type', 'application/xml; charset=utf-8')
|
||
}
|
||
|
||
if (process.env.NODE_ENV === 'development') {
|
||
console.log(`✅ RSS响应: ${proxyRes.statusCode} ${req.url}`)
|
||
}
|
||
})
|
||
|
||
proxy.on('error', (err, req, res) => {
|
||
console.error('❌ RSS代理错误:', err.message)
|
||
})
|
||
},
|
||
},
|
||
// 翻译API代理转发到腾讯翻译(需要放在/api之前,优先匹配)
|
||
'/api/translate': {
|
||
target: 'https://fanyi.qq.com',
|
||
changeOrigin: true,
|
||
secure: true,
|
||
timeout: 10000, // 设置10秒超时
|
||
rewrite: (path) => {
|
||
// 腾讯翻译接口路径是 /api/translate,需要保留所有查询参数
|
||
const pathWithoutPrefix = path.replace(/^\/api\/translate/, '/api/translate')
|
||
if (process.env.NODE_ENV === 'development') {
|
||
console.log('翻译代理路径重写:', path, '->', pathWithoutPrefix)
|
||
}
|
||
return pathWithoutPrefix
|
||
},
|
||
configure: (proxy, options) => {
|
||
proxy.on('proxyReq', (proxyReq, req, res) => {
|
||
// 添加必要的请求头,模拟浏览器请求
|
||
proxyReq.setHeader('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36')
|
||
proxyReq.setHeader('Accept', 'application/json, text/plain, */*')
|
||
proxyReq.setHeader('Accept-Language', 'zh-CN,zh;q=0.9,en;q=0.8')
|
||
proxyReq.setHeader('Referer', 'https://fanyi.qq.com/')
|
||
proxyReq.setHeader('Content-Type', 'application/json; charset=UTF-8')
|
||
// 移除Origin,避免CORS问题
|
||
if (proxyReq.getHeader('Origin')) {
|
||
proxyReq.removeHeader('Origin')
|
||
}
|
||
if (process.env.NODE_ENV === 'development') {
|
||
console.log(`🌐 翻译代理请求: ${req.method} ${req.url} -> ${proxyReq.path}`)
|
||
console.log(`🌐 代理目标: https://fanyi.qq.com${proxyReq.path}`)
|
||
}
|
||
})
|
||
|
||
proxy.on('proxyRes', (proxyRes, req, res) => {
|
||
// 添加CORS头部
|
||
res.setHeader('Access-Control-Allow-Origin', '*')
|
||
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
|
||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
|
||
res.setHeader('Access-Control-Expose-Headers', 'Content-Type')
|
||
|
||
// 确保Content-Type正确
|
||
if (proxyRes.headers['content-type']) {
|
||
res.setHeader('Content-Type', proxyRes.headers['content-type'])
|
||
} else {
|
||
res.setHeader('Content-Type', 'application/json; charset=utf-8')
|
||
}
|
||
|
||
if (process.env.NODE_ENV === 'development') {
|
||
console.log(`✅ 翻译响应: ${proxyRes.statusCode} ${req.url}`)
|
||
// 如果是错误状态码,记录详细信息
|
||
if (proxyRes.statusCode >= 400) {
|
||
console.error(`❌ 翻译API错误: ${proxyRes.statusCode} ${req.url}`)
|
||
}
|
||
}
|
||
})
|
||
|
||
proxy.on('error', (err, req, res) => {
|
||
console.error('❌ 翻译代理错误:', err.message)
|
||
})
|
||
},
|
||
},
|
||
// 天气API代理转发到中国气象局(需要放在/api之前,优先匹配)
|
||
'/api/weather': {
|
||
target: 'https://weather.cma.cn',
|
||
changeOrigin: true,
|
||
secure: true,
|
||
rewrite: (path) => path.replace(/^\/api\/weather/, '/api/weather'),
|
||
configure: (proxy, options) => {
|
||
proxy.on('proxyReq', (proxyReq, req, res) => {
|
||
// 添加必要的请求头,模拟浏览器请求
|
||
proxyReq.setHeader('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36')
|
||
proxyReq.setHeader('Accept', 'application/json, text/plain, */*')
|
||
proxyReq.setHeader('Accept-Language', 'zh-CN,zh;q=0.9,en;q=0.8')
|
||
proxyReq.setHeader('Referer', 'https://weather.cma.cn/')
|
||
proxyReq.setHeader('Origin', 'https://weather.cma.cn')
|
||
proxyReq.setHeader('X-Proxy-By', 'Vite-Dev-Server')
|
||
if (process.env.NODE_ENV === 'development') {
|
||
console.log(`🌤️ 天气API代理: ${req.method} ${req.url}`)
|
||
}
|
||
})
|
||
|
||
proxy.on('proxyRes', (proxyRes, req, res) => {
|
||
// 添加CORS头部
|
||
res.setHeader('Access-Control-Allow-Origin', '*')
|
||
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS')
|
||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
|
||
if (process.env.NODE_ENV === 'development') {
|
||
console.log(`✅ 天气API响应: ${proxyRes.statusCode} ${req.url}`)
|
||
}
|
||
})
|
||
|
||
proxy.on('error', (err, req, res) => {
|
||
console.error('❌ 天气API代理错误:', err.message)
|
||
})
|
||
},
|
||
},
|
||
// API代理转发到后端服务器
|
||
'/api': {
|
||
target: 'http://localhost:3000',
|
||
changeOrigin: true,
|
||
secure: false,
|
||
rewrite: (path) => path.replace(/^\/api/, '/api'),
|
||
// 优化Network面板显示
|
||
// 保持原始头部信息
|
||
preserveHeaderKeyCase: true,
|
||
// 添加CORS头部,改善Network面板显示
|
||
headers: {
|
||
'Access-Control-Allow-Origin': '*',
|
||
'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE,PATCH,OPTIONS',
|
||
'Access-Control-Allow-Headers': 'Content-Type, Authorization, Content-Length, X-Requested-With',
|
||
},
|
||
configure: (proxy, options) => {
|
||
// 简化代理日志
|
||
proxy.on('proxyReq', (proxyReq, req, res) => {
|
||
// 添加标识头部,帮助Network面板识别
|
||
proxyReq.setHeader('X-Proxy-By', 'Vite-Dev-Server')
|
||
if (process.env.NODE_ENV === 'development') {
|
||
console.log(`🔄 代理: ${req.method} ${req.url}`)
|
||
}
|
||
})
|
||
|
||
proxy.on('proxyRes', (proxyRes, req, res) => {
|
||
// 添加响应头部,改善Network面板显示
|
||
res.setHeader('X-Proxy-By', 'Vite-Dev-Server')
|
||
res.setHeader('Access-Control-Allow-Origin', '*')
|
||
if (process.env.NODE_ENV === 'development') {
|
||
console.log(`✅ 响应: ${proxyRes.statusCode} ${req.url}`)
|
||
}
|
||
})
|
||
|
||
proxy.on('error', (err, req, res) => {
|
||
console.error('❌ 代理错误:', err.message)
|
||
})
|
||
},
|
||
},
|
||
// 媒体文件代理转发到后端服务器
|
||
'/media': {
|
||
target: 'http://localhost:3000',
|
||
changeOrigin: true,
|
||
secure: false,
|
||
rewrite: (path) => path.replace(/^\/media/, '/media'),
|
||
// 优化Network面板显示
|
||
// 保持原始头部信息
|
||
preserveHeaderKeyCase: true,
|
||
// 添加CORS头部,改善Network面板显示
|
||
headers: {
|
||
'Access-Control-Allow-Origin': '*',
|
||
'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE,PATCH,OPTIONS',
|
||
'Access-Control-Allow-Headers': 'Content-Type, Authorization, Content-Length, X-Requested-With',
|
||
},
|
||
configure: (proxy, options) => {
|
||
// 简化代理日志
|
||
proxy.on('proxyReq', (proxyReq, req, res) => {
|
||
// 添加标识头部,帮助Network面板识别
|
||
proxyReq.setHeader('X-Proxy-By', 'Vite-Dev-Server')
|
||
if (process.env.NODE_ENV === 'development') {
|
||
console.log(`🔄 媒体代理: ${req.method} ${req.url}`)
|
||
}
|
||
})
|
||
|
||
proxy.on('proxyRes', (proxyRes, req, res) => {
|
||
// 添加响应头部,改善Network面板显示
|
||
res.setHeader('X-Proxy-By', 'Vite-Dev-Server')
|
||
res.setHeader('Access-Control-Allow-Origin', '*')
|
||
if (process.env.NODE_ENV === 'development') {
|
||
console.log(`✅ 媒体响应: ${proxyRes.statusCode} ${req.url}`)
|
||
}
|
||
})
|
||
|
||
proxy.on('error', (err, req, res) => {
|
||
console.error('❌ 媒体代理错误:', err.message)
|
||
})
|
||
},
|
||
},
|
||
},
|
||
},
|
||
define: {
|
||
// 环境变量定义,确保在没有.env文件时也能正常工作
|
||
__VITE_API_BASE_URL__: JSON.stringify('http://localhost:3000/api'),
|
||
__VITE_APP_TITLE__: JSON.stringify('Hertz Admin'),
|
||
__VITE_APP_VERSION__: JSON.stringify('1.0.0'),
|
||
},
|
||
build: {
|
||
sourcemap: true,
|
||
rollupOptions: {
|
||
output: {
|
||
manualChunks: {
|
||
vue: ['vue', 'vue-router', 'pinia'],
|
||
antd: ['ant-design-vue'],
|
||
utils: ['axios', 'echarts'],
|
||
},
|
||
},
|
||
},
|
||
},
|
||
})
|