vue.config.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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.10:18086",
  40. "http://192.168.1.113:18086", //郭丹
  41. // "http://192.168.1.11:18086", //谭zx
  42. // "http://aiot.zoomwin.com.cn:51001",
  43. );
  44. // element-ui / ele-admin 仍使用 Sass @import,静音 Dart Sass 弃用警告
  45. const sassOptions = {
  46. outputStyle: "expanded",
  47. importer: transformElementScss(),
  48. quietDeps: true,
  49. silenceDeprecations: [
  50. "import",
  51. "global-builtin",
  52. "color-functions",
  53. "slash-div",
  54. "legacy-js-api",
  55. ],
  56. };
  57. module.exports = {
  58. publicPath,
  59. lintOnSave: false,
  60. outputDir: "dist",
  61. productionSourceMap: false,
  62. configureWebpack: {
  63. performance: {
  64. maxAssetSize: 2000000,
  65. maxEntrypointSize: 2000000,
  66. },
  67. output: {
  68. // 与 EOM/WT 一致:子应用打包成 umd 库格式
  69. library: {
  70. type: "umd",
  71. name: `${name}`,
  72. },
  73. chunkLoadingGlobal: `webpackJsonp_${name}`,
  74. },
  75. // Vue2 + vue-style-loader:样式靠副作用注入,不导出 default;
  76. // Webpack5 会对 `import style0 from ...` 报 missing export,可安全忽略
  77. ignoreWarnings: [
  78. {
  79. message:
  80. /export ['"]default['"] \(imported as ['"]style\d+['"]\) was not found/,
  81. },
  82. ],
  83. },
  84. devServer: {
  85. port: Number(process.env.VUE_APP_PORT || process.env.PORT || 8091),
  86. open: [publicPath.endsWith("/") ? publicPath : `${publicPath}/`],
  87. historyApiFallback: {
  88. index: path.posix.join(
  89. publicPath.endsWith("/") ? publicPath : `${publicPath}/`,
  90. "index.html",
  91. ),
  92. htmlAcceptHeaders: ["text/html"],
  93. rewrites: [
  94. {
  95. from: /^\/(api|kkfile)(\/|$)/,
  96. to: (context) => context.parsedUrl.pathname,
  97. },
  98. ],
  99. },
  100. setupMiddlewares(middlewares) {
  101. middlewares.unshift({
  102. name: "redirect-to-app",
  103. middleware(req, res, next) {
  104. const url = (req.url || "").split("?")[0];
  105. const appIndex = publicPath.endsWith("/")
  106. ? publicPath
  107. : `${publicPath}/`;
  108. if (
  109. url === "/" ||
  110. url === "/page-hr" ||
  111. url === "/page-hr/" ||
  112. url === "/hr-pc" ||
  113. url === "/hr-pc/"
  114. ) {
  115. res.redirect(appIndex);
  116. return;
  117. }
  118. next();
  119. },
  120. });
  121. return middlewares;
  122. },
  123. proxy: {
  124. "/api": {
  125. target: apiProxy.target,
  126. changeOrigin: true,
  127. pathRewrite: apiProxy.keepApiPrefix ? undefined : { "^/api": "" },
  128. onProxyRes: stripProxyCacheHeaders,
  129. },
  130. "/kkfile": {
  131. target: "http://aiot.zoomwin.com.cn:51010",
  132. changeOrigin: true,
  133. pathRewrite: { "^/kkfile": "/kkfile" },
  134. },
  135. },
  136. headers: {
  137. "Access-Control-Allow-Origin": "*",
  138. },
  139. },
  140. chainWebpack(config) {
  141. config.plugins.delete("prefetch");
  142. config.module.rule("svg").exclude.add(resolve("src/icons")).end();
  143. config.module
  144. .rule("icons")
  145. .test(/\.svg$/)
  146. .include.add(resolve("src/icons"))
  147. .end()
  148. .use("svg-sprite-loader")
  149. .loader("svg-sprite-loader")
  150. .options({
  151. symbolId: "icon-[name]",
  152. })
  153. .end();
  154. if (process.env.NODE_ENV !== "development") {
  155. config.plugin("compressionPlugin").use(
  156. new CompressionWebpackPlugin({
  157. test: /\.(js|css|html)$/,
  158. threshold: 10240,
  159. }),
  160. );
  161. }
  162. },
  163. css: {
  164. loaderOptions: {
  165. css: {
  166. esModule: false,
  167. },
  168. sass: { sassOptions },
  169. scss: { sassOptions },
  170. },
  171. },
  172. };