import type { LayoutMenuItem } from './menu' import { mdiHome } from '@mdi/js' import { defineStore } from 'pinia' import { computed, ref } from 'vue' export interface BreadcrumbItem { title: string to?: string disabled?: boolean icon?: string } interface BreadcrumbPayload { path: string menuItems: LayoutMenuItem[] favoriteItems?: LayoutMenuItem[] fallbackTitle?: string | null homeLabel?: string homeIcon?: string } function buildTrail(items: LayoutMenuItem[], targetPath: string): LayoutMenuItem[] | null { const walk = (nodes: LayoutMenuItem[], trail: LayoutMenuItem[]): LayoutMenuItem[] | null => { for (const node of nodes) { const nextTrail = [...trail, node] if (node.path && node.path === targetPath) return nextTrail if (node.subItems?.length) { const found = walk(node.subItems, nextTrail) if (found) return found } } return null } return walk(items || [], []) } function toBreadcrumbItems( trail: LayoutMenuItem[], homeLabel: string, homeIcon: string ): BreadcrumbItem[] { const isHomePath = (path?: string) => path === '/' || path === '' const startsWithHome = trail.length > 0 && isHomePath(trail[0]?.path) const crumbs: BreadcrumbItem[] = [] if (!startsWithHome) { crumbs.push({ title: homeLabel, to: '/', icon: homeIcon, }) } for (const [index, node] of trail.entries()) { const isLast = index === trail.length - 1 crumbs.push({ title: node.title, to: isLast ? undefined : node.path, icon: startsWithHome && index === 0 ? homeIcon : undefined, }) } return crumbs } export const useBreadcrumbStore = defineStore('breadcrumbs', () => { const items = ref([]) const homeLabel = ref('首頁') const homeIcon = ref(mdiHome) const setBreadcrumbs = (payload: BreadcrumbPayload) => { if (!payload?.path) return homeLabel.value = payload.homeLabel ?? homeLabel.value homeIcon.value = payload.homeIcon ?? homeIcon.value const trailFromMenu = buildTrail(payload.menuItems || [], payload.path) const trailFromFavorite = payload.favoriteItems?.length ? buildTrail(payload.favoriteItems, payload.path) : null const trail = trailFromMenu || trailFromFavorite if (trail?.length) { items.value = toBreadcrumbItems(trail, homeLabel.value, homeIcon.value) return } if (payload.fallbackTitle && payload.fallbackTitle !== homeLabel.value) { items.value = [ { title: homeLabel.value, to: '/', icon: homeIcon.value, }, { title: payload.fallbackTitle, }, ] return } items.value = [ { title: homeLabel.value, to: '/', icon: homeIcon.value, }, ] } const reset = () => { items.value = [] } const breadcrumbItems = computed(() => items.value) return { items, breadcrumbItems, setBreadcrumbs, reset, } })