natasha 1 ano atrás
pai
commit
3513468e47

+ 1 - 0
package.json

@@ -36,6 +36,7 @@
         "vue-router": "3.0.6",
         "vue-slider-component": "^3.2.14",
         "vuex": "3.1.0",
+        "web-highlighter": "^0.7.4",
         "xgplayer": "^3.0.0-next.56"
     },
     "devDependencies": {

+ 3 - 1
src/main.js

@@ -24,7 +24,9 @@ import '@/icons' // icon
 import VueDND from 'awe-dnd'
 Vue.use(VueDND)
 
-
+// 导入打印组件
+import Print from "@/plugins/print";
+Vue.use(Print);
 
 // import VideoPlayer from 'vue-video-player'
 // require('video.js/dist/video-js.css')

+ 143 - 0
src/plugins/print.js

@@ -0,0 +1,143 @@
+const Print = function(dom, options) {
+    if (!(this instanceof Print)) return new Print(dom, options);
+
+    this.options = this.extend({
+            noPrint: ".no-print",
+            onStart: function() {},
+            onEnd: function() {},
+        },
+        options
+    );
+
+    if (typeof dom === "string") {
+        this.dom = document.querySelector(dom);
+    } else {
+        this.dom = dom;
+    }
+
+    this.init();
+};
+Print.prototype = {
+    init: function() {
+        let content = this.getStyle() + this.getHtml();
+        this.writeIframe(content);
+    },
+    extend: function(obj, obj2) {
+        for (let k in obj2) {
+            obj[k] = obj2[k];
+        }
+        return obj;
+    },
+
+    getStyle: function() {
+        let str = "";
+        let styles = document.querySelectorAll("style,link");
+        for (let i = 0; i < styles.length; i++) {
+            str += styles[i].outerHTML;
+        }
+        str +=
+            "<style>" +
+            (this.options.noPrint ? this.options.noPrint : ".no-print") +
+            "{display:none;}</style>";
+
+        return str;
+    },
+
+    getHtml: function() {
+        var inputs = document.querySelectorAll("input");
+        var textareas = document.querySelectorAll("textarea");
+        var selects = document.querySelectorAll("select");
+        let cells = document.querySelectorAll(".cell");
+
+        for (var k = 0; k < inputs.length; k++) {
+            if (inputs[k].type == "checkbox" || inputs[k].type == "radio") {
+                if (inputs[k].checked == true) {
+                    inputs[k].setAttribute("checked", "checked");
+                } else {
+                    inputs[k].removeAttribute("checked");
+                }
+            } else if (inputs[k].type == "text") {
+                inputs[k].setAttribute("value", inputs[k].value);
+            } else {
+                inputs[k].setAttribute("value", inputs[k].value);
+            }
+        }
+
+        for (var k2 = 0; k2 < textareas.length; k2++) {
+            if (textareas[k2].type == "textarea") {
+                textareas[k2].innerHTML = textareas[k2].value;
+            }
+        }
+
+        for (var k3 = 0; k3 < selects.length; k3++) {
+            if (selects[k3].type == "select-one") {
+                var child = selects[k3].children;
+                for (var i in child) {
+                    if (child[i].tagName == "OPTION") {
+                        if (child[i].selected == true) {
+                            child[i].setAttribute("selected", "selected");
+                        } else {
+                            child[i].removeAttribute("selected");
+                        }
+                    }
+                }
+            }
+        }
+
+        const tableNode = document.querySelectorAll(
+            ".el-table__header,.el-table__body"
+        );
+        console.log(tableNode);
+        //el-table 打印预览的时候,宽度占满
+        for (let k6 = 0; k6 < tableNode.length; k6++) {
+            const tableItem = tableNode[k6];
+            tableItem.style.width = "100%";
+        }
+
+        return this.dom.outerHTML;
+    },
+
+    writeIframe: function(content) {
+        let w;
+        let doc;
+        let iframe = document.createElement("iframe");
+        let f = document.body.appendChild(iframe);
+        iframe.id = "myIframe";
+        iframe.style = "position:absolute;width:0;height:0;top:-10px;left:-10px;";
+        w = f.contentWindow || f.contentDocument;
+        doc = f.contentDocument || f.contentWindow.document;
+        doc.open();
+        doc.write(content);
+        doc.close();
+        this.toPrint(w, function() {
+            document.body.removeChild(iframe);
+        });
+    },
+
+    toPrint: function(w, cb) {
+        let _this = this;
+        w.onload = function() {
+            try {
+                setTimeout(function() {
+                    w.focus();
+                    typeof _this.options.onStart === "function" &&
+                        _this.options.onStart();
+                    if (!w.document.execCommand("print", false, null)) {
+                        w.print();
+                    }
+                    typeof _this.options.onEnd === "function" && _this.options.onEnd();
+                    w.close();
+                    cb && cb();
+                });
+            } catch (err) {
+                console.log("err", err);
+            }
+        };
+    },
+};
+const MyPlugin = {};
+MyPlugin.install = function(Vue, options) {
+    // 4. 添加实例方法
+    Vue.prototype.$print = Print;
+};
+export default MyPlugin;

+ 2 - 2
src/store/watermark.js

@@ -35,10 +35,10 @@ let setWatermark = (str, date) => {
     div.style.position = 'absolute'
     div.style.zIndex = '100000'
     div.style.width = document.documentElement.clientWidth + 'px'
-    div.style.height = document.documentElement.clientHeight + 'px'
+    div.style.height = document.documentElement.clientHeight * 100 + 'px'
     div.style.background = 'url(' + can.toDataURL('image/png') + ') left top repeat'
         //   document.body.appendChild(div)
-    div.style.opacity = '0.4' // 水印的透明度
+    div.style.opacity = '1' // 水印的透明度
     let show = document.getElementById("showWaterMark") // 控制水印显示的区域,设置id = show,表示在该范围内有水印
     if (!show) {
         clearInterval(watermarkTimer)

+ 2 - 2
src/views/bookShelf/articleDetail.vue

@@ -1437,9 +1437,9 @@ export default {
     width: 428px;
     height: 90%;
     overflow: auto;
-    max-height: 702px;
+    max-height: 712px;
     position: fixed;
-    top: 50%;
+    top: 40%;
     left: 50%;
     z-index: 9;
     font-size:0;

+ 38 - 116
src/views/bookShelf/components/NotesModel.vue

@@ -3,30 +3,11 @@
   <div class="NNPE-ArticleView" v-if="articleInfo">
     <template v-if="resArr[0]&&resArr[0].wordsList">
         <h2>
-            <span v-for="(itemR,indexR) in resArr[0].wordsList" :key="indexR" :style="{color:colorObj.titleColor,fontSize:(wordFontsize+30)+'px',lineHeight:(wordFontsize+38)+'px',marginRight:'10px',fontWeight:'700',cursor:'pointer'}" 
-            :class="[
-                    itemR.tokens[9]===''?'marginRight':'',itemR.marginRight?'marginSingleRight':'',
-                    itemR.isExplain||itemR.explainNumber?'hasExplain':''
-                ]">
-                <template v-if="itemR.isShow">
+            <span v-for="(itemR,indexR) in resArr[0].wordsList" :key="indexR" :style="{color:colorObj.titleColor,fontSize:(wordFontsize+30)+'px',lineHeight:(wordFontsize+38)+'px',marginRight:'10px',fontWeight:'700',cursor:'pointer'}">
                     <span
-                            class="NNPE-chs"
-                            :class="[
-                                itemR.type,itemR.tokens[9]===''?'marginRight':'',itemR.highIndex?'fontWeight':'',itemR.marginRight?'marginSingleRight':'',
-                            ]"
-                            >{{ itemR.tokens[2] }}</span
-                        >
-                        <span
-                            class="NNPE-chs NNPE-chs-both"
-                            v-if="resArr[0].wordsList[indexR + 1] &&
-                            resArr[0].wordsList[indexR + 1].tokens[2] &&
-                            enFhList.indexOf(resArr[0].wordsList[indexR + 1].tokens[2]) > -1"
-                            :class="[
-                                    resArr[0].wordsList[indexR + 1].type,resArr[0].wordsList[indexR + 1].tokens[8]===''?'marginLeft':'',resArr[0].wordsList[indexR + 1].highIndex?'fontWeight':'',resArr[0].wordsList[indexR + 1].marginRight?'marginSingleRight':'',
-                                ]"
-                            >{{ resArr[0].wordsList[indexR + 1].tokens[2] }}</span
-                        >
-                </template>
+                        class="NNPE-chs"
+                        >{{ itemR.text }}</span
+                    >
                 <!-- {{itemR.tokens[2]}} -->
             </span>
         </h2>
@@ -58,41 +39,11 @@
           v-for="(item, index) in resArr"
           :key="'detail' + index"
         >
-          <div class="wordsList-box">
             <template v-if="index!==0">
-                <div class="nnpe-sentence-box">
-                    <div v-for="(pItem, pIndex) in item.wordsList" :key="'wordsList' + pIndex" class="word-box" :class="[pItem.isExplain||pItem.explainNumber?'hasExplain':'']">
-                        <template v-if="pItem.isShow">
-                            <div
-                                :class="[
-                                'NNPE-words',
-                                ]"
-                            >
-                                <span
-                                    class="NNPE-chs"
-                                    :class="[
-                                        pItem.type,pItem.tokens[9]===''?'marginRight':'',pItem.highIndex?'fontWeight':'',pItem.marginRigh?'marginSingleRight':''
-                                    ]"
-                                    :style="{fontSize:wordFontsize + 'px',color: colorObj.contentColor}"
-                                    >{{ pItem.tokens[2] }}</span
-                                >
-                                <span
-                                    class="NNPE-chs NNPE-chs-both"
-                                    v-if="item.wordsList[pIndex + 1] &&
-                                    item.wordsList[pIndex + 1].tokens[2] &&
-                                    enFhList.indexOf(item.wordsList[pIndex + 1].tokens[2]) > -1"
-                                    :class="[
-                                            item.wordsList[pIndex + 1].type,item.wordsList[pIndex + 1].tokens[8]===''?'marginLeft':'',item.wordsList[pIndex + 1].highIndex?'fontWeight':'',item.wordsList[pIndex + 1].marginRight?'marginSingleRight':''
-                                        ]"
-                                    :style="{fontSize:wordFontsize + 'px',color: colorObj.contentColor}"
-                                    >{{ item.wordsList[pIndex + 1].tokens[2] }}</span
-                                >
-                            </div>
-                        </template>
-                    </div>
-                </div>
+                <span v-for="(pItem, pIndex) in item.wordsList" :key="'wordsList' + pIndex" class="word-box" :style="{fontSize:wordFontsize + 'px',color: colorObj.contentColor}">
+                        {{ pItem.text }}
+                </span>
             </template>
