<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-dropdown split-button type="primary" @click="transHandle" @command="handleCommand" :disabled="isPublish">
          {{layoutStr}}
          <el-dropdown-menu slot="dropdown">
            <el-dropdown-item :command="1">10:7</el-dropdown-item>
            <el-dropdown-item :command="2">16:9</el-dropdown-item>
            <el-dropdown-item :command="3">4:3</el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>
				<el-button  type="primary" plain style="width:120px;height:40px;" @click="$router.push({path:'/pptenlist'})">返回列表</el-button>
      </div>
      <!--全加载-->
      <template v-if="loadingAll">
        <div class="ppt-wrap flex-center">
          <!-- 封面 -->
          <div class="ppt-item" id="cover">
            <Cover :pageInfo="coverInfo.page"></Cover>
          </div>
          <!-- 内容 -->
          <div class="ppt-item" v-for="(item,index) in pageList" :key="item.id">
            <div class="title-wrap">
              {{item.title}} 
            </div>
            <component
              :is="getComponentName(item.modelId)"
              :ref="`pptPage_${index}`"
              :pageIndex="index"
              :pageItem="item" 
              type="show"
              >
            </component>
          </div>
          <!-- 封底 -->
          <div class="ppt-item" id="back">
            <img src="~@/assets/img/ppt_en_last.png" class="pptbg" style="width:100%;height:100%;object-fit: fill !important;"/>
          </div>
        </div>
      </template>
  </div>
</template>

