const CompressionWebpackPlugin = require("compression-webpack-plugin"); const { transformElementScss } = require("ele-admin/lib/utils/dynamic-theme"); const path = require("path"); const { name } = require("./package.json"); function resolve(dir) { return path.join(__dirname, dir); } // 与 EOM(/eos)、WT(/wt) 一致:静态资源部署在短路径 /hr,乾坤路由前缀才是 /page-hr const publicPath = process.env.VUE_APP_PUBLIC_PATH || "/hr"; /** * 本地 /api 代理。 * - 直连后端(18086 等):接口在根路径,需要剥掉 /api * - 网关/nginx(51010 等):/api 才是接口,剥掉会打到前端 HTML(ele-admin-template) */ function resolveApiProxyTarget(rawTarget) { const target = String(rawTarget || "") .trim() .replace(/\/$/, ""); const alreadyHasApi = /\/api$/i.test(target); const isDirectBackend = /:(18086|18087|18186|18187)(\/|$)/.test(target); return { target: alreadyHasApi ? target.replace(/\/api$/i, "") : target, keepApiPrefix: alreadyHasApi || !isDirectBackend, }; } function stripProxyCacheHeaders(proxyRes) { proxyRes.headers["cache-control"] = "no-store, no-cache, must-revalidate"; proxyRes.headers.pragma = "no-cache"; proxyRes.headers.expires = "0"; delete proxyRes.headers.etag; delete proxyRes.headers["last-modified"]; const contentType = String(proxyRes.headers["content-type"] || ""); if (proxyRes.statusCode === 200 && contentType.includes("text/html")) { proxyRes.statusCode = 502; } } const apiProxy = resolveApiProxyTarget( process.env.VUE_APP_PROXY_TARGET || // "http://192.168.1.147:18090", "http://aiot.zoomwin.com.cn:51010", ); // element-ui / ele-admin 仍使用 Sass @import,静音 Dart Sass 弃用警告 const sassOptions = { outputStyle: "expanded", importer: transformElementScss(), quietDeps: true, silenceDeprecations: [ "import", "global-builtin", "color-functions", "slash-div", "legacy-js-api", ], }; module.exports = { publicPath, lintOnSave: false, outputDir: "dist", productionSourceMap: false, configureWebpack: { performance: { maxAssetSize: 2000000, maxEntrypointSize: 2000000, }, output: { // 与 EOM/WT 一致:子应用打包成 umd 库格式 library: { type: "umd", name: `${name}`, }, chunkLoadingGlobal: `webpackJsonp_${name}`, }, // Vue2 + vue-style-loader:样式靠副作用注入,不导出 default; // Webpack5 会对 `import style0 from ...` 报 missing export,可安全忽略 ignoreWarnings: [ { message: /export ['"]default['"] \(imported as ['"]style\d+['"]\) was not found/, }, ], }, devServer: { port: Number(process.env.VUE_APP_PORT || process.env.PORT || 8091), open: [publicPath.endsWith("/") ? publicPath : `${publicPath}/`], historyApiFallback: { index: path.posix.join( publicPath.endsWith("/") ? publicPath : `${publicPath}/`, "index.html", ), htmlAcceptHeaders: ["text/html"], rewrites: [ { from: /^\/(api|kkfile)(\/|$)/, to: (context) => context.parsedUrl.pathname, }, ], }, setupMiddlewares(middlewares) { middlewares.unshift({ name: "redirect-to-app", middleware(req, res, next) { const url = (req.url || "").split("?")[0]; const appIndex = publicPath.endsWith("/") ? publicPath : `${publicPath}/`; if ( url === "/" || url === "/page-hr" || url === "/page-hr/" || url === "/hr-pc" || url === "/hr-pc/" ) { res.redirect(appIndex); return; } next(); }, }); return middlewares; }, proxy: { "/api": { target: apiProxy.target, changeOrigin: true, pathRewrite: apiProxy.keepApiPrefix ? undefined : { "^/api": "" }, onProxyRes: stripProxyCacheHeaders, }, "/kkfile": { target: "http://aiot.zoomwin.com.cn:51010", changeOrigin: true, pathRewrite: { "^/kkfile": "/kkfile" }, }, }, headers: { "Access-Control-Allow-Origin": "*", }, }, chainWebpack(config) { config.plugins.delete("prefetch"); config.module.rule("svg").exclude.add(resolve("src/icons")).end(); config.module .rule("icons") .test(/\.svg$/) .include.add(resolve("src/icons")) .end() .use("svg-sprite-loader") .loader("svg-sprite-loader") .options({ symbolId: "icon-[name]", }) .end(); if (process.env.NODE_ENV !== "development") { config.plugin("compressionPlugin").use( new CompressionWebpackPlugin({ test: /\.(js|css|html)$/, threshold: 10240, }), ); } }, css: { loaderOptions: { css: { esModule: false, }, sass: { sassOptions }, scss: { sassOptions }, }, }, };