当前位置:   article > 正文

vue3:菜单、标签页和面包屑联动效果

vue3:菜单、标签页和面包屑联动效果

概要

提示:这里可以添加技术概要

例如:

openAI 的 GPT 大模型的发展历程。

1.整体思路

 在之前做的后台项目中,菜单、标签页和面包屑之间的联动,自己都是通过在路由前置守卫中,定义bus总线程通过.emit 和 .on 发送拿取需要的数据,然后再做一些自己想要的处理,实现自己想要的结果,后面在修改项目的时候就想要一中比较方便的办法,抛弃了通过bus总线程实现的方法。选择通过状态管理stroe来存储当前的路由信息控制三者的联动效果。

2.实现过程

1.创建你的组件文件,nav:菜单 、header:面包屑 、tabs:标签页,然后先把自己的基础页面信息。

2.将这些组件引入到view->index文件中

页面整体效果

2.创建store文件: index.ts  、user.ts 文件名字自己随便取,后面需要引入的时候不要把名称写错了

   store  //我习惯了命名 store  ,实际可以取名 pinia 因为用到的组件库就是这个

            * index

            * user

 index.ts

  1. import {createPinia} from 'pinia'
  2. let pinia= createPinia()
  3. export default pinia

user.ts

  1. import {defineStore} from 'pinia'
  2. const useCounterStore = defineStore('appStore',{
  3. state :()=> ({
  4. menuiIsCollapse : false , //menu导航栏是否为展开
  5. NavList: [], //menu导航栏的数据
  6. TabsList:[], //标签页的显示数据
  7. NowRouterPath :'' //当前路由
  8. }),
  9. actions : {
  10. //菜单栏的缩放
  11. updataMenuWidth(status:boolean){
  12. this.menuiIsCollapse = status
  13. this.menuiIsCollapse ? document.documentElement.style.setProperty('--el-aside-width', '64px') : document.documentElement.style.setProperty('--el-aside-width', '220px')
  14. },
  15. //第一次进入网站获取nav导航栏信息
  16. oneSetMenuList (obj:any | []){
  17. this.NavList = obj
  18. },
  19. //获取当前的路由信息
  20. getNowRouterPath(path:object){
  21. this.NowRouterPath = path.fullPath
  22. this.screenTabsList(path)
  23. },
  24. //筛选tabs的数据显示 不存在就添加到 TabsList 中
  25. screenTabsList(to_data:object){
  26. if(this.TabsList.length == 0){
  27. this.TabsList.push(to_data)
  28. }else{
  29. if(!this.TabsList.some(item => item.fullPath == to_data.fullPath)){
  30. this.TabsList.push(to_data)
  31. }
  32. }
  33. }
  34. },
  35. })
  36. export default useCounterStore

3.创建router.ts文件,在src目录下新建router文件夹 - > 新建index.ts文件

  1. import { createRouter ,createWebHashHistory , createWebHistory } from "vue-router";
  2. import pinia from '@/store'
  3. import useUserStore from "@/store/user";
  4. import { inject } from "vue";
  5. //必须 否则下面使用 _store 会报错
  6. useUserStore(pinia)
  7. const routes = [你自己的菜单数据]
  8. const router = createRouter({
  9. history : createWebHashHistory(),
  10. routes
  11. })
  12. //前置路由守卫
  13. router.beforeEach((to, from, next) => {
  14. //调用 store 在mian.ts中定义
  15. const _store = inject('store')
  16. //将当前路由信息传递给store->user.ts中的方法
  17. _store.getNowRouterPath(to)
  18. const isAuthenticated = checkIfUserIsAuthenticated();
  19. if (to.matched.some(record => record.meta.requiresAuth) && !isAuthenticated) {
  20. next({ name: 'login' });
  21. } else if (to.name === 'login' && isAuthenticated) {
  22. next({ name: 'index' });
  23. } else {
  24. next();
  25. }
  26. });
  27. function checkIfUserIsAuthenticated() {
  28. // 检查本地存储中的认证状态
  29. const isAuthenticated = localStorage.getItem('isAuthenticated');
  30. // 如果认证状态为true,则返回true;否则返回false
  31. return isAuthenticated === 'true';
  32. }
  33. export default router

没有注册userUserStore出现的错误,大概意思就是当前没有组件生产,拿取不到Mian.ts中注册的pinia:

