cxmo преди 1 година
родител
ревизия
e6d3f60a62

+ 45 - 0
src/views/ppt_manage/mixins/pptEditorMixins.js

@@ -25,6 +25,8 @@ export default{
 
       refreshLoading:null,//一键刷新的loading
       refreshBtnLoading:false,//一键刷新的按钮loading
+
+      isEditTitle:false,
     }
   },
   directives: {
@@ -48,6 +50,49 @@ export default{
     },
   },
   methods:{
+    //切换标题编辑模式
+    handleEditTitle(){
+        //判断当前ppt是否添加了页面
+        if(this.pageList.length===0){
+        this.$message.warning('请至少添加一页!')
+        return
+        }
+        this.isEditTitle = !this.isEditTitle
+        if(this.isEditTitle){
+            //初始化该页标题的数据
+            if(!this.currentItem.titleContent){
+                this.currentItem.titleContent = this.currentItem.title||''
+            }
+            if(!this.currentItem.titleDetail){
+                this.currentItem.titleDetail = {top:5.5,left:10,width:68,height:7}
+                this.$set(this.pageList,this.currentIndex,this.currentItem) 
+            }
+            
+            this.$nextTick(()=>{
+                this.$refs.titleEditor.initFlag = true
+                this.$refs.titleEditor.initTitleEditor()
+            })
+        }
+    },
+    //获取标题内容
+    handleTextChange({val,richContent}){
+        this.currentItem.title = val
+        this.currentItem.titleContent = richContent
+        this.$set(this.pageList,this.currentIndex,this.currentItem) 
+    },
+    //获取标题样式 需要存一份px单位
+    handleTitelStyleChange({top,left,width,height}){
+        const baseWidth = 900
+        const baseHeight = 630
+        this.currentItem.titleDetail = {
+            top,left,width,height,
+            baseTop:baseHeight*top/100,//px单位,基准为编辑页的ppt大小
+            baseLeft:baseWidth*left/100,
+            baseWidth:baseWidth*width/100,
+            baseHeight:baseHeight*height/100
+        }
+        this.$set(this.pageList,this.currentIndex,this.currentItem) 
+    },
     //显示切换模板弹窗
     handleChangeFormat(item){
       this.choosedItem = item

+ 26 - 0
src/views/ppt_manage/mixins/pptMixins.js

@@ -82,6 +82,32 @@ export default {
     }
   },
   methods: {
+    //配置自定义标题内容
+    setPPTTitle(slide,page){
+        const {titleDetail,titleContent,title} = page
+        //兼容之前的ppt
+        if(!titleDetail||!titleContent){
+            slide.addText(this.pageList[i].title, {
+                placeholder:"slideTitle",
+                x:'10%',
+                y:'5.5%',
+                w:'68%',
+                h:'7%',
+                color:'333333'
+              });
+        }else{
+            const {left,top,width,height} = titleDetail
+            const text = toTextProps(toJson(titleContent))
+            slide.addText(text,{
+                placeholder:"slideTitle",
+                x:left+'%',
+                y:top+'%',
+                w:width+'%',
+                h:height+'%'
+            })
+        }
+        return slide
+    },
     //配置自定义封面内容
     setPPTCover(cover,pptCoverContent='',title=''){
         let contentList = []

+ 15 - 2
src/views/ppt_manage/newVersion/components/catalog/pptContent.vue

@@ -15,11 +15,24 @@
             <img :src="pptBackImage" class="pptbg" />
           </div>
           <div class="ppt-item" v-else :key="item.id">
-              <div class="title-wrap" :title="item.title" 
+              <!-- <div class="title-wrap" :title="item.title" 
                 :style="`${getStrCount(item.title)<58?'':'top:0;height:14%;'}`"
                 :class="{'title-ellipsis':getStrCount(item.title)>172}">
                 {{item.title}}
-              </div>
+              </div> -->
+              <!-- 自定义标题 -->
+              <div class="custom-title-wrap editor-content" 
+                    :style="item.titleDetail?{
+                        left:item.titleDetail.baseLeft+'px',
+                        top:item.titleDetail.baseTop+'px',
+                        width:item.titleDetail.baseWidth+'px',
+                        height:item.titleDetail.baseHeight+'px',
+                    }:{
+                        left:'90px',top:'34.65px',width:'612px',height:'44.1px'
+                    }">
+                    <div class="title" v-html="item.titleContent" v-if="item.titleContent"></div>
+                    <div v-else class="normal-title">{{item.title}}</div>
+                </div>
               <component  :is="getComponentName(item.modelId)"
                           :ref="`pptPage_${index-1}`"
                           :pageIndex="index-1"

+ 110 - 0
src/views/ppt_manage/newVersion/components/editor/titleEditorTool.vue

@@ -0,0 +1,110 @@
+<template>
+    <div class="title-editor-tool-wrap">
+        <el-collapse v-model="activeNames" class="tool-list">
+            <el-collapse-item title="位置设置" name="position">
+                <div>
+                    <span class="demonstration">上下(%)</span>
+                    <el-slider v-model="detail.top" :max="14"></el-slider>
+                </div>
+                <div>
+                    <span class="demonstration">左右(%)</span>
+                    <el-slider v-model="detail.left"></el-slider>
+                </div>
+            </el-collapse-item>
+            <el-collapse-item title="大小设置" name="size">
+                <div>
+                    <span class="demonstration">宽度(%)</span>
+                    <el-slider v-model="detail.width" :max="100 - detail.left"></el-slider>
+                </div>
+                <div>
+                    <span class="demonstration">高度(%)</span>
+                    <el-slider v-model="detail.height" :max="14-detail.top"></el-slider>
+                </div>
+            </el-collapse-item>
+            <el-collapse-item title="内容设置" name="content">
+                <div class="editor-tool"></div>
+                <div class="editor" id="editorDom">
+                    <Editor v-model="content" 
+                        :init="setting" 
+                        ref="editor"/>
+                </div>
+            </el-collapse-item>
+        </el-collapse>
+    </div>
+</template>
+
+<script>
+import tinymce from "tinymce";
+import Editor from "@tinymce/tinymce-vue";
+import "tinymce/themes/silver";
+import "tinymce/plugins/lists"; //列表插件
+import "tinymce/plugins/quickbars"; //快速栏插件
+import "tinymce/plugins/fullscreen"; //全屏插件
+import "tinymce/plugins/paste"; //黏贴插件
+import "tinymce/icons/default/icons";
+import {setting} from '../../utils/tinymceSetting'
+export default {
+    components:{Editor},
+    props:{
+        currentItem:{
+            type:Object,
+        }
+    },
+    data() {
+        return {
+            activeNames:'',
+            content:'&*^%',
+            detail:{
+                left:0,top:0,width:0,height:0,
+            },
+            initFlag:false,
+            setting:{
+                ...setting,
+                language: "zh_CN",
+                language_url: require("../../utils/zh_CN.js"),
+                select:'#editorDom',//将编辑器渲染至该dom
+                fixed_toolbar_container: ".editor-tool",//将toolbar渲染至该dom
+                toolbar_persist:true,//是否一直显示toolbar
+                placeholder:"点击输入标题",
+            },
+        };
+    },
+    watch:{
+        content(con){
+            if(this.initFlag){
+                this.initFlag = false
+                return
+            }
+            const { elementId } = this.$refs.editor;
+            let richContent = tinymce.editors[elementId].getContent();
+            let val = tinymce.editors[elementId].getContent({ format: "text" });
+            this.$emit("textChange", { val, richContent });
+        },
+        detail:{
+            handler(newval){
+                //若init还未执行完成,不派发事件
+                if(this.initFlag) return
+                this.$emit("styleChange",newval)
+            },
+            deep:true
+        },
+    },
+    methods: {
+        initTitleEditor(){
+            const {left,top,width,height} = this.currentItem.titleDetail
+            this.detail = {left,top,width,height}
+            this.content = this.currentItem.titleContent
+        },
+    },
+};
+</script>
+
+<style scoped lang="scss">
+.title-editor-tool-wrap{
+    #editorDom{
+        min-height: 200px;
+        border:1px solid #ccc;
+        border-top: none;
+    }
+}
+</style>

+ 27 - 0
src/views/ppt_manage/newVersion/css/common.scss

@@ -48,6 +48,33 @@ $titleColor:#333333;
         -webkit-box-orient:vertical;
         word-break: break-word;
     }
