This commit is contained in:
freedakgmail
2025-11-23 15:56:24 +08:00
parent 8b32cda811
commit 3375a79ed4
74 changed files with 99918 additions and 1227 deletions
+183 -24
View File
@@ -28,20 +28,35 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
? Math.max(...existingMembers.map(m => m.generation || 1))
: 1
// 获取始祖的姓氏(第1世成员)
const ancestorSurname = existingMembers.find(m => m.generation === 1)?.surname || ""
// Initialize state with default values or initialData
const [formData, setFormData] = useState<Partial<FamilyMember>>({
id: initialData?.id || crypto.randomUUID(),
surname: initialData?.surname || "李",
givenName: initialData?.givenName || "",
fullName: initialData?.fullName || "",
gender: (initialData?.gender || "MALE").toUpperCase() as "MALE" | "FEMALE",
generation: initialData?.generation || maxGeneration,
spouseIds: initialData?.spouseIds || [],
childrenIds: initialData?.childrenIds || [],
tags: initialData?.tags || [],
fatherId: initialData?.fatherId,
motherId: initialData?.motherId,
...initialData,
const [formData, setFormData] = useState<Partial<FamilyMember>>(() => {
const defaults = {
id: crypto.randomUUID(),
surname: ancestorSurname,
givenName: "",
fullName: "",
gender: "MALE" as "MALE" | "FEMALE",
generation: maxGeneration,
spouseIds: [],
childrenIds: [],
tags: [],
}
if (!initialData) return defaults
return {
...defaults,
...initialData,
gender: (initialData.gender || "MALE").toUpperCase() as "MALE" | "FEMALE",
spouseIds: initialData.spouseIds || [],
childrenIds: initialData.childrenIds || [],
tags: initialData.tags || [],
fatherId: initialData.fatherId || undefined,
motherId: initialData.motherId || undefined,
}
})
const [errors, setErrors] = useState<string[]>([])
@@ -81,14 +96,48 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
if (errors.length > 0) setErrors([])
}
const handleSpouseChange = (spouseId: string) => {
// If selecting "none" (empty string), clear spouses
if (!spouseId) {
setFormData((prev) => ({ ...prev, spouseIds: [] }))
return
}
// Otherwise set as single spouse (logic can be expanded for multiple later)
setFormData((prev) => ({ ...prev, spouseIds: [spouseId] }))
const handleSpouseAdd = (spouseId: string) => {
if (!spouseId || spouseId === "none") return
setFormData((prev) => {
const currentSpouses = prev.spouseIds || []
// 避免重复添加
if (currentSpouses.includes(spouseId)) return prev
return {
...prev,
spouseIds: [...currentSpouses, spouseId]
}
})
}
const handleSpouseRemove = (spouseId: string) => {
setFormData((prev) => ({
...prev,
spouseIds: (prev.spouseIds || []).filter(id => id !== spouseId)
}))
}
const handleChildAdd = (childId: string) => {
if (!childId || childId === "none") return
setFormData((prev) => {
const currentChildren = prev.childrenIds || []
// 避免重复添加
if (currentChildren.includes(childId)) return prev
return {
...prev,
childrenIds: [...currentChildren, childId]
}
})
}
const handleChildRemove = (childId: string) => {
setFormData((prev) => ({
...prev,
childrenIds: (prev.childrenIds || []).filter(id => id !== childId)
}))
}
const validate = (): boolean => {
@@ -341,17 +390,30 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
<div className="space-y-2">
<Label htmlFor="spouseId"> (Spouse)</Label>
<Select value={formData.spouseIds?.[0] || "none"} onValueChange={handleSpouseChange}>
<Select value="none" onValueChange={handleSpouseAdd}>
<SelectTrigger>
<SelectValue placeholder="选择配偶 Select Spouse" />
<SelectValue placeholder="添加配偶..." />
</SelectTrigger>
<SelectContent>
<SelectItem value="none"> (None)</SelectItem>
<SelectItem value="none"></SelectItem>
{potentialRelatives
.filter((m) => {
// 必须是同世系
if (m.generation !== formData.generation) return false
// 通常配偶应该是异性
if (m.gender === formData.gender) return false
// 排除已选的配偶
if (formData.spouseIds?.includes(m.id)) return false
// 一夫多妻:女性可以选择已有配偶的男性,男性只能选择未婚女性
if (formData.gender === 'MALE') {
// 男性:排除已有配偶的女性
if (m.spouseIds && m.spouseIds.length > 0) return false
}
// 女性:可以选择已有配偶的男性(一夫多妻)
// 排除直系亲属
// 1. 排除父母
if (m.id === formData.fatherId || m.id === formData.motherId) return false
@@ -372,6 +434,103 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
))}
</SelectContent>
</Select>
{/* 已选配偶列表 */}
{formData.spouseIds && formData.spouseIds.length > 0 && (
<div className="mt-2 space-y-2">
{formData.spouseIds.map((spouseId, index) => {
const spouse = existingMembers.find(m => m.id === spouseId)
if (!spouse) return null
return (
<div key={spouseId} className="flex items-center justify-between p-2 bg-muted rounded">
<span className="text-sm">
{index + 1}. {spouse.fullName} ({spouse.generation})
</span>
<Button
type="button"
variant="ghost"
size="sm"
onClick={() => handleSpouseRemove(spouseId)}
className="h-6 w-6 p-0 hover:bg-destructive hover:text-destructive-foreground"
>
×
</Button>
</div>
)
})}
</div>
)}
</div>
<div className="space-y-2">
<Label htmlFor="childId"> (Children)</Label>
<Select value="none" onValueChange={handleChildAdd}>
<SelectTrigger>
<SelectValue placeholder="添加子女..." />
</SelectTrigger>
<SelectContent>
<SelectItem value="none"></SelectItem>
{potentialRelatives
.filter((m) => {
// 必须是下一世系
if (m.generation !== (formData.generation || 1) + 1) return false
// 排除已选的子女
if (formData.childrenIds?.includes(m.id)) return false
// 排除配偶
if (formData.spouseIds?.includes(m.id)) return false
// 排除配偶身份的成员(没有父母但有配偶的)
const isSpouse = !m.fatherId && !m.motherId && m.spouseIds && m.spouseIds.length > 0
if (isSpouse) return false
// 根据当前成员性别,排除已有对应父母的子女
if (formData.gender === 'MALE') {
// 男性:排除已有父亲的子女
if (m.fatherId) return false
} else if (formData.gender === 'FEMALE') {
// 女性:排除已有母亲的子女
if (m.motherId) return false
}
return true
})
.map((m) => (
<SelectItem key={m.id} value={m.id}>
{m.fullName} ({m.generation})
</SelectItem>
))}
</SelectContent>
</Select>
{/* 已选子女列表 */}
{formData.childrenIds && formData.childrenIds.length > 0 && (
<div className="mt-2 space-y-2">
{formData.childrenIds.map((childId, index) => {
const child = existingMembers.find(m => m.id === childId)
if (!child) return null
return (
<div key={childId} className="flex items-center justify-between p-2 bg-muted rounded">
<span className="text-sm">
{index + 1}. {child.fullName} ({child.generation})
</span>
<Button
type="button"
variant="ghost"
size="sm"
onClick={() => handleChildRemove(childId)}
className="h-6 w-6 p-0 hover:bg-destructive hover:text-destructive-foreground"
>
×
</Button>
</div>
)
})}
</div>
)}
</div>
</CardContent>
</Card>