vue.config.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. const CompressionWebpackPlugin = require("compression-webpack-plugin");
  2. const { transformElementScss } = require("ele-admin/lib/utils/dynamic-theme");
  3. const path = require("path");
  4. const { name } = require("./package.json");
  5. function resolve(dir) {
  6. return path.join(__dirname, dir);
  7. }
  8. // 与 EOM(/eos)、WT(/wt) 一致:静态资源部署在短路径 /hr,乾坤路由前缀才是 /page-hr
  9. const publicPath = process.env.VUE_APP_PUBLIC_PATH || "/hr";
  10. /**
  11. * 本地 /api 代理。
  12. * - 直连后端(18086 等):接口在根路径,需要剥掉 /api
  13. * - 网关/nginx(51010 等):/api 才是接口,剥掉会打到前端 HTML(ele-admin-template)
  14. */
  15. function resolveApiProxyTarget(rawTarget) {
  16. const target = String(rawTarget || "")
  17. .trim()
  18. .replace(/\/$/, "");
  19. const alreadyHasApi = /\/api$/i.test(target);
  20. const isDirectBackend = /:(18086|18087|18186|18187)(\/|$)/.test(target);
  21. return {
  22. target: alreadyHasApi ? target.replace(/\/api$/i, "") : target,
  23. keepApiPrefix: alreadyHasApi || !isDirectBackend,
  24. };
  25. }
  26. function stripProxyCacheHeaders(proxyRes) {
  27. proxyRes.headers["cache-control"] = "no-store, no-cache, must-revalidate";
  28. proxyRes.headers.pragma = "no-cache";
  29. proxyRes.headers.expires = "0";
  30. delete proxyRes.headers.etag;
  31. delete proxyRes.headers["last-modified"];
  32. const contentType = String(proxyRes.headers["content-type"] || "");
  33. if (proxyRes.statusCode === 200 && contentType.includes("text/html")) {
  34. proxyRes.statusCode = 502;
  35. }
  36. }
  37. const apiProxy = resolveApiProxyTarget(
  38. process.env.VUE_APP_PROXY_TARGET ||
  39. // "http://192.168.1.147:18090",
  40. "http://aiot.zoomwin.com.cn:51010",
  41. );
  42. // element-ui / ele-admin 仍使用 Sass @import,静音 Dart Sass 弃用警告
  43. const sassOptions = {
  44. outputStyle: "expanded",
  45. importer: transformElementScss(),
  46. quietDeps: true,
  47. silenceDeprecations: [
  48. "import",
  49. "global-builtin",
  50. "color-functions",
  51. "slash-div",
  52. "legacy-js-api",
  53. ],
  54. };
  55. module.exports = {
  56. publicPath,
  57. lintOnSave: false,
  58. outputDir: "dist",
  59. productionSourceMap: false,
  60. configureWebpack: {
  61. performance: {
  62. maxAssetSize: 2000000,
  63. maxEntrypointSize: 2000000,
  64. },
  65. output: {
  66. // 与 EOM/WT 一致:子应用打包成 umd 库格式
  67. library: {
  68. type: "umd",
  69. name: `${name}`,
  70. },
  71. chunkLoadingGlobal: `webpackJsonp_${name}`,
  72. },
  73. // Vue2 + vue-style-loader:样式靠副作用注入,不导出 default;
  74. // Webpack5 会对 `import style0 from ...` 报 missing export,可安全忽略
  75. ignoreWarnings: [
  76. {
  77. message:
  78. /export ['"]default['"] \(imported as ['"]style\d+['"]\) was not found/,
  79. },
  80. ],
  81. },
  82. devServer: {
  83. port: Number(process.env.VUE_APP_PORT || process.env.PORT || 8091),
  84. open: [publicPath.endsWith("/") ? publicPath : `${publicPath}/`],
  85. historyApiFallback: {
  86. index: path.posix.join(
  87. publicPath.endsWith("/") ? publicPath : `${publicPath}/`,
  88. "index.html",
  89. ),
  90. htmlAcceptHeaders: ["text/html"],
  91. rewrites: [
  92. {
  93. from: /^\/(api|kkfile)(\/|$)/,
  94. to: (context) => context.parsedUrl.pathname,
  95. },
  96. ],
  97. },
  98. setupMiddlewares(middlewares) {
  99. middlewares.unshift({
  100. name: "redirect-to-app",
  101. middleware(req, res, next) {
  102. const url = (req.url || "").split("?")[0];
  103. const appIndex = publicPath.endsWith("/")
  104. ? publicPath
  105. : `${publicPath}/`;
  106. if (
  107. url === "/" ||
  108. url === "/page-hr" ||
  109. url === "/page-hr/" ||
  110. url === "/hr-pc" ||
  111. url === "/hr-pc/"
  112. ) {
  113. res.redirect(appIndex);
  114. return;
  115. }
  116. next();
  117. },
  118. });
  119. return middlewares;
  120. },
  121. proxy: {
  122. "/api": {
  123. target: apiProxy.target,
  124. changeOrigin: true,
  125. pathRewrite: apiProxy.keepApiPrefix ? undefined : { "^/api": "" },
  126. onProxyRes: stripProxyCacheHeaders,
  127. },
  128. "/kkfile": {
  129. target: "http://aiot.zoomwin.com.cn:51010",
  130. changeOrigin: true,
  131. pathRewrite: { "^/kkfile": "/kkfile" },
  132. },
  133. },
  134. headers: {
  135. "Access-Control-Allow-Origin": "*",
  136. },
  137. },
  138. chainWebpack(config) {
  139. config.plugins.delete("prefetch");
  140. config.module.rule("svg").exclude.add(resolve("src/icons")).end();
  141. config.module
  142. .rule("icons")
  143. .test(/\.svg$/)
  144. .include.add(resolve("src/icons"))
  145. .end()
  146. .use("svg-sprite-loader")
  147. .loader("svg-sprite-loader")
  148. .options({
  149. symbolId: "icon-[name]",
  150. })
  151. .end();
  152. if (process.env.NODE_ENV !== "development") {
  153. config.plugin("compressionPlugin").use(
  154. new CompressionWebpackPlugin({
  155. test: /\.(js|css|html)$/,
  156. threshold: 10240,
  157. }),
  158. );
  159. }
  160. },
  161. css: {
  162. loaderOptions: {
  163. css: {
  164. esModule: false,
  165. },
  166. sass: { sassOptions },
  167. scss: { sassOptions },
  168. },
  169. },
  170. };