nuxt3+bootstrap5搭建响应式官网(一)——响应式网站导航

2,138 阅读1分钟

倒腾了半天,开始出现各种问题,终于弄出了想要的效果,二级导航效果细节后续再细调。记录一下,供参考。

任务一:创建一个在PC端正常显示的导航栏,在移动端以浮动抽屉的形式展示网站导航。

效果图如下:

Snipaste_2023-07-11_22-55-25.png

Snipaste_2023-07-11_22-56-05.png

Snipaste_2023-07-11_22-59-53.png

如何引入bootstrap?

  1. 安装bootstrap npm install bootstrap
  2. plugins/下新建useBootstrap.client.js
  3. nuxt.config.js中引入插件
//useBootstrap.client.js
import * as bootstrap from 'bootstrap'

export default defineNuxtPlugin(nuxtApp => {
  nuxtApp.provide('bootstrap', bootstrap)
})

//nuxt.config.js
export default defineNuxtConfig({
....
plugins: [
    {
      src:'@/plugins/useBootstrap.client.js',
    }
  ],
  css:[
    '@/assets/css/element.scss',
    'bootstrap/dist/css/bootstrap.min.css',
    // '@coreui/coreui/dist/css/coreui.min.css',
    '@/assets/css/main.scss',
    '@/assets/css/mh_bootstrap.scss'
  ], 

})

创建响应式NavBar组件

<template>
  <nav class="navbar navbar-expand-md fixed-top head-wrap">
    <div class="container-fluid head-box">
      <nuxt-link :to="{ name: 'index' }" class="navbar-brand mh-logo">
      </nuxt-link>
      <!-- 导航按钮 -->
      <button
        class="navbar-toggler"
        type="button"
        data-bs-toggle="offcanvas"
        data-bs-target="#offcanvasNavbarLight"
        aria-controls="offcanvasNavbarLight"
      >
        <span class="mh-navbar-toggle-ico" style="color: #fff"></span>
      </button>

      <!-- 折叠导航栏。S -->
      <div
        class="offcanvas offcanvas-end"
        tabindex="-1"
        id="offcanvasNavbarLight"
        aria-labelledby="offcanvasNavbarLightLabel"
      >
        <div class="offcanvas-header">
          <h5 class="offcanvas-title" id="offcanvasNavbarLightLabel">
            Offcanvas
          </h5>
          <button
            type="button"
            class="btn-close"
            data-bs-dismiss="offcanvas"
            aria-label="Close"
          ></button>
        </div>
        <div class="offcanvas-body">
          <ul class="navbar-nav me-auto mb-2 mb-md-0 navbar-nav-scroll">
            <li
              class="nav-item"
              v-for="(site, index) in pageList"
              :key="'nav_' + index"
            >
              <div
                v-if="site.children && site.children.length > 1"
                class="dropdown"
              >
                <nuxt-link
                  :to="site.url"
                  active-class="navCur"
                  title="site.name"
                  class="dropdown-toggle nav-link"
                  data-bs-toggle="dropdown"
                  aria-expanded="false"
                  >{{ site.name }}
                </nuxt-link>
                <div class="dropdown-menu nav-down-box">
                  <ul>
                    <li
                      v-for="(snav, index) in site.children"
                      :key="'snav_' + index"
                    >
                      <nuxt-link
                        class="dropdown-item"
                        :to="{
                          path: site.url + '/' + snav.param,
                          query: snav.param,
                        }"
                        >{{ snav.name }}</nuxt-link
                      >
                    </li>
                  </ul>
                </div>
              </div>
              <nuxt-link v-else :to="site.url" class="nav-link">{{
                site.name
              }}</nuxt-link>
            </li>
          </ul>
          <div class="d-flex zixun-phone">
            <i class="zx-icon"></i>
            <span>021-59921831</span>
          </div>
        </div>
      </div>
      <!-- 折叠导航栏。E -->
    </div>
  </nav>
</template>

<script setup>
import usePageMenu from "@/composables/usePageMenu.js";

const { pageList } = usePageMenu();
</script>

<style lang="scss" scoped>
.nav-bar {
  --bs-navbar-color: #fff;
  --bs-navbar-nav-link-padding-x: 40px;
}
.mh-logo {
  display: inline-block;
  width: 188px;
  height: 100%;
  background: url(~/assets/images/mh_logo.png) no-repeat center;
  background-size: contain;
}

.head-wrap {
  position: relative;
  z-index: 10;
  box-sizing: border-box;
  height: 80px;
  padding: 10px 0;
  background-color: rgba(0, 0, 0, 0.78);

  // &::after {
  //   content: "";
  //   display: flex;
  //   height: 1px;
  //   width: 100%;
  //   background-color: rgba(255, 255, 255, 0.5);
  // }

  .head-box {
    max-width: 1400px;
    position: relative;
    z-index: 10;
    color: #fff;
    height: 60px;
    // overflow: hidden;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  .mh-navbar-toggle-ico {
    display: inline-block;
    width: 32px;
    height: 32px;
    background: url(~/assets/images/nav_ico.png) no-repeat center;
    background-size: contain;
  }

  .head-left {
    font-size: 28px;
    font-weight: 600;
    vertical-align: middle;
    display: inline-block;
    word-spacing: 0.3em;
    height: 100%;
  }

  .zixun-phone {
    display: flex;
    align-items: center;
    color: #d81e06;
    margin-left: 20px;
    font-size: 18px;
    animation: bounce-up 2s linear infinite;
    .zx-icon {
      display: inline-block;
      width: 40px;
      height: 40px;
      margin-right: 10px;
      background: url(~/assets/images/ico_phone.png) no-repeat center;
      background-size: contain;
    }
  }
}




:deep(.navbar-toggler) {
  color: #fff;
}

@media (min-width: 768px) {
  .lg-nav-box {
    display: flex;
  }
  :deep(.navbar-expand-md .nvabar-nav .nav-link) {
    padding-right: var(--bs-navbar-nav-link-padding-x);
    padding-left: var(--bs-navbar-nav-link-padding-x);
  }
  .zixun-phone {
    display: none;
  }
}
</style>