vue tab标签实现并与菜单联动

主要思路,每点击一下侧边栏菜单(也就是访问URL),根据这个URL匹配的路由中meta属性中的自定义的isTabView属性,判断是否需要显示为TAB,是的话就增加这个TAB标签或者切换到这个TAB标签。

本TAB组件主要特点:

1.菜单联动,切换标签时菜单也会切换(其实是因为router.path)

2.标签数据都保存到sessionStorage,页面打开时也会根据sessionStorage恢复之前的标签

3.添加功能左移动,右移动(当标签书过多时),关闭当前,关闭所有

还有个是标签:首页(/control)是一直保留的,可以自行修改

第一步肯定是拦截到这个访问URL的事件,每次访问都要调用addOpendPage方法

router.afterEach((to, from, next) => {
  let param = {app: router.app, name: to.name, params: to.params, query: to.query, meta: to.meta, path: to.path}
  store.dispatch('addOpendPage', param)
  NProgress.done() // 结束Progress
})

然后直接放核心代码,也是使用vuex的,在store目录下的modules目录,保存为tabview.js


import * as utils from '@/utils'
import router from "../../router"

const tabview = {
  state: {
    pageOpendList: []
  },
  mutations: {
    /**
     * 初始化设置tab 一般默认首页,页面加载时调用
     * @method tabOpendListInit
     */
    setOpenedList(state) {
      const local = sessionStorage.pageOpendList && JSON.parse(sessionStorage.pageOpendList).length > 0
      if (local) {
        state.pageOpendList = JSON.parse(sessionStorage.pageOpendList)
      }
      // var exist = false;
      // for (var k in state.pageOpendList) {
      //   if (state.pageOpendList[k].path == '/control')
      //     exist = true;
      // }
      //默认添加首页
      if (!utils.objArrayContains(state.pageOpendList,'path','/control')) {
        state.pageOpendList.splice(0, 0, {
          meta: {title: "首页", isTabView: true},
          name: "control",
          params: {},
          path: "/control",
          query: {}
        })
        sessionStorage.pageOpendList = JSON.stringify(state.pageOpendList)
      }
    },
    /*
     * @描述: 关闭所有标签
     * @参数注释:
     * @param state
     * @返回值:
     * @创建人:  LoneKing
     * @创建时间:  16:04 2019/7/26
     */
    closeAllOpenedList(state) {
      sessionStorage.pageOpendList = ''
      state.pageOpendList = []
      router.push({
        name: "control"
      })
    },
    /**
     * 每次打开页面都会经过此方法,用于合并参数
     * @method setPageOpendList
     */
    setPageOpendList(state, res) {
      const {index, query, params, meta, path} = res
      let opendPage = state.pageOpendList[index]
      if (params) {
        opendPage.params = params
      }
      if (query) {
        opendPage.query = query
      }
      if (meta) {
        opendPage.meta = meta
      }
      if (path) {
        opendPage.path = path
      }
      state.pageOpendList.splice(index, 1, opendPage)
      sessionStorage.pageOpendList = JSON.stringify(state.pageOpendList)
    },
    increateTag(state, tag) {
      const local = sessionStorage.pageOpendList && JSON.parse(sessionStorage.pageOpendList).length > 0
      if (local) {
        state.pageOpendList = JSON.parse(sessionStorage.pageOpendList)
      }

      if (!utils.objArrayContains(state.pageOpendList,'path',tag.path)) {
        state.pageOpendList.push(tag)
        sessionStorage.pageOpendList = JSON.stringify(state.pageOpendList)
      }
    },
    /**
     * @param {*} state
     * @param {当前页签信息} obj
     * @param { 当前实例 } obj.vm
     * @param { 路由name} obj.name
     */
    closeOpendList(state, name) {
      const lists = state.pageOpendList
      for (let i = 0; i < lists.length; i++) {
        if (lists[i].name === name) {
          const lastName = state.pageOpendList[i - 1].name
          state.pageOpendList.splice(i, 1)
          sessionStorage.setItem('pageOpendList', JSON.stringify(state.pageOpendList))
          router.push({
            name: lastName
          })
        }
      }
    },
    /**
     * @param {*} state
     * @param {当前页签信息} obj
     * @param { 当前实例 } obj.vm
     * @param { 路由name} obj.name
     */
    closeOtherOpendList(state, name) {
      const lists = state.pageOpendList
      for (let i = 0; i < lists.length; i++) {
        if (lists[i].name === name) {
          state.pageOpendList = []
          state.pageOpendList.push(lists[i])
          //默认添加首页
          if (!utils.objArrayContains(state.pageOpendList,'path','/control')) {
            state.pageOpendList.splice(0, 0, {
              meta: {title: "首页", isTabView: true},
              name: "control",
              params: {},
              path: "/control",
              query: {}
            })
            sessionStorage.pageOpendList = JSON.stringify(state.pageOpendList)
          }
          break
        }
      }
    }
  },
  actions: {
    /**
     * @method addOpendPage
     * @param vm 当前实例
     * @param name 当前路由name
     * @param query 查询参数
     * @param param 查询参数
     * 一般放在router BeforeAfter(BeforeEach) 执行
     */
    addOpendPage: ({commit, state}, param) => {
      let {vm, name, params = '', query = '', meta = '', path = ''} = param
      let pageOpendList = state.pageOpendList
      let opendLen = pageOpendList.length
      let i = 0
      let tagHasOpened = false
      if (opendLen > 0) {
        for (; i < opendLen; i++) {
          //本来用的是name 但是viewList会出现重复情况 于是改成path
          if (pageOpendList[i].path === path) {
            commit('setPageOpendList', {
              index: i,
              params,
              query,
              meta,
              path,
            })
            tagHasOpened = true
            break
          }
        }
      }
      /**
       * 注入参数 如果tab未打开
       */
      if (!tagHasOpened && name) {
        let tag = {
          name: name
        }
        if (params) {
          tag.params = params
        }
        if (query) {
          tag.query = query
        }
        if (meta && meta.isTabView) {
          tag.meta = meta
        } else if (meta && !meta.isTabView) {
          return
        }
        if (path) {
          tag.path = path
        }
        commit('increateTag', tag)
      }
    }
  }
}

