Pārlūkot izejas kodu

Merge branch 'custom' of http://8.136.199.33:3000/eta_front/eta_front into custom

Karsa 1 gadu atpakaļ
vecāks
revīzija
6fb8c93c5c

BIN
src/assets/img/ppt_m/list_bg.png


BIN
src/assets/img/ppt_m/move_ico2.png


+ 12 - 0
src/lang/modules/Slides/pptList.js

@@ -114,6 +114,12 @@ export const listEn = {
   is_delete_ppt: "Are you sure you want to delete this PowerPoint slide?",
   add_least_one: "Please add at least one PowerPoint slide.",
   preview_report_btn: "Preview report.",
+
+  //列表页操作栏
+  list_ppt_item_publish:'Publish',
+  list_ppt_item_preview:'Demo',
+  list_ppt_item_download:'Download',
+  list_ppt_item_edit:'Edit'
 };
 
 /* 中文 */
@@ -230,4 +236,10 @@ export const listZh = {
   is_delete_ppt: "确定删除该页ppt吗",
   add_least_one: "请至少添加一张",
   preview_report_btn: "预览报告",
+
+  //列表页操作栏
+  list_ppt_item_publish:'预览发布',
+  list_ppt_item_preview:'演示',
+  list_ppt_item_download:'下载',
+  list_ppt_item_edit:'编辑'
 };

+ 42 - 8
src/views/ppt_manage/mixins/pptMixins.js

@@ -70,7 +70,10 @@ export default {
 
       //截面散点设置英文props
 			enChartInfo: {},
-			enEdblist:[]
+			enEdblist:[],
+
+
+        chartSVGDataMap:{}
     }
   },
   methods: {
@@ -592,7 +595,8 @@ export default {
           this.initChart(
 				    `chart_${index}_${item.position}`,
 				    this.optionMap[item.chartId],
-				    page
+				    page,
+                    item.chartId
 			    )
         }else{
           //显示图表已被删除
@@ -604,7 +608,7 @@ export default {
 			
       });
     },
