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

news2024/11/16 3:38:44

        在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.coloradmin.cn/o/2165075.html

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

相关文章

Centos7.9安装.Net 8.0框架

1.背景 在Centos7.9编译运行Net8.0项目时&#xff0c;会用到.Net8.0框架。所以我实操了如何在Centos7.9去安装.Net8.0框架。本文记录了相关操作过程&#xff0c;是一个比较好的参考。 2.操作 2.1 安装框架 sudo rpm -Uvh https://packages.microsoft.com/config/centos/8/pa…

一文理解AXI4-lite与AXI4-stream协议

AXI4-lite与AXI4-stream协议 上篇博文《AMBA3.0 AXI总线入门》浅要介绍AXI4总线协议&#xff0c;AXI总线作为一种总线&#xff0c;可以挂载多个主设备&#xff08;master&#xff09;和从设备&#xff08;slave&#xff09;&#xff0c;AXI总线协议定义了主设备和从设备之间如何…

Docker本地部署Chatbot Ollama搭建AI聊天机器人并实现远程交互

文章目录 前言1. 拉取相关的Docker镜像2. 运行Ollama 镜像3. 运行Chatbot Ollama镜像4. 本地访问5. 群晖安装Cpolar6. 配置公网地址7. 公网访问8. 固定公网地址 前言 本文主要分享如何在群晖NAS本地部署并运行一个基于大语言模型Llama 2的个人本地聊天机器人并结合内网穿透工具…

Web安全-SQL注入之联合查询注入

声明 环境 墨者学院-SQL手工注入漏洞测试(MySQL数据库-字符型) 判断是否存在漏洞 http://124.70.64.48:42937/new_list.php?idtingjigonggao and 12-- and 11正常 http://124.70.64.48:42937/new_list.php?idtingjigonggao and 12-- and 12出错&#xff0c;存在字符型注入…

yolov8训练数据集——labelme的json文件转txt文件

yolov8的环境搭建&#xff0c;参考&#xff1a;Home - Ultralytics YOLO Docs 1.把标注好的json文件和jpg放同一个目录下。 2.运行转换脚本文件labelme2yolo.py文件&#xff1a; # -*- coding: utf-8 -*-import os import numpy as np import json from glob import glob im…

python学习笔记(3)——控制语句

控制语句 我们在前面学习的过程中&#xff0c;都是很短的示例代码&#xff0c;没有进行复杂的操作。现在&#xff0c;我们将开始学习流程控制语句。 前面学习的变量、数据类型&#xff08;整数、浮点数、布尔&#xff09;、序列&#xff08;字符串、列表、元组、字 典、集合&am…

GPS在Linux下的使用(war driving的前置学习)

1.ls /dev/tty* 列出所有与 tty 相关的设备文件。这些设备文件通常对应终端设备 ttyUSB0是GPS端口 2.cat /dev/ttyUSB0 用于读取并显示连接到 /dev/ttyUSB0 串口设备发送的原始数据 这种是GPS定位不全的&#xff0c;要拿到更开阔的地方 这种是GPS定位全的 因为会持续输出…

大模型神书《HuggingFace自然语言处理详解——基于BERT中文模型的任务实战》读完少走几年弯路!

这几年&#xff0c;自然语言处理&#xff08;NLP&#xff09;绝对是机器学习领域最火的方向。那么今天给大家带来一本 《HuggingFace自然语言处理详解——基于BERT中文模型的任务实战》 这本大模型书籍资料已经上传CSDN&#xff0c;朋友们如果需要可以微信扫描下方CSDN官方认证…

MoveIt控制机械臂的运动实现——机器人抓取系统基础系列(二)

文章目录 概要1 用户接口和代码案例2 不同的规划类型2.1 关节空间规划2.2 工作空间规划2.3 笛卡尔空间规划 3 MoveIt运行实操4 相关资料推荐小结 概要 MoveIt为开发者提供了针对机械臂的集成化开发平台&#xff0c;由一系列操作相关的功能包组成&#xff0c;包括运动规划、操作…

一、人物朝向摄像机移动

一、导入插件Cinemachine 重命名为ThirdPersonCamera Follow和LookAt 选择为player 镜像Y轴&#xff0c;取消X轴 摄像机绑定模式为World Space 二、挂载代码PlayerController using System.Collections; using System.Collections.Generic; using UnityEngine;public class…

