Vue implements switching and closing of multiple tab pages

1. Realize the effect

2. Implementation principle 

vuex, realizes the state management of the current active item, the current tab list, the translateX of the current tab, the current cache page, and the current route.

Save the data in vuex to sessionStorage to avoid page refresh loss, and clear the data when the browser is closed.

Through ref positioning, get the current window width and all the widths of the tab tags of the current route, judge the two, and realize the processing of multiple tabs exceeding the window width.

When the tab page is clicked, the corresponding activation item is obtained, and the selected state of the left menu bar is dynamically realized, and the watch is used to monitor, updateActiveName and updateOpened.

When the tab tag is closed, splice deletes the current tab, and if it is the last item deleted, jumps to the previous page of the item. When the length is 1, jump to the home page.

3. Main code

store.js

import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
    state: {
        catch_components: [],
        activePath: '/index',
        openNames: [],
        activeName: "",
        tranx: "-0",
        tabList: [
            { path: '/index', label: '首页', name: '首页' }
        ]
    },
    mutations: {
 
        //清空vuex数据
        clearTabs(state) {
            state.catch_components = []
            state.activePath = '/homepage'
            state.openNames = []
            state.activeName = ""
            state.tranx = "-0"
            state.tabList = [
                { path: '/homepage', label: '首页', name: 'home' }
            ]
        },
        // 跳转页面执行
        selectMenu(state, submenu) {
            var activePath = submenu.path
            var oldTabList = state.tabList
            var result = oldTabList.some(item => {
                if (item.path === activePath) {
                    return true
                }
            })
            if (!result) {
                oldTabList.push({
                    path: submenu.path,
                    name: submenu.name,
                    label: submenu.label,
                    index: submenu.index,
                    subName: submenu.subName
                })
            }
            state.activePath = activePath
            state.tabList = oldTabList
            state.activeName = submenu.subName + "-" + submenu.index
            state.openNames = [submenu.subName]
        },
        // 添加keepalive缓存
        addKeepAliveCache(state, val) {
            if (val === '/homepage') {
                return
            }
            if (state.catch_components.indexOf(val) === -1) {
                state.catch_components.push(val)
            }
        },
        // 删除keepalive缓存
        removeKeepAliveCache(state, val) {
            let cache = state.catch_components

            for (let i = 0; i < cache.length; i++) {
                if (cache[i] === val) {
                    cache.splice(i, 1);
                }
            }
            state.catch_components = cache
        },
        setTranx(state, val) {
            console.log(val)
            state.tranx = val
        },
        //关闭菜单
        closeTab(state, val) {
            state.activePath = val.activePath
            state.tabList = val.tabList
            state.openNames = val.openNames
            state.activeName = val.activeName
        },
        // 点击标签选择菜单
        changeMenu(state, val) {
            state.activePath = val.path
            state.activeName = val.subName + "-" + val.index
            state.openNames = [val.subName]
        }
    },
})

page code

computed: {
  ...mapState({
  activePath: (state) => state.activePath, // 已选中菜单
    tabList: (state) => state.tabList, // tags菜单列表
    catch_components: (state) => state.catch_components, // keepalive缓存
    openNames: (state) => state.openNames,
    activeName: (state) => state.activeName,
    tranx: (state) => state.tranx,
  }),
},
watch: {
   openNames() {
     this.$nextTick(() => {
       this.$refs.asideMenu.updateOpened();
     });
   },
   activeName() {
     this.$nextTick(() => {
       this.$refs.asideMenu.updateActiveName();
     });
   },
 },
