当前位置: 首页 > article >正文

Vue3入门 - ElementPlus中左侧菜单和Tabs菜单组合联动效果

        在Vue3中,ElementPlus是使用比较广泛的UI组件库,提供了丰富的界面元素支持项目开发需求。在后台管理系统中,左侧或顶部的菜单栏通常包含多个子菜单项,通过菜单的展开和收缩功能,用户可以方便地查看或隐藏不需要的菜单项,从而优化界面布局,提供用户体验。

        在ElementPlus中,可通过修改属性default-openeds和unique-opened来实现菜单项的展开和收缩.、默认展开等功能;并使用JS数组中reduce方法来完成default-openeds数组读取。

一、界面搭建

       Vue3的项目创建之前篇幅已讲过,这里就不再讲Vue3项目创建相关基础内容了。

        先在项目中创建一个菜单组件(Navigation.vue),代码如下:

<template>
  <el-row>
    <el-col :span="5">
      <el-menu
        :default-active="defaultActive"
        :default-openeds="defaultOpeneds"
        class="el-menu-vertical-demo">
        <el-sub-menu :index="item.id+''" v-for="(item, index) in menuList" :key="item.id">
          <template #title>
            <span>{{item.name}}</span>
          </template>
          <el-sub-menu :index="sub.id+''" v-for="(sub, idx) in item.children" :key="sub.id">
            <template #title>{{sub.name}}</template>
            <el-menu-item :index="third.id+''" 
                        v-for="(third, i) in sub.children" 
                        :key="third.id"
                        @click="selectItemEvent(third)">
              {{third.name}}
            </el-menu-item>
          </el-sub-menu>
        </el-sub-menu>
      </el-menu>
    </el-col>
    <el-col :span="1">&nbsp;</el-col>
    <el-col :span="18">
      <el-tabs v-model="defaultActive" class="demo-tabs">
        <el-tab-pane :label="item.name" :name="item.id+''" v-for="(item, index) in tabsList" :key="item.id">
          {{item.name}}
        </el-tab-pane>
      </el-tabs>
    </el-col>
  </el-row>
</template>

<script lang="ts" setup>
import { reactive, ref } from 'vue'
type MenuType = {
  id: number
  name: string
  path?: string
  children?: Array<MenuType>
}
const menuList = reactive([
  {
    "id": 1000,
    "name": "商品模块",
    "path": "goods/index",
    "children": [
      {
        "id": 10001000,
        "name": "基本信息",
        "path": "goods/base/index",
        "children": [
          {
            "id": 1000100101,
            "name": "年份信息",
            "path": "goods/base/year"
          },
          {
            "id": 1000100102,
            "name": "颜色信息",
            "path": "goods/base/color"
          },
          {
            "id": 1000100103,
            "name": "商品信息",
            "path": "goods/base/info"
          }
        ]
      }
    ]
  },
  {
    "id": 2000,
    "name": "人事信息",
    "path": "personnel/index",
    "children": [
      {
        "id": 20001000,
        "name": "基本信息",
        "path": "personnel/base/index",
        "children": [
          {
            "id": 2000100101,
            "name": "部门信息",
            "path": "personnel/base/unit"
          },
          {
            "id": 2000100102,
            "name": "社保信息",
            "path": "personnel/base/social"
          },
          {
            "id": 2000100103,
            "name": "人事信息",
            "path": "personnel/base/info"
          }
        ]
      }
    ]
  },
  {
    "id": 3000,
    "name": "会员信息",
    "path": "account/index",
    "children": [
      {
        "id": 30001000,
        "name": "基础信息",
        "path": "account/base/index",
        "children": [
          {
            "id": 3000100101,
            "name": "会员等级",
            "path": "account/base/level"
          },
          {
            "id": 3000100102,
            "name": "会员信息",
            "path": "account/base/info"
          }
        ]
      }
    ]
  }
])
const defaultOpeneds = reactive([])    // 默认展开项
const defaultActive = ref('1000100101') // 当前选择菜单的index
const tabsList = reactive([])            // tab菜单数据列表

// 菜单点击事件
const selectItemEvent = (item: MenuType) => {

}
</script>

        组件完成后,将其在App.vue中引入注入到html代码中。

<script setup lang="ts">
import { RouterView } from 'vue-router'
import Navigation from '@/components/Navigation.vue'
</script>

<template>
  <Navigation></Navigation>
  <RouterView />
</template>

二、保持一个子菜单的展开

        当将Menu菜单的属性unique-opened设置为true(是否只保持一个子菜单的展开,默认为false),则始终保持一个分类的展开,其他分类会自动收缩的效果。

        代码如下:

<template>
  <el-row>
    <el-col :span="5">
      <el-menu
        :default-active="defaultActive"
        :default-openeds="defaultOpeneds"
        :unique-opened="true"
        class="el-menu-vertical-demo">
        <el-sub-menu :index="item.id+''" v-for="(item, index) in menuList" :key="item.id">
          <template #title>
            <span>{{item.name}}</span>
          </template>
          <el-sub-menu :index="sub.id+''" v-for="(sub, idx) in item.children" :key="sub.id">
            <template #title>{{sub.name}}</template>
            <el-menu-item :index="third.id+''" 
                        v-for="(third, i) in sub.children" 
                        :key="third.id"
                        @click="selectItemEvent(third)">
              {{third.name}}
            </el-menu-item>
          </el-sub-menu>
        </el-sub-menu>
      </el-menu>
    </el-col>
    <el-col :span="1">&nbsp;</el-col>
    <el-col :span="18">
      <el-tabs v-model="defaultActive" class="demo-tabs">
        <el-tab-pane :label="item.name" :name="item.id+''" v-for="(item, index) in tabsList" :key="item.id">
          {{item.name}}
        </el-tab-pane>
      </el-tabs>
    </el-col>
  </el-row>