查询最近正在执行的sql(DM8 : 达梦数据库)

查询最近正在执行的sql DM8 : 达梦数据库 1 查询最近正在执行的sql2 更多达梦数据库学习使用列表 1 查询最近正在执行的sql 迁移数据时 , 业务无响应 , 查看最近活动的sql , 有没有迁移相关的表 , 通过最后的时间字段 , 判断会话是否正在执行 SELECT SESS_ID, SQL_TEXT, STATE…

三条命令实现linux内核升级

rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm 如果报&#xff1a;curl: (35) Peer reports incompatible or unsupported protocol version. 执行命令&#xff1a; yum update -y nss curl libcurl 2.yum --enablerepoelrepo-kernel install …

iOS六大设计原则设计模式

六大设计原则&#xff1a; 一、单一职责原则 一个类或者模块只负责完成一个职责或者功能。 类似于&#xff1a;UIView 和 CALayer 二、开放封闭原则 对扩展开放&#xff0c;对修改封闭。 我们要尽量通过扩展软件实体来解决需求变化&#xff0c;而不是通过修改已有的代码来…

Redis主从集群搭建

一、什么是主从复制 主从复制&#xff0c;是指将一台redis服务的数据复制到集群中其他的redis服务&#xff0c;前者称(Maste)&#xff0c;后者称(Slave)&#xff1b;数据的复制是单向的&#xff0c;只能从主节点复制到从节点&#xff0c;一个主节点可以有多个从节点&#xff0…

【CubeMX学习笔记】关于CAN通信协议

目录 一、CAN通信简介 二、CAN数据帧类型 三、格式帧 四、位同步 传输数据时可能遇到的问题 最小时间单位 硬同步 再同步 波特率的计算 STM32中的CAN外设 一、原理图 二、标识符筛选 三、配置单个邮箱&#xff08;正常模式或自发自收只需要修改模式&#xff09; …

探索 ShellGPT:终端中的 AI 助手

文章目录 探索 ShellGPT&#xff1a;终端中的 AI 助手背景介绍ShellGPT 是什么&#xff1f;如何安装 ShellGPT&#xff1f;简单的库函数使用方法场景应用常见问题及解决方案总结 探索 ShellGPT&#xff1a;终端中的 AI 助手 背景介绍 在当今快速发展的技术领域&#xff0c;命…

人体动捕相关算法

SMPL、SMPLify SMPL&#xff08;Skinned Multi-Person Linear (SMPL) Model&#xff09;Keep it SMPL: Automatic Estimation of 3D Human Pose and Shape from a Single Image&#xff0c;2015 SMPL 关节点数量&#xff1a;K23&#xff0c;其中0号节点&#xff08;不在K之…

一区黏菌算法+双向深度学习+注意力机制!SMA-BiTCN-BiGRU-Attention黏菌算法优化双向时间卷积双向门控循环单元融合注意力机制多变量回归预测

一区黏菌算法双向深度学习注意力机制&#xff01;SMA-BiTCN-BiGRU-Attention黏菌算法优化双向时间卷积双向门控循环单元融合注意力机制多变量回归预测 目录 一区黏菌算法双向深度学习注意力机制&#xff01;SMA-BiTCN-BiGRU-Attention黏菌算法优化双向时间卷积双向门控循环单元…

keil的debug功能

文章目录 一.窗口介绍二.功能2.1 debug断点调试和窗口变量2.2 四个花括号功能2.2.1 进去函数和下一步功能2.2.2 跳过函数和跳过该行代码功能2.2.3 函数内部跳出功能2.2.4 执行到光标处 2.3 查看内部寄存器 一.窗口介绍 二.功能 2.1 debug断点调试和窗口变量 先打开下图debug断…

Scanner流程控制语句

1. Scanner类 Scanner的意思是扫描 Scanner是JDK提供的一个类&#xff0c;位于java.util包下&#xff0c;所以我们如果需要使用则必须导包&#xff0c;导包的语句必须在声明包之后&#xff0c;在声明类之前 Scanner类是用来接受用户输入的各种信息 Scanner类提供了用于接受…