Files
2026-07-21 19:26:22 +08:00

119 lines
4.2 KiB
TypeScript

import { fileURLToPath, URL } from 'node:url'
import vue from '@vitejs/plugin-vue'
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'vite'
import { VitePWA } from 'vite-plugin-pwa'
type GetModuleInfo = (id: string) => { importers: readonly string[] } | null
/** pako / jszip 等 office 深依赖路径无特征,沿 importer 链上溯:
* 任一条链经过 @vue-office 即视为 office 生态模块 */
function fromVueOffice(id: string, getModuleInfo: GetModuleInfo, seen = new Set<string>()): boolean {
if (seen.has(id)) return false
seen.add(id)
const importers = getModuleInfo(id)?.importers ?? []
return importers.some(
(imp) => imp.includes('@vue-office') || fromVueOffice(imp, getModuleInfo, seen),
)
}
/** 大体积三方库归入命名 chunk:主入口显著瘦身,命名亦供 PWA 预缓存排除
* (这些块全部只被懒加载路径引用,拆出不影响首屏)。 */
function vendorChunk(id: string, getModuleInfo: GetModuleInfo): string | undefined {
if (!id.includes('node_modules')) return undefined
if (id.includes('@vue-office/pdf') || id.includes('pdfjs')) return 'office-pdf'
if (id.includes('@vue-office/excel') || id.includes('exceljs') || id.includes('xlsx'))
return 'office-excel'
if (id.includes('@vue-office/docx') || id.includes('docx')) return 'office-docx'
if (id.includes('echarts') || id.includes('zrender')) return 'echarts'
if (id.includes('naive-ui')) return 'naive-ui'
if (id.includes('highlight.js')) return 'hljs'
if (fromVueOffice(id, getModuleInfo)) return 'office-shared'
return undefined
}
export default defineConfig({
plugins: [
vue(),
tailwindcss(),
VitePWA({
registerType: 'autoUpdate',
manifest: {
name: 'OCI Portal',
short_name: 'OCI Portal',
description: '自托管 OCI 多租户管理面板',
lang: 'zh-CN',
display: 'standalone',
theme_color: '#F5F4ED',
background_color: '#F5F4ED',
icons: [
{ src: '/pwa-192.png', sizes: '192x192', type: 'image/png' },
{ src: '/pwa-512.png', sizes: '512x512', type: 'image/png' },
{ src: '/pwa-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
],
},
workbox: {
globPatterns: ['**/*.{js,css,html,svg,png,woff2}'],
// 预缓存只收应用壳(入口/naive-ui/echarts/路由块);office 全家
// (pdf/excel/docx/shared,合计 ~6MB)安装时不下载,首次使用再入缓存
globIgnores: ['**/office-*.js'],
navigateFallback: 'index.html',
// API 与 AI 网关直通不落 SPA 壳也不缓存:运维数据必须实时
navigateFallbackDenylist: [/^\/api\//, /^\/ai\//],
runtimeCaching: [
{
// 未进预缓存的大块兜底:assets 带内容 hash 不可变,CacheFirst 恒安全
urlPattern: /\/assets\/.+\.js$/,
handler: 'CacheFirst',
options: {
cacheName: 'lazy-vendor',
expiration: { maxEntries: 30, maxAgeSeconds: 30 * 24 * 3600 },
},
},
],
},
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
// noVNC 1.7 使用 top-level await,需较新的构建目标(现代浏览器均支持);
// 生产构建与 dev 依赖预构建(esbuild)都要提高 target,否则 dev 会报同一错误
build: {
target: 'es2022',
// 现存最大懒块 office-pdf(pdfjs 全量)~2.8MB,均为按需加载不入首屏;
// 阈值取其上放宽,只为「新失控块」鸣笛而不对既有三方整包常鸣
chunkSizeWarningLimit: 2900,
rollupOptions: {
output: {
manualChunks: (id, { getModuleInfo }) => vendorChunk(id, getModuleInfo),
},
},
},
optimizeDeps: {
esbuildOptions: {
target: 'es2022',
},
},
server: {
port: 5173,
host: '127.0.0.1',
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
ws: true,
},
// 只代理网关端点 /ai/*;裸 '/ai' 前缀会误拦 /ai-gateway 页面路由
'^/ai/': {
target: 'http://localhost:8080',
changeOrigin: true,
ws: true,
},
},
},
})