export default tabview

getter.js只需要这一个属性就行

const getters = {
  pageOpendList: state => state.tabview.pageOpendList
}
export default getters

TabView组件代码,保存为TabView.js 然后直接import,调用就行

<template>
    <div class="tag-view-wrap">
        <el-button @click="moveLeft"  class="left left-button" icon="el-icon-d-arrow-left" circle
                   style="padding:0;" type="default"></el-button>
        <div class="tabs-wrap" id="tabs-wrap">
            <div :style="{marginLeft:tabMarginLeft+'px'}" class="tabs breadcrumb-move">
                <el-tag
                        :class="{ active: item.path === $route.path }"
                        :closable="item.name !== 'control'"
                        :key="item.id"
                        @click.native="jump(item)"
                        @close="close(item)"
                        class="tag-view"
                        v-for="item in pageOpendList">
                    {{menuTitle[item.path]}}
                </el-tag>
            </div>
        </div>
        <el-dropdown class="right close-button" trigger="click">
            <el-button size="mini" type="default">
                操作<i class="el-icon-arrow-down el-icon--right"></i>
            </el-button>
            <el-dropdown-menu slot="dropdown">
                <el-dropdown-item @click.native="closeOther">关闭其他</el-dropdown-item>
                <el-dropdown-item @click.native="closeAll">关闭所有</el-dropdown-item>
                <el-dropdown-item @click.native="clearMenuCache">清除菜单缓存</el-dropdown-item>
                <el-dropdown-item @click.native="clearTabCache">清除Tab缓存</el-dropdown-item>
            </el-dropdown-menu>
        </el-dropdown>
        <el-button @click="moveRight" circle class="right right-button" icon="el-icon-d-arrow-right"
                   type="default"></el-button>
    </div>
</template>

<script>
    import {mapGetters} from 'vuex'

    export default {
        data() {
            return {
                tabMarginLeft: 0
            }
        },
        computed: {
            ...mapGetters([
                'pageOpendList',
                'menuTitle',
            ]),
        },
        watch: {
            //自动调整tab位置滚动
            pageOpendList: {
                handler: function (newVal, oldVal) {
                    let tabs = document.getElementsByClassName("tag-view")
                    let wrapWidth = document.getElementById("tabs-wrap").offsetWidth
                    let currentActiveTabWidth = 0
                    for (let index = 0; index < newVal.length; index++) {
                        //+4是因为每个tab之间margin-left 4
                        currentActiveTabWidth += tabs[index].offsetWidth + 4
                        if (this.$route.path === newVal[index].path) {
                            log(this.$route.path)
                            break
                        }
                    }
                    if (currentActiveTabWidth > wrapWidth) {
                        this.moveRight()
                    } else {
                        this.moveLeft()
                    }
                },
                deep: true
            }
        },
        beforeCreate() {
            this.$store.commit('setOpenedList')
        },
        methods: {
            clearMenuCache() {
                sessionStorage.menus = ''
                window.location.reload()
            },
            clearTabCache() {
                sessionStorage.pageOpendList = ''
                window.location.reload()
            },
            moveLeft() {
                this.tabMarginLeft = 0
            },
            moveRight() {
                let totalWidth = this.getTabsWidth()
                let wrapWidth = document.getElementById("tabs-wrap").offsetWidth
                if (totalWidth > wrapWidth) {
                    this.tabMarginLeft = wrapWidth - totalWidth - 180
                }
            },
            getTabsWidth() {
                let totalWidth = 0
                let tabs = document.getElementsByClassName("tag-view")
                for (let i = 0; i < tabs.length; i++) {
                    //+4是因为每个tab之间margin-left 4
                    totalWidth += tabs[i].offsetWidth + 4
                }
                return totalWidth
            },
            closeOther() {
                this.$store.commit("closeOtherOpendList", this.$route.name)
            },
            closeAll() {
                this.$store.commit("closeAllOpenedList")
            },
            jump(item) {
                const {params, query} = item
                /**
                 * @description
                 * 下面四种情况考虑到参数传递的问题,所以单独处理
                 */
                if (params) {
                    this.$router.push({
                        name: item.name,
                        params: params
                    })
                    return
                }
                if (query) {
                    this.$router.push({
                        name: item.name,
                        query: query
                    })
                    return
                }
                if (query && params) {
                    this.$router.push({
                        name: item.name,
                        params: params,
                        query: query
                    })
                    return
                }
                this.$router.push({
                    name: item.name
                })
            },
            close(item) {
                this.$store.commit("closeOpendList", item)
            }
        }
    }
