Explorar el Código

0.0.44

增加Gzipch超级压缩
Sean hace 4 meses
padre
commit
3fff1f35f9

+ 10 - 3
.env.production

@@ -1,8 +1,15 @@
 # just a flag
 ENV = 'production'
 
-# base websocket
-VUE_APP_BASE_WEBSOCKET = 'ws://admin.bjzxtw.org.cn:9506'
+# 测试环境,当你需要打包到正式环境且需要开启Gzip的时候,你需要使用下面那个
 # base api
-VUE_APP_BASE_API = 'https://admin.bjzxtw.org.cn/zxtapi'
+VUE_APP_BASE_API = 'http://183.131.25.186:9501'
+# base websocket
+VUE_APP_BASE_WEBSOCKET = 'ws://183.131.25.186:9506'
+
+# 正式环境,当你需要打包到测试环境且需要开启Gzip的时候,你需要使用上面那个
+# base websocket
+# VUE_APP_BASE_WEBSOCKET = 'ws://admin.bjzxtw.org.cn:9506'
+# # base api
+# VUE_APP_BASE_API = 'https://admin.bjzxtw.org.cn/zxtapi'
 

+ 1 - 0
package.json

@@ -25,6 +25,7 @@
     "axios": "0.18.1",
     "clipboard": "2.0.4",
     "codemirror": "5.45.0",
+    "compression-webpack-plugin": "^6.1.1",
     "core-js": "3.6.5",
     "driver.js": "0.9.5",
     "dropzone": "5.5.1",

BIN
src/assets/template/sector/demo.png


+ 84 - 8
src/layout/components/template/componentMenu.vue

@@ -1,20 +1,55 @@
 <template>
-  <div class="componentMenuBox">
-    <logo />
-    <div v-if="type == 'sector'" class="sectorBox">
-      板块
+  <div class="componentMenuBox" id="componentMenuBox">
+    <div class="componentMenuTitle">
+      <div v-if="type == 'sector'">可选板块:</div>
+      <div v-if="type == 'component'">可选组件:</div>
     </div>
-    <div v-if="type == 'component'" class="componentBox">
-      组件
+    <div v-if="type == 'sector'">
+      <el-scrollbar wrap-class="scrollbar-wrapper">
+        <div class="sectorBox">
+          <div class="sectorItemBox">
+            <div class="sectorItem">
+              <img src="@/assets/template/sector/demo.png" />
+            </div>
+            <div class="sectorItemTitle">页头板块</div>
+          </div>
+          <div class="sectorItemBox">
+            <div class="sectorItem">
+              <img src="@/assets/template/sector/demo.png" />
+            </div>
+            <div class="sectorItemTitle">导航板块</div>
+          </div>
+          <div class="sectorItemBox">
+            <div class="sectorItem">
+              <img src="@/assets/template/sector/demo.png" />
+            </div>
+            <div class="sectorItemTitle">页尾板块</div>
+          </div>
+          <div class="sectorItemBox">
+            <div class="sectorItem">
+              <img src="@/assets/template/sector/demo.png" />
+            </div>
+            <div class="sectorItemTitle">页尾板块</div>
+          </div>
+          <div class="sectorItemBox">
+            <div class="sectorItem">
+              <img src="@/assets/template/sector/demo.png" />
+            </div>
+            <div class="sectorItemTitle">页尾板块</div>
+          </div>
+        </div>
+        
+      </el-scrollbar>
     </div>
+    <!-- <div v-if="type == 'component'" class="componentBox">
+      组件
+    </div> -->
   </div>
 </template>
 
 <script>
