diff --git a/.astro/content.d.ts b/.astro/content.d.ts index 96b2fc3..c0082cc 100644 --- a/.astro/content.d.ts +++ b/.astro/content.d.ts @@ -194,6 +194,6 @@ declare module 'astro:content' { LiveContentConfig['collections'][C]['loader'] >; - export type ContentConfig = typeof import("./../src/content.config.mjs"); + export type ContentConfig = typeof import("../src/content.config.mjs"); export type LiveContentConfig = never; } diff --git a/.astro/settings.json b/.astro/settings.json index 1151f3b..c267df2 100644 --- a/.astro/settings.json +++ b/.astro/settings.json @@ -1,5 +1,5 @@ { "_variables": { - "lastUpdateCheck": 1774419633747 + "lastUpdateCheck": 1776058818761 } } \ No newline at end of file diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 320eaee..e8137b2 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -4,24 +4,54 @@ import 'font-awesome/css/font-awesome.min.css'; const { title = "浙江贝凡网络科技", activeNav = "home" } = Astro.props; -const navItems = [ +interface NavChild { + id: string; + label: string; + href: string; + icon: string; +} + +interface NavItem { + id: string; + label: string; + href: string; + icon: string; + children?: NavChild[]; +} + +const navItems: NavItem[] = [ { id: 'home', label: '公司介绍', href: '/', icon: 'fa-building' }, - { id: 'qazk', label: '企安智控', href: '/qazk', icon: 'fa-cogs' }, + { + id: 'products', label: '产品中心', href: '/qazk', icon: 'fa-cubes', + children: [ + { id: 'qazk', label: '企安智控', href: '/qazk', icon: 'fa-cogs' }, + { id: 'elderly', label: '智慧养老', href: '/elderly', icon: 'fa-heartbeat' }, + { id: 'construction', label: '智慧工地', href: '/construction', icon: 'fa-industry' }, + { id: 'kitchen', label: '明厨亮灶', href: '/kitchen', icon: 'fa-cutlery' }, + { id: 'education', label: '学校教育', href: '/education', icon: 'fa-graduation-cap' }, + ] + }, { id: 'customization', label: '本地化定制', href: '/customization', icon: 'fa-wrench' }, { id: 'partnership', label: '生态合作', href: '/partnership', icon: 'fa-handshake-o' }, ]; -function getNavLinkClass(itemId: string) { +function isActive(item: NavItem): boolean { + if (item.id === activeNav) return true; + if (item.children) return item.children.some(child => child.id === activeNav); + return false; +} + +function getNavLinkClass(item: NavItem) { const baseClass = 'flex items-center gap-2 px-4 py-2.5 rounded-full font-medium transition-all duration-300'; - if (itemId === activeNav) { + if (isActive(item)) { return `${baseClass} bg-primary text-white shadow-md`; } return `${baseClass} text-gray-600 hover:text-primary hover:bg-primary/10`; } -function getMobileNavLinkClass(itemId: string) { +function getMobileNavLinkClass(item: NavItem) { const baseClass = 'flex flex-col items-center justify-center py-2 px-2 min-w-[4.5rem] rounded-xl transition-all duration-300'; - if (itemId === activeNav) { + if (isActive(item)) { return `${baseClass} bg-primary text-white`; } return `${baseClass} text-gray-600 hover:bg-primary/10`; @@ -41,10 +71,35 @@ function getMobileNavLinkClass(itemId: string) { - + { - navItems.map(item => ( - + navItems.map(item => item.children ? ( + + + + {item.label} + + + + + {item.children.map(child => ( + + + {child.label} + + ))} + + + + ) : ( + {item.label} @@ -70,8 +125,16 @@ function getMobileNavLinkClass(itemId: string) { { - navItems.map(item => ( - + navItems.map(item => item.children ? ( + + + {item.label} + + ) : ( + {item.label} @@ -84,6 +147,29 @@ function getMobileNavLinkClass(itemId: string) { + + + + {navItems.find(item => item.children)?.children?.map(child => ( + + + {child.label} + + ))} + + + + + + @@ -148,6 +234,140 @@ function getMobileNavLinkClass(itemId: string) { lastScrollTop = scrollTop; }, { passive: true }); })(); + + // Desktop dropdown hover + keyboard navigation + (function() { + var dropdown = document.querySelector('[data-dropdown]'); + if (!dropdown) return; + + var trigger = dropdown.querySelector('[data-dropdown-trigger]') as HTMLElement; + var panel = dropdown.querySelector('[data-dropdown-panel]') as HTMLElement; + var chevron = dropdown.querySelector('[data-dropdown-chevron]') as HTMLElement; + var menuItems = panel.querySelectorAll('[role="menuitem"]') as NodeListOf; + var closeTimer: ReturnType | null = null; + + function openPanel() { + if (closeTimer) { clearTimeout(closeTimer); closeTimer = null; } + trigger.setAttribute('aria-expanded', 'true'); + panel.classList.remove('opacity-0', 'invisible'); + panel.classList.add('opacity-100', 'visible'); + if (chevron) chevron.style.transform = 'rotate(180deg)'; + } + + function closePanel() { + trigger.setAttribute('aria-expanded', 'false'); + panel.classList.add('opacity-0', 'invisible'); + panel.classList.remove('opacity-100', 'visible'); + if (chevron) chevron.style.transform = ''; + } + + function schedulClose() { + closeTimer = setTimeout(closePanel, 150); + } + + // Click: toggle (touch-friendly) + trigger.addEventListener('click', function() { + if (trigger.getAttribute('aria-expanded') === 'true') { + closePanel(); + } else { + openPanel(); + } + }); + + // Hover: mouse users + trigger.addEventListener('mouseenter', openPanel); + trigger.addEventListener('mouseleave', schedulClose); + panel.addEventListener('mouseenter', openPanel); + panel.addEventListener('mouseleave', schedulClose); + + // Keyboard + trigger.addEventListener('keydown', function(e: KeyboardEvent) { + if (e.key === 'ArrowDown') { + e.preventDefault(); + openPanel(); + menuItems[0]?.focus(); + } + if (e.key === 'Escape') { + closePanel(); + trigger.focus(); + } + }); + + panel.addEventListener('keydown', function(e: KeyboardEvent) { + var items = Array.from(menuItems); + var idx = items.indexOf(document.activeElement as HTMLElement); + if (e.key === 'ArrowDown') { + e.preventDefault(); + items[(idx + 1) % items.length].focus(); + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + items[(idx - 1 + items.length) % items.length].focus(); + } else if (e.key === 'Escape') { + closePanel(); + trigger.focus(); + } + }); + + document.addEventListener('click', function(e) { + if (!(dropdown as HTMLElement).contains(e.target as Node)) { + closePanel(); + } + }); + })(); + + // Mobile product dropdown + (function() { + var trigger = document.querySelector('[data-mobile-dropdown-trigger]') as HTMLElement; + var panel = document.getElementById('mobile-product-panel'); + var backdrop = document.getElementById('mobile-product-backdrop'); + if (!trigger || !panel) return; + + var isOpen = false; + + function updatePanelPosition() { + var navContainer = document.getElementById('mobile-nav-container'); + if (navContainer && panel) { + var rect = navContainer.getBoundingClientRect(); + panel.style.top = (rect.bottom) + 'px'; + } + } + + function openPanel() { + isOpen = true; + updatePanelPosition(); + trigger.setAttribute('aria-expanded', 'true'); + panel!.classList.remove('-translate-y-full', 'opacity-0', 'pointer-events-none'); + panel!.classList.add('translate-y-0', 'opacity-100', 'pointer-events-auto'); + if (backdrop) { + backdrop.classList.remove('opacity-0', 'pointer-events-none'); + backdrop.classList.add('opacity-100', 'pointer-events-auto'); + } + } + + function closePanel() { + isOpen = false; + trigger.setAttribute('aria-expanded', 'false'); + panel!.classList.add('-translate-y-full', 'opacity-0', 'pointer-events-none'); + panel!.classList.remove('translate-y-0', 'opacity-100', 'pointer-events-auto'); + if (backdrop) { + backdrop.classList.add('opacity-0', 'pointer-events-none'); + backdrop.classList.remove('opacity-100', 'pointer-events-auto'); + } + } + + trigger.addEventListener('click', function(e) { + e.preventDefault(); + isOpen ? closePanel() : openPanel(); + }); + + if (backdrop) { + backdrop.addEventListener('click', closePanel); + } + + window.addEventListener('scroll', function() { + if (isOpen && window.innerWidth < 1024) closePanel(); + }, { passive: true }); + })();