import { defineConfig, loadEnv } from 'vite'; import vue from '@vitejs/plugin-vue'; import ViteCompression from 'vite-plugin-compression'; import ViteComponents from 'unplugin-vue-components/vite'; import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'; import { EleAdminResolver } from 'ele-admin-pro/lib/utils/resolvers'; import { DynamicAntdLess } from 'ele-admin-pro/lib/utils/dynamic-theme'; import { resolve } from 'path'; import { visualizer } from 'rollup-plugin-visualizer'; import { splitVendorChunkPlugin } from 'vite'; // 简化的智能端口管理(避免构建时模块解析问题) function getSmartPort() { try { // 从环境变量获取基础配置 const basePort = parseInt(process.env.VITE_BASE_PORT || '3000'); const tenantId = process.env.VITE_TENANT_ID || '10258'; const environment = process.env.NODE_ENV || 'development'; // 简化的端口计算 let recommendedPort = basePort; if (environment === 'development') { // 开发环境:基础端口 + 租户偏移 const tenantOffset = (parseInt(tenantId) % 1000) * 10; recommendedPort = basePort + tenantOffset; } else if (environment === 'test') { recommendedPort = basePort + 1000; } else if (environment === 'production') { recommendedPort = 8080; // 生产环境使用标准端口 } console.log('🎯 智能端口计算:', { environment, tenantId, basePort, recommendedPort }); return recommendedPort; } catch (error) { console.warn('⚠️ 端口计算失败,使用默认端口 3000:', error); return 3000; } } export default defineConfig(({ command, mode }) => { // Load env for Vite config usage (including non-VITE_ keys like AI_API_KEY). const env = loadEnv(mode, process.cwd(), ''); const isBuild = command === 'build'; // 智能端口配置(仅在开发模式下) const smartPort = !isBuild ? getSmartPort() : undefined; return { // 在这里增加 base 写子路径 base: '/', resolve: { alias: { '@/': resolve('src') + '/', 'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js' } }, // 智能服务器配置 server: { port: smartPort || 3000, host: '0.0.0.0', // 允许外部访问 open: true, // 自动打开浏览器 cors: true, // 启用 CORS // 代理配置 proxy: { '/api': { target: env.VITE_API_URL || process.env.VITE_API_URL || 'https://server.websoft.top', changeOrigin: true, secure: false, configure: (proxy, _options) => { proxy.on('error', (err, _req, _res) => { console.log('proxy error', err); }); proxy.on('proxyReq', (proxyReq, req, _res) => { console.log( 'Sending Request to the Target:', req.method, req.url ); }); proxy.on('proxyRes', (proxyRes, req, _res) => { console.log( 'Received Response from the Target:', proxyRes.statusCode, req.url ); }); } }, // OpenAI-compatible gateway reverse proxy (dev only). // Example: // GET /ai-proxy/models -> https://ai.websoft.top/api/v1/models // POST /ai-proxy/chat/completions -> https://ai.websoft.top/api/v1/chat/completions '/ai-proxy': { target: env.AI_PROXY_TARGET || 'https://ai-api.websoft.top', changeOrigin: true, secure: false, rewrite: (path) => path.replace( /^\/ai-proxy/, env.AI_PROXY_REWRITE_PREFIX || '/api/v1' ), configure: (proxy) => { proxy.on('proxyReq', (proxyReq) => { // Inject auth for local dev to avoid putting API keys in the browser. const key = env.AI_API_KEY || process.env.AI_API_KEY; if (key && !proxyReq.getHeader('Authorization')) { const trimmed = String(key).trim(); const value = /^bearer\\s+/i.test(trimmed) ? trimmed : `Bearer ${trimmed}`; proxyReq.setHeader('Authorization', value); } }); } }, // Ollama native API reverse proxy (dev only). // GET /proxy/api/tags -> http://47.119.165.234:11434/api/tags // POST /proxy/api/chat -> http://47.119.165.234:11434/api/chat '/proxy': { target: 'http://47.119.165.234:11434', changeOrigin: true, secure: false, rewrite: (path) => path.replace(/^\/proxy/, '') } }, // 端口冲突时的处理 strictPort: false // 允许自动选择其他端口 }, // 预览服务器配置(用于生产构建预览) preview: { port: smartPort ? smartPort + 1000 : 4173, host: '0.0.0.0', open: true, cors: true, strictPort: false }, plugins: [ vue({ script: { defineModel: true, propsDestructure: true } }), // 组件按需引入 ViteComponents({ dts: false, resolvers: [ AntDesignVueResolver({ importStyle: isBuild ? 'less' : false }), EleAdminResolver({ importStyle: isBuild ? 'less' : false }) ], directoryAsNamespace: true }), // 代码分割 splitVendorChunkPlugin(), // gzip 压缩 ViteCompression({ disable: !isBuild, threshold: 10240, algorithm: 'gzip', ext: '.gz' }), // brotli 压缩 ViteCompression({ disable: !isBuild, threshold: 10240, algorithm: 'brotliCompress', ext: '.br' }), // 打包分析 isBuild && visualizer({ filename: 'dist/stats.html', open: false, gzipSize: true, brotliSize: true }) ].filter(Boolean), css: { preprocessorOptions: { less: { javascriptEnabled: true, plugins: [new DynamicAntdLess()], modifyVars: { // 组件样式开发环境全局引入生产环境按需引入 'style-entry-file': isBuild ? 'as-needed' : 'global-import' } } } }, optimizeDeps: { include: [ 'sortablejs', 'vuedraggable', 'echarts/core', 'echarts/charts', 'echarts/renderers', 'echarts/components', 'vue-echarts', 'echarts-wordcloud', 'xlsx', 'lodash-es', 'dayjs', 'crypto-js', 'js-md5', 'qrcode', 'nprogress' ], exclude: ['@iconify/json'] }, build: { target: 'es2015', cssCodeSplit: true, chunkSizeWarningLimit: 1000, rollupOptions: { output: { // 手动分包 manualChunks: { // Vue 生态 'vue-vendor': ['vue', 'vue-router', 'pinia'], // UI 组件库 'ui-vendor': ['ant-design-vue', 'ele-admin-pro'], // 工具库 'utils-vendor': ['lodash-es', 'dayjs', 'crypto-js', 'js-md5'], // 图表库 'charts-vendor': ['echarts', 'vue-echarts', 'echarts-wordcloud'], // 编辑器 'editor-vendor': ['tinymce', 'bytemd', 'md-editor-v3'], // 文件处理 'file-vendor': ['xlsx', 'exceljs', 'file-saver', 'ali-oss'] }, // 文件命名 chunkFileNames: (chunkInfo) => { const facadeModuleId = chunkInfo.facadeModuleId ? chunkInfo.facadeModuleId .split('/') .pop() .replace(/\.\w+$/, '') : 'chunk'; return `js/${facadeModuleId}-[hash].js`; }, assetFileNames: (assetInfo) => { const info = assetInfo.name.split('.'); const ext = info[info.length - 1]; if ( /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/i.test(assetInfo.name) ) { return `media/[name]-[hash].${ext}`; } if (/\.(png|jpe?g|gif|svg)(\?.*)?$/.test(assetInfo.name)) { return `images/[name]-[hash].${ext}`; } if (/\.(woff2?|eot|ttf|otf)(\?.*)?$/i.test(assetInfo.name)) { return `fonts/[name]-[hash].${ext}`; } return `assets/[name]-[hash].${ext}`; } } }, // 压缩配置 minify: 'terser', terserOptions: { compress: { drop_console: true, drop_debugger: true, pure_funcs: ['console.log'] } } } }; });