Files
guilixu-admin/vite.config.ts
2026-05-31 12:09:14 +08:00

275 lines
9.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { defineConfig } 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 { resolve } from 'path';
import { visualizer } from 'rollup-plugin-visualizer';
/**
* AntDv4 兼容:替换 ele-admin-pro Less 中的 colorPalette JS 调用
* AntDv4 移除了 Less 的 colorPalette 函数ele-admin-pro 的 common.less
* 中有 ~`colorPalette('@{primary-color}', 5)` 等 JS 表达式,
* 此插件在 Less 编译前将其替换为硬编码的颜色值。
*/
function colorPaletteReplacer() {
return {
name: 'color-palette-replacer',
enforce: 'pre',
async transform(code, id) {
if (!id.includes('ele-admin-pro') || !id.endsWith('.less')) {
return null;
}
console.log('🎨 Processing Less file:', id);
let result = code;
result = result.replace(/~`colorPalette\('@\{primary-color\}',\s*(\d+)\)\s*`/g, (_match, idx) => {
const colors = { 1: '#e6f4ff', 2: '#bae0ff', 3: '#91caff', 4: '#69b1ff', 5: '#4096ff', 6: '#1677ff', 7: '#0958d9', 8: '#003eb3', 9: '#002c8c', 10: '#001d66' };
return colors[idx] || '#1677ff';
});
result = result.replace(/~`colorPalette\('@\{success-color\}',\s*(\d+)\)\s*`/g, (_match, idx) => {
const colors = { 1: '#f6ffed', 2: '#d9f7be', 3: '#b7eb8f', 4: '#95de64', 5: '#73d13d', 6: '#52c41a', 7: '#389e0d', 8: '#237804', 9: '#135200', 10: '#092b00' };
return colors[idx] || '#52c41a';
});
result = result.replace(/~`colorPalette\('@\{warning-color\}',\s*(\d+)\)\s*`/g, (_match, idx) => {
const colors = { 1: '#fffbe6', 2: '#fff1b8', 3: '#ffe58f', 4: '#ffd666', 5: '#ffc53d', 6: '#faad14', 7: '#d48806', 8: '#ad6800', 9: '#874d00', 10: '#612500' };
return colors[idx] || '#faad14';
});
result = result.replace(/~`colorPalette\('@\{error-color\}',\s*(\d+)\)\s*`/g, (_match, idx) => {
const colors = { 1: '#fff2f0', 2: '#ffccc7', 3: '#ffa39e', 4: '#ff7875', 5: '#ff4d4f', 6: '#f5222d', 7: '#cf1322', 8: '#a8071a', 9: '#820014', 10: '#5c0011' };
return colors[idx] || '#ff4d4f';
});
if (result !== code) {
console.log('✅ Replaced colorPalette in:', id);
}
return result !== code ? result : null;
}
};
}
/**
* AntDv4 兼容:将 ele-admin-pro 引用的 AntDv3 样式模块重定向到空模块
* AntDv4 使用 CSS-in-JS不再有 es/xxx/style 目录
*/
function antdStyleRedirect() {
const emptyModule = resolve('src/styles/antd-less-stub/empty-style.js');
return {
name: 'antd-style-redirect',
enforce: 'pre',
resolveId(source) {
if (source.startsWith('ant-design-vue/es/') && source.endsWith('/style')) {
return emptyModule;
}
return null;
}
};
}
// 简化的智能端口管理(避免构建时模块解析问题)
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 }) => {
const isBuild = command === 'build';
const smartPort = !isBuild ? getSmartPort() : undefined;
return {
base: '/',
resolve: {
alias: {
'@/': resolve('src') + '/',
'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js',
// AntDv4 兼容层:将 ele-admin-pro 引用的 AntD Less 主题文件重定向到 stub
'ant-design-vue/es/style/themes/default.less': resolve('src/styles/antd-less-stub/default.less'),
'ant-design-vue/es/style/themes/dark.less': resolve('src/styles/antd-less-stub/dark.less')
}
},
server: {
port: smartPort || 3000,
host: '0.0.0.0',
open: true,
proxy: {
'/api': {
target: 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);
});
},
}
},
strictPort: false,
},
preview: {
port: smartPort ? smartPort + 1000 : 4173,
host: '0.0.0.0',
open: true,
strictPort: false,
},
plugins: [
colorPaletteReplacer(),
antdStyleRedirect(),
vue({
script: {
defineModel: true,
propsDestructure: true
}
}),
ViteComponents({
dts: false,
resolvers: [
AntDesignVueResolver({
importStyle: false // AntDv4 使用 CSS-in-JS无需 Less 导入
}),
EleAdminResolver({
importStyle: isBuild ? 'less' : false
})
],
directoryAsNamespace: true
}),
ViteCompression({
disable: !isBuild,
threshold: 10240,
algorithm: 'gzip',
ext: '.gz'
}),
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,
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', 'tinymce']
},
build: {
target: 'es2015',
cssCodeSplit: true,
chunkSizeWarningLimit: 1000,
rollupOptions: {
output: {
manualChunks(id) {
if (!id.includes('node_modules')) return undefined;
if (id.includes('/vue-router/') || id.includes('/pinia/') || id.includes('/vue/')) {
return 'vue-vendor';
}
if (id.includes('/ant-design-vue/') || id.includes('/ele-admin-pro/')) {
return 'ui-vendor';
}
if (id.includes('/lodash-es/') || id.includes('/dayjs/') || id.includes('/crypto-js/') || id.includes('/js-md5/')) {
return 'utils-vendor';
}
if (id.includes('/echarts/') || id.includes('/vue-echarts/') || id.includes('/echarts-wordcloud/')) {
return 'charts-vendor';
}
if (id.includes('/tinymce/') || id.includes('/bytemd/') || id.includes('/md-editor-v3/')) {
return 'editor-vendor';
}
if (id.includes('/xlsx/') || id.includes('/exceljs/') || id.includes('/file-saver/') || id.includes('/ali-oss/')) {
return 'file-vendor';
}
return undefined;
},
chunkFileNames: (chunkInfo) => {
const name = chunkInfo.facadeModuleId
? chunkInfo.facadeModuleId.split('/').pop().replace(/\.\w+$/, '')
: 'chunk';
return `js/${name}-[hash].js`;
},
assetFileNames: (assetInfo) => {
const name = (assetInfo.name || '').split('/').pop();
if (/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/i.test(name)) {
return `media/[hash].[ext]`;
}
if (/\.(png|jpe?g|gif|svg)(\?.*)?$/.test(name)) {
return `images/[hash].[ext]`;
}
if (/\.(woff2?|eot|ttf|otf)(\?.*)?$/i.test(name)) {
return `fonts/[hash].[ext]`;
}
return `assets/[hash].[ext]`;
}
}
},
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log']
}
}
}
};
});