vue菜单栏联动内容页面tab的实现示例

 更新时间:2024年01月19日 09:23:23   作者:coinisi_li  
本文主要介绍了vue菜单栏联动内容页面tab的实现示例,左侧菜单栏与右侧内容部分联动,当点击左侧的菜单,右侧会展示对应的tab,具有一定的参考价值,感兴趣的可以了解一下

一、需求

需要实现效果:左侧菜单栏与右侧内容部分联动,当点击左侧的菜单,右侧会展示对应的tab,没有点击时,不展示(如刚进入页面没有点击菜单,则没有tab);点击后没有关闭tab再打开其他菜单(如测试项目2),则测试项目2的tab高亮为选中状态。

实现效果:

二、整体实现思路

1.环境:vue、element-ui

2.首先,在el-tab-pane中,是展示的tab(如上图的测试项目1、测试项目2)。因此我们创建了一个数组activeTabs来储存tab的信息。

  • :label="getTabTitle(route.path)" 对应的则是tab展示的标题内容,我用了一个方法getTabTitle获取路由对应的标题。
  • :name="route.meta.title" 则是与el-tabs下的 v-model="activeName"相对应,如果name的值与v-model的值一样,则表示当前选中的tab。
  • 代码中,v-model="activeName" 用于控制 el-tabs 组件的当前活动标签,而 :name="route.meta.title" 用于为每个标签指定一个唯一的名称,这样就可以通过这个名称来确定当前选中的标签。通过这两个指令影响到同一个数据(例如 activeName 和 route.meta.title),以达到实现标签切换的效果。
  • 当前选中的状态由 @tab-click="selectTab" 事件处理器决定。当点击一个标签时(tab-click 事件),会触发 selectTab 方法。在 selectTab 方法中,this.activeName 属性根据点击的标签的 meta.title 进行更新。v-model="activeName" 会自动反映这个变化,使得 el-tabs 组件根据 activeName 的值来确定哪个标签是当前选中的,从而产生高亮效果。
  •  @edit="handleTabsEdit"是为了对tab做一些操作,点击 tabs 的新增按钮或 tab 被关闭后触发。

值得注意的是v-model="activeName"、selectTab、 :name="route.meta.title"几个的对应关系,以及其数据结构。

   <el-tabs
      v-model="activeName"
      editable
      @edit="handleTabsEdit"
      @tab-click="selectTab"
    >
      <el-tab-pane
        v-for="(route, index) in activeTabs"
        :key="index"
        :label="getTabTitle(route.path)"
        :name="route.meta.title"
      >
      </el-tab-pane>
    </el-tabs>

三、我遇到的问题

1.数据结构问题,一开始拿到的只是name或label、path,这样是不行的,最好还是拿到当前对应菜单栏的完整router,方便我们操作。

2.点击菜单栏对应的tab没有高亮但内容显示了

3.点击关闭tab,tab关闭了但下面的内容没有关闭,没有跳转到下一个剩余tab的内容。

4.点击tab之间相互切换,功能是正常的页面内容切换,但存在问题tab没有高亮,有时候要点击两次才会高亮,判段问题是出在没有更新调用selectTab。

四、具体代码

<template>
  <section class="app-main">
    <el-tabs
      v-model="activeName"
      editable
      @edit="handleTabsEdit"
      @tab-click="selectTab"
    >
      <el-tab-pane
        v-for="(route, index) in activeTabs"
        :key="index"
        :label="getTabTitle(route.path)"
        :name="route.meta.title"
      >
      </el-tab-pane>
    </el-tabs>
    <transition name="fade-transform" mode="out-in">  //展示具体页面内容
      <content   //自定义组件
      >
        <div class="router-inner-container">
          <router-view v-show="activeName" :key="key" /> //v-show="activeName"非常重要,注意不要用v-if,用来与当前tab对应,即关闭tab也关闭当前内容。
        </div>
      </content>
    </transition>
  </section>
</template>

<script>
import { mapGetters } from 'vuex'; //引入vuex,来拿到我的所有菜单路由

