import { ConfigEnv, UserConfig, defineConfig } from "vite"; import { createHtmlPlugin } from "vite-plugin-html"; import vue from "@vitejs/plugin-vue"; import path, { resolve } from "path"; import { createSvgIconsPlugin } from "vite-plugin-svg-icons"; import { viteMockServe } from "vite-plugin-mock"; import ViteAutoImport from "unplugin-auto-import/vite"; import UnoCSS from "@unocss/vite"; import presetUno from "@unocss/preset-uno"; import presetRemToPx from "@unocss/preset-rem-to-px"; export default defineConfig(({ mode }: ConfigEnv): UserConfig => { // 构建产物直出 dist/(notice / flow-center 同口径):projman 前端发布要求 zip 根含 index.html, // 不得再套 dist/base 中间层;dev(serve) 不写产物,两者共用该值无冲突 const outDir = "dist"; return { plugins: [ vue(), createSvgIconsPlugin({ iconDirs: [resolve(process.cwd(), "src/assets/icons")], symbolId: "icon-[dir]-[name]", inject: "body-last", customDomId: "__svg__icons__dom__", }), viteMockServe({ mockPath: "./src/assets/mock", // 仅 --mode mock 时启用 mock 中间件;dev/联调/正式均走真实后端 localEnabled: mode === "mock", }), ViteAutoImport({ imports: [ "vue", "vue-router", { "view-ui-plus": ["Button", "Table", "Modal", "Message", "Form", "FormItem", "Input", "Select"], }, { from: "alova/client", imports: ["usePagination", "useRequest", "useWatcher", "useForm"], }, ], dts: "./src/auto-imports.d.ts", dirs: ["src/api"], // 自动导入 api hooks }), createHtmlPlugin({ minify: true, pages: [ { entry: "src/main.ts", template: "index.html", filename: "index.html", injectOptions: { data: { title: "表单中心", injectScript: ``, }, }, }, ], }), UnoCSS({ presets: [presetUno(), presetRemToPx({ baseFontSize: 4 })], shortcuts: { "flex-center": "flex items-center justify-center", btn: "px-16px py-8px rounded-4px cursor-pointer transition-all duration-200", card: "bg-white rounded-8px shadow-md p-16px", "text-ellipsis": "truncate", "text-multi-ellipsis": "line-clamp-2", }, theme: { fontSize: { xs: "12px", sm: "14px", base: "16px", lg: "18px", xl: "20px", "2xl": "24px", }, }, }), // 主题接入已免插件化(A 类,规范 §2.1):唯一接入点 = index.html 的 loader; // 下面的 /static 代理只是 dev 联调便利(prod 同源用不到),删除不影响线上主题 ], resolve: { alias: { "@": path.resolve(__dirname, "./src"), }, }, server: { host: "localhost", // 本地 dev 端口(执行手册 §5 P6:service=8086 / front=8089) port: 8089, proxy: { // dev 正式模式:serviceHost 为空(同源相对路径 /api/...),经此代理转发到本地后端 // (后端 CAS 过滤器对 /api/* 不回 CORS 头,浏览器直连 8086 会被拦,必须同源代理) "/api": { target: "http://localhost:8086", changeOrigin: true, }, // 主题通道本地联调:dev 下 loader 走同源相对,manifest/CSS 经此代理到中央通道。 // 联调本地通道源:YschTheme 侧 npm run serve:static(默认 9527),再用 // YH_THEME_PROXY_TARGET=http://localhost:9527 覆盖本 target "/static": { target: process.env.YH_THEME_PROXY_TARGET || "http://base.yunhaixingtong.com", changeOrigin: true, }, }, }, base: "./", build: { target: "modules", outDir, assetsDir: "assets", sourcemap: false, minify: "esbuild", chunkSizeWarningLimit: 1000, cssTarget: "chrome61", // 安卓微信 webview 不支持 #RGBA 十六进制颜色 rollupOptions: { input: { main: resolve("index.html"), }, output: { chunkFileNames: "static/js/[name]-[hash].js", entryFileNames: "static/js/[name]-[hash].js", assetFileNames: (assetInfo: any) => { if (assetInfo.type === "asset" && /\.(jpe?g|png|gif|svg)$/i.test(assetInfo.name)) { return "static/img/[name].[hash][ext]"; } if (assetInfo.type === "asset" && /\.(ttf|woff|woff2|eot)$/i.test(assetInfo.name)) { return "static/fonts/[name].[hash][ext]"; } return "static/[ext]/[name]-[hash].[ext]"; }, }, manualEntrypoint: "index.html", }, }, }; });