<script>
import Cover from './components/CoverEn.vue';
import TransReport from './components/catalog/transReport.vue';
import {countComponentName,pptInit,toTextProps,toJson,svg2Base64,getImgRealSize,calcScale,getShapeOptions,createRandomCode,getTableData} from './utils/untils';
import {marginTop,modelConfig,pptSlideMaster,pptCoverEn} from './utils/config';
import pptmixin from '../mixins/pptMixins';
import mixins from '../mixins/mixins';
import html2canvas from 'html2canvas';
import pptxgen from "pptxgenjs";
import moment from 'moment';
import{dataBaseInterface,getOSSSign} from "@/api/api.js"
import {pptEnInterface} from '@/api/modules/pptEnApi.js';
import Highcharts from "highcharts/highstock";
import HighchartszhCN  from '@/utils/highcahrts-zh_CN'
import HightchartsExport from 'highcharts/modules/exporting';
HightchartsExport(Highcharts)
HighchartszhCN(Highcharts)
export default {
  components: {Cover,TransReport},
  mixins:[pptmixin,mixins],
  data() {
    return {
      isPublish:false,//是否在发布中
      coverInfo:{
        id:0,
        title:'封面',
        index:{
          id:0,
          title:'封面',
          idName:'cover'
        },
        /* page:{
          Title:'ppt标题阿巴阿巴',
          ReportType:'ppt副标题阿巴阿巴',
          BackgroundImg:'https://hzstatic.hzinsights.com/ppt/bg3.jpg',
          PptDate:'2022年7月'
        } */
      },//封面信息
      backInfo:{
        id:-1,
        title:'封底',
        idName:'back'
      },//封底信息
      pageList:[],//ppt数组(页面信息)
      pptFile:null,//
      base64Url:null,
      loadingInstance:null,
      loadingAll:true,//是否加载全部,false代表使用虚拟列表加载
      LayoutType:1,//ppt比例,1:默认比例10:7,2:16:9,3:4:3
      coefficient:0.7,//宽高系数
      layoutStr:'10:7（默认）发布',
      ReportId:0,//ppt对应的双周报id,如果没转过,则为0
      transReportShow:false,//转双周报的弹窗是否显示
      transChartType:2,//生成PPT时,转换动态图表的方式:1.将svg传至服务端转换;2.调用changeUrl转换
      currentLang:'en',//语言标识
    };
  },
  watch:{
    LayoutType(newval){
      let coefficient = newval===1?0.7:newval===2?0.5625:0.75
      this.coefficient = coefficient
    }
  },
  methods: {
    async transHandle(){
      //需要校验PPT
      const {result,hintText} = this.checkPPT()
      if(!result){
        this.$message.warning(hintText)
        return
      }
      this.loadingInstance = this.$loading({
        lock:true,
        fullscreen: true,
        text: "生成ppt中...",
      });
      this.isPublish = true
      if(this.loadingAll){
        await this.pageToPptx()       
      } 
      this.isPublish = false
    },
    //计算ppt的版式名称
    getComponentName(modelId){
        return countComponentName(modelId);
    },
    async init(){
      if(this.loadingAll){
        this.dataLoading = this.$loading({
          lock: true,
          text: '拼命加载中...',
          spinner: 'el-icon-loading',
          customClass:'loading',
          background: 'rgba(255, 255, 255, 0.8)'
        });
        await this.getpptData()
        //加载每一页
        for(let i=0;i<this.pageList.length;i++){
          await this.initPageElements(this.pageList[i],'show')
        }
        this.dataLoading.close();
      }
    },
    async getpptData(){
      const {id} = this.$route.query
      if(id){
        await this.getpptDataById(id)
        const {status} = this.result
        if(status===200){
          const {content,FirstPage,ReportId} = this.result
          this.pageList = content
          this.coverInfo.page = FirstPage
          this.coverInfo.page.imgLocalUrl = pptCoverEn[FirstPage.TemplateType-1]
          this.ReportId = ReportId
        }else{
          this.$message.error('获取ppt数据失败!')
          this.dataLoading.close();
          window.close()
        }
      }else{
        this.$message.error('无对应的ppt数据!')
        window.close()
      }
      //this.pageList = pageList
    },
    //将dom转图片
    //普通dom:封面封底;动态图表
    async htmlToCanvans(el,index = -1){
      let canvas = null,refEl=null
      await this.$nextTick()
      if(index===-1){
        //dom->img
        console.log('el',$(`#${el}`))
        refEl = $(`#${el}`)[0]
      }
      canvas = await html2canvas(
          refEl,
          {
          useCORS: true,
          allowTaint:true,
          backgroundColor: null,
          scale: 2,
          imageTimeout: 0,
          x:refEl.getBoundingClientRect().left+$(document).scrollLeft() /* +8.5 */,
          y:refEl.getBoundingClientRect().top+$(document).scrollTop()
          }
        )
        //this.checkImg(canvas.toDataURL("image/png"))
        return canvas.toDataURL("image/png")
    },
    //测试用.下载图片
    checkImg(dataURL,fileType='png'){
      let link = document.createElement('a')
      link.href = dataURL
      link.download = `test.${fileType}`
      link.click()
    },
    //测试用
    downloadSvg(svg){
      let base64Url = 'data:image/svg+xml;charset=utf-8,'+encodeURIComponent(svg)
      this.checkImg(base64Url,'svg')
    },
    //生成pptx
    async pageToPptx(){
      //开始计时
      const start = Date.now()
      let pptx = pptInit(new pptxgen(),this.LayoutType,'en');
      //添加一页空白页,后续转换需要
      pptx.addSlide()
      const length = this.pageList.length;
      for (let i = 0; i < length; i++) {
        let slide = pptx.addSlide({ masterName: pptSlideMaster.title });
        //添加背景图片
        //slide.background = { path: "/static/pptnextimg.png" };
        slide.addText(this.pageList[i].title, {
          placeholder:"slideTitle",
          x:'8%',
          y:'5.5%',
          w:'62%',
          h:'7%',
          color:'333333'
        });
        const elements = this.pageList[i].elements;
        const elLength = elements.length;
        for (let j = 0; j < elLength; j++) {
          let imgData = null,textData = null,imgData2 = null,imgData2Obj=null,sheetData=null
          if (elements[j].type === "chart") {
            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背景色是黑色不是透明
              }
            }
          } else if (elements[j].type === 'text'){
            console.log('text...')
            textData = toTextProps(toJson(elements[j].richContent))
          }else if (elements[j].type==='image'){
            console.log("img/image...")
            imgData2 = this.pageList[i].elements[j].src
            imgData2Obj = {
              imgWidth:$(`#image_${i}_${elements[j].position}`).width(),
              imgHeight:$(`#image_${i}_${elements[j].position}`).height(),
              naturalWidth:document.getElementById(`image_${i}_${elements[j].position}`).naturalWidth,
              naturalHeight:document.getElementById(`image_${i}_${elements[j].position}`).naturalHeight
            }
          }else if (elements[j].type==='sheet'){
            console.log('table...')
            const sheetId = elements[j].sheetId
            sheetData = getTableData(this.sheetDataMap[sheetId])
          }
          const { x, y, width, height } = this.getPosition(
            this.pageList[i].modelId,
            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,
                y: y,
                w: width,
                h: height,
                size: { type: "contain" },
              });
            }
            
          }else if (textData){
            slide.addText(textData,{
              x:x,
              y:y,
              w:width,
              h:height,
              margin:10,
              fontSize: 16*0.75,
              valign:'top'
            })
          }else if(imgData2){
            //console.log('src',imgData2)
            const realSize = getImgRealSize(imgData2Obj)
            const percentWidth = Number(width.substring(0,width.length-1))
            const percentHeight = Number(height.substring(0,height.length-1))
            const offsetX = realSize.width===imgData2Obj.imgWidth?0:(percentWidth-(realSize.width/imgData2Obj.imgWidth*percentWidth))/2
            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
            slide.addImage({
              path:imgData2+'?v='+new Date().getTime(),
              x:realX+'%',
              y:realY+'%',
              w:offsetX===0?width:(realSize.width/imgData2Obj.imgWidth*percentWidth)+'%',
              h:offsetY===0?height:(realSize.height/imgData2Obj.imgHeight*percentHeight)+'%',
              size:{type:"contain"}
            })
          }else if(sheetData){
            slide.addTable(sheetData,{
              x:x,y:y,w:width,h:height,border:{type:'solid',pt:1}
            })
          } 
        }
        //在添加完版式后,添加图层元素
        const layers = this.pageList[i].layers||[]
        const layersLength = layers.length
        for(let j = 0; j < layersLength; j++){
          const {percentageTop,percentageLeft,percentageWidth,percentageHeight} = layers[j]
          //percent单位基于预留标题位置，所以top和height的百分比需要调整
          let position = {x:percentageLeft*100,y:percentageTop*100*0.86+marginTop,w:percentageWidth*100,h:percentageHeight*100*0.86}
          //type为shape&line
          if(['shape','line'].includes(layers[j].type)){
            const scale = calcScale({w:906,h:906*0.7},{w:$('.ppt-item').width(),h:$('.ppt-item').width()*this.coefficient})
            let options = getShapeOptions(layers[j],position,scale)
            //console.log('options',options)
            if(layers[j].shapeType==='Circle'){ //circle的points太复杂,直接用插件预设的
                slide.addShape('ellipse', options);
            }else{
                slide.addShape('custGeom',options) //其他形状的points自己预设
            }
          }
          //type为text,则转换内部文字
          if(layers[j].type==='text'){
            let textData = toTextProps(toJson(layers[j].richContent))
            slide.addText(textData,{
              x:position.x+'%',
              y:position.y+'%',
              w:position.w+'%',
              h:position.h+'%',
              margin:10,
              fontSize: 16*0.75,
              valign:'top'
            })
          }
        }
      }
      //添加封底
      let back = pptx.addSlide()
      let backId = this.loadingAll?'back':'changeback'
      //let backImg = await this.htmlToCanvans(backId)
      let backImg = $(`#${backId} img`)[0].src
      back.addImage({
        path: backImg,
        x: 0,
        y: 0,
        w:'100%',
        h: '100%',
        size: { type: "contain" },
      })
      //为了把封面放到第一页,操作pptx.slides达不成想要的效果,于是弄了个pptx2
      //将封面放在最后生成是因为htmlToCanvans占用太多内存会导致页面假死
      let pptx2 = pptInit(new pptxgen(),this.LayoutType,'en');
      //添加封面
      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:`\nHORIZON INSIGHTS PTE. LTD.`,
         options:{fontSize:16*0.75,breakLine:true}},
        {text:`\n — ${this.coverInfo.page.ReportType} —`,options:{fontSize:16*0.75,breakLine:true}}, 
        {text:'\nFICC 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:'ffffff',
        align:'center',
        fontFace:'SimHei'
      })
      //遍历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();
      //结束计时
      const end = Date.now()
      console.log("转换ppt用时:",Math.floor((end-start)/1000),' s')
      //pptx2.writeFile({ fileName: "test.pptx" });//本地测试用
      this.publishLoading = this.$loading({
        fullscreen:true,
        text:"发布中..."
      })
      //console.log('pptx',pptx)
      pptx2.write('blob').then((data)=>{
        // 上传到阿里云oss
        this.handleUploadToOSS(data)
      })
    },
    //计算各版式下,对应position在ppt中的位置
    getPosition(modelId, position) {
      //找到modelConfig[modelId].elments[position]
      const model = modelConfig.find((i) => i.modelId === modelId);
      const { x, y, width, height } = model.elements.find(
        (i) => i.position === position
      );
      return {
          x:x+'%',y:y+marginTop+'%',width:width+'%',height:height+'%'
      };
    },
    async publishPPT(url){
      const res = await pptEnInterface.publishPPT({
          PptId:parseInt(this.$route.query.id),
          PptxUrl:url
        })
      if(res.Ret===200){
        this.$message.success('发布成功!')
        this.publishLoading&&this.publishLoading.close()
        //如果该ppt已转成双周报,则发布成功后更新双周报
        //需求池272 PPT生成报告后，PPT更新内容或报告更新内容两者互不影响 遂注释
        /* if(this.ReportId!==0){
          pptEnInterface.transPPTtoReport({
            PptId:parseInt(this.$route.query.id),
          }).then(res=>{}) 
        } */
      }
    },
    //将svgDom转为base64 png
    changeUrl(id){
      let img = new Image()
      const svgHtml = $(`#${id} svg`)[0].outerHTML
      /* img.src = `data:image/svg+xml;base64,${window.btoa(unescape(encodeURIComponent(svgHtml)))}` */
      img.src = svg2Base64($(`#${id} svg`)[0]);//不支持unescape时可用
      const imgW = $(`#${id}`)[0].offsetWidth*4,imgH = $(`#${id}`)[0].offsetHeight*4
      let canvas = document.createElement('canvas')
      canvas.width = imgW
      canvas.height = imgH
      return new Promise(resolve=>{
        img.onload = ()=>{
          //console.log('src',img.src)
          let ctx = canvas.getContext("2d")
          ctx.drawImage(img, 0, 0, imgW, imgH) 
          resolve(canvas.toDataURL("image/png"))
        }
      })
      //return canvas.toDataURL("image/png")
    },
    //上传到阿里云oss
    async handleUploadToOSS(file){
      console.log(file);
      // 获取oss临时签名
      const res=await getOSSSign()
      if(res.Ret!==200) return

      let accessKeyId=res.Data.AccessKeyId
      let accessKeySecret=res.Data.AccessKeySecret
      let stsToken=res.Data.SecurityToken

      const ALOSSINS=new OSS({
        // yourRegion填写Bucket所在地域。以华东1（杭州）为例，Region填写为oss-cn-hangzhou。
        region: "oss-cn-shanghai",
        // 从STS服务获取的临时访问密钥（AccessKey ID和AccessKey Secret）。
        accessKeyId: accessKeyId,
        accessKeySecret: accessKeySecret,
        // 从STS服务获取的安全令牌（SecurityToken）。
        stsToken: stsToken,
        // 填写Bucket名称，例如examplebucket。
        bucket: "hzchart",
        endpoint:'hzstatic.hzinsights.com',
        cname:true,
        timeout:6000000
      });
      // 生成文件名
      let t=new Date()
      let month=moment(t).format('YYYYMM')
      let day=moment(t).format('YYYYMMDD')
      /* const temName=`ppt/${month}/${day}/${this.coverInfo.page.Title}.pptx` */
      const temName=`ppt/${month}/${day}/${createRandomCode(32)}.pptx`
      console.log('文件名',temName);

      const options = {
        // 获取分片上传进度、断点和返回值。
        progress: (p, cpt, res) => {
          console.log(p);
            // this.percentage=parseInt(p*100)
        },
        // 设置并发上传的分片数量。
        parallel: 10,
        // 设置分片大小。默认值为1 MB，最小值为100 KB。
        partSize: 1024 * 1024 * 10, // 10MB
      };
      try {
        const res=await ALOSSINS.multipartUpload(temName,file,{...options})
        console.log('上传结果',res);
        if(res.res.status===200){
          let url='https://hzstatic.hzinsights.com/'+res.name
          console.log('oss文件地址',url);
          this.publishPPT(url)
        }
      } catch (error) {
        console.log(error);
        this.$message.warning('上传失败，请刷新重试')
      }
    },
    //chart->svg
    getChartSvg(id){
      //console.log(Highcharts.charts)
      let chart = null
      Highcharts.charts.map((i)=>{
        if(i.renderTo.id===id){
          chart = i
        }
      })
      return chart.getSVG({
        chart: {
          width: $(`#${id}`)[0].offsetWidth,
          height: $(`#${id}`)[0].offsetHeight,
        }
      })
    },
    //校验PPT
    checkPPT(){
      //检验每一页
      for(let i=0;i<this.pageList.length;i++){
        if(!this.pageList[i].title){
          return {result:false,hintText:`第${i+1}页内容不完整,请重新编辑!`}
        }
        //无内容
        if(this.pageList[i].elements.length===0){
          return {result:false,hintText:`第${i+1}页内容不完整,请重新编辑!`}
        }
        //有文本框,但是文本框无内容
      }
      return {result:true,hintText:''}
    },
    async handleCommand(command){
      this.LayoutType = command
      this.layoutStr = ['','10:7（默认）发布','16:9 发布','4:3 发布'][this.LayoutType]
      await this.init()
    },
  /*   getTransSet(data){
      pptEnInterface.transReport({
        PptId:Number(this.$route.query.id),
        ClassifyIdSecond:data.type,
        Title:data.title
      }).then(res=>{
        if(res.Ret===200){
          this.$message.success('转换成功')
          this.transReportShow = false
          this.ReportId = res.Data.report_id
        }
      })
    }, */
    //pageList[].layers 图层元素自适应
    changeLayerStyle(){
      for(let i=0;i<this.pageList.length;i++){
        if(this.pageList[i].layers){
          this.pageList[i].layers.map(shape=>{
            const scale = calcScale({w:906,h:906*0.7},{w:$('.ppt-item').width(),h:$('.ppt-item').width()*this.coefficient})
            $('.shape').css('transform',`scale(${scale.x},${scale.y})`)
            if(shape.type==='text'){
              //text元素的shape不需要scale
              $('.shape-text').css('transform',`scale(1)`)
            }
          })
        } 
      }
    }
  },
  mounted(){
    this.init()
  },
  updated(){
    /*
      不考虑图层,不缩放ppt-item;不会拉伸图表;
    */
    $('.ppt-item').css('height',$('.ppt-item').width()*this.coefficient);
    this.contentScale = $('.ppt-item').width()/906
    this.$nextTick(()=>{
      if($('.content')[0]){
        $('.content').css('transform',`scale(${this.contentScale})`)
      }
      this.changeLayerStyle()
    })
    window.onresize = ()=>{
      $('.ppt-item').css('height',$('.ppt-item').width()*this.coefficient);
      this.changeLayerStyle()
    }
  },
  destroyed(){
    window.onresize = null
  }
};
</script>

