"use client" import { useState, useMemo, useEffect, useCallback, memo } from "react" import { SiteHeader } from "@/components/site-header" import { useFamily } from "@/context/family-context" import { useRouter } from "next/navigation" import { Input } from "@/components/ui/input" import { Button } from "@/components/ui/button" import { Card, CardContent } from "@/components/ui/card" import { Search, Plus, Filter, X, Users, User } from "lucide-react" import Link from "next/link" import { AvatarDisplay } from "@/components/ui/avatar-display" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Slider } from "@/components/ui/slider" import { Label } from "@/components/ui/label" import { Badge } from "@/components/ui/badge" import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover" import type { FamilyMember } from "@/types/family" import { permissions } from "@/lib/permissions" import { MemberNameWithStatus } from "@/components/member-name-with-status" import { useSession } from "next-auth/react" import { EmptyStateBadge } from "@/components/empty-state-badge" // 占位符类型 interface PlaceholderMember { id: string isPlaceholder: true } type MemberOrPlaceholder = FamilyMember | PlaceholderMember // 提取占位符生成逻辑为独立函数 function addPlaceholdersToFamilyUnit( member: FamilyMember, spouses: FamilyMember[], processedSpouses: Set ): MemberOrPlaceholder[] { const result: MemberOrPlaceholder[] = [member] let familyUnitSize = 1 // 主成员自己 // 添加配偶 if (member.spouseIds && member.spouseIds.length > 0) { member.spouseIds.forEach(spouseId => { const spouse = spouses.find(m => m.id === spouseId) if (spouse && !processedSpouses.has(spouseId)) { result.push(spouse) processedSpouses.add(spouseId) familyUnitSize++ } }) } // 计算需要添加的占位符数量,确保填满4的倍数 const placeholdersNeeded = (4 - (familyUnitSize % 4)) % 4 for (let i = 0; i < placeholdersNeeded; i++) { result.push({ id: `placeholder-${member.id}-${i}`, isPlaceholder: true }) } return result } // 判断是否是占位符 function isPlaceholder(member: MemberOrPlaceholder): member is PlaceholderMember { return 'isPlaceholder' in member && member.isPlaceholder === true } // 成员卡片组件 - 使用 memo 优化 const MemberCard = memo(function MemberCard({ member, allMembers, onLocateInTree, }: { member: FamilyMember allMembers: FamilyMember[] onLocateInTree: (id: string) => void }) { // 判断是否是配偶 const isFounder = member.isFounder === true const isSpouse = !member.fatherId && !member.motherId && member.spouseIds && member.spouseIds.length > 0 && !isFounder // 获取配偶信息 const spouse = isSpouse && member.spouseIds?.[0] ? allMembers.find(m => m.id === member.spouseIds[0]) : null const spouseName = spouse ? spouse.fullName : null // 获取父母信息 const father = member.fatherId ? allMembers.find(m => m.id === member.fatherId) : null const mother = member.motherId ? allMembers.find(m => m.id === member.motherId) : null return (

{isFounder && 👑} {member.courtesyName && ( 字 {member.courtesyName} )}

{/* 父母信息 - 放在姓名右侧 */} {(father || mother) && (
父母: {father && ( { e.preventDefault() e.stopPropagation() window.location.href = `/members/${father.id}${window.location.search}` }} > )} {father && mother && ·} {mother && ( { e.preventDefault() e.stopPropagation() window.location.href = `/members/${mother.id}${window.location.search}` }} > )}
)}
{isFounder ? (

家族始祖

) : isSpouse && spouseName ? (

{spouseName}的配偶

) : (

{member.generationName ? `字辈: ${member.generationName}` : "未录入字辈"}

)} {/* 籍贯和生卒年并排 */}

{member.ancestralHome || "籍贯未知"}

