diff --git a/app/components/header/Header.tsx b/app/components/header/Header.tsx new file mode 100644 index 0000000..a707a6e --- /dev/null +++ b/app/components/header/Header.tsx @@ -0,0 +1,34 @@ +import { useEffect, useState } from "react"; + +export function Header(){ + const [currentTime, setCurrentTime] = useState(new Date()); + + useEffect(() => { + const timer = setInterval(() => { + setCurrentTime(new Date()); + }, 1000); // 每秒更新一次 + + // 清理定时器 + return () => clearInterval(timer); + }, []); + + return ( +
+ {/* 时间显示 */} +
+
+ {currentTime.toLocaleString('zh-CN')} +
+
+
+

console.log('登录')}>登录

+
+
+

console.log('注册')}>注册

+
+
+ ) +} \ No newline at end of file diff --git a/app/components/header/TopNav.tsx b/app/components/header/TopNav.tsx new file mode 100644 index 0000000..f5dfd1f --- /dev/null +++ b/app/components/header/TopNav.tsx @@ -0,0 +1,105 @@ +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 ( +
+ {/* 搜索框与导航菜单组合 */} +
+ {/* 搜索框 */} +
+
+ 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" + /> + + + +
+
+ + {/* 导航菜单 */} + +
+
+ ); +}; diff --git a/app/components/untils/Carousel.tsx b/app/components/untils/Carousel.tsx index fabc5f2..e9eff99 100755 --- a/app/components/untils/Carousel.tsx +++ b/app/components/untils/Carousel.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import Autoplay from "embla-carousel-autoplay"; -import { Card, CardContent } from "@/components/ui/card"; +import { Card, CardContent } from "@/ui/card"; import { Carousel, CarouselContent, @@ -10,7 +10,7 @@ import { CarouselNext, CarouselPrevious, type CarouselApi, -} from "@/components/ui/carousel"; +} from "@/ui/carousel"; export function CarouselDemo() { const [api, setApi] = React.useState(); diff --git a/app/images/header.png b/app/images/header.png new file mode 100644 index 0000000..75a8636 Binary files /dev/null and b/app/images/header.png differ diff --git a/app/routes/news.tsx b/app/routes/news.tsx index 42308ab..743cbbd 100755 --- a/app/routes/news.tsx +++ b/app/routes/news.tsx @@ -1,5 +1,5 @@ import { CarouselDemo } from "@/components/untils/Carousel"; -import NewsList from "@/components/list/NewsList"; +import type { Route } from "./+types/news"; export function meta( ) { @@ -10,5 +10,5 @@ export function meta( ) { } export default function Home() { - return ; + return ; } diff --git a/app/components/ui/badge.tsx b/app/ui/badge.tsx similarity index 100% rename from app/components/ui/badge.tsx rename to app/ui/badge.tsx diff --git a/app/components/ui/button.tsx b/app/ui/button.tsx similarity index 100% rename from app/components/ui/button.tsx rename to app/ui/button.tsx diff --git a/app/components/ui/card.tsx b/app/ui/card.tsx similarity index 100% rename from app/components/ui/card.tsx rename to app/ui/card.tsx diff --git a/app/components/ui/carousel.tsx b/app/ui/carousel.tsx similarity index 99% rename from app/components/ui/carousel.tsx rename to app/ui/carousel.tsx index 71cff4c..99a04c7 100644 --- a/app/components/ui/carousel.tsx +++ b/app/ui/carousel.tsx @@ -5,7 +5,7 @@ import useEmblaCarousel, { import { ArrowLeft, ArrowRight } from "lucide-react" import { cn } from "@/lib/utils" -import { Button } from "@/components/ui/button" +import { Button } from "@/ui/button" type CarouselApi = UseEmblaCarouselType[1] type UseCarouselParameters = Parameters diff --git a/app/components/ui/dialog.tsx b/app/ui/dialog.tsx similarity index 100% rename from app/components/ui/dialog.tsx rename to app/ui/dialog.tsx diff --git a/app/components/ui/input.tsx b/app/ui/input.tsx similarity index 100% rename from app/components/ui/input.tsx rename to app/ui/input.tsx diff --git a/app/components/ui/label.tsx b/app/ui/label.tsx similarity index 100% rename from app/components/ui/label.tsx rename to app/ui/label.tsx diff --git a/app/components/ui/select.tsx b/app/ui/select.tsx similarity index 100% rename from app/components/ui/select.tsx rename to app/ui/select.tsx diff --git a/app/components/ui/sonner.tsx b/app/ui/sonner.tsx similarity index 100% rename from app/components/ui/sonner.tsx rename to app/ui/sonner.tsx diff --git a/app/components/ui/table.tsx b/app/ui/table.tsx similarity index 100% rename from app/components/ui/table.tsx rename to app/ui/table.tsx