export default {
  name: 'AppMain',
  props: {
    noTag: {
      type: Boolean,
      default: false
    },
    noMap: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      activeName: null, // 默认选中第一个 tab
      activeTabs: [] // 存储当前显示的 tab
    };
  },
  computed: {
    ...mapGetters(['sidebar', 'sidebarRouters']), //菜单所有路由sidebarRouters
    key() {
      return this.$route.path;
    },
    isStatisticsView() {
      if (this.$route.path == '/home/index') {
        return true;
      } else {
        return false;
      }
    }
  },

  methods: {
    selectTab(tab, event) {

      if (Array.isArray(tab) && tab.length > 0) {  //由于数据结构问题做的判段,拿到activeName 
        this.activeName = tab[0].meta.title;
      } else if (typeof tab === 'object' && tab.meta) {
        this.activeName = tab.meta.title;
      } else if (Array.isArray(tab) && !tab.length > 0) {
        this.activeName = ''; //当所有tab都关闭时,关闭所有内容,否则页面会tab都关闭了,但还有最后一个关闭的tab的页面内容。
      }
    },
    handleTabsEdit(targetName, action) {
      // 处理标签页编辑事件
      if (action === 'remove') {
        // 删除标签页
        this.removeTab(targetName);
      }
    },
    removeTab(targetName) {
      const tabs = this.activeTabs;
      const index = tabs.findIndex((tab) => tab.meta.title === targetName);
      if (index !== -1) {
        tabs.splice(index, 1);
        this.selectTab(tabs, event); //很重要,更新activeName,否则删除后不会切换下一个对应tab也不会切换页面内容
      }
    },
    updateActiveTabs() {
      const currentPath = this.$route;
      // 判断对象是否在 activeTabs 中存在
      const existsInTabs = this.activeTabs.some(
        (tab) => tab.hasOwnProperty('path') && tab.path === currentPath.path
      );
      if (!existsInTabs) {
        // 如果当前路由不在 activeTabs 中,将其添加进去
        this.activeTabs.push(currentPath);
      }
      this.selectTab(currentPath, event); //很重要,更新activeName,否则切换菜单栏时对应的tab不会高亮不会切换
    },
    findMatchingRoute(routes, targetPath) {   //为了处理数组的
      for (const route of routes) {
        if (route.path === targetPath) {
          return route;
        }
        if (route.children && route.children.length > 0) {
          const matchingChild = this.findMatchingRoute(
            route.children,
            targetPath
          );
          if (matchingChild) {
            return matchingChild;
          }
        }
      }
      return null;
    },
    getTabTitle(route) {
      // 根据路由信息获取对应的标题
      const matchingRoute = this.findMatchingRoute(this.sidebarRouters, route);
      return matchingRoute ? matchingRoute.meta.title : '';
    },
    findMatchingMenu(routes, targetTitle) {
      // 递归查找匹配的菜单项
      for (const route of routes) {
        if (route.meta) {
          if (route.meta.title === targetTitle) {
            return route;
          }

          if (route.children && route.children.length > 0) {
            const matchingChild = this.findMatchingMenu(
              route.children,
              targetTitle
            );
            if (matchingChild) {
              return matchingChild;
            }
          }
        }
      }

      return null;
    }
  },
  mounted() {},
  watch: {
    $route(to, from) {
      if (to && to.meta && !to.meta.noMap) {
        this.$nextTick(() => {
          this.$refs.jmap.resizeMap();
        });
      }
      // 更新 activeTabs
      this.updateActiveTabs();
    },
    activeName: {
      immediate: true,
      deep: true,
      handler(val, oldVal) {
        if (val && val !== oldVal) {
          const matchingMenu = this.findMatchingMenu(this.sidebarRouters, val);
          if (matchingMenu) {
            this.$router.push({ path: matchingMenu.path });  //切换tab时,切换路由来对应想要页面内容
          }
        }
      }
    }
  }
};
</script>

<style lang="scss">
.app-main {
  /*50 = navbar  */
  height: calc(100vh - 50px);
  width: 100%;
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: #f3f3f3;
  &.no-header {
    height: 100vh;
  }
}
.fixed-header + .app-main {
  padding-top: 60px;
}

.single-layout {
  .app-main {
    padding: 0px;
  }
}

.map-style + .router-inner-container {
  position: absolute;
}
</style>

<style lang="scss">
// fix css style bug in open el-dialog
.el-popup-parent--hidden {
  .fixed-header {
    padding-right: 15px;
  }
}
.no-header {
  .float-panel {
    height: 100vh;
  }
}
</style>

到此这篇关于vue菜单栏联动内容页面tab的实现示例的文章就介绍到这了,更多相关vue菜单栏联动页面tab内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue中使用gojs/jointjs的示例代码

    vue中使用gojs/jointjs的示例代码

    这篇文章主要介绍了vue中使用gojs/jointjs的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • 详解Vue如何实现自定义动画与动画效果设计

    详解Vue如何实现自定义动画与动画效果设计

    在Vue中,动画效果是非常有用的,它可以使用户界面变得更加生动、有趣,本文中我们将学习如何在Vue中进行自定义动画与动画效果设计,感兴趣的可以了解一下
    2023-06-06
  • Vue组件实现卡片动画倒计时示例详解

    Vue组件实现卡片动画倒计时示例详解

    这篇文章主要介绍了Vue组件实现卡片动画倒计时示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • vue3中的响应式原理-effect

    vue3中的响应式原理-effect

    这篇文章主要介绍了vue3中的响应式原理-effect,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • VueX浏览器刷新如何实现保存数据

    VueX浏览器刷新如何实现保存数据

    这篇文章主要介绍了VueX浏览器刷新如何实现保存数据,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • vuex 第三方包实现数据持久化的方法

    vuex 第三方包实现数据持久化的方法

    本文主要介绍了vuex 第三方包实现数据持久化的方法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • validate 注册页的表单数据校验实现详解

    validate 注册页的表单数据校验实现详解

    这篇文章主要为大家介绍了validate 注册页的表单数据校验实现详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • vue实现在一个方法执行完后执行另一个方法的示例

    vue实现在一个方法执行完后执行另一个方法的示例

    今天小编就为大家分享一篇vue实现在一个方法执行完后执行另一个方法的示例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-08-08
  • Vue中$set()的使用方法场景分析

    Vue中$set()的使用方法场景分析

    由于 Vue 会在初始化实例时进行双向数据绑定,使用Object.defineProperty()对属性遍历添加 getter/setter 方法,所以属性必须在 data 对象上存在时才能进行上述过程 ,这样才能让它是响应的,这篇文章主要介绍了Vue中$set()的使用方法场景分析,需要的朋友可以参考下
    2023-02-02
  • Vue.js列表渲染绑定jQuery插件的正确姿势

    Vue.js列表渲染绑定jQuery插件的正确姿势

    这篇文章主要为大家详细介绍了Vue.js列表渲染绑定jQuery插件的正确姿势,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06

最新评论