-          </div>
         </div>
       </div>
     </template>
@@ -102,6 +53,10 @@
 
 <script>
 import AudioLine from "@/components/common/AudioLine.vue"
+import Highlighter from 'web-highlighter';
+const highlighter = new Highlighter({
+    exceptSelectors: ['pre', 'code']
+});
 export default {
   name: "ArticleView",
   props: [ "titleFontsize", "wordFontsize", "colorObj","articleType","articleInfo"],
@@ -153,7 +108,7 @@ export default {
       ],
       articleImg: {}, // 文章图片
       tokensArr: [],
-      sentenceList: [],
+      sentenceList: []
     };
   },
   computed: {
@@ -196,10 +151,7 @@ export default {
             resArr.push(obj)
         }
         this.sentenceList.forEach((item,index) => {
-            item.StyleTokens.forEach((items,indexs)=>{
-                items.isShow = this.enFhList.indexOf(items.tokens[2])==-1
-                resArr[item.pno].wordsList.push(items)
-            })
+            resArr[item.pno].wordsList.push(item)
           });
           this.resArr = resArr;
     },
@@ -210,6 +162,22 @@ export default {
   mounted() {
     if (this.articleInfo) {
       this.handleData();
+      highlighter
+        .on('selection:hover', ({id}) => {
+            // 通过添加 class,实现类似 hover 效果
+            highlighter.addClass('highlight-wrap-hover', id);
+        })
+        .on('selection:hover-out', ({id}) => {
+            // 鼠标离开时清除悬停样式
+            highlighter.removeClass('highlight-wrap-hover', id);
+        })
+        .on('selection:create', ({sources}) => {
+            // sources = sources.map(hs => ({hs}));
+            console.log(sources)
+            // 存储
+            // store.save(sources);
+        });
+      highlighter.run()
     }
   },
   beforeCreate() {}, //生命周期 - 创建之前
@@ -299,61 +267,15 @@ export default {
     }
   }
   .NNPE-detail {
-    clear: both;
-    overflow: hidden;
-    display: flex;
-    .NNPE-words {
-      float: left;
-      padding: 0;
-      &.noPadding{
-          padding:0;
-      }
-      &.sentActive {
-        background: rgba(24, 144, 255, 0.1);
-      }
-      &.overActive {
-        background: rgba(0, 0, 0, 0.06);
-      }
-      > span {
-        float: left;
-        cursor: pointer;
-        &.NNPE-chs {
-          //   font-size: 24px;
-          font-family: 'Smartisan';
-          line-height: 150%;
-          color: #000000;
-          padding: 0 3px;
-          &.wordActive {
-            color: #175DFF !important;
-          }
-          &.marginRight{
-            padding-right: 0;
-          }
-          &.marginLeft{
-            padding-left: 0;
-          }
-          &.marginSingleRight{
-            padding-right: 3px;
-          }
-          &.fontWeight{
-            font-weight: bold;
-          }
-          &.newWord{
-            color: #3459D2 !important;
-          }
-          &.phrase{
-            color: #FF802B !important;
-          }
-          &.explain{
-            // color: #23C847 !important;
-            font-weight: 400;
-          }
-        }
-        &.padding {
-          padding: 0 3px;
-          cursor: pointer;
-        }
-      }
+    // clear: both;
+    // overflow: hidden;
+    // display: flex;
+    padding: 6px 0 12px 3px;
+    .word-box {
+        font-family: 'Smartisan';
+        line-height: 150%;
+        color: #000000;
+        word-break: break-word;
     }
   }
 }

