增加产品中心二级菜单
This commit is contained in:
Vendored
+1
-1
@@ -194,6 +194,6 @@ declare module 'astro:content' {
|
|||||||
LiveContentConfig['collections'][C]['loader']
|
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;
|
export type LiveContentConfig = never;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"_variables": {
|
"_variables": {
|
||||||
"lastUpdateCheck": 1774419633747
|
"lastUpdateCheck": 1776058818761
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+229
-9
@@ -4,24 +4,54 @@ import 'font-awesome/css/font-awesome.min.css';
|
|||||||
|
|
||||||
const { title = "浙江贝凡网络科技", activeNav = "home" } = Astro.props;
|
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: 'home', label: '公司介绍', href: '/', icon: 'fa-building' },
|
||||||
|
{
|
||||||
|
id: 'products', label: '产品中心', href: '/qazk', icon: 'fa-cubes',
|
||||||
|
children: [
|
||||||
{ id: 'qazk', label: '企安智控', href: '/qazk', icon: 'fa-cogs' },
|
{ 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: 'customization', label: '本地化定制', href: '/customization', icon: 'fa-wrench' },
|
||||||
{ id: 'partnership', label: '生态合作', href: '/partnership', icon: 'fa-handshake-o' },
|
{ 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';
|
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} bg-primary text-white shadow-md`;
|
||||||
}
|
}
|
||||||
return `${baseClass} text-gray-600 hover:text-primary hover:bg-primary/10`;
|
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';
|
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} bg-primary text-white`;
|
||||||
}
|
}
|
||||||
return `${baseClass} text-gray-600 hover:bg-primary/10`;
|
return `${baseClass} text-gray-600 hover:bg-primary/10`;
|
||||||
@@ -43,8 +73,33 @@ function getMobileNavLinkClass(itemId: string) {
|
|||||||
</a>
|
</a>
|
||||||
<nav class="hidden lg:flex items-center gap-3">
|
<nav class="hidden lg:flex items-center gap-3">
|
||||||
{
|
{
|
||||||
navItems.map(item => (
|
navItems.map(item => item.children ? (
|
||||||
<a href={item.href} class={getNavLinkClass(item.id)}>
|
<div class="relative" data-dropdown>
|
||||||
|
<button type="button" class={`${getNavLinkClass(item)} cursor-pointer`}
|
||||||
|
aria-haspopup="true" aria-expanded="false" data-dropdown-trigger>
|
||||||
|
<i class={`fa ${item.icon}`}></i>
|
||||||
|
<span>{item.label}</span>
|
||||||
|
<i class="fa fa-angle-down text-xs ml-0.5 transition-transform duration-200" data-dropdown-chevron></i>
|
||||||
|
</button>
|
||||||
|
<div class="absolute top-full left-1/2 -translate-x-1/2 pt-2 opacity-0 invisible transition-all duration-200 z-50"
|
||||||
|
data-dropdown-panel role="menu">
|
||||||
|
<div class="bg-white rounded-xl shadow-xl border border-gray-100 py-2 min-w-[200px]">
|
||||||
|
{item.children.map(child => (
|
||||||
|
<a href={child.href} role="menuitem"
|
||||||
|
class={`flex items-center gap-3 px-4 py-2.5 transition-colors ${
|
||||||
|
child.id === activeNav
|
||||||
|
? 'bg-primary/10 text-primary font-medium'
|
||||||
|
: 'text-gray-700 hover:bg-primary/10 hover:text-primary'
|
||||||
|
}`}>
|
||||||
|
<i class={`fa ${child.icon} w-5 text-center`}></i>
|
||||||
|
<span>{child.label}</span>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<a href={item.href} class={getNavLinkClass(item)}>
|
||||||
<i class={`fa ${item.icon}`}></i>
|
<i class={`fa ${item.icon}`}></i>
|
||||||
<span>{item.label}</span>
|
<span>{item.label}</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -70,8 +125,16 @@ function getMobileNavLinkClass(itemId: string) {
|
|||||||
<div class="flex items-center justify-between px-2 py-2">
|
<div class="flex items-center justify-between px-2 py-2">
|
||||||
<div class="flex items-center gap-1 overflow-x-auto no-scrollbar">
|
<div class="flex items-center gap-1 overflow-x-auto no-scrollbar">
|
||||||
{
|
{
|
||||||
navItems.map(item => (
|
navItems.map(item => item.children ? (
|
||||||
<a href={item.href} class={getMobileNavLinkClass(item.id)}>
|
<button type="button"
|
||||||
|
class={getMobileNavLinkClass(item)}
|
||||||
|
aria-haspopup="true" aria-expanded="false"
|
||||||
|
data-mobile-dropdown-trigger>
|
||||||
|
<i class={`fa ${item.icon} text-lg mb-1`}></i>
|
||||||
|
<span class="text-xs font-medium whitespace-nowrap">{item.label}</span>
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<a href={item.href} class={getMobileNavLinkClass(item)}>
|
||||||
<i class={`fa ${item.icon} text-lg mb-1`}></i>
|
<i class={`fa ${item.icon} text-lg mb-1`}></i>
|
||||||
<span class="text-xs font-medium whitespace-nowrap">{item.label}</span>
|
<span class="text-xs font-medium whitespace-nowrap">{item.label}</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -84,6 +147,29 @@ function getMobileNavLinkClass(itemId: string) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile Product Panel -->
|
||||||
|
<div id="mobile-product-panel"
|
||||||
|
class="lg:hidden fixed left-0 right-0 z-30 bg-white shadow-xl border-b border-gray-200 transform -translate-y-full opacity-0 pointer-events-none transition-all duration-300"
|
||||||
|
style="top: 108px;" role="menu">
|
||||||
|
<div class="max-w-md mx-auto px-4 py-4 grid grid-cols-3 gap-3">
|
||||||
|
{navItems.find(item => item.children)?.children?.map(child => (
|
||||||
|
<a href={child.href} role="menuitem"
|
||||||
|
class={`flex flex-col items-center gap-1.5 py-3 px-2 rounded-xl transition-colors ${
|
||||||
|
child.id === activeNav
|
||||||
|
? 'bg-primary/10 text-primary'
|
||||||
|
: 'hover:bg-primary/10'
|
||||||
|
}`}>
|
||||||
|
<i class={`fa ${child.icon} text-xl ${child.id === activeNav ? 'text-primary' : 'text-primary'}`}></i>
|
||||||
|
<span class={`text-xs font-medium ${child.id === activeNav ? 'text-primary' : 'text-gray-700'}`}>{child.label}</span>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile Product Backdrop -->
|
||||||
|
<div id="mobile-product-backdrop"
|
||||||
|
class="lg:hidden fixed inset-0 z-20 bg-black/20 opacity-0 pointer-events-none transition-opacity duration-300"></div>
|
||||||
|
|
||||||
<!-- Spacer for mobile to prevent content overlap (header 3.5rem + nav 2.5rem) -->
|
<!-- Spacer for mobile to prevent content overlap (header 3.5rem + nav 2.5rem) -->
|
||||||
<div class="lg:hidden h-[6rem] transition-all duration-300"></div>
|
<div class="lg:hidden h-[6rem] transition-all duration-300"></div>
|
||||||
|
|
||||||
@@ -148,6 +234,140 @@ function getMobileNavLinkClass(itemId: string) {
|
|||||||
lastScrollTop = scrollTop;
|
lastScrollTop = scrollTop;
|
||||||
}, { passive: true });
|
}, { 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<HTMLElement>;
|
||||||
|
var closeTimer: ReturnType<typeof setTimeout> | 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 });
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
Reference in New Issue
Block a user