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 或者有其它问题,也欢迎在评论区提出来。

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

相关推荐
Midsummer4 天前
nuxt安装报错-网络问题
vue.js·nuxt.js
Avan_菜菜5 天前
Nuxt3 中使用 pnpm 安装的 NuxtImg 使用会提示找不到图片
前端·npm·nuxt.js
古韵6 天前
alova 3.3.0 发布:开发体验提升的重大更新
node.js·nuxt.js·next.js
尖椒土豆sss2 个月前
Nuxt3框架入门:第一个简单demo
前端·nuxt.js
尖椒土豆sss2 个月前
Nuxt3框架入门:初始化项目
前端·nuxt.js
林梦泽2 个月前
使用Cloudflare搭建Sink短链接|榨干大善人
nuxt.js
24岁学前端2 个月前
达成成就 - Nuxt Contributor 😁
nuxt.js
uhhuh2 个月前
关于Nuxt框架中的数据持久化问题
nuxt.js
用户061760544432 个月前
关于Nuxt3+Vue3的基础使用
nuxt.js
程序员爱钓鱼3 个月前
在 Nuxt 3 中实现和使用 SEO 数据:通过 useState 管理全局 SEO 信息
vue.js·后端·nuxt.js