Skip to content

大家好,我是村长!

本节我们实现项目基础布局,前面大家也看到了,我们的网站包括两个布局:

  • 基础布局:用于网站内容部分;
  • 登录布局:用于登录、注册部分。

基础布局

基础布局为上中下结构,包括:

  • MyHeader
  • Content
  • MyFooter

创建基础布局

下面我们创建基础布局,layouts/default.vue

vue
    <template>
      <div class="min-w-[1024px] bg-gray-100 flex flex-col min-h-screen">
        <MyHeader />
        <main class="container m-auto mt-20">
          <slot />
        </main>
        <MyFooter />
      </div>
    </template>

MyHeader 组件

这里我们需要创建 components/MyHeader.vue 和 components/MyFooter.vue。

MyHeader 组件:包含 logo、导航栏、登录按钮或用户信息。

vue
    <script setup>
    const route = useRoute()
    const menus = ref([
      { path: '/', label: '首页' },
      { path: '/column', label: '专栏' },
      { path: '/course', label: '课程' },
    ])
    </script>
    
    <template>
      <div class="bg-white fixed top-0 left-0 right-0 shadow-sm z-1000">
        <div class="container m-auto flex items-center h-[60px] px-4">
          <NButton text strong class="text-xl" @click="navigateTo('/')">
            羊村学堂
          </NButton>
    
          <div class="flex-1 flex items-center px-4">
            <Menu
              v-for="menu in menus"
              :key="menu.path"
              :active="route.path === menu.path"
              @click="navigateTo(menu.path)"
            >
              {{ menu.label }}
            </Menu>
          </div>
    
          <NuxtLink to="/login">
            <NButton secondary strong>
              登录
            </NButton>
          </NuxtLink>
        </div>
      </div>
    </template>

Menu.vue 组件:

vue
    <script setup>
    defineProps({
      active: {
        type: Boolean,
        default: false,
      },
    })
    </script>
    
    <template>
      <div class="transition-all duration-[0.2s] px-2 py-1 mx-1 rounded cursor-pointer hover:(bg-blue-50 text-blue-700) active:(!bg-blue-100)" :class="{ 'menu-item-active': active }">
        <slot />
      </div>
    </template>
    
    <style>
    .menu-item-active {
      background-color: rgb(219, 254, 238);
      color: rgb(52, 157, 96);
    }
    </style>

MyFooter组件

MyFooter组件:链接和版权信息。

vue
    <template>
      <div class="mt-auto bg-dark-400 text-light-500">
        <div class="flex items-center justify-center pb-1 pt-2">
          <a href="https://github.com/57code/nuxt-app" class="p-3">项目代码</a>
          <a href="https://s.juejin.cn/ds/AC8SpfR/" class="p-3">视频课程</a>
        </div>
        <div class="mx-auto border-solid border-0 border-t border-gray-700 text-center py-4">
          Copyright© 2023 by YCXT
        </div>
      </div>
    </template>

登录/注册布局

下面完成登录布局,登录、注册页面仅需一个居中的容器即可:

创建登录布局

下面创建登录布局组件,layouts/blank.vue:

vue
    <template>
      <div class="min-h-screen flex justify-center items-center bg-gray-100">
        <div class="shadow-lg bg-white rounded-lg p-5">
          <slot />
        </div>
      </div>
    </template>

应用布局

添加布局组件

首先给 app.vue 添加布局组件:

vue
    <template>
      <div>
        <NMessageProvider>
          <!-- 添加NuxtLayout -->
          <NuxtLayout>
            <NuxtPage />
          </NuxtLayout>
        </NMessageProvider>
      </div>
    </template>
    
    <style>
    a {
      color: white;
      text-decoration: none;
    }
    </style>

创建页面

下面创建相关页面测试一下:

  • course.vue;

  • column.vue;

  • login.vue;

  • register.vue。

course.vue 和 column.vue 使用默认布局,也暂时不需要内容。login.vue 和 register.vue 需要明确指定 blank 布局:

typescript
    definePageMeta({
      layout: 'blank',
    })

下面是 login.vue 和 register.vue 完整内容:

vue
    <script setup lang="ts">
    useHead({
      title: '登录',
    })
    
    definePageMeta({
      layout: 'blank',
    })
    </script>
    
    <template>
      <h2 class="flex justify-between">
        返回羊村
        <nuxt-link to="/register">
          <NButton quaternary type="primary" size="tiny">
            还未入村?
          </NButton>
        </nuxt-link>
      </h2>
    
      <NAlert title="演示账号和密码为:test" type="info" class="mb-6" />
    
      <NForm ref="formRef" class="w-[340px]" size="large">
        <NFormItem :show-label="false" path="username">
          <NInput clearable placeholder="用户名" />
        </NFormItem>
        <NFormItem :show-label="false" path="password">
          <NInput clearable placeholder="密码" type="password" />
        </NFormItem>
    
        <div>
          <NButton class="w-full" type="primary">
            登录
          </NButton>
        </div>
      </NForm>
    </template>
    
    
    
    <script setup lang="ts">
    useHead({
      title: '注册',
    })
    
    definePageMeta({
      layout: 'blank',
    })
    </script>
    
    <template>
      <h2>加入羊群</h2>
      <NForm ref="formRef" class="w-[340px]" size="large">
        <NFormItem :show-label="false" path="username">
          <NInput clearable placeholder="用户名" />
        </NFormItem>
        <NFormItem :show-label="false" path="password">
          <NInput clearable placeholder="密码" type="password" />
        </NFormItem>
        <NFormItem :show-label="false" path="repassword">
          <NInput clearable placeholder="确认密码" type="password" />
        </NFormItem>
    
        <div>
          <NButton class="w-full" type="primary">
            登录
          </NButton>
        </div>
        <div class="flex justify-center items-center w-full text-xs mt-5 text-gray-600">
          注册即同意
          <NButton quaternary type="primary" size="tiny">
            《服务协议》
          </NButton>

          <NButton quaternary type="primary" size="tiny">
            《隐私政策》
          </NButton>
        </div>
      </NForm>
    </template>

最终效果

主页和登录页在应用了布局之后的最终效果如下:

下次预告

布局结束,打好了基础,接下来完成项目登录注册部分的业务!

Released under the MIT License.