<style lang="scss">
@import './css/common.scss';
@import './css/format.scss';
</style>
<style scoped lang="scss">
$titleHeight:7%;
$titleColor:#333333;
.publish-page-wrap{
  #scroller{
      height:100vh;
      overflow:auto;
      &::-webkit-scrollbar {
        width: 10px;
      }
    }
  .ppt-wrap{
    width:100%;
    margin-top: 15px;
    flex-direction: column;
    .ppt-item{
      width:1008px;
      height: 705.6px;
      /* width: 906px; */
      /* height: 628.594px; */
      background-color: pink;
      margin-bottom: 20px;
      background: url('~@/assets/img/ppt_en_bg.png') no-repeat center;
      background-size: 100% 104.5%;
      position:relative;
      border: 4px solid transparent;
      transform-origin: 0 0;
      &#cover,&#back{
        /* width:898px; */
        width: 1000px;
        border: none;
        overflow: hidden;
      }
      .title-wrap{
        width: 62%;
        height: $titleHeight;
        position: absolute;
        top: 5.5%;
        left: 8%;
        z-index: 100;
        display: flex;
        font-size: 24px;
        color:$titleColor;
        align-items: center;
        word-break: break-word;
      }
      .cover{
        position: relative;
      }
      .pptbg{
        width:100%;
      }
    }
  }
  .pub-btn-list{
    position: fixed;
    top:20px;
    right:3%;
    z-index:10;
  }
  .fixed-wrap{
    position:fixed;
    top:100px;
    left:100px;
    width:1100px;
    height: 770px;
  }
}
</style>
