246 lines
9.8 KiB
TypeScript
246 lines
9.8 KiB
TypeScript
"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>
|
||
)
|
||
}
|