handleClose(tab, index) {
  var oldOpenNames = this.$store.state.openNames,
    oldActiveName = this.$store.state.activeName,
    oldActivePath = this.$store.state.activePath,
    oldTabList = this.$store.state.tabList;
  let length = oldTabList.length - 1;
  for (let i = 0; i < oldTabList.length; i++) {
    let item = oldTabList[i];
    if (item.path === tab.path) {
      oldTabList.splice(i, 1);
    }
  }
  // 删除keepAlive缓存
  this.$store.commit("removeKeepAliveCache", tab.path);
  if (tab.path !== oldActivePath) {
    return;
  }
  if (length === 1) {
    this.$store.commit("closeTab", {
      activePath: "/index",
      tabList: oldTabList,
    });
    this.$router.push({ path: oldTabList[index - 1].path });
    return;
  }
  if (index === length) {
    oldActivePath = oldTabList[index - 1].path;
    oldOpenNames = [oldTabList[index - 1].subName];
    oldActiveName =
      oldTabList[index - 1].subName + "-" + oldTabList[index - 1].index;
    this.$store.commit("closeTab", {
      activePath: oldActivePath,
      tabList: oldTabList,
      openNames: oldOpenNames,
      activeName: oldActiveName,
    });
    this.$router.push({ path: oldTabList[index - 1].path });
  } else {
    oldActivePath = oldTabList[index].path;
    oldOpenNames = [oldTabList[index].subName];
    oldActiveName =
      oldTabList[index].subName + "-" + oldTabList[index].index;
    this.$store.commit("closeTab", {
      activePath: oldActivePath,
      tabList: oldTabList,
      openNames: oldOpenNames,
      activeName: oldActiveName,
    });
    this.$router.push({ path: oldTabList[index].path });
  }
  this.getTrans(2);
},
changeMenu(item) {
  var oldActivePath = this.$store.state.activePath;
  if (oldActivePath === item.path) {
    return;
  }
  this.$store.commit("changeMenu", item);
  this.$router.push({ path: item.path });
  this.$nextTick(() => {
    this.getTrans(0);
  });
},
selectMenu(item, i, subName) {
  // 加入keepalive缓存
  this.$store.commit("addKeepAliveCache", item.path);
  var submenu = {
    path: item.path,
    name: item.title,
    label: item.title,
    index: i,
    subName: subName,
  };
  this.$store.commit("selectMenu", submenu);
  this.$router.push({ path: item.path });
  this.$nextTick(() => {
    this.getTrans(0);
  });
},
getTrans(e) {
  let width = 0;
  if (this.$refs.tags) {
    width = this.$refs.tags.clientWidth;
  }
  this.tabList.map((item, index) => {
    if (item.path === this.activePath) {
      this.currentIndex = index;
    }
    if (this.$refs[`tag${index}`] && this.$refs[`tag${index}`][0]) {
      this.$set(
        this.tabList[index],
        "width",
        this.$refs[`tag${index}`][0].$el.clientWidth + 4
      );
    }
  });
  let list = this.tabList.filter((item, index) => {
    return index <= this.currentIndex;
  });
  let totalWidth = list.reduce((total, currentValue) => {
    return total + Number(currentValue.width);
  }, 0);
  let totalAllWidth = this.tabList.reduce((total, currentValue) => {
    return total + Number(currentValue.width);
  }, 0);

  if (e == 0) {
    if (Number(width) > Number(totalWidth) || Number(width) == 0) {
      this.setTranx(-0);
      return false;
    }
    this.setTranx(Number(width) - Number(totalWidth) - 60);
  } else if (e == 1) {
    if (Number(width) > Number(totalAllWidth)) {
      return false;
    }
    this.setTranx(Number(width) - Number(totalAllWidth) - 60);
  } else {
    if (
      Number(width) > Number(totalAllWidth) &&
      this.$store.state.tranx < 0
    ) {
      this.setTranx(-0);
    }
  }
},
setTranx(val) {
  this.$store.commit("setTranx", val);
},
<Menu
  ref="asideMenu"
  :active-name="activeName"
  :open-names="openNames"
  accordion
  theme="light"
  :style="{ width: 'auto' }"
  :class="isCollapsed ? 'collapsed-menu' : 'menu-item'"
>
  <MenuItem
    @click.native="selectMenu({ path: '/index', title: '首页' })"
    name="index"
    key="index"
  >
    <Icon type="ios-paw"></Icon>
    <span class="menuTitle">首页</span>
  </MenuItem>
  <Submenu
    v-for="(item, index) in menuMap"
    :name="index"
    :key="index"
    class="sub_title"
  >
    <template slot="title">
      <svg class="icon" aria-hidden="true" v-if="item.fonticon">
        <use :xlink:href="item.fonticon"></use>
      </svg>
      <Icon :type="item.icon" v-else />
      <span class="menuTitle">{
   
   { item.title }}</span>
    </template>
    <template v-if="item.children">
      <MenuItem
        v-for="(each, i) in item.children"
        :name="index + '-' + i"
        :key="index + '-' + i"
        @click.native="selectMenu(each, i, index)"
        ><span class="menuTitle">{
   
   { each.title }}</span>
      </MenuItem>
    </template>
  </Submenu>
</Menu>

<Row class="head-tags">
  <div class="head-left left" @click="setTranx(0)">
    <Icon type="ios-rewind" size="30" color="#ffc0cb" />
  </div>
  <div class="tags-box">
    <div
      ref="tags"
      class="tags-box-scroll"
      :style="{ transform: `translateX(${tranx}px)` }"
    >
      <Tag
        :ref="'tag' + index"
        class="tags-item"
        :class="{ 'tags-item-active': activePath === item.path }"
        v-for="(item, index) in tabList"
        :key="index"
        :name="item.path"
        :closable="item.path !== '/index'"
        @click.native="changeMenu(item)"
        @on-close="handleClose(item, index)"
        >{
   
   { item.label }}</Tag
      >
    </div>
  </div>
  <div class="head-left right" @click="getTrans(1)">
    <Icon type="ios-fastforward" size="30" color="#ffc0cb" />
  </div>
</Row>

Guess you like

Origin blog.csdn.net/aaa123aaasqw/article/details/130627600