409 lines
13 KiB
TypeScript
409 lines
13 KiB
TypeScript
'use client';
|
|
|
|
import { Book, Menu, Sunset, Trees, Zap } from "lucide-react";
|
|
import { Tractor, Map, Clipboard, Package, Brain, Droplets, Settings } from 'lucide-react';
|
|
import { MessageBell } from './components/MessageBell';
|
|
import { UserProfile } from './components/UserProfile';
|
|
import { ThemeToggle } from './ThemeToggle';
|
|
import { AuthProvider } from './components/auth/AuthContext';
|
|
import { useElementHeight } from '@/hooks/useElementHeight';
|
|
import { useViewHeight } from '@/hooks/useViewHeight';
|
|
import { usePathname } from 'next/navigation';
|
|
import { useRef, useEffect, useState } from 'react';
|
|
// 注释掉 Accordion 相关导入,因为不再需要二级菜单
|
|
// import {
|
|
// Accordion,
|
|
// AccordionContent,
|
|
// AccordionItem,
|
|
// AccordionTrigger,
|
|
// } from "@/components/ui/accordion";
|
|
import { useLayoutStore } from '@/stores/useLayoutStore';
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
NavigationMenu,
|
|
NavigationMenuContent,
|
|
NavigationMenuItem,
|
|
NavigationMenuLink,
|
|
NavigationMenuList,
|
|
NavigationMenuTrigger,
|
|
} from "@/components/ui/navigation-menu";
|
|
import {
|
|
Sheet,
|
|
SheetContent,
|
|
SheetHeader,
|
|
SheetTitle,
|
|
SheetTrigger,
|
|
} from "@/components/ui/sheet";
|
|
|
|
interface MenuItem {
|
|
title: string;
|
|
url: string;
|
|
description?: string;
|
|
icon?: React.ReactNode;
|
|
items?: MenuItem[];
|
|
}
|
|
|
|
interface Navbar1Props {
|
|
logo?: {
|
|
url: string;
|
|
src: string;
|
|
alt: string;
|
|
title: string;
|
|
};
|
|
menu?: MenuItem[];
|
|
auth?: {
|
|
login: {
|
|
title: string;
|
|
url: string;
|
|
};
|
|
signup: {
|
|
title: string;
|
|
url: string;
|
|
};
|
|
};
|
|
}
|
|
const navbarData = {
|
|
logo: {
|
|
url: "/",
|
|
src: "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/logos/shadcnblockscom-icon.svg",
|
|
alt: "Crop-X Logo",
|
|
title: "智慧农业生产管理系统",
|
|
},
|
|
menu: [
|
|
{
|
|
title: "智能农机管理系统",
|
|
url: "/agricultural-machinery",
|
|
description: "农机档案、实时监控、精准作业管理",
|
|
icon: <Tractor className="size-5 shrink-0" />,
|
|
},
|
|
{
|
|
title: "地块信息管理系统",
|
|
url: "/land-information",
|
|
description: "地块档案、地图管理、空间分析",
|
|
icon: <Map className="size-5 shrink-0" />,
|
|
},
|
|
{
|
|
title: "农事操作管理系统",
|
|
url: "/farming-operation",
|
|
description: "农事计划、任务管理、操作执行",
|
|
icon: <Clipboard className="size-5 shrink-0" />,
|
|
},
|
|
{
|
|
title: "农业资产管理系统",
|
|
url: "/agricultural-asset",
|
|
description: "基础信息、采购管理、库存管理",
|
|
icon: <Package className="size-5 shrink-0" />,
|
|
},
|
|
{
|
|
title: "AI作物模型精准决策系统",
|
|
url: "/ai-crop-model",
|
|
description: "数据感知、模型应用、智能决策",
|
|
icon: <Brain className="size-5 shrink-0" />,
|
|
},
|
|
{
|
|
title: "水肥一体化控制系统",
|
|
url: "/water-fertilizer-control",
|
|
description: "水肥机管理、智能灌溉、配方管理",
|
|
icon: <Droplets className="size-5 shrink-0" />,
|
|
},
|
|
{
|
|
title: "中心配置管理系统",
|
|
url: "/central-config",
|
|
description: "租户管理、用户管理、系统监控",
|
|
icon: <Settings className="size-5 shrink-0" />,
|
|
},
|
|
{
|
|
title: "API 测试示例",
|
|
url: "/api-example",
|
|
description: "测试和展示 OpenAPI 客户端调用",
|
|
icon: <Brain className="size-5 shrink-0" />,
|
|
},
|
|
],
|
|
auth: {
|
|
login: { title: "登录", url: "/login" },
|
|
signup: { title: "注册", url: "/register" },
|
|
},
|
|
};
|
|
const Navbar1 = () => {
|
|
const logo = navbarData.logo
|
|
const menu = navbarData.menu
|
|
const auth = navbarData.auth
|
|
const pathname = usePathname()
|
|
const containerStyle = {
|
|
maxWidth:"100%",marginLeft:"0px",marginRight:"0px",paddingLeft:"1rem",paddingRight:"0rem"
|
|
}
|
|
|
|
// 检查当前路径是否匹配菜单项
|
|
const isMenuActive = (url: string) => {
|
|
// 精确匹配
|
|
if (pathname === url) return true;
|
|
// 检查是否是该菜单下的子路径
|
|
if (pathname.startsWith(url + '/')) return true;
|
|
return false;
|
|
}
|
|
|
|
// 使用自定义 Hook 计算高度
|
|
const { elementRef, updateHeight } = useElementHeight({
|
|
immediate: true, // 立即计算高度
|
|
onUpdate: (height: number) => {
|
|
// 更新 Zustand store 中的状态
|
|
const { setNavigatorHeight } = useLayoutStore.getState();
|
|
setNavigatorHeight(height);
|
|
}
|
|
});
|
|
|
|
|
|
// 监听页面高度变化
|
|
useViewHeight();
|
|
|
|
const handleMessageClick = () => {
|
|
// 处理消息点击事件,可以跳转到消息中心页面
|
|
console.log('Navigate to message center');
|
|
};
|
|
|
|
const handleProfileClick = () => {
|
|
// 处理个人中心点击事件
|
|
console.log('Navigate to profile page');
|
|
};
|
|
|
|
return (
|
|
<AuthProvider>
|
|
<section className="py-4" ref={elementRef}>
|
|
<div className="container" style = {containerStyle}>
|
|
{/* Desktop Menu */}
|
|
<nav className="hidden justify-between lg:flex">
|
|
<div className="flex items-center gap-6">
|
|
{/* Logo */}
|
|
|
|
<span className="flex items-center gap-2">
|
|
<div className="flex items-center gap-3 flex-shrink-0">
|
|
<div className="w-10 h-10 bg-green-600 rounded-lg flex items-center justify-center">
|
|
<Tractor className="w-6 h-6 text-white" />
|
|
</div>
|
|
<div>
|
|
<h1 className="text-green-800">智慧农业生产管理系统</h1>
|
|
<p className="text-xs text-muted-foreground">Smart Agriculture Management System</p>
|
|
</div>
|
|
</div>
|
|
</span>
|
|
<div className="flex items-center gap-1 overflow-x-auto flex-1 min-w-0" style={{ maxWidth: '70vw' }}>
|
|
<style jsx>{`
|
|
div::-webkit-scrollbar {
|
|
height: 6px;
|
|
}
|
|
div::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
div::-webkit-scrollbar-thumb {
|
|
background-color: #d1d5db;
|
|
border-radius: 3px;
|
|
}
|
|
div::-webkit-scrollbar-thumb:hover {
|
|
background-color: #9ca3af;
|
|
}
|
|
div {
|
|
scrollbar-width: thin;
|
|
scrollbar-color: #d1d5db transparent;
|
|
}
|
|
.navigation-menu-container {
|
|
max-width: 70vw;
|
|
}
|
|
`}</style>
|
|
<NavigationMenu>
|
|
<NavigationMenuList className="flex gap-1 min-w-max">
|
|
{menu.map((item) => renderMenuItem(item, isMenuActive))}
|
|
</NavigationMenuList>
|
|
</NavigationMenu>
|
|
</div>
|
|
</div>
|
|
<div className="flex gap-2" style = {{alignItems:"center"}}>
|
|
<ThemeToggle />
|
|
<MessageBell onMessageClick={handleMessageClick} />
|
|
<UserProfile onProfileClick={handleProfileClick} />
|
|
</div>
|
|
</nav>
|
|
|
|
{/* Mobile Menu */}
|
|
<div className="block lg:hidden">
|
|
<div className="flex items-center justify-between">
|
|
{/* Logo */}
|
|
<a href={logo.url} className="flex items-center gap-2">
|
|
<img
|
|
src={logo.src}
|
|
className="max-h-8 dark:invert"
|
|
alt={logo.alt}
|
|
/>
|
|
</a>
|
|
<Sheet>
|
|
<SheetTrigger asChild>
|
|
<Button variant="outline" size="icon">
|
|
<Menu className="size-4" />
|
|
</Button>
|
|
</SheetTrigger>
|
|
<SheetContent className="overflow-y-auto">
|
|
<SheetHeader>
|
|
<SheetTitle>
|
|
<a href={logo.url} className="flex items-center gap-2">
|
|
<img
|
|
src={logo.src}
|
|
className="max-h-8 dark:invert"
|
|
alt={logo.alt}
|
|
/>
|
|
</a>
|
|
</SheetTitle>
|
|
</SheetHeader>
|
|
<div className="flex flex-col gap-6 p-4">
|
|
{/* 简化移动端菜单,不再使用 Accordion */}
|
|
<div className="flex w-full flex-col gap-4">
|
|
{menu.map((item) => renderMobileMenuItem(item, isMenuActive))}
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-3">
|
|
<div className="flex justify-center">
|
|
<ThemeToggle />
|
|
</div>
|
|
<div className="flex justify-center">
|
|
<MessageBell onMessageClick={handleMessageClick} />
|
|
</div>
|
|
<div className="flex justify-center">
|
|
<UserProfile onProfileClick={handleProfileClick} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</SheetContent>
|
|
</Sheet>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</AuthProvider>
|
|
);
|
|
};
|
|
|
|
const renderMenuItem = (item: MenuItem, isMenuActive: (url: string) => boolean) => {
|
|
// 注释掉二级菜单相关代码,项目不需要二级菜单
|
|
// if (item.items) {
|
|
// return (
|
|
// <NavigationMenuItem key={item.title}>
|
|
// <NavigationMenuTrigger className="whitespace-nowrap">{item.title}</NavigationMenuTrigger>
|
|
// <NavigationMenuContent className="bg-popover text-popover-foreground">
|
|
// {item.items.map((subItem) => (
|
|
//
|
|
// <NavigationMenuLink asChild key={subItem.title} className="w-80">
|
|
// <SubMenuLink item={subItem} />
|
|
// </NavigationMenuLink>
|
|
// ))}
|
|
// </NavigationMenuContent>
|
|
// </NavigationMenuItem>
|
|
// );
|
|
// }
|
|
|
|
return (
|
|
<NavigationMenuItem key={item.title}>
|
|
<NavigationMenuLink
|
|
href={item.url}
|
|
data-menu-item="true"
|
|
data-menu-url={item.url}
|
|
className={`
|
|
inline-flex h-10 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium gap-2 whitespace-nowrap relative
|
|
${isMenuActive(item.url)
|
|
? 'bg-primary/10'
|
|
: 'bg-background hover:bg-muted hover:text-accent-foreground'
|
|
}
|
|
[&:not([data-active])]:text-foreground
|
|
`}
|
|
>
|
|
{item.icon && (
|
|
<span className={`
|
|
shrink-0
|
|
${isMenuActive(item.url)
|
|
? 'text-primary'
|
|
: 'text-muted-foreground'
|
|
}
|
|
hover:text-primary
|
|
[&.group-data-[state=open]]:text-muted-foreground
|
|
`}>
|
|
{item.icon}
|
|
</span>
|
|
)}
|
|
<div className="relative">
|
|
<span className={isMenuActive(item.url) ? 'text-primary' : ''}>
|
|
{item.title}
|
|
</span>
|
|
{/* 激活菜单项下方的横条 */}
|
|
{isMenuActive(item.url) && (
|
|
<div className="absolute -bottom-1 left-0 right-0 h-[0.5px] bg-primary"></div>
|
|
)}
|
|
</div>
|
|
</NavigationMenuLink>
|
|
</NavigationMenuItem>
|
|
);
|
|
};
|
|
|
|
const renderMobileMenuItem = (item: MenuItem, isMenuActive: (url: string) => boolean) => {
|
|
// 注释掉移动端二级菜单相关代码
|
|
// if (item.items) {
|
|
// return (
|
|
// <AccordionItem key={item.title} value={item.title} className="border-b-0">
|
|
// <AccordionTrigger className="text-md py-0 font-semibold hover:no-underline gap-2">
|
|
// {item.icon && <span className="shrink-0">{item.icon}</span>}
|
|
// {item.title}
|
|
// </AccordionTrigger>
|
|
// <AccordionContent className="mt-2">
|
|
// {item.items.map((subItem) => (
|
|
// <SubMenuLink key={subItem.title} item={subItem} />
|
|
// ))}
|
|
// </AccordionContent>
|
|
// </AccordionItem>
|
|
// );
|
|
// }
|
|
|
|
return (
|
|
<a
|
|
key={item.title}
|
|
href={item.url}
|
|
className={`
|
|
text-md font-semibold flex items-center gap-2 p-2 rounded-md transition-colors
|
|
${isMenuActive(item.url)
|
|
? 'bg-primary/10 text-primary'
|
|
: 'hover:bg-muted hover:text-accent-foreground'
|
|
}
|
|
`}
|
|
>
|
|
{item.icon && (
|
|
<span className={`
|
|
shrink-0
|
|
${isMenuActive(item.url) ? 'text-primary' : 'text-muted-foreground'}
|
|
`}>
|
|
{item.icon}
|
|
</span>
|
|
)}
|
|
<span className={isMenuActive(item.url) ? 'text-primary' : ''}>
|
|
{item.title}
|
|
</span>
|
|
</a>
|
|
);
|
|
};
|
|
|
|
// 注释掉 SubMenuLink 组件,因为不再需要二级菜单
|
|
// const SubMenuLink = ({ item }: { item: MenuItem }) => {
|
|
// return (
|
|
// <a
|
|
// className="hover:bg-muted hover:text-accent-foreground flex min-w-80 select-none flex-row gap-4 rounded-md p-3 leading-none no-underline outline-none transition-colors"
|
|
// href={item.url}
|
|
// >
|
|
// <div className="text-foreground">{item.icon}</div>
|
|
// <div>
|
|
// <div className="text-sm font-semibold">{item.title}</div>
|
|
// {item.description && (
|
|
// <p className="text-muted-foreground text-sm leading-snug">
|
|
// {item.description}
|
|
// </p>
|
|
// )}
|
|
// </div>
|
|
// </a>
|
|
// );
|
|
// };
|
|
|
|
export { Navbar1 };
|