跳转至

vue多标签页效果

1. 业务场景

vue框架实现的后台管理系统中添加多标签页;记录用户已经打开过的页面,方便页面的快速切换;

效果如图:

marker1

2. 任务

  1. 当用户点击左侧菜单项,切换页面时,右侧内容顶部出现标签页;
  2. 重复出现的标签页自动过滤;
  3. 点击标签页能快速跳转到相应页面;
  4. 可以关闭指定标签页;
  5. 刷新网页,标签页状态保持不变;

3. 实现任务

  • vuex定义路由数组记录浏览过的路由

     state: {
         visitedviews: [],//存放所有浏览过的且不重复的路由数据
     }
    
  • watch观察路由变化

    watch: {
        $route() {
          this.addViewTags();
        }
    }
    
  • 切换菜单,路由变化时将路由对象添加到vuex定义的路由数组中

    //路由改变时执行的方法
    addViewTags() {
        if (this.$route.name) {
            const route = this.$route;
            this.$store.dispatch("addVisitedViews", route);
        }
    }
    
  • 计算属性监听store存储标签数据变化

    computed: {
        visitedViews() {
            //store中取值,可以定义getter取值 this.$store.getters.visitedviews
            //return this.$store.state.tagsview.visitedviews;
            return this.$store.getters.visitedviews;
        }
    },
    
  • 关闭标签时删除vuex路由数组中路由对象

    //如果关闭的是当前打开的路由需要将路由改为数组最后一次push进去的路由 
    delSelectTag(route) {
        this.$store.dispatch("delVisitedViews", route).then(views => {
            //只有在关闭当前打开的标签页才会有影响
            if (this.isActive(route)) {
                let lastView = views.slice(-1)[0]; //选取路由数组中的最后一位
                if (lastView) {
                    this.$router.push(lastView);
                } else {
                    this.$router.replace("/");
                }
            }
        });
    }
    
  • vuex存储store具体实现:

store/modules/下新建tagsview.js

image-20200512200444295

const tagsview = {
    state: {
        visitedviews: [],//存放所有浏览过的且不重复的路由数据
    },
    mutations: {
        ADD_VISITED_VIEWS: (state, view) => {//打开新页签--添加路由数据的方法      
            if (state.visitedviews.some(v => v.path == view.path)) return;
            console.log('view ',view);
            state.visitedviews.push({
                name: view.name,
                path: view.path,
                title: view.meta.title || 'no-title'
            })
        },
        DEL_VISITED_VIEWS: (state, view) => {//关闭页签--删除路由数据的方法
            for (let [i, v] of state.visitedviews.entries()) {
                if (v.path == view.path) {
                    state.visitedviews.splice(i, 1)
                    break
                }
            }
        },

    },
    actions: {
        //调用这里去触发mutations,如何调用?在组件内使用this.$store.dispatch('action中对应名字', 参数)
        addVisitedViews({ commit }, view) {
            commit('ADD_VISITED_VIEWS', view)
        },

       //删除数组存放的路由之后,需要再去刷新路由,这是一个异步的过程,需要有回掉函数,所以使用并返回promise对象,也可以让组件在调用的时候接着使用.then的方法
        delVisitedViews({ commit,state }, view) {
            //commit('DEL_VISITED_VIEWS',view)
            return new Promise((resolve) => {//resolve方法:成功回调的方法
                commit('DEL_VISITED_VIEWS', view);
                resolve([...state.visitedviews]);
            })
        }
  }
}
export default tagsview
  • 封装标签页组件TagsView

  • 标签项由 router-link 组件实现

    <router-link :to="path">添加客户</router-link>
    
  • TagsView组件具体实现

    <template>
      <div class="tags-view-container">
        <router-link
          v-for="tag in Array.from(visitedViews)"
          :to="tag.path"
          :key="tag.path"
          class="tags-view-item"
          :class="isActive(tag)?'active':''"
        >
          <span class="el-icon-switch-button" v-show="isActive(tag)"></span>
          {{tag.title}}
          <span class="el-icon-close" @click.prevent.stop="delSelectTag(tag)"></span>
        </router-link>
      </div>
    </template>
    
    <script>
    export default {
      name: "TagsView",
      data() {
        return {
          active: "active"
        };
      },
      computed: {
        visitedViews() {
          //store中取值,可以定义getter取值 this.$store.getters.visitedviews
          return this.$store.state.tagsview.visitedviews;
          //return this.$store.getters.visitedviews;
        }
      },
      methods: {
        isActive(route) {
          return route.path == this.$route.path;
        },
        addViewTags() {
          //路由改变时执行的方法
          if (this.$route.name && this.$route.name != "index") {
            const route = this.$route;
            this.$store.dispatch("addVisitedViews", route);
          }
        },
        delSelectTag(route) {
          //先提交删除数据的方法,数组删除出掉数据后,如果关闭的是当前打开的路由需要将路由改为数组最后一次push进去的路由
          this.$store.dispatch("delVisitedViews", route).then(views => {
            if (this.isActive(route)) {
              //只有在关闭当前打开的标签页才会有影响
              let lastView = views.slice(-1)[0]; //选取路由数组中的最后一位
              if (lastView) {
                this.$router.push(lastView);
              } else {
                this.$router.replace("/index");
              }
            }
          });
        }
      },
      watch: {
        $route() {
          this.addViewTags();
        }
      }
    };
    </script>
    
    <style lang="css" scoped>
    .tags-view-container {
      margin: 10px 0;
      padding: 10px 15px;
      box-shadow: 0 4px 8px 0 rgba(7, 17, 27, 0.1);
      background-color: #fff;
    }
    
    .tags-view-item {
      margin-right: 10px;
      border: 1px solid rgb(224, 219, 219);
      background: rgb(243, 247, 243);
      padding: 3px 8px;
      display: inline-block;
      font-size: 14px;
      color: rgb(44, 43, 44);
    }
    
    .tags-view-item:hover {
      background: #40bb84;
      color: white;
    }
    
    .active {
      margin-right: 10px;
      border: 1px solid rgb(224, 219, 219);
      background: #40bb84;
      padding: 3px 8px;
      display: inline-block;
      color: white;
    }
    </style>
    
  • cookie存储tags标签数据

    /**
     * 持久存储封装 cookie
     * 
     */
    import Cookies from "js-cookie";
    const Tags_KEY = 'tags-key';
    /**
     * 保存存tags对象
     * @param {*} tags
     */
    const saveTags = (tags) => {
        Cookies.set(Tags_KEY, tags)
    }
    
    /**
     * 获取tags状态对象
     */
    const getTags = () => {
        const tagsStr = Cookies.get(Tags_KEY);
        if (tagsStr) {
            return JSON.parse(tagsStr);
        } else {
            return null;
        }
    }
    /**
     * 移除tags对象
     */
    const removeTags = () => {
        Cookies.remove(Tags_KEY);
    }
    
    export { saveTags, getTags, removeTags }
    

4. 得到的结果

  1. watch侦听器的应用
  2. 路由对象$route使用
  3. 组件封装
  4. vuex使用
  5. cookie存储标签页