[vue3] 统一页面导航标签

 ✨✨个人主页:沫洺的主页

📚📚系列专栏: 📖 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>

 🎑这样再执行强制回退就不会出现上面那种情况了

 🎑完事!!!

💖💖总结说明 

💦这部分内容有很多细节点,在代码中的注释都有详细解释,在图片中也有大概的一个思路进行分析,最终的目的就是为了将标签页和菜单项完美的进行关联,并且解决上述的两种场景

  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沫洺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值