-import Logo from '../Sidebar/Logo'
 
 export default {
-  components: { Logo },
   name: 'componentMenu',
   props: {
     type: {
@@ -46,5 +81,46 @@ export default {
     left: 0;
     z-index: 1001;
     overflow: hidden;
+    .sectorBox {
+      height: 100%;
+    }
+    .el-scrollbar {
+      height: calc(100vh - 100px);
+    }
+    .componentMenuTitle {
+      padding: 37px 0 30px 0;
+      color: #fff;
+      font-size: 18px;
+      div {
+        border-left: 3px solid #5570F1;
+        padding-left: 25px;
+      }
+    }
+    .sectorItemBox {
+      box-sizing: border-box;
+      padding: 0 20px 40px 20px;
+      height: 130px;
+      cursor: pointer;
+      .sectorItem {
+        border: 1px solid #333644;
+        padding: 10px;
+        border-radius: 8px;
+        &:hover {
+          background: #333644;
+          transform: scale(1.1);
+          transition: all 0.2s ease-in-out;
+        }
+        img {
+          display: block;
+          width: 100%;
+        }
+      }
+      .sectorItemTitle {
+        color: #fff;
+        font-size: 14px;
+        padding: 10px 0 0 0;
+        text-align: center;
+      }
+    }
   }
 </style>

+ 15 - 1
src/styles/global.less

@@ -71,7 +71,7 @@
   align-items: center;
   justify-content: center;
 }
-.listDeleteBtn,.listEditBtn,.listMainBtn {
+.listDeleteBtn,.listEditBtn,.listMainBtn,.listSaveBtn {
   width:70px;
   height:28px;
   line-height:28px;
@@ -104,6 +104,13 @@
 .listMainBtn:hover {
   background:#CAD9CB;
 }
+.listSaveBtn{
+  color:#fff;
+  background:#2F2F2F;
+}
+.listSaveBtn:hover {
+  background:#646464;
+}
 
 //表单样式微调
 //1.网站列表
@@ -267,6 +274,8 @@
   }
 }
 
+
+
 /*页面布局 end------------------------------------------------------------>*/
 
 /*quill汉化 start------------------------------------------------------------>*/
@@ -344,3 +353,8 @@
 /*quill汉化 end------------------------------------------------------------>*/
 
 
+/*自助建站左侧菜单 el-scrollbar start------------------------------------------------------------>*/
+.el-scrollbar__wrap {
+  overflow-x: hidden !important; /* 强制隐藏横向滚动条 */
+}
+/*自助建站左侧菜单 el-scrollbar end------------------------------------------------------------>*/

+ 1 - 1
src/views/chat/hall.vue

@@ -976,7 +976,7 @@
           </div>
           <div class="searchWindowFooter">
             <el-button type="info" @click="cancelEditGroupMember">取消</el-button>
-            <el-button type="primary" @click="saveEditGroupMember" :disabled="groupUserList.filter(item=>item.status).length<2">完成</el-button>
+            <el-button type="primary" @click="saveEditGroupMember" :disabled="groupUserList.filter(item=>item.status).length<=2">完成</el-button>
           </div>
         </div>
       </div>

+ 0 - 0
src/views/template/components/CityCascader.vue → src/views/template/public/CityCascader.vue


+ 11 - 11
src/views/template/components/creatWebsite/indexComponents.vue → src/views/template/public/indexComponents.vue

@@ -108,36 +108,36 @@ export default {
 
 /* 背景图样式 */
 .itemMenuBg1 {
-  background: url('../../../../assets/creatWebsite/menu/default/top_menu.png') no-repeat 10px #f0f2f5;
+  background: url('../../../assets/creatWebsite/menu/default/top_menu.png') no-repeat 10px #f0f2f5;
 }
 .itemMenuBg2 {
-  background: url('../../../../assets/creatWebsite/menu/default/logo.png') no-repeat 10px #f0f2f5;
+  background: url('../../../assets/creatWebsite/menu/default/logo.png') no-repeat 10px #f0f2f5;
 }
 .itemMenuBg3 {
-  background: url('../../../../assets/creatWebsite/menu/default/nav_menu.png') no-repeat 10px #f0f2f5;
+  background: url('../../../assets/creatWebsite/menu/default/nav_menu.png') no-repeat 10px #f0f2f5;
 }
 .itemMenuBg4 {
-  background: url('../../../../assets/creatWebsite/menu/default/banner.png') no-repeat 10px #f0f2f5;
+  background: url('../../../assets/creatWebsite/menu/default/banner.png') no-repeat 10px #f0f2f5;
 }
 .itemMenuBg5 {
-  background: url('../../../../assets/creatWebsite/menu/default/class_list.png') no-repeat 10px #f0f2f5;
+  background: url('../../../assets/creatWebsite/menu/default/class_list.png') no-repeat 10px #f0f2f5;
 }
 .itemMenuBg6 {
-  background: url('../../../../assets/creatWebsite/menu/default/img_list.png') no-repeat 10px #f0f2f5;
+  background: url('../../../assets/creatWebsite/menu/default/img_list.png') no-repeat 10px #f0f2f5;
 }
 .itemMenuBg7 {
-  background: url('../../../../assets/creatWebsite/menu/default/text_list.png') no-repeat 10px #f0f2f5;
+  background: url('../../../assets/creatWebsite/menu/default/text_list.png') no-repeat 10px #f0f2f5;
 }
 .itemMenuBg8 {
-  background: url('../../../../assets/creatWebsite/menu/default/img_text_list.png') no-repeat 10px #f0f2f5;
+  background: url('../../../assets/creatWebsite/menu/default/img_text_list.png') no-repeat 10px #f0f2f5;
 }
 .itemMenuBg9 {
-  background: url('../../../../assets/creatWebsite/menu/default/level.png') no-repeat 10px #f0f2f5;
+  background: url('../../../assets/creatWebsite/menu/default/level.png') no-repeat 10px #f0f2f5;
 }
 .itemMenuBg10 {
-  background: url('../../../../assets/creatWebsite/menu/default/link.png') no-repeat 10px #f0f2f5;
+  background: url('../../../assets/creatWebsite/menu/default/link.png') no-repeat 10px #f0f2f5;
 }
 .itemMenuBg11 {
-  background: url('../../../../assets/creatWebsite/menu/default/text.png') no-repeat 10px #f0f2f5;
+  background: url('../../../assets/creatWebsite/menu/default/text.png') no-repeat 10px #f0f2f5;
 }
 </style>

+ 0 - 0
src/views/template/components/step.vue → src/views/template/public/step.vue


+ 0 - 0
src/views/template/components/creatWebsite/tableTitle.vue → src/views/template/public/tableTitle.vue


+ 0 - 0
src/views/template/components/tableTitle.vue → src/views/template/style/1/tableTitle.vue


+ 2 - 2
src/views/template/templateBase.vue

@@ -152,9 +152,9 @@
 // 引入公用样式
 import '@/styles/global.less';
 //表格标题
-import tableTitle from './components/tableTitle';
+import tableTitle from './public/tableTitle';
 //步骤条
-import step from './components/step';
+import step from './public/step';
 
 export default {
   components: {

+ 16 - 8
src/views/template/templateCreat.vue

@@ -5,9 +5,15 @@
         <el-button icon="el-icon-refresh" type="warning" size="small">随机生成模板</el-button>
       </div>
       <div>
-        <el-button icon="el-icon-right" type="primary" size="small" @click="gotoList">完成配置</el-button>
+        <el-button icon="el-icon-right" type="primary" size="small" @click="gotoList">首页</el-button>
+        <el-button icon="el-icon-right" type="primary" size="small" @click="gotoList">分类页</el-button>
+        <el-button icon="el-icon-right" type="primary" size="small" @click="gotoList">列表页</el-button>
+        <el-button icon="el-icon-right" type="primary" size="small" @click="gotoList">详情页</el-button>
+      </div>
+      <div>
+        <el-button type="primary" size="small" @click="gotoList">保存</el-button>
         <el-button icon="el-icon-view" type="warning" size="small">预览</el-button>
-        <el-button icon="el-icon-circle-close" type="info" size="small" @click="goStyle">放弃配置</el-button>
+        <el-button icon="el-icon-circle-close" type="info" size="small" @click="goStyle">退出编辑</el-button>
       </div>
     </div>
     <div class="pageTabsBox">
@@ -24,9 +30,9 @@
     </div> -->
 
     <!-- 工具栏 start ---------------------------------------->
-    <div class="menuPostionBox">
+    <!-- <div class="menuPostionBox">
       <MenuTopBox @add-module="addModule"/>
-    </div>
+    </div> -->
     <!-- 工具栏 end ---------------------------------------->
 
     <!-- 栅格布局 预览模式 start ---------------------------------------->
@@ -289,16 +295,19 @@ import '@/styles/global.less';
 //引入vue-grid-layout
 import { GridLayout, GridItem } from 'vue-grid-layout';
 //标题
-import tableTitle from './components/tableTitle';
+import tableTitle from './public/tableTitle';
 //导入新组件
-import MenuTopBox from './components/creatWebsite/indexComponents.vue'; 
+//import MenuTopBox from './public/indexComponents.vue'; 
+
+
+
 
 export default {
   components: {
     GridLayout,
     GridItem,
     tableTitle,
-    MenuTopBox
+    //MenuTopBox
   },
   data() {
     return {
@@ -529,7 +538,6 @@ export default {
           content:""
         });
       }
-
     },
     //删除模块
     deleteModule(item) {

+ 25 - 4
src/views/template/templateList.vue

@@ -53,11 +53,12 @@
                 <div v-if="scope.row.status==1">已应用</div>
               </template>
             </el-table-column>
-            <el-table-column fixed="right" label="操作" header-align="center">
+            <el-table-column fixed="right" label="操作" header-align="center" width="180">
               <template slot-scope="scope">
                 <div class="listBtnBox">
-                  <div class="listEditBtn" @click="getDataMain(scope.row.id, tableData)" v-if="scope.row.status==1"><i class="el-icon-edit-outline"></i>编辑</div>
+                  <div class="listEditBtn" @click="getDataMain(scope.row.id)" v-if="scope.row.status==1"><i class="el-icon-edit-outline"></i>编辑</div>
                   <div class="listMainBtn" @click="creatWebsite(scope.row.id)" v-if="scope.row.status==0"><i class="el-icon-brush"></i>构建</div>
+                  <div class="listSaveBtn"><i class="el-icon-finished"></i>应用</div>
                 </div>
               </template>
             </el-table-column>
@@ -77,11 +78,11 @@
 
 <script>
 //表格标题
-import tableTitle from './components/tableTitle';
+import tableTitle from './public/tableTitle';
 //引入公用样式
 import '@/styles/global.less';
 //步骤条
-import step from './components/step';
+import step from './public/step';
 
 export default {
   components: {
@@ -180,6 +181,26 @@ export default {
       this.getData();
     },
     //列表和分页相关 end ------------------------------------------------------------>
+
+    //构建网站
+    creatWebsite(){
+      this.$router.push({
+        path: '/templateBase',
+        query: {
+          id: this.editId
+        }
+      });
+    },
+
+    //编辑网站
+    getDataMain(){
+      this.$router.push({
+        path: '/templateCreat',
+        query: {
+          id: this.editId
+        }
+      });
+    }
   },
   mounted(){
     //this.getData();

+ 2 - 2
src/views/template/templateStyle.vue

@@ -105,11 +105,11 @@
 
 <script>
 //表格标题
-import tableTitle from './components/tableTitle';
+import tableTitle from './public/tableTitle';
 //引入公用样式
 import '@/styles/global.less';
 //步骤条
-import step from './components/step';
+import step from './public/step';
 
 export default {
   components: {

+ 7 - 7
src/views/website/WebsiteList.vue

@@ -148,7 +148,7 @@
           <el-form-item label="上级网系:" :label-width="formLabelWidth" prop="website_column_arr_id" class="custom-align-right">
             <el-cascader v-model="form.website_column_arr_id" :props="{checkStrictly:true}" :options="website_column_arr"></el-cascader>
           </el-form-item>
-          <el-form-item label="城市:" :label-width="formLabelWidth" class="custom-align-right">
+          <el-form-item label="城市:" :label-width="formLabelWidth" prop="city_arr_id" class="custom-align-right">
             <CityCascader v-model="form.city_arr_id" @update-city-id="updateFormCityId"></CityCascader>
           </el-form-item>
           <el-form-item label="网站logo:" prop="logo" :label-width="formLabelWidth" :class="['custom-form-item']" class="custom-align-right">
@@ -219,9 +219,8 @@
             </template>
             <el-input type="textarea" v-model="form.description" class="custom-textarea" placeholder="请输入网站描述"></el-input>
           </el-form-item>
-          <el-form-item label="模板:" :label-width="formLabelWidth" class="custom-align-right" prop="template_id">
+          <!-- <el-form-item label="模板:" :label-width="formLabelWidth" class="custom-align-right" prop="template_id">
             <div class="webSiteTemplate" @click="getTemplateList">
-              <!-- <div class="webSiteTitle"></div> -->
               <div class="webSiteTemplateImg">
                 <div>
                   <img v-if="TemplateImg" :src="TemplateImg" class="selectWebSiteTemplateImg">
@@ -233,7 +232,7 @@
                 </div>
               </div>
             </div>
-          </el-form-item>
+          </el-form-item> -->
         </div>
       </el-form>
       <div slot="footer" class="dialog-footer">
@@ -282,7 +281,7 @@ export default {
     }
     const validateColumn = (rule,value,callback) => {
       if (value.length === 0) {
-          callback(new Error('必须选择一个上级网系!'))
+          callback(new Error('该项不能为空!'))
       } else {
           callback()
       }
@@ -328,7 +327,7 @@ export default {
         title:"",//需要提交的网站标题
         keywords:"",//需要提交的网站标题
         description:"",//需要提交的网站描述
-        template_id:""//选择的网站皮肤
+        //template_id:""//选择的网站皮肤
       },
       //3.2 表单验证规则
       formRules: {
@@ -342,12 +341,13 @@ export default {
         //网系不能为空 注意,因为是select框,只有提交的时候才会验证
         website_column_arr_id: [{type:'array',required:true,trigger:'change',message:'必须选择一个网系!',validator:validateColumn}],
         //网站标题,关键词,描述不能为空
+        city_arr_id:[{required:true,trigger:'blur',validator:validateColumn}],
         title:[{required:true,trigger:'blur',validator:validateEmpty}],
         keywords:[{required:true,trigger:'blur',validator:validateEmpty}],
         description:[{required:true,trigger:'blur',validator:validateEmpty}],
         logo:[{required:true,trigger:'blur',validator:validateEmpty}],
         logoUrl:[{required:true,trigger:'blur',validator:validateEmpty}],
-        template_id:[{required:true,trigger:'blur',validator:validateEmpty}],
+        //template_id:[{required:true,trigger:'blur',validator:validateEmpty}],
       },
       //3.3 通过api获的的数据 弹窗
       website_column_arr:[],//api获得的网系列表

+ 68 - 84
vue.config.js

@@ -1,30 +1,17 @@
 'use strict'
 const path = require('path')
 const defaultSettings = require('./src/settings.js')
-const webpack = require('webpack');
+const webpack = require('webpack')
+const CompressionWebpackPlugin = require('compression-webpack-plugin') // 引入 Gzip 压缩插件
 
 function resolve(dir) {
   return path.join(__dirname, dir)
 }
 
 const name = defaultSettings.title || 'vue Element Admin' // page title
-
-// If your port is set to 80,
-// use administrator privileges to execute the command line.
-// For example, Mac: sudo npm run
-// You can change the port by the following method:
-// port = 9527 npm run dev OR npm run dev --port = 9527
 const port = process.env.port || process.env.npm_config_port || 9527 // dev port
 
-// All configuration item explanations can be find in https://cli.vuejs.org/config/
 module.exports = {
-  /**
-   * You will need to set publicPath if you plan to deploy your site under a sub path,
-   * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,
-   * then publicPath should be set to "/bar/".
-   * In most cases please use '/' !!!
-   * Detail: https://cli.vuejs.org/config/#publicpath
-   */
   publicPath: '/',
   outputDir: 'dist',
   assetsDir: 'static',
@@ -37,58 +24,60 @@ module.exports = {
     overlay: {
       warnings: false,
       errors: true
-    },
-    //before: require('./mock/mock-server.js')
-    // proxy: {
-    //   '/authority/getRecursionMenu': {
-    //     target: 'http://192.168.1.201:9501',
-    //     changeOrigin: true,
-    //     logLevel: 'debug',
-    //     onProxyReq: (proxyReq) => {
-    //       proxyReq.setHeader('Origin', 'http://192.168.1.201:8099');
-    //       console.log('Origin header set to http://192.168.1.201:8099');
-    //     },
-    //     pathRewrite: { '^http://192.168.1.201:9501': '' } // 重写完整路径
-    //   }
-    // }
+    }
+    // 如有需要,可在此处配置代理
   },
-  configureWebpack: {
-    // provide the app's title in webpack's name field, so that
-    // it can be accessed in index.html to inject the correct title.
-    name: name,
-    resolve: {
-      alias: {
-        '@': resolve('src')
-      }
-    },
-    plugins: [
+  configureWebpack: config => {
+    const plugins = [
       new webpack.ProvidePlugin({
         'window.Quill': 'quill/dist/quill.js',  // 确保 Quill 在全局作用域内可用
-        Quill: 'quill/dist/quill.js'  // 手动引入 Quill
+        Quill: 'quill/dist/quill.js'            // 手动引入 Quill
       })
     ]
+
+    if (process.env.NODE_ENV === 'production') {
+      console.log('正在执行Gzip极限压缩!')
+      // 在生产环境中加入 Gzip 压缩插件
+      plugins.push(
+        new CompressionWebpackPlugin({
+          filename: '[path].gz[query]',
+          algorithm: 'gzip',
+          test: /\.(js|css|html|svg)$/, // 需要压缩的文件类型
+          threshold: 10240, // 大于10KB的文件才进行压缩
+          minRatio: 0.8     // 压缩比低于0.8才进行压缩
+        })
+      )
+    }
+
+    return {
+      name: name,
+      resolve: {
+        alias: {
+          '@': resolve('src')
+        }
+      },
+      plugins
+    }
   },
   chainWebpack(config) {
-    // it can improve the speed of the first screen, it is recommended to turn on preload
-    // it can improve the speed of the first screen, it is recommended to turn on preload
+    // 预加载配置
     config.plugin('preload').tap(() => [
       {
         rel: 'preload',
-        // to ignore runtime.js
-        // https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/cli-service/lib/config/app.js#L171
         fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
         include: 'initial'
       }
     ])
 
-    // when there are many pages, it will cause too many meaningless requests
+    // 删除 prefetch 插件,减少无用请求
     config.plugins.delete('prefetch')
 
-    // set svg-sprite-loader
+    // 设置 svg-sprite-loader
     config.module
       .rule('svg')
       .exclude.add(resolve('src/icons'))
       .end()
+
     config.module
       .rule('icons')
       .test(/\.svg$/)
@@ -101,44 +90,39 @@ module.exports = {
       })
       .end()
 
-    config
-      .when(process.env.NODE_ENV !== 'development',
-        config => {
-          config
-            .plugin('ScriptExtHtmlWebpackPlugin')
-            .after('html')
-            .use('script-ext-html-webpack-plugin', [{
-            // `runtime` must same as runtimeChunk name. default is `runtime`
-              inline: /runtime\..*\.js$/
-            }])
-            .end()
-          config
-            .optimization.splitChunks({
-              chunks: 'all',
-              cacheGroups: {
-                libs: {
-                  name: 'chunk-libs',
-                  test: /[\\/]node_modules[\\/]/,
-                  priority: 10,
-                  chunks: 'initial' // only package third parties that are initially dependent
-                },
-                elementUI: {
-                  name: 'chunk-elementUI', // split elementUI into a single package
-                  priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
-                  test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
-                },
-                commons: {
-                  name: 'chunk-commons',
-                  test: resolve('src/components'), // can customize your rules
-                  minChunks: 3, //  minimum common number
-                  priority: 5,
-                  reuseExistingChunk: true
-                }
-              }
-            })
-          // https:// webpack.js.org/configuration/optimization/#optimizationruntimechunk
-          config.optimization.runtimeChunk('single')
+    config.when(process.env.NODE_ENV !== 'development', config => {
+      config
+        .plugin('ScriptExtHtmlWebpackPlugin')
+        .after('html')
+        .use('script-ext-html-webpack-plugin', [{
+          inline: /runtime\..*\.js$/
+        }])
+        .end()
+
+      config.optimization.splitChunks({
+        chunks: 'all',
+        cacheGroups: {
+          libs: {
+            name: 'chunk-libs',
+            test: /[\\/]node_modules[\\/]/,
+            priority: 10,
+            chunks: 'initial' // 仅打包初始依赖的第三方
+          },
+          elementUI: {
+            name: 'chunk-elementUI', // 将 elementUI 单独拆分
+            priority: 20,
+            test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // 为适应 cnpm
+          },
+          commons: {
+            name: 'chunk-commons',
+            test: resolve('src/components'), 
+            minChunks: 3, 
+            priority: 5,
+            reuseExistingChunk: true
+          }
         }
-      )
+      })
+      config.optimization.runtimeChunk('single')
+    })
   }
 }