【通俗易懂】vue实现tagsview标签导航栏切换菜单功能【详细注释,都能看的懂】

前言:

后台系统现在基本都要有tagsview标签这个功能了,很多人是看网上的模板vue-element-admin内的tagsview。我也看了网上的很多资料和这个模板,但是很可惜,我是个小白,看了半天没看懂,也不知道如何复制,如何修改。所以我只能自己慢慢写一个这个功能,通过两天时间不断试错尝试,总算是写出来了功能。我相信不只有我一个人看不懂,那我就写个详细的攻略给你们看看吧,希望帮助更多的小白少掉点头发。如果看完还是不懂的可以评论问我。

效果图

在这里插入图片描述

基于

实现基于:vue element-ul vuex 右键菜单插件

思路:

利用vuex来实现动态响应
1,点击菜单获取菜单的名字用commit传给vuex内保存
2,tagsview组件拿到名字渲染出来。
3,添加点击事件,点击标签跳转对应路由。
4,通过动态class来设置高亮效果
5,右键菜单通过插件来实现
6,给叉号加点击事件,点击后关闭当前标签。通过传参拿到当前项和index来实现

功能:目前是主要的功能,更多细节功能可以扩展

1,点击菜单增加一个对应名字的tagsview标签
2,点击tagsview标签可以跳转对应的菜单路由
3,tagsview标签会根据跳转的页面不同高亮显示
4,点击关闭按钮可以关闭tagsview标签
5,点击tagsview标签会对应展开菜单,并选中对应菜单
6,右键tagsview标签可以出现菜单,里面有关闭和关闭全部选项
7,点击右键菜单内关闭会关闭一个,点击关闭全部会把所有tagsview标签栏清空
8,关闭时,如果关闭的是最右边的则向左边tagsview标签跳转路由,如果不是则向右跳转。如果删除后没有tagsview标签了,那么自动跳转首页
9,所有tagsview标签关闭后,标签导航栏隐藏不显示。有tagsview标签时显示。

上代码:

创建一个tagsview.vue组件:把这个代码复制进去。
<template>
  <!-- calss添加了一个样式 -->
  <div  class="tagsbox">
    <div
      @contextmenu.prevent="openMenu(item,$event)"
      :class="isActive(item.url)?'active':''"
      class="tagsview"
      v-for="(item, index) in tags"
      :key="index"
      @click="tagsmenu(item)"
    >
      {{ item.name }}
      <!-- 这个地方一定要click加个stop阻止,不然会因为事件冒泡一直触发父元素的点击事件,无法跳转另一个路由 -->
      <span class="el-icon-close tagsicon" @click.stop="handleClose(item,index)"></span>
      <ul v-show="visible" class="contextmenu" :style="{left:left+'px',top:top+'px'}">
      <li @click.stop="handleClose(item,index)">关闭</li>
      <li @click.stop="cleartags($route.path)">关闭所有</li>
    </ul>
    </div>
  </div>
</template>

