<template> <div class="recent_pages"> <h4><i class="iconfont icon-recent"></i> <!-- <ContextMenu>最近访问:</ContextMenu> --> 最近访问: </h4> <div class="history_buttons"> <div class="btn_left" @click="onLeft" :class="{'disabled' : startIndex <= 0 }"><i class="iconfont icon-left"></i></div> <div class="list_box" ref="box"> <div class="list" :style="btnListStyle" ref="btnlist"> <div class="unit" :class="isActive(item) ? 'active' : ''" v-for="item,index in recentPages" ref="unit"> <p class="btn_main" @click="onClickItem(item)" :key="item.path" :style="{width:item.title.length*14+'px'}"> {{item.title}} </p> <el-tooltip effect="light" content="记住" placement="bottom"> <div class="btn_pin" :class="isPin[index] ? 'active' :'unactive'" @click="onPin(item,index)"> <i></i> </div> </el-tooltip> <!-- <p class=" btn_pin" :class="{active:item.isPinned}" @click="onPin(item)"><i></i></p> --> <i class="btn_close iconfont icon-guanbi" @click="onRemoveItem(item)"></i> </div> </div> </div> <div class="btn_right" @click="onRight" :class="{ 'disabled': lastIndex == -1 || lastIndex >= widthArr.length - 1 }"><i class="iconfont icon-right"></i></div> </div> </div> </template> <script> import { mapMutations, mapState } from 'vuex'; import ContextMenu from './ContextMenu.vue'; export default{ data(){ return { posiLeft:0, //按钮列表绝对定位的数值 widthBox:0, widthTotal:0, lastIndex:-1, //当前能显示的最后一个元素的索引 startIndex:-1, //当前能显示的第一个元素的索引 widthArr:[], currentIndex : -1, isPin: [] } }, components:{ ContextMenu }, computed:{ ...mapState("menu", ["recentPages"]), btnListStyle(){ return { left : `${this.posiLeft}px` }; } }, watch:{ recentPages:{ handler(newV){ this.$nextTick(()=>{ // console.log("触发了Precentage的watch") this.widthArr = []; this.widthBox = this.$refs["box"].offsetWidth; this.widthTotal = this.$refs["btnlist"].offsetWidth; let temp = 0; this.isPin = newV.map(x=>x.isPinned) newV.forEach((item,index)=>{ let unitW = this.$refs["unit"][index].offsetWidth; temp += unitW; this.widthArr[index] = temp; if(temp >= this.widthBox && this.lastIndex == -1){ this.lastIndex = index; } if(item.active){ this.currentIndex = index; } // console.log(this.currentIndex, "currentIndexcurrentIndex") // console.log(this.startIndex, "startIndexstartIndex") // console.log(this.lastIndex, "lastIndexlastIndex") }) if(this.widthTotal > this.widthBox){ if (this.currentIndex >= this.lastIndex) { this.moveByRight(this.currentIndex); } else if (this.currentIndex <= this.startIndex) { this.moveByLeft(this.currentIndex); } } }) }, immediate:true, deep:true, }, $route:{ handler(newV){ this.addRecent({ title : newV.meta.title, path : newV.path, query : newV.query }); }, immediate : true } }, mounted(){ }, methods:{ ...mapMutations("menu", ["addRecent", "removeRecent","pinRecent"]), onClickItem(item){ if (this.$route.path != item.path){ if (Object.keys(item.query).length > 0){ this.$router.push({ path: item.path, query: item.query }); } else { this.$router.push(item.path); } } }, onPin(item,index){ this.isPin[index] = (!item.isPinned) this.isPin = _.cloneDeep(this.isPin) this.pinRecent(item); }, onRemoveItem(item){ this.removeRecent(item); if(this.$route.path == item.path){ if(this.recentPages.length){ let route = this.recentPages[this.recentPages.length-1] if (Object.keys(route.query).length > 0){ this.$router.push({ path: route.path, query: route.query }); } else { this.$router.push(route.path); } }else{ this.$router.push("/") } } }, isActive(item) { return this.$route.path == item.path; }, onLeft(){ this.moveByLeft(); }, onRight(){ if (this.widthTotal > this.widthBox) { this.moveByRight(); } }, moveByLeft(targetIndex){ // console.log("左侧动", this.startIndex, targetIndex); if (this.startIndex <= 0) { return } if (targetIndex != undefined) { this.startIndex = targetIndex; } else { this.startIndex -= 3; } if (this.startIndex < 0) { this.startIndex = 0; } if(this.startIndex == 0){ this.posiLeft = 0; }else{ this.posiLeft = this.widthArr[this.startIndex-1] * -1; } this.calcLastIndex(); // console.log(this.posiLeft , "posiLeft++"); }, moveByRight(targetIndex){ // console.log("右侧动", this.lastIndex, targetIndex); if(this.lastIndex >= this.widthArr.length - 1){ return } if(targetIndex!==undefined){ this.lastIndex = targetIndex; }else{ this.lastIndex += 3; } if(this.lastIndex > this.widthArr.length-1){ this.lastIndex = this.widthArr.length - 1; } // console.log(targetIndex, this.widthArr.length , "+++========") this.posiLeft = this.widthBox - this.widthArr[this.lastIndex]; this.calcStartIndex(); }, calcStartIndex(){ try{ this.widthArr.forEach((item,index)=>{ if(item > -1*this.posiLeft){ this.startIndex = index; throw new Error('找到startIndex,退出循环') } }) }catch(e){ // console.log(e); } }, calcLastIndex(){ try{ this.widthArr.forEach((item, index) => { if (item > (this.widthBox - this.posiLeft)) { //posiLeft是个负值 this.lastIndex = index; // console.log(this.lastIndex , "this.lastIndex lastIndex lastIndex lastIndex") throw new Error('找到lastIndex,退出循环') } }) }catch(e){ // console.log(e); } } } } </script> <style lang="scss" scoped> .recent_pages{ display: flex; flex-direction: row; align-items:stretch; padding: 0 20px; h4{ width: 110px; height: 32px; line-height: 32px; text-align: center; margin: 0; padding: 0; font-weight: bold; color: #3de8ff; display: flex; align-items: center; justify-content: center; .iconfont{ margin-right: 3px;} } .history_buttons { flex: 1; display: flex; flex-direction: row; .btn_left, .btn_right{ width: 26px; height: 30px; background-color: #048145;background-image: linear-gradient(180deg, #005c79 0%, #009bcc 100%); border:1px solid #009bcc; border-radius: 4px; display: flex; justify-content: center; align-items: center; &.disabled{ background-image: linear-gradient(0deg, #999 0%, #777 100%); border:1px solid #999; color: #ccc;} } .btn_left{ margin-right: 4px;} .btn_right{ margin-left: 4px;} .list_box{ flex: 1; position: relative; width: 0; overflow: hidden; margin:0; .list{ position: absolute; left: 0; top:0; display: flex; flex-direction: row; transition: all ease-in-out 0.5s; .unit { position: relative; height: 30px; padding: 0 3px; .btn_main { cursor: pointer; background-image: linear-gradient(180deg, #005c79 0%, #009bcc 100%); border:1px solid #009bcc; border-radius: 4px; height: 28px; text-align: center; line-height: 28px; font-weight: bold; font-size: 14px; box-sizing: content-box; padding: 0 18px 0 12px; word-break: keep-all; white-space: nowrap; } &.active { .btn_main { background-image: linear-gradient(180deg,#048145 0%, #078b58 100%); border:1px solid #03a9b1; } } .btn_pin{ cursor:context-menu; position: absolute; left:0; bottom:0; width:16px; height: 10px; display: flex; align-items: center; justify-content: center; i{ display: none; width:10px; height: 10px; border-radius: 3px; } &.active i{ display: block; background-color: #FA0; } } &:hover .btn_pin.unactive i { display: block; background-color: #999; } .btn_close { position: absolute; right: 6px; top:8px; font-size: 12px; color: #3de8ff; } } } } } } </style>