news/app/components/news/header/TopNav.tsx

106 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Search } from 'lucide-react';
import React, { useState } from 'react';
interface MenuItem {
label: string;
key: string;
}
// 定义 TopNavProps 接口,描述组件的 props 类型
// menuItems? 菜单项数组
// activeKey: 当前激活的菜单项
// onSearch: 搜索回调函数
// onItemClick: 菜单点击回调函数
interface TopNavProps {
menuItems?: MenuItem[];
activeKey?: string;
onSearch?: (keyword: string) => void;
onItemClick?: (key: string) => void;
}
//定义 TopNav 组件,类型为 React 函数组件,接收 TopNavProps 类型的 props
//解构并设置 menuItems 默认值如果父组件没有传入则使用默认的6个菜单项
// activeKey: 当前激活的菜单项
// onSearch: 搜索回调函数
// onItemClick: 菜单点击回调函数
export function TopNav({
menuItems = [
{ label: '首页', key: 'home' },
{ label: '烽火动态', key: 'news' },
{ label: '烽火铸魂', key: 'soul' },
{ label: '烽火训练', key: 'training' },
{ label: '联系热线', key: 'hotline' },
{ label: '综合服务', key: 'service' },
],
activeKey: externalActiveKey, // 从外部传入的 activeKey
onSearch,
onItemClick,
}: TopNavProps){
// 使用外部传入的 activeKey如果没有则使用内部状态
// 创建内部状态 internalActiveKey默认值为 'home',用于内部管理激活状态
const [internalActiveKey, setInternalActiveKey] = useState('home');
// 如果外部传入了 activeKey 则使用外部的,否则使用内部状态(支持受控和非受控模式)
const currentActiveKey = externalActiveKey !== undefined ? externalActiveKey : internalActiveKey;
// 创建搜索关键词状态,初始为空字符串
const [searchKeyword, setSearchKeyword] = useState('');
// 处理搜索提交事件, 阻止默认表单提交行为,调用搜索回调函数
const handleSearchSubmit = (e: React.FormEvent) => {
e.preventDefault();
onSearch?.(searchKeyword); // .? 可选链操作符,确保 onSearch 存在时才调用
};
// 定义菜单项点击处理函数,如果外部没有传入 activeKey 则更新内部状态,调用点击回调函数
const handleItemClick = (item: MenuItem) => {
// 更新内部状态(如果使用内部状态)
if (externalActiveKey === undefined) {
setInternalActiveKey(item.key);
}
// 调用外部回调函数
onItemClick?.(item.key); // .? 可选链操作符,确保 onItemClick 存在时才调用
};
return (
<div className="h-14 flex items-center justify-center px-8 bg-white">
{/* 搜索框与导航菜单组合 */}
<div className="flex items-center space-x-4">
{/* 搜索框 */}
<form onSubmit={handleSearchSubmit} className="relative">
<div className="relative">
<input
type="text"
value={searchKeyword}
onChange={(e) => setSearchKeyword(e.target.value)}
placeholder="搜索..."
className="pl-10 pr-4 py-2 text-sm rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent w-64 transition-all duration-200 hover:shadow-sm"
/>
<span className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400">
<Search className="w-5 h-5" />
</span>
</div>
</form>
{/* 导航菜单 */}
<ul className="flex space-x-2">
{menuItems.map((item) => {
const isActive = currentActiveKey === item.key; // 判断当前项是否激活
return (
<li key={item.key}>
<button
onClick={() => handleItemClick(item)}
className={`px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200 ${
isActive
? 'bg-blue-600 text-white shadow-md' // 激活状态样式
: 'text-gray-600 hover:bg-blue-100 hover:text-blue-700' // 非激活状态样式
}`}
>
{item.label}
</button>
</li>
);
})}
</ul>
</div>
</div>
);
};