vue:菜单栏联动内容页面tab

news2024/9/28 9:30:13

一、需求

需要实现效果:左侧菜单栏与右侧内容部分联动,当点击左侧的菜单,右侧会展示对应的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" 用于为每个标签指定一个唯一的名称,这样就可以通过这个名称来确定当前选中的标签。通过这两个指令影响到同一个数据(例如 activeNameroute.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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1396554.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java 设计者模式以及与Spring关系(一)单例和建造者模式

简介: 本文是个系列一次会出两个设计者模式作用&#xff0c;如果有关联就三个&#xff0c;除此外还会讲解在spring中作用。 23设计者模式以及重点模式 我们都知道设计者模式有3类23种设计模式&#xff0c;标红是特别重要的设计者模式建议都会&#xff0c;而且熟读于心。标蓝是…

计算机毕业设计 基于SpringBoot的红色革命文物征集管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

C#,因数分解(质因子分解)Pollard‘s Rho算法的源代码

因数分解&#xff08;也称为质因子分解&#xff09;&#xff1a;将一个大整数分解它的质因子之乘积的算法。 Pollard Rho算法的基本思路&#xff1a;先判断当前数是否是素数&#xff08;质数&#xff09;&#xff0c;如果是&#xff0c;则直接返回。如果不是&#xff0c;继续找…

ubuntu源码安装MySQL

mysql下载路径 创建新数组 mysql sudo groupadd mysql# 创建用户 mysql ,指定属组为 mysql&#xff0c;禁止其登录 # --no-create-home选项&#xff0c;创建用户时不会自动创建主目录 sudo adduser --system --no-create-home --ingroup mysql --shell /sbin/nologin mysql创…

#Pytorch 使用DDP训练第一轮,验证后第二轮卡住

问题&#xff1a;在使用DDP分布式训练的时候&#xff0c;在第一轮训练后验证结果&#xff0c;在第二轮开始时就卡住了。因为设置了dist.barrier()&#xff0c;所以只有第一个GPU跑了验证&#xff0c;在第二轮时只有第一个GPU的模型在&#xff0c;其他卡的模型都被阻塞住了。 解…

NOIP2011提高组day1 - T3:Mayan游戏(玛雅游戏)

题目链接 [NOIP2011 提高组] Mayan 游戏 题目描述 Mayan puzzle 是最近流行起来的一个游戏。游戏界面是一个 7 7 7 行 5 \times5 5 列的棋盘&#xff0c;上面堆放着一些方块&#xff0c;方块不能悬空堆放&#xff0c;即方块必须放在最下面一行&#xff0c;或者放在其他方块…

bug笔记:解决 HTTP Error 500.30 - ASP.NET Core app failed to start

总结下后端部署windos iis环境net6版本&#xff0c;500.30问题报错的一种解决方案&#xff1a; 一、问题描述 二、解决方案 检查下是否安装了net6对应的环境&#xff0c;是否已经安装 然后在事件管理器>Windows日志>应用程序&#xff0c;里面查看详细异常记录 在iis下面…

机器学习平台建设(六)

四、OpenPAI 前文介绍了机器学习平台的功能以及建设机器学习平台要考虑的因素。本节会介绍OpenPAI&#xff0c;即微软的开源机器学习平台。它可用于企业私有部署&#xff0c;也可部署在云平台中。它解决了建模训练时的算力和资源管理的问题。OpenPAI的开发很活跃&#xff0c;问…

工业设备管理系统:助力企业实现数字化转型

随着工业4.0和智能制造的快速发展&#xff0c;数字化转型已成为企业提升竞争力、适应市场变化的必然选择。工业设备管理系统作为数字化转型的关键组成部分&#xff0c;能够为企业提供实时监控、数据分析、预警和远程控制等功能&#xff0c;助力企业实现数字化转型的目标。 一、…

【办公类-21-02】20240118育婴员操作题word打印2.0

作品展示 把12页一套的操作题批量制作10份&#xff0c;便于打印 背景需求 将昨天整理的育婴师操作题共享&#xff0c; 因为题目里面有大量的红蓝颜色文字&#xff0c;中大班办公室都是黑白单面手动翻页打印。只有我待的教务室办公室有彩色打印机打印&#xff08;可以自动双面…

【GPU调用及CUDA安装 看完全会!】使用gpu进行各类训练/运行代码

检查 是否有GPU 打开任务管理器&#xff0c;我这边显示有gpu 查看有没有安装cuda nvidia-smi我没有CUDA 安装CUDA https://developer.nvidia.com/cuda-toolkit-archive 选择自定义安装 不要勾选Visual Studio 记录下面安装目录&#xff0c;如果后续环境变量没有自动…

Docker(五)访问仓库

作者主页&#xff1a; 正函数的个人主页 文章收录专栏&#xff1a; Docker 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01; 访问仓库 仓库&#xff08;Repository&#xff09;是集中存放镜像的地方。 一个容易混淆的概念是注册服务器&#xff08;Registry&#xf…

Linux下使用Docker部署MinIO实现远程上传

&#x1f4d1;前言 本文主要是Linux下通过Docker部署MinIO存储服务实现远程上传的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;CSDN主页放风讲故事 &#…

【Docker】在Windows操作系统上安装Docker

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Docker容器》序列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对…

2024-01-15(SpringMVCMybatis)

1.拦截器&#xff1a;如果我们想在多个handler方法(controller中的方法)执行之前或者之后都进行一些处理&#xff0c;甚至某些情况下需要拦截掉&#xff0c;不让handler方法执行&#xff0c;那么就可以使用SpringMVC为我们提供的拦截器。 拦截器和过滤器的区别&#xff1a;过滤…

java使用jsch处理软链接判断是否文件夹

前言 这一次主要是碰到一个问题。因为使用jsch去读取文件的时候&#xff0c;有一些文件它是使用软链接制作的一个映射。因为这里面有一个问题。如果它是软链接你就无法判断他到底是文件。还是文件夹&#xff1f;因为他没有提供可以直接读取的方法&#xff0c;用权限信息去判断…

Freemarker的基本语法及入门基础

freemarker的基本语法及入门基础 一、freemarker模板文件(*.ftl)的基本组成部分 1. 文本&#xff1a;直接输出的内容部分 2. 注释&#xff1a;不会输出的内容&#xff0c;格式为<#-- 注释内容 --> 3. 取值(插值)&#xff1a;代替输出数据模型的…

基于Spring+mybatis+vue的饮食分享系统(Java毕业设计)

大家好&#xff0c;我是DeBug&#xff0c;很高兴你能来阅读&#xff01;作为一名热爱编程的程序员&#xff0c;我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里&#xff0c;我将会结合实际项目经验&#xff0c;分享编程技巧、最佳实践以及解决问题的方法。无论你是…

INS-06003错误处理

在麒麟V10操作系统上安装Oracle RAC 19C&#xff0c;安装GI的建立互信步骤中&#xff0c;遇到INS-06003错误&#xff1a; [INS-06003] Failed to setup password SSH connectivity with following node(s) 查看详细信息&#xff1a; PRVG-11001: PRCZ-2136: PRCZ-2006: 此时在操…

C++ 设计模式之 中介者模式

【声明】本题目来源于卡码网&#xff08;题目页面 (kamacoder.com)&#xff09; 【提示&#xff1a;如果不想看文字介绍&#xff0c;可以直接跳转到C编码部分】 【设计模式大纲】 【简介】 -- 什么是中介者模式 &#xff08;第16种模式&#xff09; 中介者模式&#xff08;Medi…