<script>
//这个就是导入vuex的数据,配合下面...map用
import { mapState, mapMutations } from "vuex";
export default {
  data() {
    return {
      //右键菜单隐藏对应布尔值
      visible: false,
      //右键菜单对应位置
      top: 0,
      left: 0
    }
  },
  computed: {
    //引入vuex中state中的tags数据,一样this调用就行
    ...mapState(["tags"]),
  },
  watch:{
    //监听右键菜单的值是否为true,如果是就创建全局监听点击事件,触发closeMenu事件隐藏菜单,如果是false就删除监听
    visible(value) {
          if (value) {
            document.body.addEventListener('click', this.closeMenu)
          } else {
            document.body.removeEventListener('click', this.closeMenu)
          }
        }
  },
  methods: {
    //引入vuex中mutation方法,可以直接this.xxx调用他
    ...mapMutations(["closeTab", "cleartagsview"]),
    //点击叉叉删除的事件
    handleClose(item, index) {
      //先把长度保存下来后面用来比较做判断条件
      let length = this.tags.length - 1;
      //vuex调方法,上面...map引入的vuex方法,不会这种方法的看vue官网文档
      this.closeTab(item);
      // 如果关闭的标签不是当前路由的话,就不跳转
      if (item.url !== this.$route.path) {
        return;
      }
      // 判断:如果index和length是一样的,那就代表都是一样的长度,就是最后一位,那就往左跳转一个
      if (index === length) {
        //再判断:如果length=0,也就是说你删完了所有标签
        if (length === 0) {
          //那么再判断:如果当前路由不等于index,也就是我首页的路由
          if (this.$route.path !== "/index") {
            //那么就跳转首页。这一步的意思是:如果删除的最后一个标签不是首页就统一跳转首页,如果你删除的最后一个标签是首页标签,已经在这个首页路由上了,你还跳个什么呢。这不重复操作了吗。
            this.$router.push({ path: "/index" });
          }
        } else {
          //那么,如果上面的条件都不成立,没有length=0.也就是说你还有好几个标签,并且你删除的是最后一位标签,那么就往左边挪一位跳转路由
          this.$router.push({ path: this.tags[index - 1].url });
        }
      } else {
        // 如果你点击不是最后一位标签,点的前面的,那就往右边跳转
        this.$router.push({ path: this.tags[index].url });
      }
    },
    //点击跳转路由
    tagsmenu(item) {
      //判断:当前路由不等于当前选中项的url,也就代表你点击的不是现在选中的标签,是另一个标签就跳转过去,如果你点击的是现在已经选中的标签就不用跳转了,因为你已经在这个路由了还跳什么呢。
      if (this.$route.path !== item.url) {
        //用path的跳转方法把当前项的url当作地址跳转。
        this.$router.push({ path: item.url });
      }
    },
    //通过判断路由一致返回布尔值添加class,添加高亮效果
    isActive(route) {
          return route === this.$route.path
        },
        //右键事件,显示右键菜单,并固定好位置。
        openMenu(tag, e) {
          this.visible = true
          this.selectedTag = tag
          const offsetLeft = this.$el.getBoundingClientRect().left 
          this.left = e.clientX - offsetLeft + 210  //右键菜单距离左边的距离
          this.top = e.clientY +10  //右键菜单距离上面的距离           这两个可以更改,看看自己的右键菜单在什么位置,自己调
        },
        //隐藏右键菜单
        closeMenu() {
          this.visible = false
        },
        //右键菜单关闭所有选项,触发vuex中的方法,把当前路由当参数传过去用于判断
        cleartags(val){
          this.cleartagsview(val)
        }
  },
};
</script>

<style lang="scss" scoped>
//标签导航样式
.tagsview {
  cursor: pointer;
  margin-left: 4px;
  height: 26px;
  line-height: 26px;
  padding: 0 8px;
  border: 1px solid #d8dce5;
  border-radius: 5px;
  color: #000;
  font-size: 12px;
  display: inline-block;
}
//叉号鼠标经过样式
.tagsicon:hover{
  color: #f56c6c;

}
//标签高亮
.active{
  background-color: #40ba84;
  color: #fff;
}
//右键菜单样式
.contextmenu {
      margin: 0;
      background: #fff;
      z-index: 100;
      position: absolute;
      list-style-type: none;
      padding: 5px 0;
      border-radius: 4px;
      font-size: 12px;
      font-weight: 400;
      color: #333;
      box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
      li {
        margin: 0;
        padding: 7px 16px;
        cursor: pointer;
        &:hover {
          background: #eee;
        }
      }}
</style>

菜单栏部分

给个点击事件,把你们循环的菜单每一项拿出来传给vuex保存,用来后面渲染标签
在这里插入图片描述
通过commit把每一项菜单的数据传给vuex,这个地方也可以用console.log看一下你的每一项内path和name之类的是什么,找一个跟你路由中一致的用于后续判断依据。这个每一项就是传进来的val,写法就是val.path这样,查看点击后他会返回什么。看看跟你的当前跳转的路由path是否一样,如果是一样的就可以用path来做判断。如果是name一样就用name判断。

methods:{
            //点击把菜单的名字传出去
            clickMenu(val){
                this.$store.commit("pushtags",val)
            }
        }
主组件的部分:注意,这个不要直接复制了,你看下我tagsview组件放的位置就行了,可以自己调整位置
<template>
  <div>
    <!-- elementul中布局组件 -->
    <el-container style="height: 100vh;">
      <!-- 左侧菜单部分 -->
      <el-aside :width="subwidth">
        <!-- 菜单组件 -->
          <nav-left></nav-left>
      </el-aside>
      <el-container>
        <!-- 头部部分 -->
        <el-header>
          <!-- 头部组件 -->
            <headers></headers>
        </el-header>
        <!-- 标签导航栏组件 -->
        <tagsview></tagsview>
        <!-- 内容部分 -->
        <el-main>
          <!--if判断路由元信息内keepAlive是否为true如果是就缓存,如果缓存显示缓存的,如果不缓存就用不缓存的路由视图 -->
          <keep-alive v-if="$route.meta.keepAlive">
              <router-view></router-view>
          </keep-alive>
            <router-view v-else></router-view>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>

