115 lines
3.9 KiB
Vue
115 lines
3.9 KiB
Vue
<script setup lang="ts">
|
|
import { useRoute, useRouter } from "vue-router";
|
|
import { computed, ref, watch } from "vue";
|
|
import Breadcrumb from "@/components/ui/breadcrumb/Breadcrumb.vue";
|
|
import BreadcrumbList from "@/components/ui/breadcrumb/BreadcrumbList.vue";
|
|
import BreadcrumbItem from "@/components/ui/breadcrumb/BreadcrumbItem.vue";
|
|
import BreadcrumbLink from "@/components/ui/breadcrumb/BreadcrumbLink.vue";
|
|
import BreadcrumbSeparator from "@/components/ui/breadcrumb/BreadcrumbSeparator.vue";
|
|
import BreadcrumbEllipsis from "@/components/ui/breadcrumb/BreadcrumbEllipsis.vue";
|
|
import BreadcrumbPage from "@/components/ui/breadcrumb/BreadcrumbPage.vue";
|
|
|
|
const router = useRouter();
|
|
const route = useRoute();
|
|
|
|
type PathCrumb = { label: string; to: string };
|
|
type EllipsisCrumb = { type: "ellipsis" };
|
|
type DisplayCrumb = PathCrumb | EllipsisCrumb;
|
|
|
|
const routeTrail = ref<PathCrumb[]>([]);
|
|
|
|
function labelFromPath(path: string): string {
|
|
if (path === "/dcalc" || path === "/dcalc/home") return "首页";
|
|
if (path.startsWith("/dcalc/dnfhelper")) return "助手导入";
|
|
if (path.endsWith("/equipment")) return "装备词典";
|
|
if (path.endsWith("/skill")) return "技能面板";
|
|
if (path.endsWith("/forge")) return "打造";
|
|
if (path.includes("/character/")) {
|
|
const alter = path.split("/")[3];
|
|
return alter ? `角色 ${alter}` : "角色";
|
|
}
|
|
return "页面";
|
|
}
|
|
|
|
watch(
|
|
() => route.fullPath,
|
|
(fullPath) => {
|
|
if (!fullPath.startsWith("/dcalc")) {
|
|
routeTrail.value = [];
|
|
return;
|
|
}
|
|
|
|
const existedIndex = routeTrail.value.findIndex((item) => item.to === fullPath);
|
|
if (existedIndex >= 0) {
|
|
routeTrail.value = routeTrail.value.slice(0, existedIndex + 1);
|
|
return;
|
|
}
|
|
|
|
routeTrail.value = [
|
|
...routeTrail.value,
|
|
{
|
|
label: labelFromPath(fullPath),
|
|
to: fullPath,
|
|
},
|
|
];
|
|
},
|
|
{ immediate: true },
|
|
);
|
|
|
|
const crumbs = computed(() => routeTrail.value);
|
|
|
|
const maxCrumbs = 4; // 超过则折叠
|
|
const showEllipsis = computed(() => crumbs.value.length > maxCrumbs);
|
|
const visibleCrumbs = computed(() => {
|
|
if (!showEllipsis.value) return crumbs.value;
|
|
// 只显示第一个、...、最后两个
|
|
return [crumbs.value[0], { type: "ellipsis" }, ...crumbs.value.slice(-2)] as DisplayCrumb[];
|
|
});
|
|
|
|
function go(to: string) {
|
|
if (to && typeof to === "string" && to !== route.fullPath) router.push(to);
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<header
|
|
v-if="crumbs.length"
|
|
class="flex h-(--header-height) shrink-0 items-center gap-2 border-b transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-(--header-height)"
|
|
>
|
|
<div class="max-w-7xl mx-auto px-6 py-3 flex items-center min-h-[56px]">
|
|
<Breadcrumb>
|
|
<BreadcrumbList>
|
|
<template
|
|
v-for="(crumb, idx) in visibleCrumbs"
|
|
:key="'type' in crumb ? 'ellipsis' + idx : crumb.to"
|
|
>
|
|
<BreadcrumbSeparator v-if="idx !== 0" />
|
|
<template v-if="'type' in crumb">
|
|
<BreadcrumbEllipsis />
|
|
</template>
|
|
<template v-else>
|
|
<BreadcrumbItem>
|
|
<template v-if="idx !== visibleCrumbs.length - 1">
|
|
<BreadcrumbLink
|
|
as="a"
|
|
@click.prevent="go(crumb.to)"
|
|
class="cursor-pointer font-normal text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 transition-colors"
|
|
>{{ crumb.label }}</BreadcrumbLink
|
|
>
|
|
</template>
|
|
<template v-else>
|
|
<BreadcrumbPage class="font-semibold text-gray-900 dark:text-gray-100">{{
|
|
crumb.label
|
|
}}</BreadcrumbPage>
|
|
</template>
|
|
</BreadcrumbItem>
|
|
</template>
|
|
</template>
|
|
</BreadcrumbList>
|
|
</Breadcrumb>
|
|
</div>
|
|
</header>
|
|
</template>
|
|
|
|
<style scoped></style>
|