iT邦幫忙

2024 iThome 鐵人賽

DAY 11
0
Modern Web

蓋一個自己的 Nuxt 3 UI Module系列 第 11

元件製作 layout

  • 分享至 

  • xImage
  •  

元件製作告一段落之後就發現東西散落在各處,不是那麼方便預覽各個元件,這時候就需要做一個導覽列啦~
導覽列會常駐在頁面上,只需要在頁面的部份區域進行元件頁面的切換,因此就可以把導覽列作為 layout 的一部分啦!

  • 建立 demo-layout.vue (避免名稱跟預設衝突)
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

  • 因為這是一個展示頁面,我需要對元件進行分類且需要較大區域作為元件展示,因此做了以下規劃:

    • sidebar 可以自訂固定或收折,收折時可以透過自定義的 icon 判斷快速找到項目的位置
    • 頁面寬度可以變換為1440px或全螢幕寬
    <script lang="ts" setup>
    // 基本項目
    export interface NavBaseItem {
      title: string
      url?: string
      enable: boolean
    }
    
    //* 第二層
    export interface CollapsibleGroup extends NavBaseItem {
      icon?: string
    }
    
    //* 第一層
    export interface SidebarGroup extends NavBaseItem {
      items?: CollapsibleGroup[]
      isOpen: boolean
    }
    
    const sidebarGroup = ref([
      {
        title: '表單類',
        enable: true,
        isOpen: true,
        items: [
          { title: 'Button 按鈕', enable: true, url: '/Demo/button', icon: 'arcticons:accuweather' },
          { title: 'Input 輸入框', enable: true, url: '/Demo/input', icon: 'carbon:activity' },
          { title: 'Select 選擇框', enable: true, url: '/Demo/select', icon: 'arcticons:stack-exchange' },
          { title: 'Checkbox 複選', enable: true, url: '/Demo/checkbox', icon: 'carbon:3rd-party-connected' },
          { title: 'Radio 單選', enable: true, url: '/Demo/radio', icon: 'carbon:activity' },
          { title: 'Switch 開關', enable: true, url: '/Demo/switch', icon: 'carbon:aperture' },
        ]
      },
      {
        title: '小工具類',
        enable: true,
        items: [
          { title: 'Tabs 標籤', enable: true, url: '/Demo/tab', icon: 'arcticons:stack-exchange' },
          { title: 'Tooltip 提示', enable: true, url: '/Demo/tooltip', icon: 'carbon:activity' },
    
        ]
      }
    ])
    
    // 控制符合 route 時的樣式
    const handleGenre = computed(() => {
      return 'title-match-active'
    })
    
    // top-bar 控制 sidebar 開合
    const isSidebarFold = ref(false)
    
    const isBlock = ref(false)
    
    </script>
    <template>
      <div
        class="
        hover:bg-slate-200
          fixed bottom-10 right-10 z-[50] rounded-full
          bg-slate-100 w-12 h-12 flex items-center justify-center shadow-lg duration-100"
        @click="isBlock = !isBlock"
      >
        <Icon v-if="!isBlock" name="fluent:full-screen-maximize-16-filled" size="22"/>
        <Icon v-else name="tabler:arrows-diagonal-minimize-2" size="22"/>
      </div>
      <div class="relative z-10 mx-auto flex flex-col h-screen">
        <!-- Top-bar -->
        <header class="navigation !static">
          <div
            class="w-full"
            :class="{'max-w-[1440px]': !isBlock}"
          >
            <div class="navigation-wrap">
              <!-- LOGO -->
              <div class="flex items-center">
                <a class="navigation-logo" href="/">
    	              <div class="w-10 mr-2">
                      <img src="/" alt="logo">
                    </div>
                    我的 UI 元件庫
                </a>
                <div
                  class="navigation-menu flex items-center pr-5 duration-150 ease-in hover:scale-125"
                  @click="isSidebarFold = !isSidebarFold"
                >
                  <Icon
                    name="mdi:arrow-collapse-right"
                    :class="{'rotate-180': isSidebarFold}"
                  />
                </div>
              </div>
            </div>
          </div>
        </header>
        <div
          class="relative mx-auto flex h-[600px] w-full grow"
          :class="{'max-w-[1440px]': !isBlock}"
        >
          <!-- 左邊 Side-bar -->
          <ul
            class="first-group "
            :class="{'fold': isSidebarFold}"
          >
            <!-- 第一層 start -->
            <template
              v-for="group, groupIndx in sidebarGroup"
              :key="groupIndx"
            >
              <li
                v-if="group.enable"
                class="first-item"
              >
                <div
                  class="first-title"
                  @click="group.isOpen = !group.isOpen"
                >
                  <span>{{ group.title }}</span>
                  <div
                    class="toggle-box"
                  >
                    <Icon
                      name="material-symbols:keyboard-arrow-up"
                      size="12"
                      class="duration-150"
                      :class="{'rotate-180':!group.isOpen,} "
                    />
                  </div>
                </div>
                <!-- 第二層 start -->
                <ul
                  v-show="group.isOpen"
                  class="secondary-group"
                >
                  <template
                    v-for="child, childIndx in group.items"
                    :key="childIndx"
                  >
                    <li
                      v-if="child.enable"
                      class="secondary-item"
                    >
                      <NuxtLink
                        :to="child.url"
                        class="secondary-title "
                        :active-class="handleGenre"
                      >
                        <div class="flex space-x-4">
                          <div class="icon-box">
                            <Icon :name="child.icon" :size="24" />
                          </div>
                          <h3 class="max-w-[160px] truncate">
                            {{ child.title }}
                          </h3>
                        </div>
                      </NuxtLink>
                    </li>
                  </template>
                </ul>
              </li>
            </template>
          </ul>
          <div
            class="flex grow flex-col overflow-y-scroll p-8"
            :class="{'pl-[120px]':isSidebarFold}"
          >
            <div class="grow">
              <slot />
            </div>
          </div>
        </div>
      </div>
    </template>
    
    
  • app.vue

    // app.vue
    <template>
      <NuxtLayout name="demo-layout">
        <NuxtPage />
      </NuxtLayout>
    </template>
    
  • 最後出來的效果如下~


上一篇
元件製作 tooltip
下一篇
元件製作 layout -part.2
系列文
蓋一個自己的 Nuxt 3 UI Module30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言