<script>
//引入vuex中的state,不了解的参考vuex文档
import {mapState} from 'vuex'
//菜单组件
import navLeft from "@/components/navLeft"
//头部组件
import Headers from '../components/headers.vue';
//引入tagsview组件
import tagsview from "@/views/tagsview/tagsview.vue"
export default {
  data(){
    return{
      //菜单的宽度,默认给个200
        subwidth:'200px'
    }
  },
    components:{
      //菜单
        navLeft,
        //头部
        Headers,
        //引入tagsview组件
        tagsview
      },
      computed:{
        //引入vuex中state的变量,可以直接this.xxx调用到
        ...mapState(["isCollapse"]),
      },
      watch:{
        //监听vuex中的变量如果变动了就赋值,从而改变菜单栏缩小展开
        isCollapse(){
          if(this.isCollapse){
            this.subwidth='64px'
          }else{
            this.subwidth='200px'
          }
        }
      },
};
</script>

<style lang="scss" scoped>
//这些是布局组件内自带的,去elementul复制然后改改
.el-header, .el-footer {
    background-color: #fff;
    color: #333;
    line-height: 60px;
    box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
  }
  //左侧菜单栏样式
  .el-aside {
    background-color: #001529;
    color: #333;
    text-align: left;
    line-height: 56px;
    //下面四个是菜单折叠动画效果
    transition: width 0.15s;
  -webkit-transition: width 0.15s;
  -moz-transition: width 0.15s;
  -webkit-transition: width 0.15s;
  -o-transition: width 0.15s;
  }
  //内容区域
  .el-main {
    background-color: #E9EEF3;
    color: #333;
  }
  
  body > .el-container {
    margin-bottom: 40px;
  }
  
  .el-container:nth-child(5) .el-aside,
  .el-container:nth-child(6) .el-aside {
    line-height: 260px;
  }
  
  .el-container:nth-child(7) .el-aside {
    line-height: 320px;
  }
</style>
vuex部分:
import Vue from 'vue'
import Vuex from 'vuex'
import router from '../router/index.js'
Vue.use(Vuex)
export default new Vuex.Store({
  state: { 
    //tags数组
    tags:[],
    //tagsview标签显示隐藏
    isCollapse:false
  },
  mutations: {
    pushtags(state,val){
      //如果等于-1说明tabs不存在那么插入,否则什么都不做
      //findindex找角标,循环判断一下,如果等于那么就代表有相同的,就不必添加,如果找不到那就是-1.就添加
      let result = state.tags.findIndex(item => item.name === val.name)
      result === -1 ? state.tags.push(val) : ''
    },
    //关闭标签
    closeTab(state, val) {
      //同上,找角标,然后用角标的位置对应删除一位。splice:这是数组的删除方法
      let result = state.tags.findIndex(item => item.name === val.name)
      state.tags.splice(result, 1)
  },
  //关闭所有tagsview标签
    cleartagsview(state,val){
      //清空数组
      state.tags=[]
      //跳转到首页,val接受传过来的当前路由
      if(val !== "/index"){
        router.push({path:"/index"})
      }
    },
    //改变tagsview显示隐藏
    changeisshow(state){
      state.isCollapse=!state.isCollapse
    }
  },
  actions: {
  },
  modules: {
  }
})

右键菜单实现

第一步,安装组件

npm install vue-contextmenu --save

第二步:引入到main.js文件

import VueContextMenu from 'vue-contextmenu'
Vue.use(VueContextMenu)

第三步:使用,放在你想要弹出的地方,具体位置看我代码,放在了div上
contextmenu:这是弹出组件
prevent:这是修饰符,意思是右键的时候不要弹出默认的菜单,默认的菜单就是你们平常在电脑桌面上右键刷新的那个右键菜单。让他别出来。

@contextmenu.prevent="openMenu(item,$event)"

点击标签左侧菜单也选中对应的菜单,子菜单自动展开选中功能

