114 lines
3.5 KiB
Vue
114 lines
3.5 KiB
Vue
<script setup lang="ts">
|
|
import { ChevronRight, type LucideIcon } from "lucide-vue-next";
|
|
import type { RouteLocationRaw } from "vue-router";
|
|
|
|
defineProps<{
|
|
nav: {
|
|
label?: string;
|
|
items: {
|
|
title: string;
|
|
url?: RouteLocationRaw | string;
|
|
icon: LucideIcon | string;
|
|
isActive?: boolean;
|
|
items?: {
|
|
title: string;
|
|
url: string;
|
|
}[];
|
|
}[];
|
|
}[];
|
|
}>();
|
|
</script>
|
|
|
|
<template>
|
|
<SidebarGroup v-for="group in nav" :key="group.label">
|
|
<SidebarGroupLabel v-if="group.label">{{ group.label }}</SidebarGroupLabel>
|
|
<SidebarMenu>
|
|
<Collapsible
|
|
v-for="item in group.items"
|
|
:key="item.title"
|
|
as-child
|
|
:default-open="item.isActive"
|
|
class="group/collapsible"
|
|
>
|
|
<SidebarMenuItem>
|
|
<CollapsibleTrigger as-child>
|
|
<!-- 有跳转链接 -->
|
|
<NuxtLink
|
|
v-if="item.url"
|
|
v-slot="{ isActive, href, navigate }"
|
|
class="py-6"
|
|
:to="item.url"
|
|
custom
|
|
>
|
|
<SidebarMenuButton
|
|
as="a"
|
|
class="flex justify-start text-base pl-8"
|
|
:tooltip="item.title"
|
|
:is-active="isActive"
|
|
:href
|
|
@click="navigate"
|
|
>
|
|
<!-- 图标名 -->
|
|
<Icon
|
|
v-if="item.icon && typeof item.icon === 'string'"
|
|
:name="item.icon"
|
|
class="!size-6"
|
|
/>
|
|
<!-- 图标组件 -->
|
|
<component :is="item.icon" v-else class="!size-6" />
|
|
<span>{{ item.title }}</span>
|
|
<!-- 有子项目 -->
|
|
<ChevronRight
|
|
v-if="item.items"
|
|
class="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90"
|
|
/>
|
|
</SidebarMenuButton>
|
|
</NuxtLink>
|
|
<!-- 无跳转链接 -->
|
|
<SidebarMenuButton v-else :tooltip="item.title">
|
|
<!-- 图标名 -->
|
|
<Icon
|
|
v-if="item.icon && typeof item.icon === 'string'"
|
|
:name="item.icon"
|
|
size="16px"
|
|
/>
|
|
<!-- 图标组件 -->
|
|
<component :is="item.icon" v-else />
|
|
<span>{{ item.title }}</span>
|
|
<ChevronRight
|
|
v-if="item.items"
|
|
class="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90"
|
|
/>
|
|
</SidebarMenuButton>
|
|
</CollapsibleTrigger>
|
|
|
|
<CollapsibleContent v-if="item.items">
|
|
<SidebarMenuSub>
|
|
<SidebarMenuSubItem
|
|
v-for="subItem in item.items"
|
|
:key="subItem.title"
|
|
>
|
|
<NuxtLink
|
|
v-slot="{ isActive, href, navigate }"
|
|
:to="subItem.url"
|
|
custom
|
|
>
|
|
<SidebarMenuSubButton
|
|
as="a"
|
|
as-child
|
|
:is-active="isActive"
|
|
:href
|
|
@click="navigate"
|
|
>
|
|
<span>{{ subItem.title }}</span>
|
|
</SidebarMenuSubButton>
|
|
</NuxtLink>
|
|
</SidebarMenuSubItem>
|
|
</SidebarMenuSub>
|
|
</CollapsibleContent>
|
|
</SidebarMenuItem>
|
|
</Collapsible>
|
|
</SidebarMenu>
|
|
</SidebarGroup>
|
|
</template>
|