Nuxt3 仿腾讯课堂多级下拉菜单 Dropdown 制作

本文由 Loui 原创,如需转载,请先私信或评论。

背景

腾讯课堂 PC 端网页顶部导航条有这样一个多级下拉菜单,鼠标悬浮上去可以一级级地查看课程分类,效果非常好,想用 Nuxt3 仿照做一个。

直接使用 antdv 里的 Dropdown 组件也可以实现类似的效果,如下所示:

直接用这个组件和腾讯课堂的效果还是有一些不同,也不太好展示具体的课程分类。所以还是决定自己做一个,下面是最终效果和实现步骤,供大家参考、批评。

最终效果

我这里使用的是 Nuxt3 ,其实具体实现的代码和使用 Vue3 去做是差不多的,简单改一下就能在 Vue3 项目中使用,和框架并没有很大关系。

实现步骤

第一步:准备三个响应式变量

vue 复制代码
<script setup lang="ts"> 
const isFirstOpened = ref(false) 
const isSecondOpened = ref(false) 
const secondId = ref(-1)  // secondId 默认是 -1
</script>

第二步:准备页面结构和样式

vue 复制代码
<template>

  // 最大的div,使用相对定位,之后出现的弹框都放到它里面,并设置成绝对定位
  <div
    class="box"
    @mouseenter="isFirstOpened = true"
    @mouseleave="isFirstOpened = false"
  >
    课程分类
    
    // 这里我使用的是 Nuxt 里的 Icon 模块:https://nuxt.com/modules/icon
    // 你需要在项目中额外下载引入才能使用
    <Icon
      name="icon-park:up"
      class="icon"
      :class="{ rotate: isFirstOpened }"
    />

    // 一级弹框,只有一个,里面是很多个 .first-item
    // 根据 isFirstOpened 决定是否显示
    // 如果鼠标移入了它,那就让 isSecondOpened 变成 true
    // 如果鼠标移出了它,那就让 isSecondOpened 变成 false,secondId 恢复成原始值 -1
    <div
      v-show="isFirstOpened"
      class="first"
      @mouseenter="() => { isSecondOpened = true }"
      @mouseleave="() => { isSecondOpened = false; secondId = -1 }"
    >
      // 循环产生 7 个 .first-item
      // 由于暂时没有准备数据,所以就直接简单地循环7次,你可以根据需要改成遍历后台传过来的数据
      // 如果鼠标移入了一个 item,那就让 secondId 变成相应的序号
      <div
        v-for="i in 7"
        :key="i"
        class="first-item"
        @mouseenter="() => { secondId = i }"
      >
        <NuxtLink>
          IT·互联网
        </NuxtLink>
        
        // 二级弹框,每个 .first-item 里都有一个
        // 根据 isSecondOpened 和 secondId 决定是否显示
        // 里面的内容就是相应分类下细分分类
        <div
          v-show="isSecondOpened && secondId === i"
          class="second"
        >
          <div
            v-for="ii in 7"
            :key="ii"
            class="line"
          >
            <NuxtLink class="left">
              前沿技术{{ secondId }}
            </NuxtLink>
            <div class="right">
              <NuxtLink
                v-for="iii in 6"
                :key="iii"
                class="right-item"
                to="/"
              >
                人工智能{{ secondId }}
              </NuxtLink>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.box {
  position: relative; 
  display: flex;
  align-items: center;
  height: 68px;
  font-size: 14px;
  color: #3e454d;
  cursor: pointer;

  // 给下拉图标加了一点动画效果,和腾讯课堂保持一致
  .icon {
    margin-left: 2px;
    font-size: 16px;
    transition: all 0.3s ease-in-out;
    transform: rotate(-180deg);

    &.rotate {
      transform: rotate(0);
    }
  }

  .first {
    position: absolute;
    top: 80%;
    left: 50%;
    width: 83px;
    padding: 6px;
    background-color: #fff;
    border-radius: 10px;
    box-shadow: rgb(0 80 179 / 12%) 0 8px 30px;
    transform: translateX(-50%);

    .first-item {
      display: block;
      padding: 13px 0;
      font-weight: bolder;
      color: #2b2c30;
      text-align: center;
      border-radius: 9px;
      transition: all 0.1s ease-in-out;

      &:hover {
        background-color: $primary-bg;
      }

      .second {
        position: absolute;
        top: 0;
        left: 100%;
        // 二级弹框需要设置最小高度,和腾讯课堂保持一致
        min-height: 316px;
        padding: 10px 20px;
        background-color: #fff;
        border-radius: 12px;
        box-shadow: rgb(0 80 179 / 12%) 0 8px 30px;

        .line {
          display: flex;
          align-items: flex-start;
          width: 498px;
          padding: 10px 0;
          font-size: 12px;
          transition: all 0.1s ease-in-out;

          .left {
            min-width: 80px;
            text-align: left;

            &:hover {
              color: $primary;
            }
          }

          .right {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;

            .right-item {
              font-weight: normal;
              color: #667280;
              text-decoration: none;

              &:hover {
                color: $primary;
              }
            }
          }
        }
      }
    }
  }
}
</style>

总结

在这个下拉菜单中,我们通过控制响应式变量的值来显示和隐藏不同的部分。这套代码其实还有很多可以完善的地方,如果你有其他更好的实现方法或者思路,欢迎在评论区补充。如果你发现了什么 Bug 或者有其它问题,也欢迎在评论区提出来。

如果这篇文章有帮到你,还请点一个不要钱的赞,感谢支持 ❤️

相关推荐
JXDN1 个月前
Nuxt.js代码风格配置
vue.js·nuxt.js
Amd7941 个月前
Nuxt.js 应用中的 modules:done 事件钩子详解
生命周期·前端开发·nuxt.js·钩子函数·代码示例·应用初始化·modules:done
Amd7941 个月前
Nuxt.js 应用中的 modules:before 事件钩子详解
生命周期·配置·模块·nuxt.js·初始化·钩子·环境设置
熊猫在哪1 个月前
nuxt3脚手架安装报错解决方法
前端·javascript·nuxt.js
Amd7941 个月前
Nuxt.js 应用中的 restart 事件钩子详解
nuxt.js·实例方法·开发技巧·普通重启·硬重启·应用重启·重启方法
Amd7941 个月前
Nuxt.js 应用中的 close 事件钩子详解
生命周期·nuxt.js·日志记录·状态保存·资源清理·应用关闭·close钩子
Amd7941 个月前
Nuxt.js 应用中的 ready 事件钩子详解
生命周期·前端开发·nuxt.js·请求处理·应用初始化·nuxt实例·ready钩子
Amd7941 个月前
Nuxt.js 应用中的 kit:compatibility 事件钩子详解
浏览器·开发·应用·nuxt.js·插件·兼容性·钩子
Amd7941 个月前
Nuxt.js 应用中的 page:transition:finish 钩子详解
前端开发·nuxt.js·状态管理·钩子函数·ui更新·页面动画·页面过渡
Amd7941 个月前
Nuxt.js 应用中的 page:finish 钩子详解
nuxt.js·性能分析·用户体验·钩子·suspense·状态更新·page:finish