+ 127 - 8
src/views/bookShelf/components/PrintModel.vue

@@ -6,10 +6,45 @@
             <p class="article-title">打印预览</p>
         </div>
         <div class="print-model-bottom">
-            <div class="print-model-bottom-left" id="showWaterMark">
-
+            <div class="print-model-bottom-left" id="showWaterMark" ref="printArea">
+                <div class="print-inner">
+                    <template v-if="resArr[0]&&resArr[0].wordsList">
+                        <h2>
+                            <span v-for="(itemR,indexR) in resArr[0].wordsList" :key="indexR" :style="{color:colorObj.titleColor,fontSize:(wordFontsize+30)+'px',lineHeight:(wordFontsize+38)+'px',marginRight:'10px',fontWeight:'700',cursor:'pointer'}">
+                                    <span
+                                        class="NNPE-chs"
+                                        >{{ itemR.text }}</span
+                                    >
+                                <!-- {{itemR.tokens[2]}} -->
+                            </span>
+                        </h2>
+                    </template>
+                    <h6 class="nnpe-article-author" :style="{color:colorObj.sourceColor,fontSize:(wordFontsize-4)+'px',lineHeight:(wordFontsize+4)+'px',fontWeight:'400'}">
+                        {{articleInfo.art_author+' · '+articleInfo.study_phase_name+'版 · 第 '+articleInfo.iss_no+' 期 · '+articleInfo.release_date+' · '+articleInfo.chn_item+(articleInfo.page_no_in_pub?' · P'+articleInfo.page_no_in_pub:'')}}
+                    </h6>
+                    <template v-if="resArr.length > 0">
+                        <div class="table-box">
+                            <div
+                            :class="['NNPE-detail']"
+                            v-for="(item, index) in resArr"
+                            :key="'detail' + index"
+                            >
+                                <template v-if="index!==0">
+                                    <span v-for="(pItem, pIndex) in item.wordsList" :key="'wordsList' + pIndex" class="word-box" :style="{fontSize:wordFontsize + 'px',color: colorObj.contentColor}">
+                                            {{ pItem.text }}
+                                    </span>
+                                </template>
+                            </div>
+                        </div>
+                    </template>
+                </div>
+            </div>
+            <div class="print-model-bottom-right">
+                <div>
+                    <el-button @click="goBack" size="small">取消</el-button>
+                    <el-button type="primary" @click="handlePrint" size="small">打印</el-button>
+                </div>
             </div>