在这里插入图片描述
这个功能我当时乍一想也觉得有点复杂,后来发现elementul自带一个属性一句话解决
没错就这一句话,加在el-menu上,他就会根据当前路由自动激活对应菜单。注意是menu不是menu-item。最外层的容器

:default-active="$route.path"

标签切换时自动高亮

是这一句话的作用,其实就一个逻辑,根据当前路由判断一下是不是一样,如果是一样的路由,那就给他加个class。这个active我已经css写好样式了。添加就亮不添加就不亮

:class="isActive(item.url)?'active':''"

右键关闭所有

首先右键关闭我就直接再调用了一次叉号的方法完事。
这里要说的是关闭所有的小注意项:
关闭所有很明显很简单就是把数组清空,这里注意清空要用【】不能用 " ",如果用 " "表示清空就会报错。
像这样:

 //关闭所有tagsview标签
    cleartagsview(state,val){
      //像这里一样后面加的是[]
      state.tags=[]
      //跳转到首页,val接受传过来的当前路由
      if(val !== "/index"){
        router.push({path:"/index"})
      }
    },

注意项:

1,样式可以改的,比如我用的div,你们可以用tag组件,或者tags组件或者自己写div,span之类的循环也行。这个自行更改

2,我是用的el-menu组件写的侧边栏菜单,所以我用它自带的属性开启路由模式了,所以url就是我的路由,我这个组件内有很多用path和url来对比判断的方法,因为我url和path是一致的,所以我可以这么用,你们用的时候一定要看下自己的路由信息哪个是一样的,有些人是name一样,那就换成name。这点很重要,不然不生效!!!

3,如果中途有问题,建议多用log看看,然后可以用alert弹框来测试一下是否执行某些地方。这样就很好更改了。

4,你的标签导航栏组件引入主组件结构内,放在全局,因为是所有页面都要用的,自己看看放什么位置好,自己选中,我是放在了页头的下面,有些人会放在内容部分的上面。随意

5,vuex使用会实时更新,但是刷新也会没有,如果你想要一直存在,就保存到本地去。

如果看了还是有不了解的可以评论提问
  • 58
    点赞
  • 257
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 51
    评论
vue2tagsview实现标签导航栏切换右侧点击菜单功能的一种常见方式是使用路由和动态组件。以下是一个简单的示例: 1. 首先,确保你已经安装了vue-router和vue2tagsview: ```bash npm install vue-router vue2tagsview ``` 2. 创建一个基本的Vue应用,并配置路由: ```javascript // main.js import Vue from 'vue'; import VueRouter from 'vue-router'; import Vue2TagsView from 'vue2tagsview'; import Home from './components/Home.vue'; import About from './components/About.vue'; import Contact from './components/Contact.vue'; Vue.use(VueRouter); Vue.use(Vue2TagsView); const routes = [ { path: '/', component: Home }, { path: '/about', component: About }, { path: '/contact', component: Contact }, ]; const router = new VueRouter({ routes, }); new Vue({ router, render: (h) => h(App), }).$mount('#app'); ``` 3. 在App.vue中使用vue2tagsview组件: ```vue <template> <div id="app"> <vue2-tags-view></vue2-tags-view> <router-view></router-view> </div> </template> <script> export default { name: 'App', }; </script> ``` 4. 创建你的页面组件(例如Home.vue、About.vue、Contact.vue): ```vue <template> <div> <h1>{{ title }}</h1> <p>{{ content }}</p> </div> </template> <script> export default { name: 'Home', data() { return { title: 'Home Page', content: 'Welcome to the home page!', }; }, }; </script> ``` ```vue <template> <div> <h1>{{ title }}</h1> <p>{{ content }}</p> </div> </template> <script> export default { name: 'About', data() { return { title: 'About Page', content: 'This is the about page!', }; }, }; </script> ``` ```vue <template> <div> <h1>{{ title }}</h1> <p>{{ content }}</p> </div> </template> <script> export default { name: 'Contact', data() { return { title: 'Contact Page', content: 'Feel free to contact us!', }; }, }; </script> ``` 现在,当你在标签导航栏中点击不同的标签时,右侧的内容会切换到对应的页面组件。这就是如何在vue2tagsview实现标签导航栏切换右侧点击菜单功能的基本步骤。你可以根据你的实际需求进行进一步的定制和扩展。
评论 51
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

接口写好了吗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值