{/* 生卒年和年龄 */} {(member.birthDate || member.deathDate) && (
{member.birthDate && new Date(member.birthDate).getFullYear()} {member.deathDate && ` - ${new Date(member.deathDate).getFullYear()}`} {!member.deathDate && member.birthDate && ' - '} {member.isLunarDate && ( 农历 )}
{(() => { if (!member.birthDate) return null const birthYear = new Date(member.birthDate).getFullYear() const deathYear = member.deathDate ? new Date(member.deathDate).getFullYear() : new Date().getFullYear() const age = deathYear - birthYear return (
{member.deathDate ? `享年${age}岁` : `${age}岁`}
) })()}
)}
{member.tags && member.tags.length > 0 && (
{member.tags.map((tag) => ( {tag === '始祖' ? '👑 始祖' : tag} ))}
)}
) }) export default function MembersPage() { const { treeData, currentTree, searchMembers, setHighlightedMemberId } = useFamily() const { data: session, status } = useSession() const router = useRouter() const [query, setQuery] = useState("") const [showFilters, setShowFilters] = useState(false) // 未登录时重定向到登录页 useEffect(() => { if (status === 'unauthenticated') { router.push('/auth/signin') } }, [status, router]) // 从 URL 参数读取搜索关键词 useEffect(() => { const params = new URLSearchParams(window.location.search) const searchParam = params.get('search') if (searchParam) { setQuery(searchParam) } }, []) // Filter States const [selectedSurname, setSelectedSurname] = useState("all") const [selectedHome, setSelectedHome] = useState("all") const [statusFilter, setStatusFilter] = useState("all") const [generationRange, setGenerationRange] = useState<[number, number]>([1, 100]) // Derived Data const allMembers = useMemo(() => Object.values(treeData.members), [treeData]) const { uniqueSurnames, uniqueHomes, minGen, maxGen } = useMemo(() => { const surnames = new Set() const homes = new Set() let min = 1000 let max = 0 allMembers.forEach((m) => { if (m.surname) surnames.add(m.surname) if (m.ancestralHome) homes.add(m.ancestralHome) if (m.generation) { if (m.generation < min) min = m.generation if (m.generation > max) max = m.generation } }) return { uniqueSurnames: Array.from(surnames).sort(), uniqueHomes: Array.from(homes).sort(), minGen: min === 1000 ? 1 : min, maxGen: max === 0 ? 100 : max, } }, [allMembers]) // Initialize slider when data loads - 修复:使用 useEffect 而不是 useMemo useEffect(() => { if (minGen !== 1000 && maxGen !== 0) { // 只在初始加载时设置一次 setGenerationRange([minGen, maxGen]) } }, [minGen, maxGen]) const filteredMembers = useMemo(() => { let result = allMembers // 1. Search Query if (query) { const lowerQuery = query.toLowerCase() result = result.filter( (m) => m.fullName.toLowerCase().includes(lowerQuery) || m.courtesyName?.toLowerCase().includes(lowerQuery) || m.artName?.toLowerCase().includes(lowerQuery), ) } // 2. Surname Filter if (selectedSurname !== "all") { result = result.filter((m) => m.surname === selectedSurname) } // 3. Home Filter if (selectedHome !== "all") { result = result.filter((m) => m.ancestralHome === selectedHome) } // 4. Status Filter if (statusFilter !== "all") { if (statusFilter === "living") { result = result.filter((m) => !m.deathDate) } else if (statusFilter === "deceased") { result = result.filter((m) => !!m.deathDate) } } // 5. Generation Filter result = result.filter((m) => m.generation >= generationRange[0] && m.generation <= generationRange[1]) // Sort return result.sort((a, b) => a.generation - b.generation || (a.birthDate || "").localeCompare(b.birthDate || "")) }, [allMembers, query, selectedSurname, selectedHome, statusFilter, generationRange]) // 按世代分组并排序 - 优化后的版本 const membersByGeneration = useMemo(() => { const groups = new Map() // 第一步:按世代分组 filteredMembers.forEach(member => { const gen = member.generation if (!groups.has(gen)) { groups.set(gen, []) } groups.get(gen)!.push(member) }) // 第二步:转换为数组并排序 return Array.from(groups.entries()) .sort((a, b) => a[0] - b[0]) .map(([generation, members]) => { // 区分家族成员和配偶 const familyMembers = members.filter(m => { const isFounder = m.isFounder === true const hasFamilyParents = m.fatherId || m.motherId if (hasFamilyParents || isFounder) return true if (generation === 1 && (!m.spouseIds || m.spouseIds.length === 0)) return true return false }) // 对家族成员按年龄排序 const sortedFamilyMembers = familyMembers.sort((a, b) => { const aIsFounder = a.isFounder === true const bIsFounder = b.isFounder === true if (aIsFounder && !bIsFounder) return -1 if (!aIsFounder && bIsFounder) return 1 const aDate = a.birthDate || "9999-12-31" const bDate = b.birthDate || "9999-12-31" return aDate.localeCompare(bDate) }) // 使用优化后的函数添加配偶和占位符 const result: MemberOrPlaceholder[] = [] const processedSpouses = new Set() sortedFamilyMembers.forEach(member => { const familyUnit = addPlaceholdersToFamilyUnit(member, members, processedSpouses) result.push(...familyUnit) }) // 计算实际成员数(不包括占位符) const actualMemberCount = result.filter(m => !isPlaceholder(m)).length return { generation, members: result, actualCount: actualMemberCount } }) }, [filteredMembers]) const clearFilters = () => { setSelectedSurname("all") setSelectedHome("all") setStatusFilter("all") setGenerationRange([1, 100]) // Reset to wide range setQuery("") } const activeFilterCount = (selectedSurname !== "all" ? 1 : 0) + (selectedHome !== "all" ? 1 : 0) + (statusFilter !== "all" ? 1 : 0) + (generationRange[0] > 1 || generationRange[1] < 100 ? 1 : 0) // 未登录时不渲染内容(已在 useEffect 中重定向) if (status === 'loading' || !session) { return null } return (

家族成员列表

setQuery(e.target.value)} />
{permissions.canCreate(currentTree?.currentUserRole) && ( )}
{/* Filter Bar */} {showFilters && (

高级筛选

{generationRange[0]} - {generationRange[1]} 世
setGenerationRange(val as [number, number])} className="py-4" />
)}
{filteredMembers.length === 0 ? (

未寻得

) : (
{membersByGeneration.map(({ generation, members, actualCount }) => (

第 {generation} 世

共 {actualCount} 人
{members.map((member) => { // 检查是否是占位符 if (isPlaceholder(member)) { return
} // 使用优化后的 MemberCard 组件 return ( { setHighlightedMemberId(id) const params = new URLSearchParams(window.location.search) router.push(`/tree?${params.toString()}`) }} /> ) })}
))}
)}
) }