附上mian.ts文件

  1. import { createApp } from 'vue'
  2. import './style.css'
  3. import App from './App.vue'
  4. import router from "./router";
  5. import ElementPlus from 'element-plus'
  6. import * as ElementPlusIconsVue from '@element-plus/icons-vue'
  7. import 'element-plus/dist/index.css'
  8. import './assets/css/global.less';
  9. import { createPinia } from "pinia";
  10. import bus from "./mitt";
  11. import useCounterStore from "@/store/user";
  12. import { storeToRefs } from 'pinia';
  13. const pinia = createPinia();
  14. const store = useCounterStore()
  15. const storerefs = storeToRefs(store)
  16. //模拟数据
  17. import { mockXHR } from "./mock/index";
  18. mockXHR()
  19. const app = createApp(App)
  20. for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  21. app.component(key, component)
  22. }
  23. app.use(pinia)
  24. app.use(router)
  25. app.use(ElementPlus)
  26. //VUE提供了provide 和 inject来解决了这个问题。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。
  27. app.provide('store_state', storerefs) //拿取到的pinia的state
  28. app.provide('store', store) //拿到的整个pinia
  29. app.provide('bus', bus)
  30. app.mount('#app')

4.菜单文件 nva->index.vue

  1. <script lang="ts" setup>
  2. import { onMounted, reactive, ref ,watch,inject} from "vue";
  3. import { useRoute } from "vue-router";
  4. import { get } from "../../axios/api";
  5. import bus from "@/mitt";
  6. const isCollapse = ref(true);
  7. const router = useRoute();
  8. let nowRouter = ref("");
  9. let NavList = ref([]);
  10. let childPath = ref([]);
  11. const store = inject('store')
  12. const store_state = inject('store_state')
  13. //监听当前路由信息的变化 newVlaue是新值 ,vlaue 是变化之前的旧值
  14. watch(()=>store_state.NowRouterPath.value,(newValue,value)=>{
  15. //判断当前的菜单数据信息中,是否包含新的路由数据,nowRouter赋值为新的路由,nav就会选中高亮显示
  16. NavList.value.map((item) => {
  17. newValue.indexOf(item.fullPath) == -1
  18. ? item.children.map((child: any) => {
  19. nowRouter.value = newValue;
  20. router.fullPath.indexOf(newValue) !== -1 ? (nowRouter.value = newValue) : "";
  21. })
  22. : "";
  23. });
  24. })
  25. onMounted(() => {
  26. //这个只是判断请求的nav数据是否存在本地中,存在就不请求接口了
  27. if(sessionStorage.getItem('NavList')){
  28. NavList.value = JSON.parse(sessionStorage.getItem('NavList'))
  29. getNowPath()
  30. }else{
  31. getNavList()
  32. }
  33. });
  34. //函数
  35. const getNavList = ()=>{
  36. let params = {
  37. token: localStorage.getItem("token"),
  38. };
  39. get("/api/getMenuList", params).then((res) => {
  40. NavList.value = res.data;
  41. store.oneSetMenuList(NavList);
  42. sessionStorage.setItem('NavList',JSON.stringify(NavList.value))
  43. getNowPath()
  44. });
  45. }
  46. const ClickToGo = (data)=>{
  47. }
  48. const getNowPath = () =>{
  49. NavList.value.map((item) => {
  50. item.children.length !== 0
  51. ? item.children.map((child: any) => {
  52. childPath.value.push(child.path);
  53. })
  54. : childPath.value.push(item.path);
  55. });
  56. childPath.value.map((item) => {
  57. if (router.path.indexOf(item) !== -1) {
  58. nowRouter.value = item;
  59. }
  60. });
  61. }
  62. </script>
  63. <template>
  64. <el-menu
  65. class="el-menu-vertical-demo"
  66. :collapse="store_state.menuiIsCollapse.value"
  67. :unique-opened="true"
  68. :router="true" //true的话,可以根据点击菜单中绑定的index'/index'定义的路由,可点击直接跳转
  69. :default-active="nowRouter"
  70. background-color="#545c64"
  71. text-color="#fff"
  72. active-text-color="#ffd04b"
  73. >
  74. <template v-for="(item, index) in NavList">
  75. <el-sub-menu v-if="item.children.length !== 0" :index="item.id">
  76. <template #title> //下拉菜单
  77. <el-icon><component :is="item.web_icon" /></el-icon>
  78. <span>{{ item.title }}</span>
  79. </template> //点击跳转菜单
  80. <el-menu-item-group v-for="(children, k) in item.children">
  81. <el-menu-item :index="children.path" @click="ClickToGo(children)">
  82. {{ children.title }}</el-menu-item
  83. >
  84. </el-menu-item-group>
  85. </el-sub-menu>
  86. <el-menu-item v-else :index="item.path">
  87. <el-icon><component :is="item.web_icon" /></el-icon>
  88. <template #title>{{ item.title }}</template>
  89. </el-menu-item>
  90. </template>
  91. </el-menu>
  92. </template>
  93. <style scoped lang="less">
  94. </style>

