✨✨个人主页:沫洺的主页
📚📚系列专栏: 📖 JavaWeb专栏📖 JavaSE专栏 📖 Java基础专栏📖vue3专栏
📖MyBatis专栏📖Spring专栏📖SpringMVC专栏📖SpringBoot专栏
📖Docker专栏📖Reids专栏📖MQ专栏📖SpringCloud专栏
💖💖如果文章对你有所帮助请留下三连✨✨
🍔效果图
🍟实现内容
- 🎫ElementPuls页面布局(上上章)
- 🎫动态菜单显示(上上章)
- 🎫实现菜单折叠效果(上一章)
- 🎫实现部分页面不使用整体框架(下一章)
- 🎫统一页面导航标签(本章)
🌭 统一页面导航标签
🎑新建components/NavHead.vue
🎑使用watch()侦听
NavHead.vue
<template> </template> <script setup lang="ts"> import { ref, onMounted, watch } from 'vue' import { useRoute, useRouter } from "vue-router"; import { appStore } from "@/store/appStore"; import { storeToRefs } from "pinia"; const route = useRoute(); const router = useRouter(); //watch() 侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。 watch( //监听什么对象(这里监听的是当前路由的路径) //当路径发生变化时,就执行回调函数 () => route.path, //新值,老值,回调函数 (newValue, oldValue) => { // newValue === oldValue console.log("新值: "+newValue, "老值: "+oldValue) }, //深度监听对象中的所有属性 { deep: true } ) </script>
🎑App.vue中导入文件,并引用
🎑看一下监听效果
🎑 使用Tabs标签设置导航标签
🎑 复制template和script标签的内容到NavHead.vue中查看效果
🎑 对原有的代码进行改动,不需要的功能可以不要
🎑 对代码进行改进(有注释可参考)
NavHead.vue
<template> <el-tabs v-model="editableTabsValue" type="card" :closable="true" class="demo-tabs" @tab-remove="tabRemove" > <el-tab-pane v-for="item in editableTabs" :key="item.name" :label="item.title" :name="item.name" > </el-tab-pane> </el-tabs> </template> <script setup lang="ts"> import { ref, onMounted, watch } from 'vue' import { useRoute, useRouter } from "vue-router"; import { appStore } from "@/store/appStore"; import { storeToRefs } from "pinia"; const route = useRoute(); const router = useRouter(); //当前活动标签,默认显示name:'1'的元素 const editableTabsValue = ref('1') //定义一个editableTabs数组,里边放了几个元素 const editableTabs = ref([ { title: 'Tab 1', name: '1', }, { title: 'Tab 2', name: '2', }, { title: 'Tab 3', name: '3', }, ]) //移除方法,当点击移除按钮时,这里的targetName就是所点击的元素的name const tabRemove = (targetName: string) => { //editableTabs.value就是 { title: 'Tab 1',name: '1',} 这样的元素 let tabs = editableTabs.value; //过滤掉除所点击的元素以外的元素,也就是说没有过滤的就是要移除的元素 editableTabs.value = tabs.filter((tab) => tab.name !== targetName) //计算活动标签是哪一个 //如果移除的是当前活动标签(元素),那么通过下面的逻辑进行高亮显示 let activeName = editableTabsValue.value if (activeName === targetName) { // 对数组进行遍历找到当前活动标签的位置 tabs.forEach((tab, index) => { if (tab.name === targetName) { // 找到之后取前后索引位置的元素 //找离活动标签最近的标签,右边优先于左边显示 const nextTab = tabs[index + 1] || tabs[index - 1] //如果nextTab存在就赋值,也就是如果右边没有就取左边的 if (nextTab) { activeName = nextTab.name } } }) // 给当前活动标签赋值新的name,进行高亮 editableTabsValue.value = activeName } } //watch() 侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。 watch( //监听什么对象(这里监听的是当前路由的路径) //当路径发生变化时,就执行回调函数 () => route.path, //新值,老值,回调函数 (newValue, oldValue) => { // newValue === oldValue console.log("新值: "+newValue, "老值: "+oldValue) }, //深度监听对象中的所有属性 { deep: true } ) </script>
- 📍将Tab标签和左边的Aside部分里的Menu菜单进行关联,也就是动态显示Tab标签页,当点击Menu菜单项时就会在Tab标签页中显示对应的标签页
- 📍当点击菜单项时,Tab标签页中如果已经存在对应的标签页时,就不需要再添加标签页,保证其唯一性
- 📍实现动态关联之后在Main部分中同样的显示Menu菜单项的内容(tab-change)
- 📍默认显示首页标签页(当初始状态时,总要显示一个首页,因此首页就不需要移除按钮,作为初始化的数据显示)
- 📍实现本地存储,当关闭网页后,再次访问所关闭的网页的路径,会通过本地存储恢复之前的状态信息,
- 📍实现当页面信息发送改变时,切换标签页时信息不被消除(场景一)
- 📍实现强制回退路径,页面初始化(场景二)
🎑 NavHead.vue
<template> <el-tabs v-model="editableTabsValue" type="card" class="demo-tabs" @tab-remove="tabRemove" @tab-change="tabChange" > <!-- :closable="item.close"动态显示移除按钮,其实目的是让首页不显示移除按钮 --> <el-tab-pane v-for="item in editableTabs" :closable="item.close" :key="item.name" :label="item.title" :name="item.name" > </el-tab-pane> </el-tabs> </template> <script setup lang="ts"> import { ref, onMounted, watch } from 'vue' import { useRoute, useRouter } from "vue-router"; import { appStore } from "@/store/appStore"; import { storeToRefs } from "pinia"; let { editableTabsValue, editableTabs } = storeToRefs(appStore()); const route = useRoute(); const router = useRouter(); //移除方法,当点击移除按钮时,这里的targetName就是所点击的元素的name const tabRemove = (targetName: string) => { //editableTabs.value就是 { title: 'Tab 1',name: '1',} 这样的元素 let tabs = editableTabs.value; //过滤掉除所点击的元素以外的元素,也就是说没有过滤的就是要移除的元素 editableTabs.value = tabs.filter((tab) => tab.name !== targetName) //计算活动标签是哪一个 //如果移除的是当前活动标签(元素),那么通过下面的逻辑进行高亮显示 let activeName = editableTabsValue.value if (activeName === targetName) { // 对数组进行遍历找到当前活动标签的位置 tabs.forEach((tab, index) => { if (tab.name === targetName) { // 找到之后取前后索引位置的元素 //找离活动标签最近的标签,右边优先于左边显示 const nextTab = tabs[index + 1] || tabs[index - 1] //如果nextTab存在就赋值,也就是如果右边没有就取左边的 if (nextTab) { activeName = nextTab.name } } }) // 给当前活动标签赋值新的name,进行高亮 editableTabsValue.value = activeName } } const tabAdd = (route: any) => { //先判断标签页是否已经存在,如果不存在才添加,findIndex判断索引 //当前路由的name是否和标签页数组item的name相等 //返回索引,如果索引为-1,则表示不存在 var index = editableTabs.value.findIndex(item => item.name === route.name); if (index === -1) { //通过push向数组中添加一个元素对象 editableTabs.value.push({ //通过路由Menu获取菜单项的title和name title: route.meta.title, name: route.name, //显示移除按钮 close: true, }); } //新添加的Tab标签页高亮 editableTabsValue.value = route.name; } const tabChange = (name: any) => { // 路由跳转到name为name的 router.push({ name: name }) } //watch() 侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。 watch( //监听什么对象(这里监听的是当前路由的路径) //当路径发生变化时,就执行回调函数 () => route.path, //新值,老值,回调函数 (newValue, oldValue) => { //console.log("新值: "+newValue, "老值: "+oldValue) //监听当路径发生改变时,就执行tabAdd添加Tab标签页的方法 tabAdd(route); }, //深度监听对象中的所有属性 { deep: true } ) </script>
//定义一个editableTabs数组,里边放初始化元素 editableTabs:[{ title: '主页', name: 'home', close: false, }], //当前活动标签,默认显示name:'home'的元素 editableTabsValue:"home"
🎑解释说明
🎑首页默认初始化显示,不需要移除
🎑 动态关联菜单项和标签页
🎑 标签页改变时触发,路由跳转,实现菜单项内容显示
🎑 其他的细节说明都在代码注释中
🎑 效果图
🌭实际操作场景一
🎑 接下来解决一种场景
🎑就比如
🎑但是当切换标签页后,会发现之前写的数据销毁了
🎑那怎么让这样的普通数据保存呢
🎑 一般的数据信息都是通过KeepAlive进行存储的
🎑将之前写的<router-view></router-view>改成下边的代码
<router-view v-slot="{Component}"> <keep-alive> <component :is="Component" /> </keep-alive> </router-view>
🌭实际操作场景二
🎑还有一种场景就是当强制回退路径时,主动打开的标签页还存在
🎑解决办法就是在初始页面(首页)中通过过滤进行数据初始化
Home.vue
<template> 首页 </template> <script setup lang="ts"> import { onMounted } from 'vue' import { appStore } from "@/store/appStore"; import { storeToRefs } from "pinia"; let { name, pass, activeTabName, tabList } = storeToRefs(appStore()); const homePage = 'home'; onMounted(() => { // console.log(activeTabName.value) if (activeTabName.value == homePage) { // console.log("执行了") activeTabName.value = homePage; //过滤,只留下首页 tabList.value = tabList.value.filter((tab) => tab.name == homePage) } }) </script>
🎑这样再执行强制回退就不会出现上面那种情况了
🎑完事!!!
💖💖总结说明
💦这部分内容有很多细节点,在代码中的注释都有详细解释,在图片中也有大概的一个思路进行分析,最终的目的就是为了将标签页和菜单项完美的进行关联,并且解决上述的两种场景