0.0.0.5
This commit is contained in:
@@ -3,8 +3,40 @@ import { BookOpen, Search, Bell, Settings } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
||||
import { useFamily } from "@/context/family-context"
|
||||
import { useState, useRef, useEffect } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
|
||||
export function SiteHeader() {
|
||||
const { searchMembers, searchResults } = useFamily()
|
||||
const [searchQuery, setSearchQuery] = useState("")
|
||||
const [showResults, setShowResults] = useState(false)
|
||||
const searchRef = useRef<HTMLDivElement>(null)
|
||||
const router = useRouter()
|
||||
|
||||
// 点击外部关闭搜索结果
|
||||
useEffect(() => {
|
||||
function handleClickOutside(event: MouseEvent) {
|
||||
if (searchRef.current && !searchRef.current.contains(event.target as Node)) {
|
||||
setShowResults(false)
|
||||
}
|
||||
}
|
||||
document.addEventListener("mousedown", handleClickOutside)
|
||||
return () => document.removeEventListener("mousedown", handleClickOutside)
|
||||
}, [])
|
||||
|
||||
const handleSearch = (query: string) => {
|
||||
setSearchQuery(query)
|
||||
searchMembers(query)
|
||||
setShowResults(query.length > 0)
|
||||
}
|
||||
|
||||
const handleSelectMember = (memberId: string) => {
|
||||
router.push(`/members/${memberId}`)
|
||||
setSearchQuery("")
|
||||
setShowResults(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<header className="sticky top-0 z-50 w-full border-b border-border/40 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||
<div className="container mx-auto flex h-16 items-center px-4">
|
||||
@@ -34,13 +66,51 @@ export function SiteHeader() {
|
||||
</nav>
|
||||
|
||||
<div className="ml-auto flex items-center gap-4">
|
||||
<div className="relative hidden sm:block">
|
||||
<div className="relative hidden sm:block" ref={searchRef}>
|
||||
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
type="search"
|
||||
placeholder="搜索成员..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => handleSearch(e.target.value)}
|
||||
className="h-9 w-64 rounded-full bg-muted pl-9 text-sm focus-visible:ring-primary"
|
||||
/>
|
||||
|
||||
{/* 搜索结果下拉框 */}
|
||||
{showResults && searchResults.length > 0 && (
|
||||
<div className="absolute top-full mt-2 w-full bg-background border border-border rounded-lg shadow-lg max-h-96 overflow-y-auto z-50">
|
||||
{searchResults.map((member) => (
|
||||
<button
|
||||
key={member.id}
|
||||
onClick={() => handleSelectMember(member.id)}
|
||||
className="w-full px-4 py-3 text-left hover:bg-muted transition-colors border-b border-border last:border-0"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium">{member.fullName}</span>
|
||||
<span className={`text-xs px-1.5 py-0.5 rounded ${member.gender === "male" ? "bg-blue-100 text-blue-700" : "bg-pink-100 text-pink-700"}`}>
|
||||
{member.gender === "male" ? "男" : "女"}
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground">第{member.generation}世</span>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
{member.birthDate && `生于 ${member.birthDate}`}
|
||||
{member.birthPlace && ` · ${member.birthPlace}`}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 无结果提示 */}
|
||||
{showResults && searchQuery && searchResults.length === 0 && (
|
||||
<div className="absolute top-full mt-2 w-full bg-background border border-border rounded-lg shadow-lg p-4 text-center text-sm text-muted-foreground z-50">
|
||||
未找到匹配的成员
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Button variant="ghost" size="icon" className="text-muted-foreground">
|
||||
|
||||
Reference in New Issue
Block a user