-    initChart(refName, options, page) {
+    initChart(refName, options, page,chartId="") {
       console.log("refName", refName);
       const index = this.pageList.findIndex((i) => i.id === page.id)
       if(index===-1) return
@@ -626,7 +630,7 @@ export default {
      
     /* 主题样式*/
     const chartTheme =  ChartThemeStyle ? JSON.parse(ChartThemeStyle) : null;
-
+    console.log('chartTheme',chartTheme)
       //console.log(options)
       this.$nextTick(() => {
         let is_linear = options.series 
@@ -727,22 +731,52 @@ export default {
 
         if(!$(`#${refName}`)[0]) return
 
-        console.log(SpecialOption)
-
+        let chart = null
         if(is_linear)
-          Highcharts.chart({
+          chart = Highcharts.chart({
             // Highcharts 配置
             ...options,
             ...SpecialOption,
             ...secialBarOpt
             });
         else
-        Highcharts.stockChart({
+        chart = Highcharts.stockChart({
           // Highcharts 配置
           ...options,
           ...SpecialOption,
           ...secialBarOpt
         });
+        
+        
+        if(isPublish&&chartId){
+            const {clientWidth,clientHeight} = $(`#${refName}`)[0]
+            const {plotBackgroundColor} = chartTheme&&chartTheme.drawOption||{}
+            const svgData = chart.getSVG({
+                    chart:{
+                        width:clientWidth,
+                        height:clientHeight,
+                        backgroundColor:null,
+                        plotBorderColor:null,
+                        plotBackgroundColor:null,
+                        plotBackgroundImage:null,
+                        plotBorderWidth:null,
+                        plotShadow:false,
+                        animation: false,
+                    }
+                })
+            let  parser = new DOMParser();
+            let svgDoc = parser.parseFromString(svgData, 'image/svg+xml');
+
+            // 查找class为'background'的rect元素并修改fill属性
+            let rectElement = svgDoc.querySelector('rect.highcharts-plot-background');
+            if (rectElement) {
+                rectElement.setAttribute('fill', plotBackgroundColor);
+            }
+            // 将修改后的SVG文档转换回字符串
+            let serializer = new XMLSerializer();
+            let updatedSvgCode = serializer.serializeToString(svgDoc);
+            this.chartSVGDataMap[`${refName}_${chartId}`] = updatedSvgCode
+        }
       });
     },
     initImages(elements,page){

+ 89 - 7
src/views/ppt_manage/newVersion/pptCatalog.vue

@@ -210,7 +210,7 @@
             @end="moveListPPT"
             :disabled="(treeName==='private'&&!selectNode)||treeName!=='private'"
             >
-            <div class="list-item" 
+            <!-- <div class="list-item" 
               v-for="item in catalogPPTList" :key="item.PptId"
               @click="changeModel('ppt',item)"
               >
@@ -228,6 +228,27 @@
                 <span>{{item.Title}}</span>
               </div>
               <span style="font-size:14px;">{{$t('Slides.creation_time')}}:{{$moment(item.PptCreateTime).format('YYYY-MM-DD')}}</span>
+            </div> -->
+            <div class="list-item" 
+                v-for="item in catalogPPTList" :key="item.PptId"
+                @click="changeModel('ppt',item)">
+                <div class="item-image" 
+                    :style="{'background-image':`url(${require('@/assets/img/ppt_m/list_bg.png')})`}">
+                        <span>{{item.Title}}</span>
+                </div>
+                <div class="item-opt">
+                    <span v-for="optItem in setListTool()" :key="optItem.key" @click.stop="handleListToolClick(optItem,item)">
+                        {{ getListMenuOptText(optItem.label) }}
+                    </span>
+                </div>
+                <div class="item-foot" style="margin-bottom:0;">
+                    <span style="font-size:14px;">{{$t('Slides.creation_time')}}:{{$moment(item.PptCreateTime).format('YYYY-MM-DD')}}</span>
+                    <img
+                        v-if="treeName==='private'&&selectNode"
+                        src="~@/assets/img/ppt_m/move_ico2.png"
+                        style="width: 14px; height: 14px; margin-right: 8px"
+                    />
+                </div>
             </div>
           </draggable>
           <div class="empty" v-if="catalogPPTList.length===0">
@@ -438,7 +459,15 @@ export default {
     } */
   },
   methods: {
-    // 
+    //ppt列表操作栏
+    getListMenuOptText(e){
+        if(e==='预览发布') return this.$t('Slides.list_ppt_item_publish')
+        if(e==='演示') return this.$t('Slides.list_ppt_item_preview')
+        if(e==='下载') return this.$t('Slides.list_ppt_item_download')
+        if(e==='编辑') return this.$t('Slides.list_ppt_item_edit')
+
+    },
+    //ppt详情操作栏
     getContentMenuOptText(e){
       if(e==='查看报告') return this.$t('Slides.view_report_btn')
       if(e==='编辑') return this.$t('Slides.edit_lable_name')
@@ -565,6 +594,34 @@ export default {
         })
       }
     },
+    //设置列表的操作按钮
+    setListTool(){
+      let list = []
+      const {checkPermissionBtn,pptPermission} = this.permissionBtn
+      const authMap = {
+          'publish':checkPermissionBtn(pptPermission.ppt_publish),
+          'present':checkPermissionBtn(pptPermission.ppt_show),
+          'download':checkPermissionBtn(pptPermission.ppt_download),
+          'edit':checkPermissionBtn(pptPermission.ppt_save),
+      }
+      //私有目录显示编辑,其他不显示
+      list = toolList.filter((i)=>{
+          return ['publish','present','download','edit'].includes(i.key)
+        })
+      if(this.treeName!=='private'){
+        list = list.filter(_ => _.key !== 'edit')
+      }
+      const sortMap = {
+        'present':0,
+        'edit':1,
+        'publish':2,
+        'download':3
+      }
+      list.sort((a,b)=>{
+        return sortMap[a.key] - sortMap[b.key]
+      })
+      return list
+    },
     //设置操作按钮
     setToolList(pptDetail={}){
       //公共目录:显示除 删除, 编辑 外的全部按钮
@@ -755,6 +812,22 @@ export default {
         })
       })
     },
+    //点击列表操作栏事件处理
+    handleListToolClick(item,{PptId,PptxUrl,Title}){
+        const urlMap = {
+        edit:`/ppteditor?id=${PptId}`,
+        publish:`/pptpublish?id=${PptId}`,
+        present:`/pptpresent?id=${PptId}`
+      }
+      if(urlMap[item.key]){
+        window.open(urlMap[item.key],'_blank')
+        return 
+      }
+      const handleCommand = {
+        'download':this.downloadPpt,
+      }
+      handleCommand[item.key]({PptxUrl,Title})
+    },
     //点击操作栏事件处理
     async handleToolClick(item){
       const urlMap = {
@@ -778,14 +851,15 @@ export default {
             window.open(urlMap[item.key],'_blank');
           }else if(item.key==='publish'){
             //this.$message.warning('该PPT页数超过60页或图表数量超过100张,无法发布,请修改后重试')
-            if(!this.pptItem.overLimitHint.chartNum){
+            /* if(!this.pptItem.overLimitHint.chartNum){
               this.$message.warning(this.$t('Slides.exceed_ppt_msg') )
               return
             }
             if(!this.pptItem.overLimitHint.pageNum){
               this.$message.warning(this.$t('Slides.exceed_sixty_ppt_msg') )
               return
-            }
+            } */
+            window.open(urlMap[item.key],'_blank');
           }     
         }else{
           if(item.key==='publish'){
@@ -1503,7 +1577,7 @@ export default {
           flex-wrap: wrap;
         }
         .list-item{
-          width: 30%;
+          width: 23%;
           background-color: white;
           border: 1px solid #ECECEC;
           border-radius: 4px;
@@ -1512,7 +1586,7 @@ export default {
           margin: 0 20px 20px 0;
           padding:10px;
           box-sizing: border-box;
-          .item-title{
+          .item-title,.item-foot{
             width:100%;
             height:20px;
             display: flex;
@@ -1549,9 +1623,17 @@ export default {
               transform: translate(-50%,-50%);
               font-size: 24px;
               color: #FFFFFF;
-              font-weight: bold;
+              /* font-weight: bold; */
             }
           }
+          .item-opt{
+            display:flex;
+            justify-content:space-between;
+            color:#0052D9;
+            cursor:pointer;
+            margin:10px 0;
+            font-size:16px;
+          }
         }
         .empty{
           position: absolute;

+ 85 - 4
src/views/ppt_manage/newVersion/pptEnCatalog.vue

@@ -205,7 +205,7 @@
             @end="moveListPPT"
             :disabled="treeName!=='private'"
             >
-            <div class="list-item" 
+            <!-- <div class="list-item" 
               v-for="item in catalogPPTList" :key="item.PptId"
               @click="changeModel('ppt',item)"
               >
@@ -223,6 +223,27 @@
                 <span>{{item.Title}}</span>
               </div>
               <span style="font-size:14px;">{{$t('Slides.creation_time')}}:{{$moment(item.PptCreateTime).format('YYYY-MM-DD')}}</span>
+            </div> -->
+            <div class="list-item" 
+                v-for="item in catalogPPTList" :key="item.PptId"
+                @click="changeModel('ppt',item)">
+                <div class="item-image" 
+                :style="{'background-image':`url(${require('@/assets/img/ppt_m/list_bg.png')})`}">
+                        <span>{{item.Title}}</span>
+                </div>
+                <div class="item-opt">
+                    <span v-for="optItem in setListTool()" :key="optItem.key" @click.stop="handleListToolClick(optItem,item)">
+                        {{ getListMenuOptText(optItem.label) }}
+                    </span>
+                </div>
+                <div class="item-foot" style="margin-bottom:0;">
+                    <span style="font-size:14px;">{{$t('Slides.creation_time')}}:{{$moment(item.PptCreateTime).format('YYYY-MM-DD')}}</span>
+                    <img
+                        v-if="treeName==='private'&&selectNode"
+                        src="~@/assets/img/ppt_m/move_ico2.png"
+                        style="width: 14px; height: 14px; margin-right: 8px"
+                    />
+                </div>
             </div>
           </draggable>
           <div class="empty" v-if="catalogPPTList.length===0">
@@ -424,6 +445,14 @@ export default {
     },
   },
   methods: {
+    //ppt列表操作栏
+    getListMenuOptText(e){
+        if(e==='预览发布') return this.$t('Slides.list_ppt_item_publish')
+        if(e==='演示') return this.$t('Slides.list_ppt_item_preview')
+        if(e==='下载') return this.$t('Slides.list_ppt_item_download')
+        if(e==='编辑') return this.$t('Slides.list_ppt_item_edit')
+
+    },
     getContentMenuOptText(e){
       if(e==='查看报告') return this.$t('Slides.view_report_btn')
       if(e==='编辑') return this.$t('Slides.edit_lable_name')
@@ -537,6 +566,34 @@ export default {
         })
       }
     },
+    //设置列表的操作按钮
+    setListTool(){
+      let list = []
+      const {checkPermissionBtn,pptPermission} = this.permissionBtn
+      const authMap = {
+          'publish':checkPermissionBtn(pptPermission.ppt_publish),
+          'present':checkPermissionBtn(pptPermission.ppt_show),
+          'download':checkPermissionBtn(pptPermission.ppt_download),
+          'edit':checkPermissionBtn(pptPermission.ppt_save),
+      }
+      //私有目录显示编辑,其他不显示
+      list = toolList.filter((i)=>{
+          return ['publish','present','download','edit'].includes(i.key)
+        })
+      if(this.treeName!=='private'){
+        list = list.filter(_ => _.key !== 'edit')
+      }
+      const sortMap = {
+        'present':0,
+        'edit':1,
+        'publish':2,
+        'download':3
+      }
+      list.sort((a,b)=>{
+        return sortMap[a.key] - sortMap[b.key]
+      })
+      return list
+    },
     //设置操作按钮
     setToolList(pptDetail={}){
       //公共目录:显示除 删除, 编辑 外的全部按钮
@@ -720,6 +777,22 @@ export default {
         })
       })
     },
+    //点击列表操作栏事件处理
+    handleListToolClick(item,{PptId,PptxUrl,Title}){
+        const urlMap = {
+        edit:`/ppteditor?id=${PptId}`,
+        publish:`/pptpublish?id=${PptId}`,
+        present:`/pptpresent?id=${PptId}`
+      }
+      if(urlMap[item.key]){
+        window.open(urlMap[item.key],'_blank')
+        return 
+      }
+      const handleCommand = {
+        'download':this.downloadPpt,
+      }
+      handleCommand[item.key]({PptxUrl,Title})
+    },
     //点击操作栏事件处理
     async handleToolClick(item){
       const urlMap = {
@@ -1393,7 +1466,7 @@ export default {
           flex-wrap: wrap;
         }
         .list-item{
-          width: 30%;
+          width: 23%;
           background-color: white;
           border: 1px solid #ECECEC;
           border-radius: 4px;
@@ -1402,7 +1475,7 @@ export default {
           margin: 0 20px 20px 0;
           padding:10px;
           box-sizing: border-box;
-          .item-title{
+          .item-title,.item-foot{
             width:100%;
             height:20px;
             display: flex;
@@ -1438,9 +1511,17 @@ export default {
               transform: translate(-50%,-50%);
               font-size: 24px;
               color: #FFFFFF;
-              font-weight: bold;
+              /* font-weight: bold; */
             }
           }
+          .item-opt{
+            display:flex;
+            justify-content:space-between;
+            color:#0052D9;
+            cursor:pointer;
+            margin:10px 0;
+            font-size:16px;
+          }
         }
         .empty{
           position: absolute;

+ 62 - 86
src/views/ppt_manage/newVersion/pptEnPublish.vue

@@ -1,11 +1,20 @@
 <template>
   <div class="publish-page-wrap page-wrap">
       <div class="pub-btn-list">
-        <!-- <div>图表转换方式:
-          <el-radio v-model="transChartType" :label="1">传服务器</el-radio>
-          <el-radio v-model="transChartType" :label="2">本地转</el-radio>
-        </div> -->
         <el-button  type="primary" plain style="width:182px;height:40px;" @click="$router.push({path:'/pptenlist'})">{{$t('Slides.return_to_list')}}</el-button>
+          <!-- 下载配置 -->
+          <!-- <div class="setting">
+            <div>
+                <span>是否压缩</span>
+                <el-radio v-model="setCompression" :label="true">是</el-radio>
+                <el-radio v-model="setCompression" :label="false">否</el-radio>
+            </div>
+            <div>
+                <span>图表转换优化</span>
+                <el-radio v-model="setCompression2" :label="true">开</el-radio>
+                <el-radio v-model="setCompression2" :label="false">关</el-radio>
+            </div>
+        </div> -->
         <el-button  type="primary" style="width:182px;height:40px;margin-left: 0;" @click="downloadPPT" :disabled="isPublish">{{$t('Slides.operations_download')}}</el-button>
         <el-dropdown split-button style="width:182px;height:40px;" type="primary" @click="transHandle" @command="handleCommand" :disabled="isPublish">
           {{layoutStr}}
@@ -51,7 +60,7 @@
 import Cover from './components/CoverEn.vue';
 import CustomCover from './components/CustomCover.vue';
 import TransReport from './components/catalog/transReport.vue';
-import {countComponentName,pptConfigInit,toTextProps,toJson,svg2Base64,getImgRealSize,calcScale,getShapeOptions,createRandomCode,getTableData,getChartInfo} from './utils/untils';
+import {countComponentName,pptConfigInit,toTextProps,toJson,svg2Base64,svgData2Base64,getImgRealSize,calcScale,getShapeOptions,createRandomCode,getTableData,getChartInfo} from './utils/untils';
 import {marginTop,modelConfig,pptSlideMaster,pptSlideMasterEn,pptCoverEn} from './utils/config';
 import pptmixin from '../mixins/pptMixins';
 import mixins from '../mixins/mixins';
@@ -105,6 +114,10 @@ export default {
       transReportShow:false,//转双周报的弹窗是否显示
       transChartType:2,//生成PPT时,转换动态图表的方式:1.将svg传至服务端转换;2.调用changeUrl转换
       currentLang:'en',//语言标识
+
+      setCompression:true,
+      setCompression2:true,
+      downloadLoading:null,
     };
   },
   watch:{
@@ -127,10 +140,11 @@ export default {
         text: this.$t('Slides.generating_powerPoint_msg'),
       });
       this.isPublish = true
-      if(this.loadingAll){
-        await this.pageToPptx()       
-      } 
-      this.isPublish = false
+      this.$nextTick(()=>{
+            setTimeout(async ()=>{
+                await this.pageToPptx() 
+            },50)
+        })
     },
     async downloadPPT(){
         this.loadingInstance = this.$loading({
@@ -139,8 +153,11 @@ export default {
             text: "生成ppt中...",
         });
         this.isPublish = true
-        await this.pageToPptx('dowload') 
-        this.isPublish = false
+        this.$nextTick(()=>{
+            setTimeout(async ()=>{
+                await this.pageToPptx('dowload') 
+            },50)
+        })
     },
     //计算ppt的版式名称
     getComponentName(modelId){
@@ -244,8 +261,21 @@ export default {
       const SlideMaster = _.cloneDeep(pptSlideMasterEn) 
       SlideMaster.objects[1] = {image: {x:0,y:0,w:10,h:7,path:this.pptBgImage}}
       let pptx = pptConfigInit(new pptxgen(),this.LayoutType,'en',SlideMaster,this.pptBgImage)
-      //添加一页空白页,后续转换需要
-      pptx.addSlide()
+      //添加封面
+      let cover = pptx.addSlide()
+      let coverId = this.loadingAll?'cover':'changecover'
+      //let coverImg = await this.htmlToCanvans(coverId)
+      let coverImg = $(`#${coverId} .cover img`)[0].src
+      cover.addImage({
+        path: coverImg,
+        x: 0,
+        y: 0,
+        w:'100%',
+        h: '100%',
+        size: { type: "contain" },
+      })
+      //自定义封面页的内容
+      cover = this.setPPTCover(cover,this.pptCoverContent,this.coverInfo.page.Title)
       const length = this.pageList.length;
       for (let i = 0; i < length; i++) {
         let slide = pptx.addSlide({ masterName: pptSlideMaster.title });
@@ -265,17 +295,10 @@ export default {
             console.log("img/chart...");
             let svgData = this.optionMap[this.pageList[i].elements[j].chartId] instanceof Object
             if(svgData){
-              if(this.transChartType===1){
-                //将svg传至服务端,返回一个线上图片地址
-                const params = new FormData();
-			          params.append('Img',svgData)
-                const { Data } = await dataBaseInterface.uploadImgSvg(params);
-                imgData = Data.ResourceUrl
-              }else if(this.transChartType===2){
                 //将svgDom转为base64 png,返回一个base64字符串
-                imgData = await this.changeUrl(`chart_${i}_${elements[j].position}`)
-                //imgData = svg2Base64($(`#chart_${i}_${elements[j].position} svg`)[0])//生成的svg背景色是黑色不是透明
-              }
+                imgData = this.setCompression2
+                         ?svgData2Base64(this.chartSVGDataMap[`chart_${i}_${elements[j].position}_${elements[j].chartId}`])
+                         :await this.changeUrl(`chart_${i}_${elements[j].position}`)
             }
           } else if (elements[j].type === 'text'){
             console.log('text...')
@@ -299,16 +322,6 @@ export default {
             elements[j].position
           );
           if (imgData) {
-            if(this.transChartType===1){
-              slide.addImage({
-                path:imgData,
-                x: x,
-                y: y,
-                w: width,
-                h: height,
-                size: { type: "contain" },
-              });
-            }else if(this.transChartType===2){
               slide.addImage({
                 data:imgData,
                 x: x,
@@ -317,7 +330,6 @@ export default {
                 h: height,
                 size: { type: "contain" },
               });
-            }
 
             //追加生成图表底部文字
             this.transChartBottomInfo(slide,{x,y,width,height},this.optionMap[this.pageList[i].elements[j].chartId])
@@ -400,62 +412,24 @@ export default {
             size: { type: "contain" },
         })
       }
-      //为了把封面放到第一页,操作pptx.slides达不成想要的效果,于是弄了个pptx2
-      //将封面放在最后生成是因为htmlToCanvans占用太多内存会导致页面假死
-      let pptx2 = pptConfigInit(new pptxgen(),this.LayoutType,'en',SlideMaster,this.pptBgImage)
-      //添加封面
-      let cover = pptx2.addSlide()
-      let coverId = this.loadingAll?'cover':'changecover'
-      //let coverImg = await this.htmlToCanvans(coverId)
-      let coverImg = $(`#${coverId} .cover img`)[0].src
-      cover.addImage({
-        path: coverImg,
-        x: 0,
-        y: 0,
-        w:'100%',
-        h: '100%',
-        size: { type: "contain" },
-      })
-      //生成的ppt需要可以在封面页更改标题和类型,所以封面信息手动写入
-      /* const coverInfo = [
-        //{text:'—————————————————————————————————\n',options:{fontSize:16*0.75,breakLine:true}},
-        {text:this.coverInfo.page.Title,options:{fontSize:28*0.75,breakLine:true}},
-        {text:`\n${this.pptCoverCompenyName||'ETA'}`,
-         options:{fontSize:16*0.75,breakLine:true}},
-        {text:`\n — ${this.coverInfo.page.ReportType} —`,options:{fontSize:16*0.75,breakLine:true}}, 
-        {text:`\n${this.pptCoverDepartName||'Research Department'}`,options:{fontSize:16*0.75,breakLine:true}},
-        {text:this.coverInfo.page.PptDate,options:{fontSize:16*0.75,breakLine:true}},
-        //{text:'\n—————————————————————————',options:{fontSize:16*0.75,breakLine:true}}
-      ]
-      cover.addText(coverInfo,{
-        x:'38%',
-        y:'50%',
-        w:'60%',
-        h:'28%',
-        color:(this.pptCoverTextColor||'#ffffff').slice(1),
-        align:'center',
-        fontFace:'SimHei'
-      }) */
-      //自定义封面页的内容
-      cover = this.setPPTCover(cover,this.pptCoverContent,this.coverInfo.page.Title)
-      //遍历pptx.slides,重新给每一项的部分属性赋值,再推入pptx2.slides中
-      //第一页不需要,因为是空白的
-      for(let i=1;i<pptx.slides.length;i++){
-        let item  = _.cloneDeep(pptx.slides[i])
-        item._name = `Slide ${i+1}`
-        item._slideNum = i+1
-        item._rId = cover._rId+i
-        item._slideId  = cover._slideId+i
-        pptx2._slides.push(item)
-      }
       this.loadingInstance.close();
+      this.isPublish = false
       //结束计时
       const end = Date.now()
       console.log("转换ppt用时:",Math.floor((end-start)/1000),' s')
       //pptx2.writeFile({ fileName: "test.pptx" });//本地测试用
       //直接下载
       if(type==='dowload'){
-        pptx2.writeFile({ fileName: `${this.coverInfo.page.Title||'unname'}.pptx` });
+        this.downloadLoading = this.$loading({
+            fullscreen:true,
+            text:'生成PPT完成,正在下载PPT...'
+        })
+        pptx2.writeFile({ 
+            fileName: `${this.coverInfo.page.Title||'unname'}.pptx`,
+            compression:this.setCompression
+        }).then(()=>{
+            this.downloadLoading.close()
+        })
         return
       }
       this.publishLoading = this.$loading({
@@ -486,15 +460,17 @@ export default {
           partSize: 1024 * 1024 * 10, // 10MB
         }
       };
-
-      pptx2.write('blob').then((data)=>{
+      //outputType为blob时不会启用压缩,为STREAM时会按nodebuffer压缩
+      pptx2.write({compression:this.setCompression,outputType:'STREAM'}).then((data)=>{
+        //将nodebuffer转为blob
+        const blob = new Blob([data])
         // 1走后端接口上传
         const uploadType=this.$setting.dynamicOutLinks.PptUpdateApi ||
                         this.$store.state.dynamicOutLinks.PptUpdateApi ||
                         JSON.parse(localStorage.getItem('dynamicOutLinks')).PptUpdateApi
         if(uploadType==1){
           let form = new FormData()
-          form.append("file",data)
+          form.append("file",blob)
           form.append("PptId",this.$route.query.id) 
           pptInterface.uploadPPTXFile(form).then(res=>{
             if(res.Ret===200){

+ 84 - 93
src/views/ppt_manage/newVersion/pptPublish.vue

@@ -1,12 +1,21 @@
 <template>
   <div class="publish-page-wrap page-wrap">
       <div class="pub-btn-list">
-        <!-- <el-button  type="primary" style="width:120px;height:40px;" @click="transHandle" :loading="isPublish">{{isPublish?'发布中':'发布'}}</el-button> -->
-        <!-- <div>图表转换方式:
-          <el-radio v-model="transChartType" :label="1">传服务器</el-radio>
-          <el-radio v-model="transChartType" :label="2">本地转</el-radio>
-        </div> -->
         <el-button  type="primary" plain style="width:182px;height:40px;" @click="$router.push({path:'/pptlist'})">{{$t('Slides.return_to_list')}}</el-button>
+
+        <!-- 下载配置 -->
+        <!-- <div class="setting">
+            <div>
+                <span>是否压缩</span>
+                <el-radio v-model="setCompression" :label="true">是</el-radio>
+                <el-radio v-model="setCompression" :label="false">否</el-radio>
+            </div>
+            <div>
+                <span>图表转换优化</span>
+                <el-radio v-model="setCompression2" :label="true">开</el-radio>
+                <el-radio v-model="setCompression2" :label="false">关</el-radio>
+            </div>
+        </div> -->
         <el-button  type="primary" style="width:182px;height:40px;margin-left: 0;" @click="downloadPPT" :disabled="isPublish">{{$t('Slides.operations_download')}}</el-button>
         <el-dropdown split-button style="width:182px;height:40px;" type="primary" @click="transHandle" @command="handleCommand" :disabled="isPublish">
           {{layoutStr}}
@@ -65,7 +74,7 @@ import Cover from './components/Cover.vue';
 import CustomCover from './components/CustomCover.vue';
 import TransReport from './components/catalog/transReport.vue';
 //import {pageList} from './utils/mock';
-import {countComponentName,pptConfigInit,toTextProps,toJson,svg2Base64,getImgRealSize,calcScale,countStrSize,getShapeOptions,createRandomCode,getTableData,getChartInfo,pptInit,rgbaToHex } from './utils/untils';
+import {countComponentName,pptConfigInit,toTextProps,toJson,svg2Base64,svgData2Base64,getImgRealSize,calcScale,countStrSize,getShapeOptions,createRandomCode,getTableData,getChartInfo,pptInit,rgbaToHex } from './utils/untils';
 import {marginTop,modelConfig,pptSlideMaster} from './utils/config';
 import pptmixin from '../mixins/pptMixins';
 import mixins from '../mixins/mixins';
@@ -117,7 +126,10 @@ export default {
       layoutStr:`10:7${this.$t('Slides.default_publish')}`,
       ReportId:0,//ppt对应的双周报id,如果没转过,则为0
       transReportShow:false,//转双周报的弹窗是否显示
-      transChartType:2,//生成PPT时,转换动态图表的方式:1.将svg传至服务端转换;2.调用changeUrl转换
+
+      setCompression:true,
+      setCompression2:true,
+      downloadLoading:null
     };
   },
   watch:{
@@ -149,20 +161,24 @@ export default {
         text: this.$t('Slides.generating_powerPoint_msg'),
       });
       this.isPublish = true
-      if(this.loadingAll){
-        await this.pageToPptx() 
-      }
-      this.isPublish = false
+      this.$nextTick(()=>{
+            setTimeout(async ()=>{
+                await this.pageToPptx() 
+            },50)
+        })
     },
     async downloadPPT(){
         this.loadingInstance = this.$loading({
             lock:true,
             fullscreen: true,
-            text: "生成ppt中...",
+            text: this.$t('Slides.generating_powerPoint_msg'),
         });
         this.isPublish = true
-        await this.pageToPptx('dowload') 
-        this.isPublish = false
+        this.$nextTick(()=>{
+            setTimeout(async ()=>{
+                await this.pageToPptx('dowload') 
+            },50)
+        })
     },
     //计算ppt的版式名称
     getComponentName(modelId){
@@ -270,8 +286,20 @@ export default {
       const SlideMaster = _.cloneDeep(pptSlideMaster) 
       SlideMaster.objects[1] = {image: {x:0,y:0,w:10,h:7,path:this.pptBgImage}}
       let pptx = pptConfigInit(new pptxgen(),this.LayoutType,'ch',SlideMaster,this.pptBgImage)
-      //添加一页空白页,后续转换需要
-      pptx.addSlide()
+      //添加封面
+      let cover = pptx.addSlide()
+      let coverId = this.loadingAll?'cover':'changecover'
+      let coverImg = $(`#${coverId} .cover img`)[0].src
+      cover.addImage({
+        path: coverImg,
+        x: 0,
+        y: 0,
+        w:'100%',
+        h: '100%',
+        size: { type: "contain" },
+      })
+      //自定义封面页的内容
+      cover = this.setPPTCover(cover,this.pptCoverContent,this.coverInfo.page.Title)
       const length = this.pageList.length;
       for (let i = 0; i < length; i++) {
         let slide = pptx.addSlide({ masterName: pptSlideMaster.title });
@@ -291,17 +319,15 @@ export default {
             console.log("img/chart...");
             let svgData = this.optionMap[this.pageList[i].elements[j].chartId] instanceof Object
             if(svgData){
-              if(this.transChartType===1){
-                //将svg传至服务端,返回一个线上图片地址
-                const params = new FormData();
-			          params.append('Img',svgData)
-                const { Data } = await dataBaseInterface.uploadImgSvg(params);
-                imgData = Data.ResourceUrl
-              }else if(this.transChartType===2){
                 //将svgDom转为base64 png,返回一个base64字符串
-                imgData = await this.changeUrl(`chart_${i}_${elements[j].position}`)
-                //imgData = svg2Base64($(`#chart_${i}_${elements[j].position} svg`)[0])//生成的svg背景色是黑色不是透明
-              }
+                //转换方式有两种,两者生成的图表无明显差别
+                /**
+                 * svgData2Base64:通过chart.getSVG获取,可能会与页面上的图表有所差别,但生成的svg更小,速度更快
+                 * changeUrl:通过html获取,页面上什么样,图表就什么样,为了保证清晰度将原图表放大4倍后通过canvas转为png,这个过程比较耗时,生成的文件也会比较大
+                 */
+                imgData = this.setCompression2
+                         ?svgData2Base64(this.chartSVGDataMap[`chart_${i}_${elements[j].position}_${elements[j].chartId}`])
+                         :await this.changeUrl(`chart_${i}_${elements[j].position}`)
             }
           } else if (elements[j].type === 'text'){
             console.log('text...')
@@ -325,16 +351,6 @@ export default {
             elements[j].position
           );
           if (imgData) { //图表
-            if(this.transChartType===1){
-              slide.addImage({
-                path:imgData,
-                x: x,
-                y: y,
-                w: width,
-                h: height,
-                size: { type: "contain" },
-              });
-            }else if(this.transChartType===2){
               slide.addImage({
                 data:imgData,
                 x: x,
@@ -343,7 +359,6 @@ export default {
                 h: height,
                 size: { type: "contain" },
               });
-            }
 
             //追加生成图表底部文字
             this.transChartBottomInfo(slide,{x,y,width,height},this.optionMap[this.pageList[i].elements[j].chartId])
@@ -365,10 +380,7 @@ export default {
             const offsetY = realSize.height===imgData2Obj.imgHeight?0:(percentHeight-(realSize.height/imgData2Obj.imgHeight*percentHeight))/2
             const realX = Number(x.substring(0,x.length-1))+offsetX
             const realY = Number(y.substring(0,y.length-1))+offsetY
-            /* const realWidth = offsetX===0?width:(realSize.width/imgData2Obj.imgWidth*percentWidth)
-            const realHeight = offsetY===0?height:(realSize.height/imgData2Obj.imgHeight*percentHeight) */
-            //console.log('position x',x,' y',y,' width',width,' height',height)
-            //console.log('x y',realX,realY,'w h',realWidth,realHeight)
+
             slide.addImage({
               path:imgData2+'?v='+new Date().getTime(),
               x:realX+'%',
@@ -430,61 +442,25 @@ export default {
             size: { type: "contain" },
         })
       }
-      
-      //为了把封面放到第一页,操作pptx.slides达不成想要的效果,于是弄了个pptx2
-      //将封面放在最后生成是因为htmlToCanvans占用太多内存会导致页面假死
-      let pptx2 = pptConfigInit(new pptxgen(),this.LayoutType,'ch',SlideMaster,this.pptBgImage)
-      //添加封面
-      let cover = pptx2.addSlide()
-      let coverId = this.loadingAll?'cover':'changecover'
-      let coverImg = $(`#${coverId} .cover img`)[0].src
-      cover.addImage({
-        path: coverImg,
-        x: 0,
-        y: 0,
-        w:'100%',
-        h: '100%',
-        size: { type: "contain" },
-      })
-      //生成的ppt需要可以在封面页更改标题和类型,所以封面信息手动写入
-      /* const coverInfo = [
-        {text:'—————————————————————————————————\n',options:{fontSize:16*0.75,breakLine:true}},
-        {text:this.coverInfo.page.Title,options:{fontSize:28*0.75,breakLine:true}},
-        {text:`\n— ${this.pptCoverCompenyName||'ETA'} ● ${this.coverInfo.page.ReportType} —`,
-         options:{fontSize:16*0.75,breakLine:false}},
-        {text:`\n${this.pptCoverDepartName||'投研部'}`,options:{fontSize:16*0.75,breakLine:true}},
-        {text:this.coverInfo.page.PptDate,options:{fontSize:16*0.75,breakLine:true}},
-        {text:'\n—————————————————————————',options:{fontSize:16*0.75,breakLine:true}}
-      ]
-      cover.addText(coverInfo,{
-        x:'38%',
-        y:'50%',
-        w:'60%',
-        h:'28%',
-        color:(this.pptCoverTextColor||'#ffffff').slice(1),
-        align:'center',
-        fontFace:'SimHei'
-      }) */
-      //自定义封面页的内容
-      cover = this.setPPTCover(cover,this.pptCoverContent,this.coverInfo.page.Title)
-      //遍历pptx.slides,重新给每一项的部分属性赋值,再推入pptx2.slides中
-      //第一页不需要,因为是空白的
-      for(let i=1;i<pptx.slides.length;i++){
-        let item  = _.cloneDeep(pptx.slides[i])
-        item._name = `Slide ${i+1}`
-        item._slideNum = i+1
-        item._rId = cover._rId+i
-        item._slideId  = cover._slideId+i
-        pptx2._slides.push(item)
-      }
+     
       this.loadingInstance.close();
+      this.isPublish = false
       //结束计时
       const end = Date.now()
       console.log("转换ppt用时:",Math.floor((end-start)/1000),' s')
-      //pptx2.writeFile({ fileName: "test.pptx" });//本地测试用
+      //pptx.writeFile({ fileName: "test.pptx" });//本地测试用
       //直接下载
       if(type==='dowload'){
-        pptx2.writeFile({ fileName: `${this.coverInfo.page.Title||'unname'}.pptx` });
+        this.downloadLoading = this.$loading({
+            fullscreen:true,
+            text:'生成PPT完成,正在下载PPT...'
+        })
+        pptx.writeFile({ 
+            fileName: `${this.coverInfo.page.Title||'unname'}.pptx`,
+            compression:this.setCompression 
+        }).then(()=>{
+            this.downloadLoading.close()
+        })
         return
       }
       this.publishLoading = this.$loading({
@@ -515,14 +491,18 @@ export default {
 
       //console.log('pptx',pptx)
       //根据配置选择是走后端接口上传还是直接前端上传到oss
-      pptx2.write('blob').then((data)=>{
+
+      //outputType为blob时不会启用压缩,为STREAM时会按nodebuffer压缩
+      pptx.write({compression:this.setCompression,outputType:'STREAM'}).then((data)=>{
+        //将nodebuffer转为blob
+        const blob = new Blob([data])
         // 1走后端接口上传
         const uploadType=this.$setting.dynamicOutLinks.PptUpdateApi ||
                         this.$store.state.dynamicOutLinks.PptUpdateApi ||
                         JSON.parse(localStorage.getItem('dynamicOutLinks')).PptUpdateApi
         if(uploadType==1){
           let form = new FormData()
-          form.append("file",data)
+          form.append("file",blob)
           form.append("PptId",this.$route.query.id) 
           pptInterface.uploadPPTXFile(form).then(res=>{
             if(res.Ret===200){
@@ -539,7 +519,7 @@ export default {
                         this.$store.state.dynamicOutLinks.ObjectStorageClient ||
                         JSON.parse(localStorage.getItem('dynamicOutLinks')).ObjectStorageClient
         // 上传到 对象存储器
-        uploadFileDirect(clientType,data,temName,options).then(url=>{
+        uploadFileDirect(clientType,blob,temName,options).then(url=>{
           console.log('文件地址',url);
           this.publishPPT(url)
         }).catch(err=>{
@@ -835,5 +815,16 @@ $titleColor:#333333;
     width:1100px;
     height: 770px;
   }
+  .el-dropdown{
+    .el-button-group{
+        display: flex;
+        width: 100%;
+        .el-button{
+            &:first-of-type{
+                flex:1;
+            }
+        }
+    }
+  }
 }
 </style>

+ 4 - 1
src/views/ppt_manage/newVersion/utils/untils.js

@@ -461,6 +461,9 @@ const encode = (input) => {
   }
   return output
 }
+export const svgData2Base64 = (svg)=>{
+    return PREFIX + encode(svg)
+}
 export const svg2Base64 = (element) => {
   const XMLS = new XMLSerializer()
   const svg = XMLS.serializeToString(element)
@@ -624,7 +627,7 @@ export const getTableData = (data)=>{
       let cellOptions = {
         colspan:cell.mc.cs===0?1:cell.mc.cs,
         rowspan:cell.mc.rs===0?1:cell.mc.rs,
-        color: cell.fc?cell.fc.substring(1):'333',
+        color: cell.fc?cell.fc.substring(1):'333333',
         fill: cell.bg ? cell.bg.substring(1):'',
         bold: cell.bl ? true : false,
         italic: cell.it ? true : false,