0.0.5.0
This commit is contained in:
+4
-4
@@ -7,7 +7,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
|
||||
import { SiteHeader } from "@/components/site-header"
|
||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import { UserPlus, Network, Map, Calendar, Users, ArrowRight, Clock, Search, BookOpen, BarChart3 } from "lucide-react"
|
||||
import { UserPlus, Network, Map, Calendar, Users, ArrowRight, Clock, Search, BookOpen, BarChart3, Calculator } from "lucide-react"
|
||||
import { useFamily } from "@/context/family-context"
|
||||
import { useMemo, useState, useEffect, useRef } from "react"
|
||||
import { StatisticsCharts } from "@/components/dashboard/statistics-charts"
|
||||
@@ -167,10 +167,10 @@ export default function DashboardPage() {
|
||||
{session && currentTree?.ownerId === session.user?.id && (
|
||||
<CollaboratorDialog />
|
||||
)}
|
||||
<Link href="/tree">
|
||||
<Link href="/relationship">
|
||||
<Button variant="outline" className="gap-2 bg-transparent">
|
||||
<Network className="h-4 w-4" />
|
||||
查看世系图
|
||||
<Calculator className="h-4 w-4" />
|
||||
计算器
|
||||
</Button>
|
||||
</Link>
|
||||
{(currentTree?.currentUserRole === "OWNER" || currentTree?.currentUserRole === "EDITOR") && (
|
||||
|
||||
@@ -1,245 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from 'react'
|
||||
import { SiteHeader } from '@/components/site-header'
|
||||
import { useFamily } from '@/context/family-context'
|
||||
import { useRelationship } from '@/hooks/use-relationship'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
|
||||
export default function RelationshipTestPage() {
|
||||
const { treeData } = useFamily()
|
||||
const { calculateRelationship, getDirectRelatives, getSiblings } = useRelationship()
|
||||
|
||||
const [fromId, setFromId] = useState<string>('')
|
||||
const [toId, setToId] = useState<string>('')
|
||||
const [result, setResult] = useState<any>(null)
|
||||
|
||||
const members = Object.values(treeData.members)
|
||||
|
||||
const handleCalculate = () => {
|
||||
if (!fromId || !toId) return
|
||||
|
||||
const relationship = calculateRelationship(fromId, toId)
|
||||
const fromMember = treeData.members[fromId]
|
||||
const toMember = treeData.members[toId]
|
||||
|
||||
setResult({
|
||||
from: fromMember,
|
||||
to: toMember,
|
||||
relationship
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col bg-background">
|
||||
<SiteHeader />
|
||||
|
||||
<main className="flex-1 container mx-auto px-4 py-8">
|
||||
<div className="max-w-4xl mx-auto space-y-6">
|
||||
{/* 标题 */}
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold font-serif mb-2">家族关系计算器</h1>
|
||||
<p className="text-muted-foreground">
|
||||
选择两个家族成员,自动计算他们之间的亲属关系
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 选择成员 */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>选择成员</CardTitle>
|
||||
<CardDescription>请选择要计算关系的两个成员</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">成员A</label>
|
||||
<Select value={fromId} onValueChange={setFromId}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择成员A" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{members.map(member => (
|
||||
<SelectItem key={member.id} value={member.id}>
|
||||
{member.fullName} (第{member.generation}世)
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">成员B</label>
|
||||
<Select value={toId} onValueChange={setToId}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择成员B" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{members.map(member => (
|
||||
<SelectItem key={member.id} value={member.id}>
|
||||
{member.fullName} (第{member.generation}世)
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onClick={handleCalculate}
|
||||
disabled={!fromId || !toId}
|
||||
className="w-full"
|
||||
>
|
||||
计算关系
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 计算结果 */}
|
||||
{result && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>计算结果</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex items-center justify-center gap-4 p-6 bg-muted rounded-lg">
|
||||
<div className="text-center">
|
||||
<div className="text-2xl font-bold font-serif mb-1">
|
||||
{result.from.fullName}
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
第{result.from.generation}世
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Badge variant="default" className="text-lg px-4 py-2">
|
||||
{result.relationship.term}
|
||||
</Badge>
|
||||
{result.relationship.path && (
|
||||
<span className="text-xs text-muted-foreground">
|
||||
路径: {result.relationship.path}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<div className="text-2xl font-bold font-serif mb-1">
|
||||
{result.to.fullName}
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
第{result.to.generation}世
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{result.relationship.description && (
|
||||
<div className="p-4 bg-blue-50 dark:bg-blue-950 rounded-lg">
|
||||
<div className="text-sm font-medium mb-1">关系说明:</div>
|
||||
<div className="text-muted-foreground">
|
||||
<strong>{result.from.fullName}</strong> 是 <strong>{result.to.fullName}</strong> 的 <strong className="text-primary">{result.relationship.term}</strong>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{result.relationship.success === false && (
|
||||
<div className="p-4 bg-yellow-50 dark:bg-yellow-950 rounded-lg">
|
||||
<div className="text-sm text-yellow-800 dark:text-yellow-200">
|
||||
⚠️ {result.relationship.error || '无法计算关系'}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* 快速查询示例 */}
|
||||
{fromId && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
{treeData.members[fromId]?.fullName} 的直系亲属
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{(() => {
|
||||
const relatives = getDirectRelatives(fromId)
|
||||
if (!relatives) return null
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{relatives.father && (
|
||||
<div className="p-3 bg-muted rounded-lg">
|
||||
<div className="text-sm text-muted-foreground mb-1">父亲</div>
|
||||
<div className="font-medium">{relatives.father.fullName}</div>
|
||||
</div>
|
||||
)}
|
||||
{relatives.mother && (
|
||||
<div className="p-3 bg-muted rounded-lg">
|
||||
<div className="text-sm text-muted-foreground mb-1">母亲</div>
|
||||
<div className="font-medium">{relatives.mother.fullName}</div>
|
||||
</div>
|
||||
)}
|
||||
{relatives.spouses.length > 0 && (
|
||||
<div className="p-3 bg-muted rounded-lg">
|
||||
<div className="text-sm text-muted-foreground mb-1">配偶</div>
|
||||
{relatives.spouses.map((spouse: any) => (
|
||||
<div key={spouse.id} className="font-medium">
|
||||
{spouse.fullName}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{relatives.children.length > 0 && (
|
||||
<div className="p-3 bg-muted rounded-lg">
|
||||
<div className="text-sm text-muted-foreground mb-1">
|
||||
子女 ({relatives.children.length}人)
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{relatives.children.slice(0, 3).map((child: any) => (
|
||||
<div key={child.id} className="font-medium text-sm">
|
||||
{child.fullName}
|
||||
</div>
|
||||
))}
|
||||
{relatives.children.length > 3 && (
|
||||
<div className="text-xs text-muted-foreground">
|
||||
还有 {relatives.children.length - 3} 人...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
|
||||
{(() => {
|
||||
const siblings = getSiblings(fromId)
|
||||
if (siblings.length === 0) return null
|
||||
|
||||
return (
|
||||
<div className="mt-4 p-3 bg-muted rounded-lg">
|
||||
<div className="text-sm text-muted-foreground mb-2">
|
||||
兄弟姐妹 ({siblings.length}人)
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{siblings.map((sibling: any) => (
|
||||
<Badge key={sibling.id} variant="outline">
|
||||
{sibling.fullName}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from 'react'
|
||||
import { SiteHeader } from '@/components/site-header'
|
||||
import { useFamily } from '@/context/family-context'
|
||||
import { useRelationship } from '@/hooks/use-relationship'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command'
|
||||
import { Check, ChevronsUpDown } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
export default function RelationshipTestPage() {
|
||||
const { treeData } = useFamily()
|
||||
const { calculateRelationship, getDirectRelatives, getSiblings } = useRelationship()
|
||||
|
||||
const [fromId, setFromId] = useState<string>('')
|
||||
const [toId, setToId] = useState<string>('')
|
||||
const [result, setResult] = useState<any>(null)
|
||||
const [openFromCombobox, setOpenFromCombobox] = useState(false)
|
||||
const [openToCombobox, setOpenToCombobox] = useState(false)
|
||||
|
||||
const members = Object.values(treeData.members)
|
||||
|
||||
const handleCalculate = () => {
|
||||
if (!fromId || !toId) return
|
||||
|
||||
const relationship = calculateRelationship(fromId, toId)
|
||||
const fromMember = treeData.members[fromId]
|
||||
const toMember = treeData.members[toId]
|
||||
|
||||
setResult({
|
||||
from: fromMember,
|
||||
to: toMember,
|
||||
relationship
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col bg-background">
|
||||
<SiteHeader />
|
||||
|
||||
<main className="flex-1 container mx-auto px-4 py-8">
|
||||
<div className="max-w-4xl mx-auto space-y-6">
|
||||
{/* 标题 */}
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold font-serif mb-2">家族关系计算器</h1>
|
||||
<p className="text-muted-foreground">
|
||||
选择两个家族成员,自动计算他们之间的亲属关系
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 选择成员 */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>选择成员</CardTitle>
|
||||
<CardDescription>请选择要计算关系的两个成员</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">成员A</label>
|
||||
<Popover open={openFromCombobox} onOpenChange={setOpenFromCombobox}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={openFromCombobox}
|
||||
className="w-full justify-between"
|
||||
>
|
||||
{fromId
|
||||
? members.find((member) => member.id === fromId)?.fullName
|
||||
: "搜索或选择成员A..."}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-full p-0" align="start">
|
||||
<Command>
|
||||
<CommandInput placeholder="输入姓名搜索..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>未找到成员</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{members.map((member) => (
|
||||
<CommandItem
|
||||
key={member.id}
|
||||
value={`${member.fullName} ${member.generation}`}
|
||||
onSelect={() => {
|
||||
setFromId(member.id)
|
||||
setOpenFromCombobox(false)
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
fromId === member.id ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
{member.fullName} (第{member.generation}世)
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">成员B</label>
|
||||
<Popover open={openToCombobox} onOpenChange={setOpenToCombobox}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={openToCombobox}
|
||||
className="w-full justify-between"
|
||||
>
|
||||
{toId
|
||||
? members.find((member) => member.id === toId)?.fullName
|
||||
: "搜索或选择成员B..."}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-full p-0" align="start">
|
||||
<Command>
|
||||
<CommandInput placeholder="输入姓名搜索..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>未找到成员</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{members.map((member) => (
|
||||
<CommandItem
|
||||
key={member.id}
|
||||
value={`${member.fullName} ${member.generation}`}
|
||||
onSelect={() => {
|
||||
setToId(member.id)
|
||||
setOpenToCombobox(false)
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
toId === member.id ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
{member.fullName} (第{member.generation}世)
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onClick={handleCalculate}
|
||||
disabled={!fromId || !toId}
|
||||
className="w-full"
|
||||
>
|
||||
计算关系
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 计算结果 */}
|
||||
{result && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>计算结果</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex items-center justify-center gap-4 p-6 bg-muted rounded-lg">
|
||||
<div className="text-center">
|
||||
<div className="text-2xl font-bold font-serif mb-1">
|
||||
{result.from.fullName}
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
第{result.from.generation}世
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Badge variant="default" className="text-lg px-4 py-2">
|
||||
{result.relationship.term}
|
||||
</Badge>
|
||||
{result.relationship.path && (
|
||||
<span className="text-xs text-muted-foreground">
|
||||
路径: {result.relationship.path}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<div className="text-2xl font-bold font-serif mb-1">
|
||||
{result.to.fullName}
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
第{result.to.generation}世
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{result.relationship.description && (
|
||||
<div className="p-4 bg-blue-50 dark:bg-blue-950 rounded-lg">
|
||||
<div className="text-sm font-medium mb-1">关系说明:</div>
|
||||
<div className="text-muted-foreground">
|
||||
<strong>{result.from.fullName}</strong> 是 <strong>{result.to.fullName}</strong> 的 <strong className="text-primary">{result.relationship.term}</strong>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{result.relationship.success === false && (
|
||||
<div className="p-4 bg-yellow-50 dark:bg-yellow-950 rounded-lg">
|
||||
<div className="text-sm text-yellow-800 dark:text-yellow-200">
|
||||
⚠️ {result.relationship.error || '无法计算关系'}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* 直系亲属信息 */}
|
||||
{(fromId || toId) && (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{fromId && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
{treeData.members[fromId]?.fullName} 的直系亲属
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{(() => {
|
||||
const relatives = getDirectRelatives(fromId)
|
||||
if (!relatives) return null
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-1 gap-3">
|
||||
{relatives.father && (
|
||||
<div className="p-3 bg-muted rounded-lg">
|
||||
<div className="text-sm text-muted-foreground mb-1">父亲</div>
|
||||
<div className="font-medium">{relatives.father.fullName}</div>
|
||||
</div>
|
||||
)}
|
||||
{relatives.mother && (
|
||||
<div className="p-3 bg-muted rounded-lg">
|
||||
<div className="text-sm text-muted-foreground mb-1">母亲</div>
|
||||
<div className="font-medium">{relatives.mother.fullName}</div>
|
||||
</div>
|
||||
)}
|
||||
{relatives.spouses.length > 0 && (
|
||||
<div className="p-3 bg-muted rounded-lg">
|
||||
<div className="text-sm text-muted-foreground mb-1">配偶</div>
|
||||
{relatives.spouses.map((spouse: any) => (
|
||||
<div key={spouse.id} className="font-medium">
|
||||
{spouse.fullName}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{relatives.children.length > 0 && (
|
||||
<div className="p-3 bg-muted rounded-lg">
|
||||
<div className="text-sm text-muted-foreground mb-1">
|
||||
子女 ({relatives.children.length}人)
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{relatives.children.slice(0, 3).map((child: any) => (
|
||||
<div key={child.id} className="font-medium text-sm">
|
||||
{child.fullName}
|
||||
</div>
|
||||
))}
|
||||
{relatives.children.length > 3 && (
|
||||
<div className="text-xs text-muted-foreground">
|
||||
还有 {relatives.children.length - 3} 人...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{(() => {
|
||||
const siblings = getSiblings(fromId)
|
||||
if (siblings.length === 0) return null
|
||||
|
||||
return (
|
||||
<div className="mt-3 p-3 bg-muted rounded-lg">
|
||||
<div className="text-sm text-muted-foreground mb-2">
|
||||
兄弟姐妹 ({siblings.length}人)
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{siblings.map((sibling: any) => (
|
||||
<Badge key={sibling.id} variant="outline">
|
||||
{sibling.fullName}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
</>
|
||||
)
|
||||
})()}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{toId && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
{treeData.members[toId]?.fullName} 的直系亲属
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{(() => {
|
||||
const relatives = getDirectRelatives(toId)
|
||||
if (!relatives) return null
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-1 gap-3">
|
||||
{relatives.father && (
|
||||
<div className="p-3 bg-muted rounded-lg">
|
||||
<div className="text-sm text-muted-foreground mb-1">父亲</div>
|
||||
<div className="font-medium">{relatives.father.fullName}</div>
|
||||
</div>
|
||||
)}
|
||||
{relatives.mother && (
|
||||
<div className="p-3 bg-muted rounded-lg">
|
||||
<div className="text-sm text-muted-foreground mb-1">母亲</div>
|
||||
<div className="font-medium">{relatives.mother.fullName}</div>
|
||||
</div>
|
||||
)}
|
||||
{relatives.spouses.length > 0 && (
|
||||
<div className="p-3 bg-muted rounded-lg">
|
||||
<div className="text-sm text-muted-foreground mb-1">配偶</div>
|
||||
{relatives.spouses.map((spouse: any) => (
|
||||
<div key={spouse.id} className="font-medium">
|
||||
{spouse.fullName}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{relatives.children.length > 0 && (
|
||||
<div className="p-3 bg-muted rounded-lg">
|
||||
<div className="text-sm text-muted-foreground mb-1">
|
||||
子女 ({relatives.children.length}人)
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{relatives.children.slice(0, 3).map((child: any) => (
|
||||
<div key={child.id} className="font-medium text-sm">
|
||||
{child.fullName}
|
||||
</div>
|
||||
))}
|
||||
{relatives.children.length > 3 && (
|
||||
<div className="text-xs text-muted-foreground">
|
||||
还有 {relatives.children.length - 3} 人...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{(() => {
|
||||
const siblings = getSiblings(toId)
|
||||
if (siblings.length === 0) return null
|
||||
|
||||
return (
|
||||
<div className="mt-3 p-3 bg-muted rounded-lg">
|
||||
<div className="text-sm text-muted-foreground mb-2">
|
||||
兄弟姐妹 ({siblings.length}人)
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{siblings.map((sibling: any) => (
|
||||
<Badge key={sibling.id} variant="outline">
|
||||
{sibling.fullName}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
</>
|
||||
)
|
||||
})()}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user