</template>

三、Tabs菜单追加数据

        当点击左侧菜单里,判断右侧tabList是否存在该项,不存在追加即可。并修正当前选中项的index,代码如下:

<script lang="ts" setup>
import { reactive, ref } from 'vue'
type MenuType = {
  id: number
  name: string
  path?: string
  children?: Array<MenuType>
}
const menuList = reactive([
    // 略....
])
let defaultOpeneds = reactive([])   // 默认展开项
const defaultActive = ref('') // 当前选择菜单的index
const tabsList = reactive([]) // tab菜单数据列表

// 菜单点击事件
const selectItemEvent = (item: MenuType) => {
  defaultActive.value = item.id + ''
  // tabList中不存在则追加
  if(!tabsList.some((sub) => sub.id == item.id)) {
    tabsList.push(item)
  }
}
</script>

        上述代码完成后,点击左侧菜单,则横向tabs菜单就显示出来了。并且则于el-tabs组件上是通过v-model="defaultActive"进行双向数据绑定的,所以tabs组件点击后,左侧菜单也会跟期一起联动显示出对应的菜单项。

三、默认展开菜单项

        在系统加载完毕后,可以通过default-openeds属性默认展开某个菜单分类;另外,系统一般会缓存上次菜单选择项的index数据,此时我们就可以读取该数据,通过它向上反查要默认打开菜单分类。

        如下图,要是默认展开第一个分类”商品模块“,则default-openeds的值应为['1000', '10001000'];上次打开如是”年份信息“项菜单,如何通过它获取到['1000', '10001000']呢?这个就使用到万能的Array.reduce()方法了,下面将通过它提取出default-openeds的值。

        通过当前菜单信息id,反查所有上级id数据;在setup中,增加Recursive()弟归函数,用于反查上级id数组并返回default-openeds属性需要的数组index。代码如下:

<script lang="ts" setup>
import { reactive, ref } from 'vue'
type MenuType = {
  id: number
  name: string
  path?: string
  children?: Array<MenuType>
}
const menuList = reactive([
    // 略...
])
const defaultOpeneds = ref<string[]>([])   // 默认展开项
const defaultActive = ref('') // 当前选择菜单的index
const tabsList = reactive([]) // tab菜单数据列表

// 递归数据
const Recursive = (menu: Array<MenuType>, key: number) : string[] => {
  return menu.reduce((alls: string[], item) => {
    // 判断其是否为数组,并且存在子项开始查询
    if(Array.isArray(item.children) && item.children.length > 0) {
      // 查询是否有符合其id项,有则为本元素id追加进数组中
      if(item.children.some(sub => sub.id == key)) {
        return alls.concat([item.id+''])
      }
      // 未查询到,则递归继续查询
      else {
        const data: string[] = Recursive(item.children, key)
        // 递归查询到数组存在,则将本元素id追加进数组,并放在首位
        if(data.length>0) return [item.id+''].concat(data)
      }
    } 
    return alls
  }, [])
}
// 默认展开项(这里1000100101先写,实际项目中在缓存中读取)
defaultOpeneds.value = Recursive(menuList, 1000100101)

// 菜单点击事件
const selectItemEvent = (item: MenuType) => {
  // 修正当前选中的index标识(通过vuex或pinia维护该数据时,可将其缓存到本地)
  defaultActive.value = item.id + ''
  // tabList中不存在则追加
  if(!tabsList.some((sub) => sub.id == item.id)) {
    tabsList.push(item)
  }
}
</script>

        此时,则可以通过当前菜单id(即html渲染时的index)向上反查上级菜单的index数据了,如下图:

        页面加载完毕后,则会显示默认展开项了,如下图:

        当然,这里只是演示demo。项目中需要将菜单数据、tabs菜单数据、默认展开的菜单、默认选中等,写入Vuex或Pinia中,根据自己项目需求进行调整即可。


http://www.kler.cn/news/314923.html

相关文章:

  • linux下的日志编写
  • Linux 文件 IO 管理(第二讲)(重定向和缓冲区)
  • 鸿蒙开发的基本技术栈及学习路线
  • 【JVM安装MinIO】
  • IO流中的异常捕获
  • 搜索引擎onesearch3实现解释和升级到Elasticsearch v8系列(三)-文档
  • C++模版初阶
  • claude,gpt,通义千问
  • Java面试篇基础部分-ReentrantLock详解(二)
  • 2024最新!!!iOS高级面试题,全!(二)
  • 深度学习对抗海洋赤潮危机!浙大GIS实验室提出ChloroFormer模型,可提前预警海洋藻类爆发
  • Vue3 中组件传递 + css 变量的组合
  • 深度学习03-神经网络01-什么是神经网络?
  • QT快速安装使用指南
  • OpenHarmony(鸿蒙南向开发)——小型系统芯片移植指南(二)
  • 安全热点问题
  • NCNN 源码(1)-模型加载-数据预处理-模型推理
  • MySQL深入原理
  • 【数学分析笔记】第3章第3节无穷小量与无穷大量的阶(2)
  • 国标GB28181视频融合监控汇聚平台的方案实现及场景应用
  • 机器学习和深度学习的区别:从基础到前沿
  • 35. 模型材质和几何体属性
  • Mapper核心配置文件
  • uniapp 整合 OpenLayer3
  • C++速通LeetCode中等第4题-三数之和
  • 本地快速部署一个简洁美观的个人Halo博客网站并发布公网远程访问
  • 20240918软考架构-------软考171-175答案解析
  • 数字IC设计\FPGA 职位经典笔试面试整理--语法篇 Verilog System Verilog(部分)
  • Docker修改默认的存储路径
  • 分布式锁之 防误删(优化之UUID防误删)