-            <div class="print-model-bottom-right"></div>
         </div>
     </div>
 </template>
@@ -18,18 +53,67 @@
 import waterMark from "../../../store/watermark"
 import { getToken } from '@/utils/auth'
 export default {
+    props: [ "titleFontsize", "wordFontsize", "colorObj","articleType","articleInfo"],
     data(){
         return{
             userMessage: getToken()?JSON.parse(getToken()):null,
+            resArr: [],
+            articleImg: {}, // 文章图片
+            sentenceList: []
         }
     },
     methods: {
         goBack(){
             this.$emit('closePrint')
+        },
+        handleData() {
+            let resArr = [];
+            let articleInfo = JSON.parse(JSON.stringify(this.articleInfo));
+            this.sentenceList = articleInfo.art_corpus_data?articleInfo.art_corpus_data.sentList:[]
+            let leg = articleInfo.art_corpus_data.sentList[articleInfo.art_corpus_data.sentList.length-1].pno
+            this.sentenceList.forEach((item,index) => {
+                item.StyleTokens = []
+                item.tokens.forEach((items,indexs)=>{
+                    let obj = {
+                        tokens: items,
+                        marginRight: indexs===item.tokens.length-1
+                    }
+                    item.StyleTokens.push(obj)
+                })
+            });
+            for(let i=0;i<leg+1;i++){
+                let obj = {
+                    wordsList: []
+                }
+                resArr.push(obj)
+            }
+            this.sentenceList.forEach((item,index) => {
+                resArr[item.pno].wordsList.push(item)
+            });
+            this.resArr = resArr;
+        },
+        // 打印
+        handlePrint(){
+            this.$nextTick(() => {
+                this.$print(this.$refs["printArea"], {
+                noPrint: ".noPrint",
+                type: "html",
+                // style: style,// 亦可使用引入的外部css;
+                onStart: () => {
+                    console.log("打印开始");
+                },
+                onEnd: () => {
+                    console.log("打印完成");
+                },
+                });
+            });
         }
     },
     mounted(){
-        waterMark.set(this.userMessage.user_name+' '+'13717582171','')
+        if (this.articleInfo) {
+            this.handleData();
+        }
+        waterMark.set(this.userMessage.user_name+' '+this.userMessage.phone,'')
     }
 };
 </script>
