元件製作告一段落之後就發現東西散落在各處,不是那麼方便預覽各個元件,這時候就需要做一個導覽列啦~
導覽列會常駐在頁面上,只需要在頁面的部份區域進行元件頁面的切換,因此就可以把導覽列作為 layout 的一部分啦!
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
因為這是一個展示頁面,我需要對元件進行分類且需要較大區域作為元件展示,因此做了以下規劃:
<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>
最後出來的效果如下~