+    .custom-title-wrap{
+        position: absolute;
+        transform-origin: 0 0;
+        &.editor-model{
+            background-color: #999999;
+        }
+        &.ppt-editor-title{
+            border:1px dashed #999;
+        }
+        .title{
+            width:100%;
+            height:100%;
+            font-size: 16px;
+            ul{
+                margin-left: 1em;
+                list-style-type: disc;
+              }
+              ol{
+                margin-left: 1em;
+                list-style-type: decimal;
+              }
+        }
+        .normal-title{
+            font-size: 16px;
+            word-break: break-all;
+        }
+    }
 
     .hint-text {
         text-align: center;

+ 34 - 5
src/views/ppt_manage/newVersion/pptEditor.vue

@@ -56,8 +56,26 @@
                         v-loading="item.isUpdating" :element-loading-text="$t('Slides.updating_chart_loading')"
                         @click="changeCurrentItem(item)">
                         <!-- 标题 -->
-                        <div class="title-wrap">
-                            <input type="text" :placeholder="$t('Slides.click_to_input_title')" v-model="item.title"/> 
+                        <!-- <div class="title-wrap" style="background-color: #999999;">
+                            <input type="text" placeholder="单击输入标题" v-model="item.title"/> 
+                        </div> -->
+                        <!-- 自定义标题 -->
+                        <div @click.stop="handleEditTitle"
+                            :class="[
+                                'ppt-editor-title',
+                                'custom-title-wrap',
+                                currentItem.id===item.id&&isEditTitle?'editor-model':''
+                                ]" 
+                            :style="item.titleDetail?{
+                                left:item.titleDetail.left+'%',
+                                top:item.titleDetail.top+'%',
+                                width:item.titleDetail.width+'%',
+                                height:item.titleDetail.height+'%',
+                            }:{
+                                left:'10%',top:'5.5%',width:'68%',height:'7%'
+                            }">
+                            <div class="title" v-html="item.titleContent" v-if="item.titleContent"></div>
+                            <div v-else class="normal-title">{{item.title}}</div>
                         </div>
                         <!-- 内容 -->
                         <component  :is="getComponentName(item.modelId)"
