let path = require("path"); const webpack = require("webpack"); const ThemeColorReplacer = require("webpack-theme-color-replacer"); const globalConfig = require("./src/config/global.config"); const settingConfig = require("./src/config/setting.config"); const cssResolverConfig = require("./src/config/cssResolver.config"); const { getMenuColors, getAntdColors, getThemeToggleColors, getFunctionalColors } = require('./src/utils/colorUtil'); const CompressionWebpackPlugin = require("compression-webpack-plugin"); const productionGzipExtensions = ["js", "css"]; const isProd = process.env.NODE_ENV === "production"; const theme = settingConfig.theme; function getThemeColors(color, $theme) { const _color = color || theme.color const mode = $theme || theme.mode const replaceColors = getThemeToggleColors(_color, mode) const themeColors = [ ...replaceColors.mainColors, ...replaceColors.subColors, ...replaceColors.menuColors, ...replaceColors.contentColors, ...replaceColors.rgbColors, ...replaceColors.functionalColors.success, ...replaceColors.functionalColors.warning, ...replaceColors.functionalColors.error, ] return themeColors } function modifyVars(color) { let _color = color || theme.color const palettes = getAntdColors(_color, theme.mode) const menuColors = getMenuColors(_color, theme.mode) const { success, warning, error } = getFunctionalColors(theme.mode) const primary = palettes[5] return { 'primary-color': primary, 'primary-1': palettes[0], 'primary-2': palettes[1], 'primary-3': palettes[2], 'primary-4': palettes[3], 'primary-5': palettes[4], 'primary-6': palettes[5], 'primary-7': palettes[6], 'primary-8': palettes[7], 'primary-9': palettes[8], 'primary-10': palettes[9], 'info-color': primary, 'success-color': success[5], 'warning-color': warning[5], 'error-color': error[5], 'alert-info-bg-color': palettes[0], 'alert-info-border-color': palettes[2], 'alert-success-bg-color': success[0], 'alert-success-border-color': success[2], 'alert-warning-bg-color': warning[0], 'alert-warning-border-color': warning[2], 'alert-error-bg-color': error[0], 'alert-error-border-color': error[2], 'processing-color': primary, 'menu-dark-submenu-bg': menuColors[0], 'layout-header-background': menuColors[1], 'layout-trigger-background': menuColors[2], 'btn-danger-bg': error[4], 'btn-danger-border': error[4], ...globalConfig.theme[theme.mode] } } // 修正 webpack-theme-color-replacer 插件提取的 css 结果 function resolveCss(output, srcArr) { let regExps = [] // 提取 resolve 配置中所有的正则配置 Object.keys(cssResolverConfig).forEach(key => { let isRegExp = false let reg = {} try { reg = eval(key) isRegExp = reg instanceof RegExp } catch (e) { isRegExp = false } if (isRegExp) { regExps.push([reg, cssResolverConfig[key]]) } }) // 去重 srcArr = dropDuplicate(srcArr) // 处理 css let outArr = [] srcArr.forEach(text => { // 转换为 css 对象 let cssObj = parseCssObj(text) // 根据selector匹配配置,匹配成功,则按配置处理 css if (cssResolverConfig[cssObj.selector] != undefined) { let cfg = cssResolverConfig[cssObj.selector] if (cfg) { outArr.push(cfg.resolve(text, cssObj)) } } else { let cssText = '' // 匹配不成功,则测试是否有匹配的正则配置,有则按正则对应的配置处理 for (let regExp of regExps) { if (regExp[0].test(cssObj.selector)) { let cssCfg = regExp[1] cssText = cssCfg ? cssCfg.resolve(text, cssObj) : '' break } // 未匹配到正则,则设置 cssText 为默认的 css(即不处理) cssText = text } if (cssText != '') { outArr.push(cssText) } } }) output = outArr.join('\n') return output } // 数组去重 function dropDuplicate(arr) { let map = {} let r = [] for (let s of arr) { if (!map[s]) { r.push(s) map[s] = 1 } } return r } /** * 从字符串解析 css 对象 * @param cssText * @returns {{ * name: String, * rules: Array[String], * toText: function * }} */ function parseCssObj(cssText) { let css = {} const ruleIndex = cssText.indexOf('{') css.selector = cssText.substring(0, ruleIndex) const ruleBody = cssText.substring(ruleIndex + 1, cssText.length - 1) const rules = ruleBody.split(';') css.rules = rules css.toText = function () { let body = '' this.rules.forEach(item => { body += item + ';' }) return `${this.selector}{${body}}` } return css } const assetsCDN = { // webpack build externals cdn预加载使用 externals: { vue: "Vue", "vue-router": "VueRouter", vuex: "Vuex", axios: "axios", nprogress: "NProgress", clipboard: "ClipboardJS", "@antv/data-set": "DataSet", "js-cookie": "Cookies", }, css: [], js: [ "//cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js", "//cdn.jsdelivr.net/npm/vue-router@3.3.4/dist/vue-router.min.js", "//cdn.jsdelivr.net/npm/vuex@3.4.0/dist/vuex.min.js", "//cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js", "//cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js", "//cdn.jsdelivr.net/npm/clipboard@2.0.6/dist/clipboard.min.js", "//cdn.jsdelivr.net/npm/@antv/data-set@0.11.4/build/data-set.min.js", "//cdn.jsdelivr.net/npm/js-cookie@2.2.1/src/js.cookie.min.js", ], }; module.exports = { devServer: { // proxy: { // '/api': { //此处要与 /services/api.js 中的 API_PROXY_PREFIX 值保持一致 // target: process.env.VUE_APP_API_BASE_URL, // changeOrigin: true, // pathRewrite: { // '^/api': '' // } // } // } }, pluginOptions: { /** * 导入css 预处理器的一些公共的样式文件变量,比如:variables , mixins , functions, * 避免在每个样式文件中手动的@import导入,然后在各个css 文件中直接使用 变量。 * * theme.less 定义的变量预先导入 直接使用 */ "style-resources-loader": { preProcessor: "less", //这个是绝对路径,不能使用 alias中配置的别名路径,如@表示的src patterns: [path.resolve(__dirname, "./src/theme/default.less")], }, }, configureWebpack: (config) => { config.entry.app = ["babel-polyfill", "whatwg-fetch", "./src/main.js"]; config.performance = { hints: false, }; config.plugins.push( new ThemeColorReplacer({ //Optional. output css file name, suport [contenthash] and [hash]. fileName: "css/theme-colors-[contenthash:8].css", //matchColors: Array Colors array for extracting css file. //Css rules which have any one of these colors will be extracted out. //抽取所有相关颜色做替换 matchColors: getThemeColors(), //injectCss: boolean Optional. Inject css text into js file, no need to download theme-colors-xxx.css any more. injectCss: true, //resolveCss: Function(resultCss : string) : string Optional. Resolve result css code as you wish. //转换原有CSS中的特定样式为自定义样式 resolveCss, }) ); // Ignore all locale files of moment.js config.plugins.push(new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)); // 生产环境下将资源压缩成gzip格式 if (isProd) { // add `CompressionWebpack` plugin to webpack plugins config.plugins.push( new CompressionWebpackPlugin({ algorithm: "gzip", test: new RegExp("\\.(" + productionGzipExtensions.join("|") + ")$"), threshold: 10240, minRatio: 0.8, }) ); } // if prod, add externals if (isProd) { config.externals = assetsCDN.externals; } }, chainWebpack: (config) => { // 生产环境下关闭css压缩的 colormin 项,因为此项优化与主题色替换功能冲突 if (isProd) { config.plugin("optimize-css").tap((args) => { args[0].cssnanoOptions.preset[1].colormin = false; return args; }); } // 生产环境下使用CDN if (isProd) { config.plugin("html").tap((args) => { args[0].cdn = assetsCDN; return args; }); } }, css: { loaderOptions: { less: { lessOptions: { modifyVars: modifyVars(), javascriptEnabled: true, }, }, }, }, publicPath: process.env.VUE_APP_PUBLIC_PATH, outputDir: "dist", assetsDir: "static", productionSourceMap: false, };