ソースを参照

Merge branch 'master' of http://8.136.199.33:3000/eta_front/eta_mobile_front

Karsa 1 年間 前
コミット
71954fa398

+ 6 - 0
package.json

@@ -10,11 +10,14 @@
     "preview": "vite preview"
   },
   "dependencies": {
+    "@esbuild-plugins/node-globals-polyfill": "^0.2.3",
+    "@esbuild-plugins/node-modules-polyfill": "^0.2.2",
     "@vant/touch-emulator": "^1.4.0",
     "@vueuse/components": "^9.13.0",
     "@vueuse/core": "^9.13.0",
     "ali-oss": "^6.17.1",
     "axios": "^1.3.4",
+    "buffer": "^6.0.3",
     "file-saver": "^2.0.5",
     "highcharts": "^9.3.2",
     "himalaya": "^1.1.0",
@@ -23,10 +26,13 @@
     "js-base64": "^3.7.5",
     "js-md5": "^0.7.3",
     "lodash": "^4.17.21",
+    "minio": "^7.0.18",
+    "minio-es": "0.0.2",
     "moment": "^2.29.4",
     "normalize.css": "^8.0.1",
     "pinia": "^2.0.36",
     "pptxgenjs": "^3.12.0",
+    "rollup-plugin-node-polyfills": "^0.2.1",
     "v3-color-picker-teleport": "^1.0.9",
     "vant": "^4.6.4",
     "vconsole": "^3.15.0",

+ 10 - 2
src/api/common.js

@@ -20,9 +20,10 @@ export function apiReportingErrInfo(params){
 
 /**
  * 获取oss临时签名
+ * @param {*} params.StorageSource number 1:oss 2:minio
  */
-export function apiGetOSSSign(){
-    return get('/resource/oss/get_sts_token',{})
+export function apiGetOSSSign(params){
+    return get('/resource/oss/get_sts_token',params)
 }
 
 /**
@@ -57,4 +58,11 @@ export function apiGetLanguageConfig(params){
  */
 export function apiGetAuthBtnList(){
     return get('/system/role/menu/buttons',{})
+}
+
+/**
+ * 获取系统动态配置配置
+ */
+export function apiGetPublicSetting(){
+    return get('/system/config',{})
 }

+ 96 - 5
src/hooks/useUploadFileToOSS.js

@@ -1,6 +1,12 @@
 // 上传文件到阿里云oss
 import OSS from 'ali-oss'
 import {apiGetOSSSign} from '@/api/common'
+/* const Minio = require('minio')
+const stream = require('stream') */
+import * as Minio from 'minio-es'
+import * as stream from 'stream'
+import * as buffer from 'buffer'
+const Buffer = buffer.Buffer
 
 /**
  * 上传到oss
@@ -10,7 +16,7 @@ import {apiGetOSSSign} from '@/api/common'
  * @returns fileUrl 返回文件在阿里云上的地址
  */
 export async function useUploadFileToOSS(data,fileName,isMultipart=false){
-    const signRes=await apiGetOSSSign()
+    const signRes=await apiGetOSSSign({StorageSource:1})
     if(signRes.Ret!==200) return
     const accessKeyId=signRes.Data.AccessKeyId
     const accessKeySecret=signRes.Data.AccessKeySecret
@@ -18,15 +24,15 @@ export async function useUploadFileToOSS(data,fileName,isMultipart=false){
 
     const ALOSSINS=new OSS({
         // yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
-        region: "oss-cn-shanghai",
+        region: signRes.Data.RegionId,
         // 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
         accessKeyId: accessKeyId,
         accessKeySecret: accessKeySecret,
         // 从STS服务获取的安全令牌(SecurityToken)。
         stsToken: stsToken,
         // 填写Bucket名称,例如examplebucket。
-        bucket: "hzchart",
-        endpoint:'hzstatic.hzinsights.com',
+        bucket: signRes.Data.Bucketname,
+        endpoint:signRes.Data.Endpoint,
         cname:true,
         timeout:6000000
     });
@@ -52,7 +58,7 @@ export async function useUploadFileToOSS(data,fileName,isMultipart=false){
         }
 
         if(res.res.status===200){
-            resUrl='https://hzstatic.hzinsights.com/'+res.name
+            resUrl=signRes.Data.Imghost+res.name
         }
     } catch (error) {
         console.log('阿里云上传失败',error);
@@ -60,4 +66,89 @@ export async function useUploadFileToOSS(data,fileName,isMultipart=false){
     
 
     return resUrl
+}
+
+//上传到minio
+// minio sdk 文档 https://min.io/docs/minio/linux/developers/javascript/API.html
+export async function useUploadToMinIO (file, fileName, options = {}){
+    const res = await apiGetOSSSign({StorageSource:2})
+    if (res.Ret !== 200) {
+        console.log("获取minio临时签名错误,res.Ret=" + res.Ret)
+        return
+    }
+    const minioClient = new Minio.Client({
+        endPoint: res.Data.Endpoint.split(':')[0],
+        port: Number(res.Data.Port) || undefined,
+        useSSL: res.Data.UseSSL.toLocaleLowerCase() == "false" ? false : true,
+        accessKey: res.Data.AccessKeyId,
+        secretKey: res.Data.SecretKeyId,
+    })
+    console.log(minioClient);
+    try {
+        var metaData = {
+            ...{
+                'Content-Type': file.type || 'application/octet-stream',
+                "Content-Length": file.size,
+            },
+            ...options
+        }
+
+        minioClient.bucketExists(res.Data.Bucketname, function (err, exists) {
+            if (err) {
+                return console.log("minio 查看桶是否存在失败" + err)
+            }
+            if (!exists) {
+                // 不存在桶,创建桶
+                console.log("桶不存在,先创建桶", res.Data.Bucketname);
+                minioClient.makeBucket(res.Data.Bucketname, res.Data.RegionId, function (err) {
+                    if (err) {
+                        console.log("minio 创建桶失败" + err)
+                        return 
+                    }
+                    let reader = new FileReader();
+                    // console.log(reader);
+                    reader.readAsArrayBuffer(file);
+                    reader.onloadend = function (e) {
+                        const dataurl = e.target.result;
+                        let bufferStream = new stream.PassThrough();
+                        // 转化成数据流  minio接受数据流格式
+                        bufferStream.end(Buffer.from(dataurl))
+                        minioClient.putObject(res.Data.Bucketname, fileName, bufferStream, file.size, metaData, function (err, etag) {
+                            if (err) {
+                                return console.log("上传到minio失败:" + err)
+                            }
+                            let fileUrl = fileName.startsWith('/') ? res.Data.ImgHost + fileName : res.Data.ImgHost + "/" + fileName
+                            return fileUrl
+                        })
+                    }
+                })
+            }
+            if (exists) {
+                // console.log("桶存在",res.Data.Bucketname);
+                let reader = new FileReader();
+                console.log('reader',reader);
+                reader.readAsArrayBuffer(file);
+                reader.onloadend = function (e) {
+                    const dataurl = e.target.result;
+                    let bufferStream = new stream.PassThrough();
+                    bufferStream.end(Buffer.from(dataurl))
+                    minioClient.putObject(res.Data.Bucketname, fileName, bufferStream, metaData, function (err, etag) {
+                        if (err) {
+                            console.log("上传到minio失败:" + err)
+                            return 
+                        }
+                        let fileUrl = fileName.startsWith('/') ? res.Data.ImgHost + fileName : res.Data.ImgHost + "/" + fileName
+                        //console.log('test',fileUrl)
+                        return fileUrl
+                    })
+                }
+            }
+        })
+    } catch (error) {
+        console.error(error);
+        if (error.name !== "cancel") {
+            //不是取消上传的则给错误提示
+            this.$message.warning("上传失败,请刷新重试");
+        }
+    }
 }

+ 9 - 0
src/main.js

@@ -17,6 +17,15 @@ import svgIcon from "@/components/SvgIcon.vue";
 //引入注册脚本
 import 'virtual:svg-icons-register'
 
+/* import { Buffer } from 'buffer'; */
+import * as buffer from 'buffer'
+if(typeof(window.global)==="undefined"){
+    window.global = window
+}
+if(typeof(window.Buffer)==="undefined"){
+    window.Buffer = buffer.Buffer
+}
+
 
 if(import.meta.env.MODE==='test'){
     const vConsole = new VConsole();

+ 26 - 0
src/store/modules/publicSetting.js

@@ -0,0 +1,26 @@
+/* 系统动态配置配置 */
+import { defineStore } from "pinia";
+import { apiGetPublicSetting } from '@/api/common'
+
+export const usePublicSettingStore = defineStore('publicSetting',{
+  state: () => {
+    return {
+      publicSetting: {}
+    }
+  },
+
+  actions: {
+    async getPublicSetting() {
+      const res = await apiGetPublicSetting();
+      if(res.Ret !== 200) return
+      let filterObj = {}
+      let data = res.Data||[]
+      data.forEach(item => {
+        if(!filterObj[item.ConfKey]) {
+          filterObj[item.ConfKey] = item.ConfVal;
+        }
+      })
+      this.publicSetting = filterObj
+    }
+  }
+})

+ 2 - 0
src/views/ppt/Detail.vue

@@ -11,6 +11,7 @@ import {useDownLoadFile} from '@/hooks/useDownLoadFile'
 import { useWindowScroll } from '@vueuse/core'
 import moment from 'moment';
 import { showToast } from 'vant';
+import { useConfigSettingStore } from '@/store/modules/etaConfig'
 
 const route=useRoute()
 const router=useRouter()
@@ -24,6 +25,7 @@ const { y } = useWindowScroll()
 let PPTInfo=ref(null)
 let conArr=ref([])
 async function getPPTDetail(){
+    await useConfigSettingStore().getBaseConfigSetting()
     const res=await apiPPTDetail({PptId:Number(pptId)})
     if(res.Ret!=200) return
     PPTInfo.value=res.Data

+ 46 - 13
src/views/ppt/hooks/usePPTPublish.js

@@ -6,9 +6,12 @@ import pptxgen from "pptxgenjs";
 import { parse } from "himalaya";
 import _ from 'lodash'
 import {pptLayout,pptSlideMaster,pptSlideMasterEn,modelConfig} from '../utils/config'
-import {useUploadFileToOSS} from '@/hooks/useUploadFileToOSS'
+import {useUploadFileToOSS,useUploadToMinIO} from '@/hooks/useUploadFileToOSS'
+import {usePublicSettingStore} from '@/store/modules/publicSetting'
 import moment from 'moment'
 
+const publicSettingStore = usePublicSettingStore()
+
 let LoadingINS=null
 let PPTContentList=[]
 let pptId=0
@@ -430,6 +433,28 @@ async function handleUploadToOSS(data){
 	}
 	handlePublishPPT(url)
 }
+// 上传到minio
+async function handleUploadToMinIO(data){
+    const loading=showLoadingToast({
+        message: "上传中...",
+        duration: 0,
+        forbidClick: true,
+      })
+    const t=new Date()
+	const month=moment(t).format('YYYYMM')
+	const day=moment(t).format('YYYYMMDD')
+	const temName=`ppt/${month}/${day}/${createRandomCode(32)}.pptx`
+	console.log('文件名',temName);
+    const url = await useUploadToMinIO(data,temName)
+    loading.close()
+    if(!url){
+        setTimeout(() => {
+            showToast('生成ppt失败')
+        }, 60);
+        return
+    }
+    handlePublishPPT(url)
+}
 
 // 页面转ppt
 async function pageToPPT(){
@@ -569,16 +594,18 @@ async function pageToPPT(){
     }
 
     //添加封底
-    let back = PPTINS.addSlide()
-    let backImg = $(`#ppt-last-page img`)[0].src
-    back.addImage({
-        path: backImg,
-        x: 0,
-        y: 0,
-        w:'100%',
-        h: '100%',
-        size: { type: "contain" },
-    })
+    if($(`#ppt-last-page img`)[0]){
+        let back = PPTINS.addSlide()
+        let backImg = $(`#ppt-last-page img`)[0].src
+        back.addImage({
+            path: backImg,
+            x: 0,
+            y: 0,
+            w:'100%',
+            h: '100%',
+            size: { type: "contain" },
+        })
+    }
 
     //为了把封面放到第一页,操作pptx.slides达不成想要的效果,于是弄了个pptx2
     //将封面放在最后生成是因为htmlToCanvans占用太多内存会导致页面假死
@@ -632,8 +659,13 @@ async function pageToPPT(){
     }
     pptx2.write('blob').then((data)=>{
       LoadingINS.close()
-      // 上传到阿里云oss
-      handleUploadToOSS(data)
+      if(publicSettingStore.publicSetting.ObjectStorageClient==='minio'){
+        //上传到minio
+        handleUploadToMinIO(data)
+      }else{
+        handleUploadToOSS(data)
+      }
+      //handleUploadToOSS(data)
     })
     // pptx2.writeFile({ fileName: "test.pptx" }) //本地测试可直接用该方法生成ppt文件
 }
@@ -670,5 +702,6 @@ export async function usePPTPublish(data,id){
         duration: 0,
         forbidClick: true,
     })
+    await publicSettingStore.getPublicSetting()
     pageToPPT()
 }

+ 27 - 8
src/views/report/EditReport.vue

@@ -10,12 +10,16 @@ import moment from 'moment'
 import { showToast,showDialog } from 'vant'
 import { useRoute, useRouter } from 'vue-router'
 import {useCachedViewsStore} from '@/store/modules/cachedViews'
+import {usePublicSettingStore} from '@/store/modules/publicSetting'
 import {reportManageBtn} from '@/hooks/useAuthBtn'
 const cachedViewsStore=useCachedViewsStore()
+const publicSettingStore = usePublicSettingStore()
 
 const router=useRouter()
 const route=useRoute()
 
+const isApprove = ref(false)
+
 
 const {lastFocusPosition,initFroalaEditor,imgUploadFlag,frolaEditorContentChange}=useInitFroalaEditor()
 let reportContentEditorIns=null//报告内容编辑器实例
@@ -98,6 +102,9 @@ async function getReportDetail(){
         }
 
     }
+    //获取审批流
+    await publicSettingStore.getPublicSetting()
+    isApprove.value = ['1','3'].includes(publicSettingStore.publicSetting.ApprovalFlow)
 }
 
 
@@ -276,6 +283,10 @@ async function handleReportOpt(type){
         //     // 显示发布提示弹窗,提示推送客群
         //     showPublishPop.value=true
         // }
+        if(isApprove.value){
+            handleConfirmPublish(1)
+            return
+        }
         // 没有客群了发布都有二次提示弹窗
         showPublishPop.value=true
     }
@@ -403,14 +414,22 @@ function onConfirmDSFBTime(time){
                     <img src="@/assets/imgs/report/icon_save2.png" alt="">
                     <span>保存</span>
                 </div>
-                <div class="item" @click="handleReportOpt('dsfb')" v-permission="reportManageBtn.reportManage_publish">
-                    <img src="@/assets/imgs/report/icon_time.png" alt="">
-                    <span>定时发布</span>
-                </div>
-                <div class="item" @click="handleReportOpt('fb')" v-permission="reportManageBtn.reportManage_publish">
-                    <img src="@/assets/imgs/report/icon_publish3.png" alt="">
-                    <span>发布</span>
-                </div>
+                <template v-if="!isApprove">
+                    <div class="item" @click="handleReportOpt('dsfb')" v-permission="reportManageBtn.reportManage_publish">
+                        <img src="@/assets/imgs/report/icon_time.png" alt="">
+                        <span>定时发布</span>
+                    </div>
+                    <div class="item" @click="handleReportOpt('fb')" v-permission="reportManageBtn.reportManage_publish">
+                        <img src="@/assets/imgs/report/icon_publish3.png" alt="">
+                        <span>发布</span>
+                    </div>
+                </template>
+                <template v-if="isApprove">
+                    <div class="item" @click="handleReportOpt('fb')" v-permission="reportManageBtn.reportManage_publish">
+                        <img src="@/assets/imgs/report/icon_publish3.png" alt="">
+                        <span>提交</span>
+                    </div>
+                </template>
             </div>
             <div class="right-btn" @click="showReportInsertPop=true">
                 <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">

+ 68 - 12
src/views/report/List.vue

@@ -1,5 +1,5 @@
 <script setup name="ReportList">
-import {computed, nextTick, reactive,ref} from 'vue'
+import {computed, nextTick, onMounted, reactive,ref} from 'vue'
 import apiReport from '@/api/report'
 import moment from 'moment'
 import ListClassify from './components/ListClassify.vue'
@@ -8,9 +8,13 @@ import { showToast,showDialog,Dialog } from 'vant';
 import { useRouter } from 'vue-router';
 import { useWindowSize } from '@vueuse/core'
 import {useCachedViewsStore} from '@/store/modules/cachedViews'
+import {usePublicSettingStore} from '@/store/modules/publicSetting'
 import {reportFrequencyOpts} from './utils/config'
 import {reportManageBtn,useAuthBtn} from '@/hooks/useAuthBtn'
 const cachedViewsStore=useCachedViewsStore()
+const publicSettingStore = usePublicSettingStore()
+
+const isApprove = ref(false)
 
 const {checkAuthBtn} = useAuthBtn()
 
@@ -155,10 +159,25 @@ async function handleReportPublish(item){
         }
         return
     }
-
+    //如果走审批流 直接发布
+    if(isApprove.value){
+        publishReportApprove()
+        showReportItemOpt.value=false
+        return
+    }
     showPublishPop.value=true
     showReportItemOpt.value=false
 }
+function publishReportApprove(){
+    apiReport.reportPublish({
+        ReportIds:activeReportData.value.Id.toString()
+    }).then(res=>{
+        if(res.Ret!==200) return 
+        showToast('发布成功')
+        handlePublishPopClose('refresh')
+    })
+}
+
 
 // 发布弹窗关闭
 function handlePublishPopClose(refresh){
@@ -173,7 +192,7 @@ function handlePublishPopClose(refresh){
 function handleReportPublishCancle(item){
     showDialog({
         title: '提示',
-        message: `是否确认取消发布?`,
+        message: `是否确认取消${isApprove.value?'提交':'发布'}?`,
         showCancelButton:true
     }).then(()=>{
         apiReport.reportPublishCancle({ReportIds:Number(item.Id)}).then(res=>{
@@ -255,10 +274,14 @@ async function onLongPressItem(e){
             ||checkAuthBtn(reportManageBtn.reportManage_publish)
             ||checkAuthBtn(reportManageBtn.reportManage_reportDel)
     }
-    if(e.State===2){ //推送消息、取消发布
+    if((e.State===2&&!isApprove)||e.State===4){ //推送消息、取消发布
         checkState = checkAuthBtn(reportManageBtn.reportManage_sendMsg)
             ||checkAuthBtn(reportManageBtn.reportManage_cancelPublish)
     }
+    if((e.State===2&&isApprove)||e.State===3){ //撤销
+        checkState = checkAuthBtn(reportManageBtn.reportManage_cancelPublish)
+    }
+    
     if(!checkState) return
     activeItem.value=e
     showReportItemOpt.value=true
@@ -292,6 +315,25 @@ const publishStatusOpt=[
         value:1
     }
 ]
+const approveStatusOpt=[
+    {
+        label:'待提交',
+        value:1
+    },
+    {
+        label:'待审批',
+        value:2
+    },
+    {
+        label:'已审批',
+        value:4
+    },
+    {
+        label:'已驳回',
+        value:3
+    }
+]
+let statusOpt = publishStatusOpt
 function handleSelectReportStatus(item){
     if(temMsgIsSendVal.value==item.value){
         temMsgIsSendVal.value=''
@@ -406,6 +448,13 @@ async function handleReportEdit(e){
     })
 }
 
+onMounted(async ()=>{
+    await publicSettingStore.getPublicSetting()
+    isApprove.value = ['1','3'].includes(publicSettingStore.publicSetting.ApprovalFlow)
+    console.log('isApprove',isApprove.value)
+    statusOpt = isApprove.value?approveStatusOpt:publishStatusOpt
+})
+
 </script>
 
 <template>
@@ -488,12 +537,12 @@ async function handleReportEdit(e){
                         </div>
                     </div>
                 </van-dropdown-item>
-                <van-dropdown-item title="发布状态" ref="publishStatusDropMenuIns">
+                <van-dropdown-item :title="isApprove?'状态':'发布状态'" ref="publishStatusDropMenuIns">
                     <div class="report-status-box">
                         <ul>
                             <li 
                                 :class="['status-item',temPublishStatusVal===item.value?'active':'']" 
-                                v-for="item in publishStatusOpt" 
+                                v-for="item in statusOpt" 
                                 :key="item.value"
                                 @click="handleSelectReportPublishStatus(item)"
                             >{{item.label}}</li>
@@ -543,8 +592,10 @@ async function handleReportEdit(e){
                             <span v-permission="reportManageBtn.reportManage_reportList_uv">UV:{{item.Uv}}</span>
                         </div>
                         <div class="status">
-                            <span v-if="item.State===1">未发布</span>
-                            <span v-if="item.State===2" class="active-status">已发布</span>
+                            <span v-if="item.State===1">{{isApprove?'待提交':'未发布'}}</span>
+                            <span v-if="item.State===2" class="active-status">{{isApprove?'待审批':'已发布'}}</span>
+                            <span v-if="item.State===3">已驳回</span>
+                            <span v-if="item.State===4">已审批</span>
                         </div>
                     </div>
                 </li>
@@ -581,12 +632,17 @@ async function handleReportEdit(e){
             <!-- <div class="title">{{activeItem.Title}}</div> -->
             <template v-if="activeItem.State==1">
                 <div class="item" @click="handleReportEdit(activeItem)" v-permission="reportManageBtn.reportManage_reportEdit">编辑</div>
-                <div class="item" @click="handleReportPublish(activeItem)" v-permission="reportManageBtn.reportManage_publish">发布</div>
+                <div class="item" @click="handleReportPublish(activeItem)" v-permission="reportManageBtn.reportManage_publish">{{isApprove?'提交':'发布'}}</div>
                 <div class="item" @click="handleReportDel(activeItem)" v-permission="reportManageBtn.reportManage_reportDel">删除</div>
             </template>
-            <template v-if="activeItem.State==2">
+            <template v-if="[2,4].includes(activeItem.State)">
+                <div class="item" @click="handleReportPublishCancle(activeItem)" v-permission="reportManageBtn.reportManage_cancelPublish">{{isApprove?'撤销':'取消发布'}}</div>
+            </template>
+            <template v-if="activeItem.State==4||(activeItem.State==2&&!isApprove)">
                 <div class="item" @click="handldReportMsgSend(activeItem)" v-if="activeItem.MsgIsSend==0&&checkAuthBtn(reportManageBtn.reportManage_sendMsg)">推送消息</div>
-                <div class="item" @click="handleReportPublishCancle(activeItem)" v-permission="reportManageBtn.reportManage_cancelPublish">取消发布</div>
+            </template>
+            <template v-if="activeItem.State==3">
+                <div class="item" @click="handleReportPublishCancle(activeItem)" v-permission="reportManageBtn.reportManage_cancelPublish">撤销</div>
             </template>
         </div>
     </van-action-sheet>
@@ -633,7 +689,7 @@ async function handleReportEdit(e){
                     <span>日期选择</span>
                 </div>
                 <div class="time-type-box">
-                    <span @click="dateType=1" :class="['item',dateType===1?'active':'']">发布时间</span>
+                    <span @click="dateType=1" :class="['item',dateType===1?'active':'']">{{isApprove?'审批时间':'发布时间'}}</span>
                     <span @click="dateType=2" :class="['item',dateType===2?'active':'']">更新时间</span>
                 </div>
             </template>

+ 21 - 0
vite.config.js

@@ -5,6 +5,9 @@ import Components from "unplugin-vue-components/vite";
 import { VantResolver } from "unplugin-vue-components/resolvers";
 import VueSetupExtend from 'vite-plugin-vue-setup-extend'
 import {createSvgIconsPlugin} from 'vite-plugin-svg-icons'
+import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill'
+import { NodeModulesPolyfillPlugin } from '@esbuild-plugins/node-modules-polyfill'
+import rollupNodePolyFill from 'rollup-plugin-node-polyfills'
 
 // https://vitejs.dev/config/
 export default ({ mode }) =>
@@ -47,10 +50,28 @@ export default ({ mode }) =>
     resolve: {
       alias: {
         "@": path.resolve(__dirname, "./src"),
+        events: 'rollup-plugin-node-polyfills/polyfills/events',
+        stream: 'rollup-plugin-node-polyfills/polyfills/stream',
+        buffer: 'rollup-plugin-node-polyfills/polyfills/buffer-es6',
+        process: 'rollup-plugin-node-polyfills/polyfills/process-es6'
       }
     },
+    optimizeDeps: {
+        esbuildOptions: {
+          // Enable esbuild polyfill plugins
+          plugins: [
+            NodeModulesPolyfillPlugin()
+          ]
+        }
+      },
     build: {
       outDir: loadEnv(mode, process.cwd()).VITE_APP_OUTDIR,
+      rollupOptions: {
+        plugins: [
+          // Enable rollup polyfills plugin used during production bundling
+          rollupNodePolyFill()
+        ]
+      }
     },
     server:{
       host:true