- 修复消息组件JSX.Element类型错误,改为React.ReactNode - 完善审核历史页面类型定义和API接口调用 - 优化验证码组件,移除备用验证码逻辑避免无限循环 - 简化系统设置页面,仅保留基本设置和外观设置 - 修复用户管理页面编辑模态框数据加载和CRUD操作 - 移除废弃的作物推荐组件文件 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
327 lines
10 KiB
TypeScript
327 lines
10 KiB
TypeScript
'use client';
|
|
|
|
import { Menu } from "lucide-react";
|
|
import { Sprout } from 'lucide-react';
|
|
import { MessageBell } from './components/MessageBell';
|
|
import { UserProfile } from './components/UserProfile';
|
|
import { ThemeToggle } from './ThemeToggle';
|
|
import { usePathname, useRouter } from 'next/navigation';
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
NavigationMenu,
|
|
NavigationMenuItem,
|
|
NavigationMenuLink,
|
|
NavigationMenuList,
|
|
} 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 {
|
|
navbarData: {
|
|
logo: {
|
|
url: string;
|
|
src: string;
|
|
alt: string;
|
|
title: string;
|
|
};
|
|
menu: MenuItem[];
|
|
auth: {
|
|
login: {
|
|
title: string;
|
|
url: string;
|
|
};
|
|
signup: {
|
|
title: string;
|
|
url: string;
|
|
};
|
|
};
|
|
};
|
|
}
|
|
const Navbar1 = ({ navbarData }: Navbar1Props) => {
|
|
const logo = navbarData.logo
|
|
const menu = navbarData.menu
|
|
const auth = navbarData.auth
|
|
const pathname = usePathname()
|
|
const router = useRouter()
|
|
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;
|
|
}
|
|
|
|
|
|
const handleMessageClick = () => {
|
|
// 处理消息点击事件,可以跳转到消息中心页面
|
|
console.log('Navigate to message center');
|
|
};
|
|
|
|
const handleProfileClick = () => {
|
|
// 处理个人中心点击事件
|
|
console.log('Navigate to profile page');
|
|
};
|
|
|
|
return (
|
|
<section className="py-4">
|
|
<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-primary rounded-lg flex items-center justify-center transition-colors">
|
|
<Sprout className="w-6 h-6 text-primary-foreground" />
|
|
</div>
|
|
<div>
|
|
<h1 className="text-primary transition-colors" style={{ color: 'var(--primary)' }}>智慧农业生产管理系统</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, router))}
|
|
</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, router))}
|
|
</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>
|
|
);
|
|
};
|
|
|
|
const renderMenuItem = (item: MenuItem, isMenuActive: (url: string) => boolean, router: any) => {
|
|
// 注释掉二级菜单相关代码,项目不需要二级菜单
|
|
// 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>
|
|
// );
|
|
// }
|
|
|
|
const handleClick = (e: React.MouseEvent) => {
|
|
e.preventDefault();
|
|
router.push(item.url);
|
|
};
|
|
|
|
return (
|
|
<NavigationMenuItem key={item.title}>
|
|
<NavigationMenuLink
|
|
href={item.url}
|
|
onClick={handleClick}
|
|
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 cursor-pointer
|
|
${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>
|
|
)}
|
|
<span className={isMenuActive(item.url) ? 'text-primary' : ''}>
|
|
{item.title}
|
|
</span>
|
|
{isMenuActive(item.url) &&<div className="absolute bottom-0 left-0 right-0 h-0.5 bg-green-600 dark:bg-green-400 rounded-full"></div>}
|
|
</NavigationMenuLink>
|
|
</NavigationMenuItem>
|
|
);
|
|
};
|
|
|
|
const renderMobileMenuItem = (item: MenuItem, isMenuActive: (url: string) => boolean, router: any) => {
|
|
// 注释掉移动端二级菜单相关代码
|
|
// 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>
|
|
// );
|
|
// }
|
|
|
|
const handleClick = (e: React.MouseEvent) => {
|
|
e.preventDefault();
|
|
router.push(item.url);
|
|
};
|
|
|
|
return (
|
|
<div
|
|
key={item.title}
|
|
onClick={handleClick}
|
|
className={`
|
|
text-md font-semibold flex items-center gap-2 p-2 rounded-md transition-colors cursor-pointer
|
|
${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>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
// 注释掉 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 };
|