This commit is contained in:
freedakgmail
2025-12-21 17:32:33 +08:00
parent cffdd75e96
commit 5c8c70097c
190 changed files with 2746 additions and 893 deletions
+118 -71
View File
@@ -10,6 +10,8 @@ import { MemberNameWithStatus } from "@/components/member-name-with-status"
import { useFamily } from "@/context/family-context"
import { useToast } from "@/components/ui/use-toast"
import type { FamilyMember } from "@/types/family"
import { GenerationWarningDialog } from "@/components/tree/generation-warning-dialog"
import { shouldShowGenerationWarning } from "@/lib/generation-utils"
export type RelationType = "child" | "spouse" | "father" | "mother"
@@ -38,11 +40,17 @@ export function AddRelationDialog({
const { toast } = useToast()
const [query, setQuery] = useState("")
const [linkingId, setLinkingId] = useState<string | null>(null)
// 代数重新计算警告对话框状态
const [showWarningDialog, setShowWarningDialog] = useState(false)
const [pendingAction, setPendingAction] = useState<{ type: 'link' | 'create', target?: FamilyMember } | null>(null)
useEffect(() => {
if (!open) {
setQuery("")
setLinkingId(null)
setShowWarningDialog(false)
setPendingAction(null)
}
}, [open])
@@ -72,7 +80,6 @@ export function AddRelationDialog({
if (member.gender === "MALE" && !fatherMatches) return false
if (member.gender === "FEMALE" && !motherMatches) return false
if (member.gender !== "MALE" && member.gender !== "FEMALE") {
// 非男女成员,允许填补任一缺失的父母
if (candidate.fatherId && candidate.motherId) return false
}
} else if (relationType === "spouse") {
@@ -81,12 +88,10 @@ export function AddRelationDialog({
if (candidate.spouseIds?.includes(member.id)) return false
if (candidate.gender === member.gender) return false
} else if (relationType === "father") {
// 父亲必须是男性,世代比当前成员小1
if (candidate.gender !== "MALE") return false
if (candidate.generation !== (member.generation - 1)) return false
if (member.fatherId === candidate.id) return false
} else if (relationType === "mother") {
// 母亲必须是女性,世代比当前成员小1
if (candidate.gender !== "FEMALE") return false
if (candidate.generation !== (member.generation - 1)) return false
if (member.motherId === candidate.id) return false
@@ -103,6 +108,17 @@ export function AddRelationDialog({
}, [member, relationType, treeData.members, normalizedQuery])
const handleLink = async (target: FamilyMember) => {
// 检查是否需要显示代数重新计算警告
if ((relationType === 'father' || relationType === 'mother') && shouldShowGenerationWarning(member, relationType)) {
setPendingAction({ type: 'link', target })
setShowWarningDialog(true)
return
}
await performLink(target)
}
const performLink = async (target: FamilyMember) => {
try {
setLinkingId(target.id)
if (relationType === "child") {
@@ -161,80 +177,111 @@ export function AddRelationDialog({
const handleCreateNew = () => {
if (!normalizedQuery) return
// 检查是否需要显示代数重新计算警告
if ((relationType === 'father' || relationType === 'mother') && shouldShowGenerationWarning(member, relationType)) {
setPendingAction({ type: 'create' })
setShowWarningDialog(true)
return
}
onCreateNew(query.trim(), relationType)
}
// 确认添加父母后的处理
const handleConfirmAddParent = () => {
if (pendingAction?.type === 'link' && pendingAction.target) {
performLink(pendingAction.target)
} else if (pendingAction?.type === 'create') {
onCreateNew(query.trim(), relationType)
}
setPendingAction(null)
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<DialogTitle>{relationType === "child" ? "子女" : relationType === "spouse" ? "配偶" : relationType === "father" ? "父亲" : "母亲"}</DialogTitle>
<DialogDescription>
</DialogDescription>
</DialogHeader>
<>
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<DialogTitle>{relationType === "child" ? "子女" : relationType === "spouse" ? "配偶" : relationType === "father" ? "父亲" : "母亲"}</DialogTitle>
<DialogDescription>
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="relation-name"></Label>
<Input
id="relation-name"
placeholder="请输入姓名或字号"
value={query}
autoFocus
onChange={(e) => setQuery(e.target.value)}
/>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="relation-name"></Label>
<Input
id="relation-name"
placeholder="请输入姓名或字号"
value={query}
autoFocus
onChange={(e) => setQuery(e.target.value)}
/>
</div>
{normalizedQuery ? (
candidates.length > 0 ? (
<div>
<p className="text-sm text-muted-foreground mb-2"></p>
<ScrollArea className="max-h-64 rounded-md border">
<div className="divide-y">
{candidates.map(({ member: candidate, fatherName, motherName }) => (
<div key={candidate.id} className="p-3 flex flex-col gap-2">
<div className="flex items-center justify-between gap-3">
<div>
<MemberNameWithStatus name={candidate.fullName} isDead={!!candidate.deathDate} />
<p className="text-xs text-muted-foreground">
{candidate.generation} · {candidate.gender === "FEMALE" ? "女" : "男"}
</p>
</div>
<Button
size="sm"
onClick={() => handleLink(candidate)}
disabled={linkingId === candidate.id}
>
{linkingId === candidate.id ? "关联中..." : "关联"}
</Button>
</div>
<div className="text-xs text-muted-foreground">
<div>{fatherName || "未登记"}</div>
<div>{motherName || "未登记"}</div>
</div>
</div>
))}
</div>
</ScrollArea>
</div>
) : (
<p className="text-sm text-muted-foreground"></p>
)
) : (
<p className="text-sm text-muted-foreground"></p>
)}
</div>
{normalizedQuery ? (
candidates.length > 0 ? (
<div>
<p className="text-sm text-muted-foreground mb-2"></p>
<ScrollArea className="max-h-64 rounded-md border">
<div className="divide-y">
{candidates.map(({ member: candidate, fatherName, motherName }) => (
<div key={candidate.id} className="p-3 flex flex-col gap-2">
<div className="flex items-center justify-between gap-3">
<div>
<MemberNameWithStatus name={candidate.fullName} isDead={!!candidate.deathDate} />
<p className="text-xs text-muted-foreground">
{candidate.generation} · {candidate.gender === "FEMALE" ? "女" : "男"}
</p>
</div>
<Button
size="sm"
onClick={() => handleLink(candidate)}
disabled={linkingId === candidate.id}
>
{linkingId === candidate.id ? "关联中..." : "关联"}
</Button>
</div>
<div className="text-xs text-muted-foreground">
<div>{fatherName || "未登记"}</div>
<div>{motherName || "未登记"}</div>
</div>
</div>
))}
</div>
</ScrollArea>
</div>
) : (
<p className="text-sm text-muted-foreground"></p>
)
) : (
<p className="text-sm text-muted-foreground"></p>
)}
</div>
<DialogFooter className="flex flex-col gap-2 sm:flex-row sm:justify-between">
<Button variant="outline" onClick={() => onOpenChange(false)} className="sm:flex-1">
</Button>
<Button onClick={handleCreateNew} disabled={!normalizedQuery} className="sm:flex-1">
{query || "成员"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<DialogFooter className="flex flex-col gap-2 sm:flex-row sm:justify-between">
<Button variant="outline" onClick={() => onOpenChange(false)} className="sm:flex-1">
</Button>
<Button onClick={handleCreateNew} disabled={!normalizedQuery} className="sm:flex-1">
"{query || ""}"
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* 代数重新计算警告对话框 */}
{pendingAction && (relationType === 'father' || relationType === 'mother') && (
<GenerationWarningDialog
open={showWarningDialog}
onOpenChange={setShowWarningDialog}
memberName={member.fullName}
relationType={relationType}
onConfirm={handleConfirmAddParent}
/>
)}
</>
)
}