@@ -121,7 +139,7 @@
             </div>
             <div class="richtext-tool"></div>
             <!-- 防止el-tabs未渲染时触发scrollToActiveTab 报错,v-if改为v-show-->
-            <div class="addppt-right-box" v-show="!isEditLayer">
+            <div class="addppt-right-box" v-show="!isEditLayer&&!isEditTitle">
               <el-tabs v-model="tabsactive">
                 <el-tab-pane :label="tab.label" :name="tab.val" v-for="tab in panelTabs" :key="tab.val"></el-tab-pane>
               </el-tabs>
@@ -187,7 +205,7 @@
               </div>
             </div>
             <!-- 图层编辑 -->
-            <div class="layer-edit-box" v-show="isEditLayer">
+            <div class="layer-edit-box" v-if="isEditLayer">
               	<el-collapse v-model="activeNames" class="tool-list">
                   <el-collapse-item :title="$t('Slides.layer_element')" name="el">
                     <div class="el-wrap">
@@ -226,6 +244,15 @@
                   </el-collapse-item>
                 </el-collapse>
             </div>
+            <!-- 标题编辑 -->
+            <div class="title-edit-box" v-if="isEditTitle">
+                <p>标题编辑模式</p>
+                <TitleEditorTool 
+                    ref="titleEditor"
+                    :currentItem="currentItem"
+                    @styleChange="handleTitelStyleChange"
+                    @textChange="handleTextChange"/>
+            </div>
           </div>
     </div>
 
@@ -310,6 +337,7 @@ import InsertCharts from './components/editor/InsertCharts.vue';
 import ContextMenu from './components/ContextMenu.vue';
 import InsertSemantics from './components/editor/InsertSemantics.vue';
 import ChooseCoverNew from './components/editor/ChooseCoverNew.vue';