</script>

<style lang="scss">

    .tag-view-wrap {
        border-bottom: 1px solid rgb(230, 230, 230);
        width: 90%;
        display: inline-block;
        white-space: nowrap;
        overflow: hidden;

        .tabs-wrap {
            width: 88%;
            float: left;
            overflow: hidden;
        }

        .left-button {
            position: relative;
            top: 12px;
            width: 28px;
            height: 28px;
            padding: 0px !important;
        }

        .right-button {
            position: relative;
            top: 12px;
            width: 28px;
            height: 28px;
            padding: 0px !important;
        }

        .close-button {
            padding-left: 3px;
        }

        .tabs {
            width: 100%;
            white-space: nowrap;
            .tag-view {
                cursor: pointer;
                margin: 0 4px;

                &.active {
                    background-color: #e63979;
                    color: #fff;

                    .el-tag__close {
                        color: #fff;
                    }
                }
            }
        }
    }
</style>

相关代码打包

[reply2down]http://down.lkcloud.top/BlogFile/tabview.zip[/reply2down]

评论

  1. 王达人
    Windows Chrome 96.0.4664.110
    7月前
    2022-3-15 17:11:26

    你没设置缓存?每次点击都重新加载,还没完全

  2. 111
    Windows Chrome 78.0.3904.108
    2年前
    2020-10-20 15:18:38

    想试一试

  3. 111
    Windows Chrome 78.0.3904.108
    2年前
    2020-10-20 15:18:06

    我来试试

  4. 想要学习得人
    Windows Edge 17.17134
    2年前
    2020-8-18 10:01:24

    试一下,看能不能行

  5. le
    Windows Chrome 84.0.4147.125
    2年前
    2020-8-14 19:25:25

    试试

  6. 小朋友
    Windows Chrome 84.0.4147.105
    2年前
    2020-8-12 16:08:03

    试一下

  7. 好东西
    Macintosh Chrome 73.0.3683.86
    2年前
    2020-7-23 16:00:06

    好东西,试一试

  8. Macintosh Chrome 83.0.4103.116
    2年前
    2020-7-20 19:26:09

    优秀

  9. Macintosh Chrome 83.0.4103.116
    2年前
    2020-7-20 19:25:16

    好东西

  10. liangzq
    Windows Chrome 78.0.3904.108
    2年前
    2020-7-20 13:42:27

    感谢

  11. nan
    Windows Chrome 83.0.4103.116
    2年前
    2020-7-14 15:26:31

    好东西,试一试,感谢分享

  12. 匿名
    Windows Chrome 83.0.4103.61
    2年前
    2020-6-30 16:41:47

    楼主写的真不错,有很大的帮助

  13. 弑神之影风
    Windows Chrome 83.0.4103.61
    2年前
    2020-6-30 15:24:53

    谢谢分享

  14. 娜娜
    Windows Chrome 83.0.4103.116
    2年前
    2020-6-27 14:45:07

    谢谢

  15. 娜娜
    Windows Chrome 83.0.4103.116
    2年前
    2020-6-27 14:41:06

    谢谢分享,万分感谢

  16. 1234
    Windows Chrome 77.0.3865.120
    2年前
    2020-6-22 14:15:52

    先试一下啊

  17. 凯西
    Windows Chrome 83.0.4103.106
    2年前
    2020-6-19 19:04:28

    谢谢分享

  18. MrXue
    Windows Chrome 83.0.4103.97
    2年前
    2020-6-10 17:29:24

    先试一试

  19. loneking
    博主
    Windows Chrome 80.0.3987.149
    2年前
    2020-6-05 20:35:32

    谢谢分享了。

  20. coder
    Windows Chrome 83.0.4103.61
    2年前
    2020-6-05 9:40:03

    谢谢分享

  21. Windows Chrome 81.0.4044.138
    2年前
    2020-6-03 18:37:26

    先试一试,感谢

  22. shamy
    Windows Chrome 75.0.3770.80
    2年前
    2020-6-01 16:29:43

    谢谢分享

  23. shamy
    Windows Chrome 75.0.3770.80
    2年前
    2020-6-01 16:29:16

    感谢分享

  24. Killing
    Windows Chrome 81.0.4044.122
    2年前
    2020-5-13 9:29:13

    试下,感谢

  25. 匿名
    Windows MSIE 11.0
    2年前
    2020-5-04 13:09:31

    谢谢博主分享了

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