@@ -42,6 +126,11 @@ export default {
         background: #FFF;
         display: flex;
         align-items: center;
+        width: 100%;
+        position: fixed;
+        top: 0;
+        left: 0;
+        z-index: 10000;
         .goback{
             color:#2F3742;
             font-size: 20px;
@@ -68,19 +157,49 @@ export default {
         }
     }
     &-bottom{
-
         display: flex;
+        justify-content: center;
+        padding-top: 46px;
         &-left{
-            width: 1190px;
-            margin: 8px;
-            min-height: 600px;
+            width: 1090px;
+            margin: 8px 346px 8px 8px;
             background: #fff;
             flex-shrink: 0;
+            .print-inner{
+                overflow: auto;
+                padding: 120px 100px;
+            }
+            .nnpe-article-author{
+                margin: 24px 0;
+                text-align: center;
+            }
+            h2{
+                text-align: center;
+            }
+            .NNPE-detail {
+                // clear: both;
+                // overflow: hidden;
+                // display: flex;
+                // padding: 6px 0 12px 3px;
+                text-indent: 2em;
+                .word-box {
+                    font-family: 'Smartisan';
+                    line-height: 150%;
+                    color: #000000;
+                    word-break: break-word;
+                }
+            }
         }
         &-right{
             width: 338px;
             padding: 24px;
             background: #FFF;
+            position: fixed;
+            z-index: 1;
+            top: 46px;
+            right: calc((100% - 1440px)/2);
+            height: 100%;
+            overflow: auto;
         }
     }
 }