0.0.0.5
This commit is contained in:
+192
-127
@@ -23,6 +23,11 @@ interface MemberFormProps {
|
||||
}
|
||||
|
||||
export function MemberForm({ initialData, existingMembers = [], onSubmit, onCancel }: MemberFormProps) {
|
||||
// 计算最大世系
|
||||
const maxGeneration = existingMembers.length > 0
|
||||
? Math.max(...existingMembers.map(m => m.generation || 1))
|
||||
: 1
|
||||
|
||||
// Initialize state with default values or initialData
|
||||
const [formData, setFormData] = useState<Partial<FamilyMember>>({
|
||||
id: initialData?.id || crypto.randomUUID(),
|
||||
@@ -30,7 +35,7 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
|
||||
givenName: initialData?.givenName || "",
|
||||
fullName: initialData?.fullName || "",
|
||||
gender: initialData?.gender || "male",
|
||||
generation: initialData?.generation || 1,
|
||||
generation: initialData?.generation || maxGeneration,
|
||||
spouseIds: initialData?.spouseIds || [],
|
||||
childrenIds: initialData?.childrenIds || [],
|
||||
fatherId: initialData?.fatherId,
|
||||
@@ -43,10 +48,32 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
|
||||
const handleChange = (field: keyof FamilyMember, value: any) => {
|
||||
setFormData((prev) => {
|
||||
const newData = { ...prev, [field]: value }
|
||||
|
||||
// Auto-update full name if surname or given name changes
|
||||
if (field === "surname" || field === "givenName") {
|
||||
newData.fullName = (newData.surname || "") + (newData.givenName || "")
|
||||
}
|
||||
|
||||
// 选择父亲时自动带出母亲
|
||||
if (field === "fatherId" && value) {
|
||||
const father = existingMembers.find(m => m.id === value)
|
||||
if (father && father.spouseIds && father.spouseIds.length > 0) {
|
||||
// 找到父亲的配偶(母亲)
|
||||
const motherId = father.spouseIds[0]
|
||||
newData.motherId = motherId
|
||||
}
|
||||
}
|
||||
|
||||
// 选择母亲时自动带出父亲
|
||||
if (field === "motherId" && value) {
|
||||
const mother = existingMembers.find(m => m.id === value)
|
||||
if (mother && mother.spouseIds && mother.spouseIds.length > 0) {
|
||||
// 找到母亲的配偶(父亲)
|
||||
const fatherId = mother.spouseIds[0]
|
||||
newData.fatherId = fatherId
|
||||
}
|
||||
}
|
||||
|
||||
return newData
|
||||
})
|
||||
// Clear errors when user makes changes
|
||||
@@ -174,67 +201,180 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* Basic Identity */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-lg font-serif">
|
||||
<User className="h-5 w-5" />
|
||||
基本信息 (Identity)
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex justify-center mb-4">
|
||||
<ImageUpload
|
||||
value={formData.avatarImageId}
|
||||
onChange={(imageId) => handleChange("avatarImageId", imageId)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="surname">姓 (Surname)</Label>
|
||||
<Input
|
||||
id="surname"
|
||||
value={formData.surname}
|
||||
onChange={(e) => handleChange("surname", e.target.value)}
|
||||
<div className="space-y-6">
|
||||
{/* 基本信息和家庭关系 - 两列布局 */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{/* 左列:基本信息 */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-lg font-serif">
|
||||
<User className="h-5 w-5" />
|
||||
基本信息 (Identity)
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex justify-center mb-4">
|
||||
<ImageUpload
|
||||
value={formData.avatarImageId}
|
||||
onChange={(imageId) => handleChange("avatarImageId", imageId)}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="givenName">名 (Given Name)</Label>
|
||||
<Input
|
||||
id="givenName"
|
||||
value={formData.givenName}
|
||||
onChange={(e) => handleChange("givenName", e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="surname">姓 (Surname)</Label>
|
||||
<Input
|
||||
id="surname"
|
||||
value={formData.surname}
|
||||
onChange={(e) => handleChange("surname", e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="givenName">名 (Given Name)</Label>
|
||||
<Input
|
||||
id="givenName"
|
||||
value={formData.givenName}
|
||||
onChange={(e) => handleChange("givenName", e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="gender">性别 (Gender)</Label>
|
||||
<Select value={formData.gender} onValueChange={(val) => handleChange("gender", val)}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="male">男 (Male)</SelectItem>
|
||||
<SelectItem value="female">女 (Female)</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="generation">世系 (Generation)</Label>
|
||||
<Input
|
||||
id="generation"
|
||||
type="number"
|
||||
value={formData.generation}
|
||||
onChange={(e) => handleChange("generation", Number.parseInt(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 右列:家庭关系 */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-lg font-serif">
|
||||
<Users className="h-5 w-5" />
|
||||
家庭关系 (Relationships)
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="gender">性别 (Gender)</Label>
|
||||
<Select value={formData.gender} onValueChange={(val) => handleChange("gender", val)}>
|
||||
<Label htmlFor="fatherId">父亲 (Father)</Label>
|
||||
<div className="flex gap-2">
|
||||
<Select
|
||||
value={formData.fatherId || "none"}
|
||||
onValueChange={(val) => handleChange("fatherId", val === "none" ? undefined : val)}
|
||||
>
|
||||
<SelectTrigger className="flex-1">
|
||||
<SelectValue placeholder="选择父亲" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">无 (None)</SelectItem>
|
||||
{potentialRelatives
|
||||
.filter((m) => m.gender === "male" && m.generation === (formData.generation || 1) - 1)
|
||||
.map((m) => (
|
||||
<SelectItem key={m.id} value={m.id}>
|
||||
{m.fullName} ({m.generation}世)
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{!formData.fatherId && (
|
||||
<Input
|
||||
placeholder="非家族成员填写姓名"
|
||||
value={formData.spouseFatherName || ""}
|
||||
onChange={(e) => handleChange("spouseFatherName", e.target.value)}
|
||||
className="flex-1"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="motherId">母亲 (Mother)</Label>
|
||||
<div className="flex gap-2">
|
||||
<Select
|
||||
value={formData.motherId || "none"}
|
||||
onValueChange={(val) => handleChange("motherId", val === "none" ? undefined : val)}
|
||||
>
|
||||
<SelectTrigger className="flex-1">
|
||||
<SelectValue placeholder="选择母亲" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">无 (None)</SelectItem>
|
||||
{potentialRelatives
|
||||
.filter((m) => m.gender === "female" && m.generation === (formData.generation || 1) - 1)
|
||||
.map((m) => (
|
||||
<SelectItem key={m.id} value={m.id}>
|
||||
{m.fullName} ({m.generation}世)
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{!formData.motherId && (
|
||||
<Input
|
||||
placeholder="非家族成员填写姓名"
|
||||
value={formData.spouseMotherName || ""}
|
||||
onChange={(e) => handleChange("spouseMotherName", e.target.value)}
|
||||
className="flex-1"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="spouseId">配偶 (Spouse)</Label>
|
||||
<Select value={formData.spouseIds?.[0] || "none"} onValueChange={handleSpouseChange}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
<SelectValue placeholder="选择配偶 Select Spouse" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="male">男 (Male)</SelectItem>
|
||||
<SelectItem value="female">女 (Female)</SelectItem>
|
||||
<SelectItem value="none">无 (None)</SelectItem>
|
||||
{potentialRelatives
|
||||
.filter((m) => {
|
||||
// 必须是同世系
|
||||
if (m.generation !== formData.generation) return false
|
||||
|
||||
// 排除直系亲属
|
||||
// 1. 排除父母
|
||||
if (m.id === formData.fatherId || m.id === formData.motherId) return false
|
||||
|
||||
// 2. 排除子女
|
||||
if (formData.childrenIds?.includes(m.id)) return false
|
||||
|
||||
// 3. 排除兄弟姐妹(同父或同母)
|
||||
if (formData.fatherId && m.fatherId === formData.fatherId) return false
|
||||
if (formData.motherId && m.motherId === formData.motherId) return false
|
||||
|
||||
return true
|
||||
})
|
||||
.map((m) => (
|
||||
<SelectItem key={m.id} value={m.id}>
|
||||
{m.fullName} ({m.generation}世)
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="generation">世系 (Generation)</Label>
|
||||
<Input
|
||||
id="generation"
|
||||
type="number"
|
||||
value={formData.generation}
|
||||
onChange={(e) => handleChange("generation", Number.parseInt(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Traditional Names */}
|
||||
<Card>
|
||||
@@ -439,81 +579,6 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Relationships */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-lg font-serif">
|
||||
<Users className="h-5 w-5" />
|
||||
家庭关系 (Relationships)
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="fatherId">父亲 (Father)</Label>
|
||||
<Select
|
||||
value={formData.fatherId || "none"}
|
||||
onValueChange={(val) => handleChange("fatherId", val === "none" ? undefined : val)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择父亲 Select Father" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">无 (None)</SelectItem>
|
||||
{potentialRelatives
|
||||
.filter((m) => m.gender === "male")
|
||||
.map((m) => (
|
||||
<SelectItem key={m.id} value={m.id}>
|
||||
{m.fullName} ({m.generation}世)
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="motherId">母亲 (Mother)</Label>
|
||||
<Select
|
||||
value={formData.motherId || "none"}
|
||||
onValueChange={(val) => handleChange("motherId", val === "none" ? undefined : val)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择母亲 Select Mother" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">无 (None)</SelectItem>
|
||||
{potentialRelatives
|
||||
.filter((m) => m.gender === "female")
|
||||
.map((m) => (
|
||||
<SelectItem key={m.id} value={m.id}>
|
||||
{m.fullName} ({m.generation}世)
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="spouseId">配偶 (Spouse)</Label>
|
||||
<Select value={formData.spouseIds?.[0] || "none"} onValueChange={handleSpouseChange}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择配偶 Select Spouse" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">无 (None)</SelectItem>
|
||||
{potentialRelatives
|
||||
.filter((m) => m.gender !== formData.gender) // Suggest opposite gender
|
||||
.map((m) => (
|
||||
<SelectItem key={m.id} value={m.id}>
|
||||
{m.fullName} ({m.generation}世)
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-4 sticky bottom-4 bg-background/90 p-4 border-t border-border backdrop-blur rounded-lg">
|
||||
|
||||
Reference in New Issue
Block a user