This commit is contained in:
2026-05-31 12:09:14 +08:00
commit e4f9eedb80
1121 changed files with 209380 additions and 0 deletions

274
vite.config.ts Normal file
View File

@@ -0,0 +1,274 @@
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']
}
}
}
};
});