5.tab组件

  1. <template>
  2. <div class="container" >
  3. <el-tabs
  4. v-model="editableTabsValue"
  5. type="card"
  6. closable
  7. @tab-remove="removeTab"
  8. @tab-click="getRouter"
  9. >
  10. <el-tab-pane
  11. v-for="(item, index) in editableTabs"
  12. :key="index"
  13. :label="item.meta.name"
  14. :name="item.fullPath"
  15. >
  16. </el-tab-pane>
  17. </el-tabs>
  18. </div>
  19. </template>
  20. <script lang="ts" setup>
  21. import { defineComponent, ref, onMounted ,inject,watch} from "vue";
  22. import bus from "@/mitt";
  23. import { useRouter } from "vue-router";
  24. const store = inject('store')
  25. const store_state = inject('store_state')
  26. //直接赋值为当前的路由 editableTabsValue 对应到 el-tab-pane中的:name 如果相同就会自动高亮显示
  27. let editableTabsValue = store_state.NowRouterPath;
  28. //pinia中保存的 tabs数据
  29. const editableTabs = store_state.TabsList
  30. // 页面加载时执行
  31. onMounted(() => {});
  32. // 移除选项卡
  33. const removeTab = (targetName: string) => {};
  34. // 点击选项卡
  35. const getRouter = (i: any) => {};
  36. </script>

6.header 面包屑组件

  1. <template>
  2. <div class="header-box">
  3. <div class="header_item">
  4. <el-breadcrumb separator="/" class="header_left_title">
  5. <el-breadcrumb-item :to="{ path: '/home' }">主页</el-breadcrumb-item>
  6. <el-breadcrumb-item>{{ titleList.Pname }}</el-breadcrumb-item>
  7. <el-breadcrumb-item v-if="titleList.name">{{
  8. titleList.name
  9. }}</el-breadcrumb-item>
  10. </el-breadcrumb>
  11. </div>
  12. </div>
  13. </template>
  14. <script setup lang="ts">
  15. import { onMounted, defineProps, provide, inject ,watchEffect ,watch} from "vue";
  16. import { useRouter, useRoute } from "vue-router";
  17. import { ref, reactive } from "vue";
  18. const router = useRoute();
  19. //面包屑显示数据
  20. let titleList = reactive({ Pname: "", name: "" });
  21. const store = inject('store')
  22. const store_state = inject('store_state')
  23. onMounted(() => {
  24. //拿取router 路由信息
  25. getMenuList(router.path);
  26. });
  27. //监听当前路由的信息变化
  28. watch(()=>store_state.NowRouterPath.value,(newValue,value)=>{
  29. getMenuList(newValue);
  30. })
  31. function getMenuList(path) {
  32. //path:当前路由 ; 拿到nav菜单数据,对比是否能找到path对应的菜单信息,
  33. let menuList = JSON.parse(sessionStorage.getItem('NavList'))
  34. menuList.map((item) => {
  35. if (item.path !== path) {
  36. //判断是否存在children 可点击菜单
  37. item.children.length !== 0
  38. ? item.children.map((child) => {
  39. //存入 下拉菜单的名称 和 筛选出的点击菜单名称
  40. if (child.path == path) {
  41. titleList.Pname = item.title;
  42. titleList.name = child.title;
  43. }
  44. })
  45. : "";
  46. } else {
  47. titleList.Pname = item.title;
  48. titleList.name = "";
  49. }
  50. });
  51. }

如有写的不明确的地方可以提出来,或者大佬们有更好的方法。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/412845
推荐阅读
相关标签
  

闽ICP备14008679号