+import TitleEditorTool from './components/editor/titleEditorTool.vue';
 export default {
   mixins:[pptmixin,//ppt页面共同逻辑
           mixins,//图表加载逻辑
@@ -319,7 +347,7 @@ export default {
   components: {
     IndexItem, ChooseCover, AddFormat, ShapePreview,
     LayerEditTool, DeletePageDialog, ChangeFormatDialog, InsertPageDialog, addMyClassifyDia, InsertCharts, ContextMenu, InsertSemantics,
-    ChooseCoverNew
+    ChooseCoverNew,TitleEditorTool
 },
   data() {
     return {
@@ -741,6 +769,7 @@ export default {
         //切换到其他活跃页的时候,需退出图层编辑模式
         if(this.currentItem&&this.currentItem.id!==id){
           this.isEditLayer = false
+          this.isEditTitle = false
           this.activeLayerEl = {}
         }
         this.pageList.map((item,index)=>{

+ 28 - 2
src/views/ppt_manage/newVersion/pptPresent.vue

@@ -49,7 +49,7 @@
             </div>
             <!-- PPT内容 -->
               <div class="ppt-item" v-else>
-                <div class="title-wrap" 
+                <!-- <div class="title-wrap" 
                     :title="currentItem.title" 
                     :style="`
                         top:${lineClamp===2?'2%':getStrCount(currentItem.title)<58?'5.5%':'0'};
@@ -57,6 +57,19 @@
                         -webkit-line-clamp:${lineClamp===2?2:3};`" 
                     :class="{'title-ellipsis':getStrCount(currentItem.title)>lineClamp*57}">
                   {{currentItem.title}}
+                </div> -->
+                <!-- 自定义标题 -->
+                <div class="custom-title-wrap editor-content" 
+                    :style="currentItem.titleDetail?{
+                        left:currentItem.titleDetail.baseLeft+'px',
+                        top:currentItem.titleDetail.baseTop+'px',
+                        width:currentItem.titleDetail.baseWidth+'px',
+                        height:currentItem.titleDetail.baseHeight+'px',
+                    }:{
+                        left:'90px',top:'34.65px',width:'612px',height:'44.1px'
+                    }">
+                    <div class="title" v-html="currentItem.titleContent" v-if="currentItem.titleContent"></div>
+                    <div v-else class="normal-title">{{currentItem.title}}</div>
                 </div>
                 <component
                   :ref="`pptPage_${currentIndex-1}`" 
@@ -108,7 +121,7 @@
             </div>
             <!-- PPT内容 -->
               <div class="ppt-item" v-else>
-                <div class="title-wrap" 
+                <!-- <div class="title-wrap" 
                     :title="currentItem.title" 
                     :style="`
                         top:${lineClamp===2?'2%':getStrCount(currentItem.title)<58?'5.5%':'0'};
@@ -116,6 +129,19 @@
                         -webkit-line-clamp:${lineClamp===2?2:3};`" 
                     :class="{'title-ellipsis':getStrCount(currentItem.title)>lineClamp*57}">
                   {{currentItem.title}}
+                </div> -->
+                <!-- 自定义标题 -->
+                <div class="custom-title-wrap editor-content" 
+                    :style="currentItem.titleDetail?{
+                        left:currentItem.titleDetail.baseLeft+'px',
+                        top:currentItem.titleDetail.baseTop+'px',
+                        width:currentItem.titleDetail.baseWidth+'px',
+                        height:currentItem.titleDetail.baseHeight+'px',
+                    }:{
+                        left:'90px',top:'34.65px',width:'612px',height:'44.1px'
+                    }">
+                    <div class="title" v-html="currentItem.titleContent" v-if="currentItem.titleContent"></div>
+                    <div v-else class="normal-title">{{currentItem.title}}</div>
                 </div>
                 <component
                   :ref="`pptPage_${currentIndex-1}`" 

+ 19 - 3
src/views/ppt_manage/newVersion/pptPublish.vue

@@ -36,13 +36,26 @@
           </div>
           <!-- 内容 -->
           <div class="ppt-item" v-for="(item,index) in pageList" :key="item.id">
-            <div class="title-wrap"
+            <!-- <div class="title-wrap"
             :style="`
                 top:${lineClamp===2?'2%':getStrCount(item.title)<58?'5.5%':'0'};
                 height:${lineClamp===2?'12%':getStrCount(item.title)<58?'7%':'14%'};
                 -webkit-line-clamp:${lineClamp===2?2:3};`" 
             :class="{'title-ellipsis':getStrCount(item.title)>lineClamp*57}">
               {{item.title}} 
+            </div> -->
+            <!-- 自定义标题 -->
+            <div class="custom-title-wrap editor-content" 
+                :style="item.titleDetail?{
+                    left:item.titleDetail.baseLeft+'px',
+                    top:item.titleDetail.baseTop+'px',
+                    width:item.titleDetail.baseWidth+'px',
+                    height:item.titleDetail.baseHeight+'px',
+                }:{
+                    left:'90px',top:'34.65px',width:'612px',height:'44.1px'
+                }">
+                <div class="title" v-html="item.titleContent" v-if="item.titleContent"></div>
+                <div v-else class="normal-title">{{item.title}}</div>
             </div>
             <component
               :is="getComponentName(item.modelId)"
@@ -304,14 +317,15 @@ export default {
       const length = this.pageList.length;
       for (let i = 0; i < length; i++) {
         let slide = pptx.addSlide({ masterName: pptSlideMaster.title });
-        slide.addText(this.pageList[i].title, {
+        /* slide.addText(this.pageList[i].title, {
           placeholder:"slideTitle",
           x:'10%',
           y:'5.5%',
           w:'68%',
           h:'7%',
           color:'333333'
-        });
+        }); */
+        slide = this.setPPTTitle(slide,this.pageList[i])
         const elements = this.pageList[i].elements;
         const elLength = elements.length;
         for (let j = 0; j < elLength; j++) {
@@ -429,6 +443,8 @@ export default {
           }
         }
       }
+      pptx.writeFile({ fileName: "test.pptx" });
+      return
       //添加封底 如果有
       if(this.pptBackImage.length){
         let back = pptx.addSlide()

+ 56 - 0
src/views/ppt_manage/newVersion/utils/tinymceSetting.js

@@ -0,0 +1,56 @@
+export const setting = {
+    menubar: false,
+    toolbar: [
+        "indent outdent alignleft aligncenter alignright alignjustify forecolor",
+        "bold italic underline strikethrough numlist bullist backcolor",
+        "fontselect fontsizeselect",
+    ],
+    quickbars_selection_toolbar:false,  
+    quickbars_insert_toolbar: false,
+    plugins: "lists quickbars paste",
+    paste_as_text: true,
+    fontsize_formats:'12px 14px 16px 18px 20px 22px 24px 36px 48px',
+    font_formats:`微软雅黑='微软雅黑';宋体='宋体';黑体='黑体';仿宋='仿宋';
+            楷体='楷体';隶书='隶书';幼圆='幼圆';Andale Mono=andale mono,times;
+            Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;
+            Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;
+            Courier New=courier new,courier;Georgia=georgia,palatino;
+            Helvetica=helvetica;Impact=impact,chicago;
+            Webdings=webdings;Wingdings=wingdings`,
+    skin_url: "/static/css",
+    content_url: "/static/css",
+    inline: true,//启动内联模式,否则会以iframe的形式渲染
+    setup:function(editor){
+        editor.on('ExecCommand',function(evt) {
+            let cmd = evt.command; 
+            //设置ul/ol li的字体大小,颜色
+            if (cmd === 'FontSize' || cmd === 'FontName' /* || cmd === 'mceApplyTextcolor' */) {
+                let val = evt.value; 
+                let node = evt.target.selection.getNode(); 
+                let nodeParent = node.parentNode; 
+                if (node.nodeName === 'SPAN' && nodeParent.nodeName === 'LI') {
+                    if (cmd === 'FontSize') { 
+                        editor.dom.setStyle(nodeParent, 'font-size', val); 
+                    } 
+                    if (cmd === 'FontName') { 
+                        editor.dom.setStyle(nodeParent, 'font-family', val); 
+                    } 
+                    /* if (cmd === 'mceApplyTextcolor') {
+                        editor.dom.setStyle(nodeParent,'color',val)
+                    } */
+                } else if (node.nodeName === 'UL' || node.nodeName === 'OL') { 
+                    let li = editor.dom.select('li', node); 
+                    if (cmd === 'FontSize') { 
+                        editor.dom.setStyle(li, 'font-size', val); 
+                    } 
+                    if (cmd === 'FontName') { 
+                        editor.dom.setStyle(li, 'font-family', val); 
+                    }
+                    /* if (cmd === 'mceApplyTextcolor') {
+                        editor.dom.setStyle(li,'color',val)
+                    } */
+                } 
+            }
+        });
+    }
+}