首页 > 其他分享 >vue实现将word转换为HTML页面,并实现类似word的目录导航和关键字搜索跳转

vue实现将word转换为HTML页面,并实现类似word的目录导航和关键字搜索跳转

时间:2024-01-26 09:22:35浏览次数:36  
标签:el vue word cur item step let 跳转 id

<template>     <div class="content">         <div class="header">             <div class="title">                 XXXXXX             </div>             <div class="search">                 <a-button class="btn" @click="previous">                     上一个                 </a-button>                 <a-input-search                     v-model:value="keyword"                     placeholder="请输入关键字"                     class="search-area"                     @search="onSearch()"
                />                 <div v-if="matchNum > 0" style="height: 50px;line-height: 50px;padding: 4px;">{{ searchIndex + '/'+ matchNum}}</div>                 <a-button class="btn" @click="next">                     下一个                 </a-button>             </div>         </div>         <div class="content-area">             <div class="left-Nav" id="left-Nav">                 <!-- <div v-for="item in tocData">                     <div style="font-size: 16px;cursor: pointer" @click="handleNodeClick(item.id,$event)" class="log-item">{{item.label}}</div>                     <div v-for="a in item.children">                         <div style="font-size: 16px;margin-left: 1em;margin-top: 1.2em;cursor: pointer" @click="handleNodeClick(a.id,$event)" class="log-item">{{a.label}}</div>                         <div v-for="b in a.children">                             <div style="font-size: 16px;margin-left: 2em;margin-top: 1.2em;cursor: pointer" @click="handleNodeClick(b.id,$event)" class="log-item">{{b.label}}</div>                         </div>                     </div>                 </div> -->                 <div style="width: 100%" class="nav">                     <a-menu                         :default-selected-keys="['1']"                         :default-open-keys="['2']"                         mode="inline"                         :theme="theme"                         :inline-collapsed="collapsed"                         :selectedKeys="selectedKeys"                     >                         <template v-for="item in tocData">                         <a-menu-item v-if="!item.children" :key="item.key">                             <a-icon type="pie-chart" />                             <span>{{ item.title }}</span>                         </a-menu-item>                         <sub-menu v-else :key="item.key + '1'" :menu-info="item" />                         </template>                     </a-menu>                 </div>
            </div>             <div class="right-content">                 <div class="word-wrap">                     <div id="wordView" ref = "aContent" v-html="wordText" v-if="wordText" />                   </div>             </div>         </div>
    </div> </template> <script>     import mammoth from "mammoth";     import { Menu } from "ant-design-vue";     const SubMenu = {         template: `             <a-sub-menu :key="menuInfo.key" v-bind="$props" v-on="$listeners">                 <span slot="title">                 <a-icon type="mail" /><span>{{ menuInfo.title }}</span>                 </span>                 <template v-for="item in menuInfo.children">                 <a-menu-item v-if="!item.children" :key="item.key"  @click=handleNodeClick(item.id)>                     <a-icon type="pie-chart" />                     <span>{{ item.title }}</span>                 </a-menu-item>                 <sub-menu v-else :key="item.key" :menu-info="item" />                 </template>             </a-sub-menu>             `,         name: "SubMenu",         // must add isSubMenu: true         isSubMenu: true,         props: {             ...Menu.SubMenu.props,             // Cannot overlap with properties within Menu.SubMenu.props             menuInfo: {             type: Object,             default: () => ({}),            },         },         methods:{             handleNodeClick(data) {                     console.log("点击目录")
                    //  实现跳转锚点                     let id = data                     let tag = 'h-'+id                     let anchorH = document.getElementById(tag).offsetTop // 得到h标签的位置                     console.log("anchorH",anchorH)                     this.changeHeight(anchorH)                                       // this.$emit('changeHeight', anchorH); // 我的页面滚动通过父组件控制,所以发射出去让父组件操作             },             changeHeight(record){                     console.log("--------")                     // let el  = this.$refs.scroll                     let el = document.getElementById("wordView")                     // console.log("el",el)                     // let cur = el.scrollTop  // el-scrollbar获取页面当前位置的属性,定位也是通过改变这个                     let cur = el.offsetTop                     // console.log("cur",cur)                     let flag = false                                         let step = 20                     // 由于有可能是向上滑,有可能向下滑,所以需要考虑移动的正负                     if (record < cur) {                         step = -step;                         flag = true                     }else if (record == cur) {                         return                     }
                    // 设置动画                     // 具体含义就是按照每8毫秒执行一次向上移动指定的步长,这样就是一个动画                     var timer = setInterval(()=>{                         if (flag) {                         //  说明小于0                             if (cur + step < record) {                             // 说明移动多了,这时候我们改变移动距离,直接移动到指定位置即可                             // el.scrollTop = record                                 window.scrollTo({ top: record, behavior: "smooth" });                                 clearInterval(timer)                             }else {                                 // el.scrollTop = cur + step                                 window.scrollTo({ top: cur + step, behavior: "smooth" });                             }                         }else{                             if (cur + step > record) {                             // 说明移动多了,这时候我们改变移动距离,直接移动到指定位置即可                             //   el.scrollTop = record                             window.scrollTo({ top: record, behavior: "smooth" });                             clearInterval(timer)                             }else {                             //  el.scrollTop = cur + step                             window.scrollTo({ top: cur + step, behavior: "smooth" });                             }                         }
                        cur += step                     },8)
            },         }     };
    export default{         components: {             "sub-menu": SubMenu,         },         data(){             return{                 wordText: "",                 wordURL: '/pro/XXX.docx',//文件地址                 flag:true,                 scrollTag:[],                 selectedKeys:['0'],                 maxum:0,                 keyword:'',                 highlightedText:'',                 searchIndex:1,                 anchorHlist:[], //搜索的元素所在的位置                 matchNum:0,  //匹配数                 curIndex:0, //当前的index
            }         },         created(){             this.getWordText();             setTimeout(()=>{                 this.makeToc()                 this.$forceUpdate()
            },2000)
                    },         mounted(){
            window.addEventListener('scroll',this.handleScroll,true)             setTimeout(()=>{                 let values = []                 console.log("this.maxum",this.maxum)                 // let hTags = document.getElementsByTagName('h');                 for (let i = 1;i <=this.maxum;i++) {                     let tag = 'h-'+i                     // console.log("tag",tag)                     let anchorH = document.getElementById(tag).offsetTop                     // console.log(anchorH)                     values.push({id:i,height:anchorH})                 }                 this.scrollTag = values                 // console.log("scrollTag",this.scrollTag)             },2100)                     },                 computed: {             // processedText() {             //     const regex = new RegExp(this.keyword, 'gi'); // g表示全局匹配,i表示不区分大小写                             //     this.highlightedText = this.wordText.replace(regex, `<span class="highlight">${this.keyword}</span>`);                         //     return this.highlightedText;             // }             matchNumText(){                 let num  = this.matchNum                 return num             }         },         updated() {         // 需要在data里面加上flag,只触发一次里面内容             // if (this.flag) {             //     this.makeToc()             //     this.flag = false             //  }
            // this.$emit('refreshScroll',values)         },         methods:{
            getWordText() {                 const xhr = new XMLHttpRequest();                 xhr.open("get", this.wordURL, true);                 console.log("======",xhr)                 xhr.responseType = "arraybuffer";                 xhr.onload = () => {                     if (xhr.status == 200) {                     mammoth.convertToHtml({ arrayBuffer: new Uint8Array(xhr.response) }).then((resultObject) => {                         this.$nextTick(() => {                         this.wordText = resultObject.value;                                         console.log("this.$refs",this.$refs)                         // this.makeToc()                         });                     });                     }                 };                 xhr.send();             },             onSearch(){                 const regex1 = new RegExp(`<span class="highlight" style="background:yellow">`, 'gi')  //删除上一次的样式                 this.wordText = this.wordText.replace(regex1,"<span>")
                this.searchIndex = 1                 const regex = new RegExp(this.keyword, 'gi'); // g表示全局匹配,i表示不区分大小写                 let wordText = this.wordText                 let matches = this.wordText.match(regex)                 // this.matchNum =  matches !== null ? matches.length : 0                 this.wordText = wordText.replace(regex, `<span class="highlight" style="background:yellow">${this.keyword}</span>`);                     // 根据关键字查找匹配的元素                 let mainContainer =document.querySelector("#wordView").querySelectorAll("p");                 console.log("mainContainer.length",mainContainer.length,mainContainer[1].textContent)                 // let anchorHlist = []                 this.anchorHlist = []                 for(let i =0;i<=mainContainer.length-1;i++){                     if(mainContainer[i].textContent.match(regex) !== null){                         let anchorH = mainContainer[i].offsetTop                         this.anchorHlist.push(anchorH)                     }                 }                 this.matchNum = this.anchorHlist.length                 this.scrollToLocation(this.checkIndex);                 console.log("mainContainer",mainContainer)                 console.log('matchNum',this.matchNum,matches,this.anchorHlist)                             },             scrollToLocation(index){                                 let height = this.anchorHlist[index]                 console.log("下一个-------",height)                 window.scrollTo({ top: height - 110, behavior: "smooth" });                 // window.scrollTo({ top: cur + step, behavior: "smooth" });             },             next() {                 this.searchIndex++;                 if(this.searchIndex > this.matchNum) this.searchIndex =  1                 this.scrollToLocation(this.searchIndex-1);             },             previous() {                                 if(this.searchIndex > 1){                     this.searchIndex--;                 }else{                     this.searchIndex = 1                 }                 this.scrollToLocation(this.searchIndex-1);             },
            // 将一个集合的数据变成一个树形的数据结构               toTree(data){                 // 删除 所有 children,以防止多次调用                 data.forEach(function (item) {                     delete item.children;                 });
                // 将数据存储为 以 id 为 KEY 的 map 索引数据列                 var map = {};                 data.forEach(function (item) {                     map[item.id] = item;                 });                 var val = [];                 data.forEach(function (item) {                     // 以当前遍历项的pid,去map对象中找到索引的id                     var parent = map[item.p_id];                     // 好绕啊,如果找到索引,那么说明此项不在顶级当中,那么需要把此项添加到,他对应的父级中                     if (parent) {                     (parent.children || (parent.children = [])).push(item);                     } else {                     //如果没有在map中找到对应的索引ID,那么直接把 当前的item添加到 val结果集中,作为顶级                     val.push(item);                     }                 });                 return val;                 },                 /**                  * 生成目录                  * */                 makeToc(){                 // 获取所有的h标签,给他们加上id,同时创建符合toTree方法要求的对象                 //{                 //          id:'',// 抛出id                 //           tag:'',// 抛出标签名称                 //          label:'',// 抛出标题                 //          p_id:'',// 抛出父级id                     // }                                 // 定义参与目录生成的标签                     const tocTags = ["H1","H2","H3","H4","H5","H6"];
                    // 目录树结果                     const tocArr = [];
                    // 获取所有标题标签                     const headDoms = Array.from(this.$refs.aContent.childNodes).filter(item => tocTags.includes(item.tagName));
                    // 遍历标题标签                     headDoms.forEach((item,index,arr) => {                         // 给标题添加id                         item.id = `h-${index + 1}`;                         // 获取当前节点前面的节点                         let prevs = arr.filter((i,j) => j < index);                         // 过滤前面的节点为合理节点                         // 如 h3节点前  只能为 h1 h2 h3                         prevs = prevs.filter(i => tocTags.filter((i,j) => j <= tocTags.findIndex(i => i == item.tagName)).includes(i.tagName));                         // 对前面的节点进行排序,距离自身节点近的排在前面                         // 如 div > p > span > img  当前为img                         // 常规获取节点为 [div,p,span,img]                         // 排序后获取节点为 [img,span,p,div]                         prevs = prevs.sort((a,b) => -(a.id.replace('h-','')) - b.id.replace('h-',''));                         // 查询距离自身节点最近的不同于当前标签的节点                         const prev = prevs.find(i => i.tagName != item.tagName);                         this.maxum = Math.max(this.maxum,index + 1)                         tocArr.push({                         id:index + 1,// 抛出id                         key:index,                         title:item.innerText,                         tag:item.tagName,// 抛出标签名称                         label:item.innerText,// 抛出标题                         p_id:item.tagName == "H1" || prev == null ? 0 : Number(prev.id.replace("h-",'')),// 抛出父级id                         })                     })
                    // 使用上述方法生成树 最后在el-tree的data中使用 tocData即可                     this.tocData = this.toTree(tocArr);                     console.log(this.tocData)                 },

                changeHeight(record){                     console("--------")                     let el  = this.$refs.scroll                     let cur = el.scrollTop  // el-scrollbar获取页面当前位置的属性,定位也是通过改变这个                     let flag = false                                         let step = 20                     // 由于有可能是向上滑,有可能向下滑,所以需要考虑移动的正负                     if (record < cur) {                     step = -step;                     flag = true                     }else if (record == cur) {                     return                     }
                    // 设置动画                     // 具体含义就是按照每8毫秒执行一次向上移动指定的步长,这样就是一个动画                     var timer = setInterval(()=>{                     if (flag) {                     //  说明小于0                         if (cur + step < record) {                         // 说明移动多了,这时候我们改变移动距离,直接移动到指定位置即可                         el.scrollTop = record                         clearInterval(timer)                         }else {                         el.scrollTop = cur + step                         }                     }else{                         if (cur + step > record) {                         // 说明移动多了,这时候我们改变移动距离,直接移动到指定位置即可                         el.scrollTop = record                         clearInterval(timer)                         }else {                         el.scrollTop = cur + step                         }                     }
                    cur += step                     },8)
                },                 // 父组件                 handleScroll(){                         // let  top = this.$refs.scroll.wrap.scrollTop                         let top  = document.getElementById("wordView").offsetTop                         let len = this.scrollTag.length -1                         for (let i = 0;i < len;i ++) {                         // 判断当前页面的位置 应该属于哪个标签,我这里是标签下的内容没有结束,目录都是这个标签高亮                             if (top >= this.scrollTag[i].height - 1 && top < this.scrollTag[i + 1].height) {                                 // -1 是我发现点击目录滑动的时候,会有<1 的误差,导致目录高亮不改变                                                                 //  让之变成                                 // this.$refs.detail.changeItem(i)  // 通过这个方法,改变目录标签高亮                                 this.selectedKeys = [i]                             }                         }                         // 特殊处理 第一个和最后一个                         if (top< this.scrollTag[0].height )  this.selectedKeys = [i]                         if (top >= this.scrollTag[len].height - 1 )  this.selectedKeys = [this.selectedKeys.length -1]                         console.log("this.selectedKeys",this.selectedKeys)
                 },


        }
    } </script> <style lang="less">
  .content {     width: 95%;     margin:0px auto;     border: 1px solid  black;     font-size: 20px;       }
  .header{     position: fixed;     top:0px;     left: 2.5%;     width: 95%;     height:100px;     border-bottom: 1px solid black;     display: flex;     justify-content: space-between;     align-items: center;     padding: 0 20px;     background-color:aliceblue;     .title{         font-size: 40px;         font-weight: bold;     }     .search{         /* width: 500px; */         height: 50px;         display: flex;         justify-content: space-around;         flex-direction: row;         .btn{             width: 20%;             height: 50px;             border-radius: 15%;             background-color: #1890ff;             color: #fff;
        }         .search-area{             width: 70%;             height: 50px;             margin: 0 10px;         }     }       }   .content-area{     width: 100%;     display: flex;     justify-content: space-between;     margin-top: 100px;     .left-Nav{         position: fixed;         top:100px;         left:2.5%;         width: 19%;                 padding:10px  30px;         .nav{             /* height: 100%;             position: relative; */         }     }     .right-content{         margin-left:20%;         width: 85%;         padding:10px  30px;         border-left: 1px solid black;         .word-wrap {             padding: 15px;             img {                 width: 80%;             }         }     }   }   h1{     font-size:36px;     height: 40px;     line-height: 40px;   }   h2{     font-size:28px;     height: 40px;     line-height: 40px;   }   img{     width: 80% !important;   }   .ant-menu{     font-size: 20px !important;   }   /* .word-wrap {     padding: 15px;     img {         width: 100%;     } } */ </style> <template>     <div class="content">         <div class="header">             <div class="title">                 指标管理平台             </div>             <div class="search">                 <a-button class="btn" @click="previous">                     上一个                 </a-button>                 <a-input-search                     v-model:value="keyword"                     placeholder="请输入关键字"                     class="search-area"                     @search="onSearch()"
                />                 <div v-if="matchNum > 0" style="height: 50px;line-height: 50px;padding: 4px;">{{ searchIndex + '/'+ matchNum}}</div>                 <a-button class="btn" @click="next">                     下一个                 </a-button>             </div>         </div>         <div class="content-area">             <div class="left-Nav" id="left-Nav">                 <!-- <div v-for="item in tocData">                     <div style="font-size: 16px;cursor: pointer" @click="handleNodeClick(item.id,$event)" class="log-item">{{item.label}}</div>                     <div v-for="a in item.children">                         <div style="font-size: 16px;margin-left: 1em;margin-top: 1.2em;cursor: pointer" @click="handleNodeClick(a.id,$event)" class="log-item">{{a.label}}</div>                         <div v-for="b in a.children">                             <div style="font-size: 16px;margin-left: 2em;margin-top: 1.2em;cursor: pointer" @click="handleNodeClick(b.id,$event)" class="log-item">{{b.label}}</div>                         </div>                     </div>                 </div> -->                 <div style="width: 100%" class="nav">                     <a-menu                         :default-selected-keys="['1']"                         :default-open-keys="['2']"                         mode="inline"                         :theme="theme"                         :inline-collapsed="collapsed"                         :selectedKeys="selectedKeys"                     >                         <template v-for="item in tocData">                         <a-menu-item v-if="!item.children" :key="item.key">                             <a-icon type="pie-chart" />                             <span>{{ item.title }}</span>                         </a-menu-item>                         <sub-menu v-else :key="item.key + '1'" :menu-info="item" />                         </template>                     </a-menu>                 </div>
            </div>             <div class="right-content">                 <div class="word-wrap">                     <div id="wordView" ref = "aContent" v-html="wordText" v-if="wordText" />                   </div>             </div>         </div>
    </div> </template> <script>     import mammoth from "mammoth";     import { Menu } from "ant-design-vue";     const SubMenu = {         template: `             <a-sub-menu :key="menuInfo.key" v-bind="$props" v-on="$listeners">                 <span slot="title">                 <a-icon type="mail" /><span>{{ menuInfo.title }}</span>                 </span>                 <template v-for="item in menuInfo.children">                 <a-menu-item v-if="!item.children" :key="item.key"  @click=handleNodeClick(item.id)>                     <a-icon type="pie-chart" />                     <span>{{ item.title }}</span>                 </a-menu-item>                 <sub-menu v-else :key="item.key" :menu-info="item" />                 </template>             </a-sub-menu>             `,         name: "SubMenu",         // must add isSubMenu: true         isSubMenu: true,         props: {             ...Menu.SubMenu.props,             // Cannot overlap with properties within Menu.SubMenu.props             menuInfo: {             type: Object,             default: () => ({}),            },         },         methods:{             handleNodeClick(data) {                     console.log("点击目录")
                    //  实现跳转锚点                     let id = data                     let tag = 'h-'+id                     let anchorH = document.getElementById(tag).offsetTop // 得到h标签的位置                     console.log("anchorH",anchorH)                     this.changeHeight(anchorH)                                       // this.$emit('changeHeight', anchorH); // 我的页面滚动通过父组件控制,所以发射出去让父组件操作             },             changeHeight(record){                     console.log("--------")                     // let el  = this.$refs.scroll                     let el = document.getElementById("wordView")                     // console.log("el",el)                     // let cur = el.scrollTop  // el-scrollbar获取页面当前位置的属性,定位也是通过改变这个                     let cur = el.offsetTop                     // console.log("cur",cur)                     let flag = false                                         let step = 20                     // 由于有可能是向上滑,有可能向下滑,所以需要考虑移动的正负                     if (record < cur) {                         step = -step;                         flag = true                     }else if (record == cur) {                         return                     }
                    // 设置动画                     // 具体含义就是按照每8毫秒执行一次向上移动指定的步长,这样就是一个动画                     var timer = setInterval(()=>{                         if (flag) {                         //  说明小于0                             if (cur + step < record) {                             // 说明移动多了,这时候我们改变移动距离,直接移动到指定位置即可                             // el.scrollTop = record                                 window.scrollTo({ top: record, behavior: "smooth" });                                 clearInterval(timer)                             }else {                                 // el.scrollTop = cur + step                                 window.scrollTo({ top: cur + step, behavior: "smooth" });                             }                         }else{                             if (cur + step > record) {                             // 说明移动多了,这时候我们改变移动距离,直接移动到指定位置即可                             //   el.scrollTop = record                             window.scrollTo({ top: record, behavior: "smooth" });                             clearInterval(timer)                             }else {                             //  el.scrollTop = cur + step                             window.scrollTo({ top: cur + step, behavior: "smooth" });                             }                         }
                        cur += step                     },8)
            },         }     };
    export default{         components: {             "sub-menu": SubMenu,         },         data(){             return{                 wordText: "",                 wordURL: '/pro/指标管理平台操作指南2.docx',//文件地址                 flag:true,                 scrollTag:[],                 selectedKeys:['0'],                 maxum:0,                 keyword:'',                 highlightedText:'',                 searchIndex:1,                 anchorHlist:[], //搜索的元素所在的位置                 matchNum:0,  //匹配数                 curIndex:0, //当前的index
            }         },         created(){             this.getWordText();             setTimeout(()=>{                 this.makeToc()                 this.$forceUpdate()
            },2000)
                    },         mounted(){
            window.addEventListener('scroll',this.handleScroll,true)             setTimeout(()=>{                 let values = []                 console.log("this.maxum",this.maxum)                 // let hTags = document.getElementsByTagName('h');                 for (let i = 1;i <=this.maxum;i++) {                     let tag = 'h-'+i                     // console.log("tag",tag)                     let anchorH = document.getElementById(tag).offsetTop                     // console.log(anchorH)                     values.push({id:i,height:anchorH})                 }                 this.scrollTag = values                 // console.log("scrollTag",this.scrollTag)             },2100)                     },                 computed: {             // processedText() {             //     const regex = new RegExp(this.keyword, 'gi'); // g表示全局匹配,i表示不区分大小写                             //     this.highlightedText = this.wordText.replace(regex, `<span class="highlight">${this.keyword}</span>`);                         //     return this.highlightedText;             // }             matchNumText(){                 let num  = this.matchNum                 return num             }         },         updated() {         // 需要在data里面加上flag,只触发一次里面内容             // if (this.flag) {             //     this.makeToc()             //     this.flag = false             //  }
            // this.$emit('refreshScroll',values)         },         methods:{
            getWordText() {                 const xhr = new XMLHttpRequest();                 xhr.open("get", this.wordURL, true);                 console.log("======",xhr)                 xhr.responseType = "arraybuffer";                 xhr.onload = () => {                     if (xhr.status == 200) {                     mammoth.convertToHtml({ arrayBuffer: new Uint8Array(xhr.response) }).then((resultObject) => {                         this.$nextTick(() => {                         this.wordText = resultObject.value;                                         console.log("this.$refs",this.$refs)                         // this.makeToc()                         });                     });                     }                 };                 xhr.send();             },             onSearch(){                 const regex1 = new RegExp(`<span class="highlight" style="background:yellow">`, 'gi')  //删除上一次的样式                 this.wordText = this.wordText.replace(regex1,"<span>")
                this.searchIndex = 1                 const regex = new RegExp(this.keyword, 'gi'); // g表示全局匹配,i表示不区分大小写                 let wordText = this.wordText                 let matches = this.wordText.match(regex)                 // this.matchNum =  matches !== null ? matches.length : 0                 this.wordText = wordText.replace(regex, `<span class="highlight" style="background:yellow">${this.keyword}</span>`);                     // 根据关键字查找匹配的元素                 let mainContainer =document.querySelector("#wordView").querySelectorAll("p");                 console.log("mainContainer.length",mainContainer.length,mainContainer[1].textContent)                 // let anchorHlist = []                 this.anchorHlist = []                 for(let i =0;i<=mainContainer.length-1;i++){                     if(mainContainer[i].textContent.match(regex) !== null){                         let anchorH = mainContainer[i].offsetTop                         this.anchorHlist.push(anchorH)                     }                 }                 this.matchNum = this.anchorHlist.length                 this.scrollToLocation(this.checkIndex);                 console.log("mainContainer",mainContainer)                 console.log('matchNum',this.matchNum,matches,this.anchorHlist)                             },             scrollToLocation(index){                                 let height = this.anchorHlist[index]                 console.log("下一个-------",height)                 window.scrollTo({ top: height - 110, behavior: "smooth" });                 // window.scrollTo({ top: cur + step, behavior: "smooth" });             },             next() {                 this.searchIndex++;                 if(this.searchIndex > this.matchNum) this.searchIndex =  1                 this.scrollToLocation(this.searchIndex-1);             },             previous() {                                 if(this.searchIndex > 1){                     this.searchIndex--;                 }else{                     this.searchIndex = 1                 }                 this.scrollToLocation(this.searchIndex-1);             },
            // 将一个集合的数据变成一个树形的数据结构               toTree(data){                 // 删除 所有 children,以防止多次调用                 data.forEach(function (item) {                     delete item.children;                 });
                // 将数据存储为 以 id 为 KEY 的 map 索引数据列                 var map = {};                 data.forEach(function (item) {                     map[item.id] = item;                 });                 var val = [];                 data.forEach(function (item) {                     // 以当前遍历项的pid,去map对象中找到索引的id                     var parent = map[item.p_id];                     // 好绕啊,如果找到索引,那么说明此项不在顶级当中,那么需要把此项添加到,他对应的父级中                     if (parent) {                     (parent.children || (parent.children = [])).push(item);                     } else {                     //如果没有在map中找到对应的索引ID,那么直接把 当前的item添加到 val结果集中,作为顶级                     val.push(item);                     }                 });                 return val;                 },                 /**                  * 生成目录                  * */                 makeToc(){                 // 获取所有的h标签,给他们加上id,同时创建符合toTree方法要求的对象                 //{                 //          id:'',// 抛出id                 //           tag:'',// 抛出标签名称                 //          label:'',// 抛出标题                 //          p_id:'',// 抛出父级id                     // }                                 // 定义参与目录生成的标签                     const tocTags = ["H1","H2","H3","H4","H5","H6"];
                    // 目录树结果                     const tocArr = [];
                    // 获取所有标题标签                     const headDoms = Array.from(this.$refs.aContent.childNodes).filter(item => tocTags.includes(item.tagName));
                    // 遍历标题标签                     headDoms.forEach((item,index,arr) => {                         // 给标题添加id                         item.id = `h-${index + 1}`;                         // 获取当前节点前面的节点                         let prevs = arr.filter((i,j) => j < index);                         // 过滤前面的节点为合理节点                         // 如 h3节点前  只能为 h1 h2 h3                         prevs = prevs.filter(i => tocTags.filter((i,j) => j <= tocTags.findIndex(i => i == item.tagName)).includes(i.tagName));                         // 对前面的节点进行排序,距离自身节点近的排在前面                         // 如 div > p > span > img  当前为img                         // 常规获取节点为 [div,p,span,img]                         // 排序后获取节点为 [img,span,p,div]                         prevs = prevs.sort((a,b) => -(a.id.replace('h-','')) - b.id.replace('h-',''));                         // 查询距离自身节点最近的不同于当前标签的节点                         const prev = prevs.find(i => i.tagName != item.tagName);                         this.maxum = Math.max(this.maxum,index + 1)                         tocArr.push({                         id:index + 1,// 抛出id                         key:index,                         title:item.innerText,                         tag:item.tagName,// 抛出标签名称                         label:item.innerText,// 抛出标题                         p_id:item.tagName == "H1" || prev == null ? 0 : Number(prev.id.replace("h-",'')),// 抛出父级id                         })                     })
                    // 使用上述方法生成树 最后在el-tree的data中使用 tocData即可                     this.tocData = this.toTree(tocArr);                     console.log(this.tocData)                 },

                changeHeight(record){                     console("--------")                     let el  = this.$refs.scroll                     let cur = el.scrollTop  // el-scrollbar获取页面当前位置的属性,定位也是通过改变这个                     let flag = false                                         let step = 20                     // 由于有可能是向上滑,有可能向下滑,所以需要考虑移动的正负                     if (record < cur) {                     step = -step;                     flag = true                     }else if (record == cur) {                     return                     }
                    // 设置动画                     // 具体含义就是按照每8毫秒执行一次向上移动指定的步长,这样就是一个动画                     var timer = setInterval(()=>{                     if (flag) {                     //  说明小于0                         if (cur + step < record) {                         // 说明移动多了,这时候我们改变移动距离,直接移动到指定位置即可                         el.scrollTop = record                         clearInterval(timer)                         }else {                         el.scrollTop = cur + step                         }                     }else{                         if (cur + step > record) {                         // 说明移动多了,这时候我们改变移动距离,直接移动到指定位置即可                         el.scrollTop = record                         clearInterval(timer)                         }else {                         el.scrollTop = cur + step                         }                     }
                    cur += step                     },8)
                },                 // 父组件                 handleScroll(){                         // let  top = this.$refs.scroll.wrap.scrollTop                         let top  = document.getElementById("wordView").offsetTop                         let len = this.scrollTag.length -1                         for (let i = 0;i < len;i ++) {                         // 判断当前页面的位置 应该属于哪个标签,我这里是标签下的内容没有结束,目录都是这个标签高亮                             if (top >= this.scrollTag[i].height - 1 && top < this.scrollTag[i + 1].height) {                                 // -1 是我发现点击目录滑动的时候,会有<1 的误差,导致目录高亮不改变                                                                 //  让之变成                                 // this.$refs.detail.changeItem(i)  // 通过这个方法,改变目录标签高亮                                 this.selectedKeys = [i]                             }                         }                         // 特殊处理 第一个和最后一个                         if (top< this.scrollTag[0].height )  this.selectedKeys = [i]                         if (top >= this.scrollTag[len].height - 1 )  this.selectedKeys = [this.selectedKeys.length -1]                         console.log("this.selectedKeys",this.selectedKeys)
                 },


        }
    } </script> <style lang="less">
  .content {     width: 95%;     margin:0px auto;     border: 1px solid  black;     font-size: 20px;       }
  .header{     position: fixed;     top:0px;     left: 2.5%;     width: 95%;     height:100px;     border-bottom: 1px solid black;     display: flex;     justify-content: space-between;     align-items: center;     padding: 0 20px;     background-color:aliceblue;     .title{         font-size: 40px;         font-weight: bold;     }     .search{         /* width: 500px; */         height: 50px;         display: flex;         justify-content: space-around;         flex-direction: row;         .btn{             width: 20%;             height: 50px;             border-radius: 15%;             background-color: #1890ff;             color: #fff;
        }         .search-area{             width: 70%;             height: 50px;             margin: 0 10px;         }     }       }   .content-area{     width: 100%;     display: flex;     justify-content: space-between;     margin-top: 100px;     .left-Nav{         position: fixed;         top:100px;         left:2.5%;         width: 19%;                 padding:10px  30px;         .nav{             /* height: 100%;             position: relative; */         }     }     .right-content{         margin-left:20%;         width: 85%;         padding:10px  30px;         border-left: 1px solid black;         .word-wrap {             padding: 15px;             img {                 width: 80%;             }         }     }   }   h1{     font-size:36px;     height: 40px;     line-height: 40px;   }   h2{     font-size:28px;     height: 40px;     line-height: 40px;   }   img{     width: 80% !important;   }   .ant-menu{     font-size: 20px !important;   }   /* .word-wrap {     padding: 15px;     img {         width: 100%;     } } */ </styl

标签:el,vue,word,cur,item,step,let,跳转,id
From: https://www.cnblogs.com/yuejucai/p/17988602

相关文章

  • Vue 3高级响应式数据探秘:原理、用法详解与实战示例!
     在Vue3中,数据的变化通过响应式系统来实现,该系统基于ES6的Proxy对象。Proxy对象允许拦截并自定义操作,因此Vue可以通过代理对象来实现对数据的监听和触发相应的操作。以下是Vue3中监测数据改变的原理、使用方法和步骤的详细描述,以及一个实例代码:原理:Vue3的响应式系统基于P......
  • Vue中JSON文件神奇应用fetch、axios异步加载与模块导入全指南
     在Vue中使用JSON文件有多种方式,包括使用fetch方法加载JSON文件、使用axios库加载JSON文件,以及将JSON文件导入为模块。以下是详细描述和相应的示例代码:1.使用fetch方法加载JSON文件:步骤:创建一个JSON文件,例如 data.json://data.json{"name":"John","age":......
  • vue项目安装更新依赖时,npm install 卡住
    在代码安装或者更新依赖时,经常碰到npminstall以后很多种卡住的情况,记录部分1.版本不对可能电脑存在多个项目,多个node版本。安装使用nvm(node版本管理工具),1.安装,先把已安装的node下载掉https://nvm.uihtm.com/download.html(目前国内可下载)https:/......
  • 2024年1月Java项目开发指南10:vite+Vue3项目创建
    新建项目安装routernpminstallvue-router在src下新建目录router,在目录下新建index.js在index.js里面配置路由import{createRouter,createWebHistory}from'vue-router';//定义路由constroutes=[//在这里配置路由];//创建路由实例constrouter=......
  • vue2.x项目升级到2.7
    背景老代码库了,但是升级到v3的话成本比较大,准备先升级到2.7,用上compositon-api,后续再引入ts,慢慢改过来。改动点//package.json{..."vue":"^2.7.0",..."vue-template-compiler":"^2.6.10",//移除掉,用不上了"vue-loader":"^15.10.0&quo......
  • vue print.js 打印 此处打印不包含页面的页码 (打印方法二)
    <template><divclass="modalContainerprintAsset"ref="modalContainer"><divv-for="(items,index)intableDataPrint":key=indexstyle="page-break-after:always;zoom:1"ref="show......
  • 开发WordPress主题和插件,如果调试。
    来源:https://www.shanhubei.com/archives/11789.html开发WordPress主题和插件,如果调试。一、使用自带,设置一下:wp-config.php文件中添加一行代码以打开调试模式define('WP_DEBUG',true);//启用调试日志记录到/wp-content/debug.log文件define('WP_DEBUG_LOG',true......
  • uniapp-vue3,封装类似于axios的请求方法
    request.jsimport{rootUrl}from"@/config/app-config.js"importhandleCachefrom'@/utils/cache/cache.js';import{showToast}from"@/utils/vant"import{clearAccountInfo}from'@/utils/clear/clear';import......
  • nginx 如何强制跳转 https
    本项目nginx作为代理服务项目上线,客户说要加个安全证书,于是安全证书是加上了,可是htttp和https都能访问网站,客户要求不行必须强制用带有https的地址访问开整这是http和https都能访问的nginx.conf 关键配置  server{listen80;lis......
  • vue2 手写思维导图编辑器,支持图片和节点拖拽(2)
    弹框模块DigitalXmindDialog.vue<template><el-dialog:title="title"width="1200px"class="auth-dialog"top="5%":append-to-body="true":lock-scroll="false":c......