This commit is contained in:
freedakgmail
2025-11-30 14:00:42 +08:00
parent 89f1e061d4
commit b7f3592db3
167 changed files with 1334 additions and 1038 deletions
+366 -451
View File
@@ -18,6 +18,7 @@ import { PhotoGallery } from "./photo-gallery"
import { StoryManager } from "./story-manager"
import { DateInputWithLunar } from "@/components/ui/date-input-with-lunar"
import { MemberNameWithStatus } from "@/components/member-name-with-status"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
interface MemberFormProps {
initialData?: Partial<FamilyMember>
@@ -25,9 +26,11 @@ interface MemberFormProps {
onSubmit: (data: FamilyMember) => void
onCancel: () => void
isFounder?: boolean
hideTabs?: boolean
activeTab?: string
}
export function MemberForm({ initialData, existingMembers = [], onSubmit, onCancel, isFounder = false }: MemberFormProps) {
export function MemberForm({ initialData, existingMembers = [], onSubmit, onCancel, isFounder = false, hideTabs = false, activeTab = "basic" }: MemberFormProps) {
// 计算最大世系
const maxGeneration = existingMembers.length > 0
? Math.max(...existingMembers.map(m => m.generation || 1))
@@ -105,6 +108,7 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
uploadedAt: initialData.updatedAt || new Date().toISOString(),
}))
// 优先使用 initialData 中的 motherId,如果没有才使用之前的值
const newMotherId = initialData.motherId !== undefined ? initialData.motherId : prev.motherId
console.log('Calculating new motherId:', {
initial: initialData.motherId,
@@ -148,37 +152,33 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
return
}
}
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 || "")
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]
// 只有当前母亲ID为空时才自动设置
if (!newData.motherId) {
if (!newData.motherId) { // Only auto-set if motherId is not already present
newData.motherId = motherId
console.log('Auto-set mother from father:', { fatherId: value, 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]
// 只有当前父亲ID为空时才自动设置
if (!newData.fatherId) {
if (!newData.fatherId) { // Only auto-set if fatherId is not already present
newData.fatherId = fatherId
console.log('Auto-set father from mother:', { motherId: value, fatherId })
}
@@ -187,153 +187,113 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
return newData
})
// Clear errors when user makes changes
if (errors.length > 0) setErrors([])
}
const handlePhotosChange = (photos: FamilyPhoto[]) => {
handleChange("photos", photos)
handleChange("photoIds", photos.map((photo) => photo.url))
setFormData((prev) => ({
...prev,
photos,
photoIds: photos.map(p => p.url),
}))
}
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]
}
})
if (spouseId === "none") return
setFormData((prev) => ({
...prev,
spouseIds: [...(prev.spouseIds || []), spouseId],
}))
}
const handleSpouseRemove = (spouseId: string) => {
setFormData((prev) => ({
...prev,
spouseIds: (prev.spouseIds || []).filter(id => id !== spouseId)
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]
}
})
if (childId === "none") return
setFormData((prev) => ({
...prev,
childrenIds: [...(prev.childrenIds || []), childId],
}))
}
const handleChildRemove = (childId: string) => {
setFormData((prev) => ({
...prev,
childrenIds: (prev.childrenIds || []).filter(id => id !== childId)
childrenIds: (prev.childrenIds || []).filter((id) => id !== childId),
}))
}
const validate = (): boolean => {
const validateForm = (): boolean => {
const newErrors: string[] = []
// 1. Date Validation
if (formData.birthDate && formData.deathDate) {
if (new Date(formData.birthDate) > new Date(formData.deathDate)) {
newErrors.push("出生日期不能晚于逝世日期")
}
if (!formData.surname?.trim()) {
newErrors.push("请填写姓氏")
}
// 2. Parent Age Validation
const birthYear = formData.birthDate ? new Date(formData.birthDate).getFullYear() : null
if (birthYear) {
if (formData.fatherId) {
const father = existingMembers.find((m) => m.id === formData.fatherId)
if (father?.birthDate) {
const fatherBirthYear = new Date(father.birthDate).getFullYear()
if (birthYear - fatherBirthYear < 15) {
newErrors.push(`与父亲的年龄差过小 (${birthYear - fatherBirthYear}岁),请确认`)
}
if (birthYear < fatherBirthYear) {
newErrors.push("出生日期不能早于父亲")
}
}
}
if (formData.motherId) {
const mother = existingMembers.find((m) => m.id === formData.motherId)
if (mother?.birthDate) {
const motherBirthYear = new Date(mother.birthDate).getFullYear()
if (birthYear - motherBirthYear < 15) {
newErrors.push(`与母亲的年龄差过小 (${birthYear - motherBirthYear}岁),请确认`)
}
if (birthYear < motherBirthYear) {
newErrors.push("出生日期不能早于母亲")
}
}
}
if (!formData.givenName?.trim()) {
newErrors.push("请填写名字")
}
// 3. Generation Validation
if (formData.fatherId) {
const father = existingMembers.find((m) => m.id === formData.fatherId)
if (father && formData.generation !== father.generation + 1) {
newErrors.push(`世系错误:父亲是第 ${father.generation} 世,子女应为第 ${father.generation + 1}`)
}
if (!formData.gender) {
newErrors.push("请选择性别")
}
// 4. Circular Reference Detection (循环引用检测)
if (formData.fatherId || formData.motherId) {
const visited = new Set<string>()
const checkCircular = (memberId: string): boolean => {
if (memberId === formData.id) return true
if (visited.has(memberId)) return false
visited.add(memberId)
const member = existingMembers.find((m) => m.id === memberId)
if (!member) return false
if (member.fatherId && checkCircular(member.fatherId)) return true
if (member.motherId && checkCircular(member.motherId)) return true
return false
}
if (formData.fatherId && checkCircular(formData.fatherId)) {
newErrors.push("检测到循环引用:不能将自己的后代设置为父亲")
}
if (formData.motherId && checkCircular(formData.motherId)) {
newErrors.push("检测到循环引用:不能将自己的后代设置为母亲")
}
if (!formData.generation || formData.generation < 1) {
newErrors.push("请填写正确的世系")
}
// 5. Self-Reference Detection (自我引用检测)
if (formData.fatherId === formData.id) {
newErrors.push("不能将自己设置为自己的父亲")
}
if (formData.motherId === formData.id) {
newErrors.push("不能将自己设置为自己的母亲")
}
if (formData.spouseIds?.includes(formData.id || "")) {
newErrors.push("不能将自己设置为自己的配偶")
}
setErrors(newErrors)
return newErrors.length === 0
}
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
if (validate()) {
onSubmit(formData as FamilyMember)
if (!validateForm()) {
return
}
// 确保所有必填字段都有值
const memberData: FamilyMember = {
id: formData.id || generateUUID(),
surname: formData.surname || "",
givenName: formData.givenName || "",
fullName: formData.fullName || `${formData.surname || ""}${formData.givenName || ""}`,
gender: formData.gender as "MALE" | "FEMALE",
generation: formData.generation || 1,
spouseIds: formData.spouseIds || [],
childrenIds: formData.childrenIds || [],
tags: formData.tags || [],
photos: formData.photos || [],
photoIds: formData.photoIds || [],
fatherId: formData.fatherId,
motherId: formData.motherId,
spouseFatherName: formData.spouseFatherName,
spouseMotherName: formData.spouseMotherName,
phone: formData.phone,
occupation: formData.occupation,
courtesyName: formData.courtesyName,
artName: formData.artName,
generationName: formData.generationName,
posthumousName: formData.posthumousName,
birthDate: formData.birthDate,
deathDate: formData.deathDate,
isLunarDate: formData.isLunarDate || false,
birthPlace: formData.birthPlace,
deathPlace: formData.deathPlace,
address: formData.address,
bio: formData.bio,
avatarUrl: formData.avatarUrl,
stories: formData.stories || [],
createdAt: formData.createdAt || new Date().toISOString(),
updatedAt: new Date().toISOString(),
}
onSubmit(memberData)
}
const potentialRelatives = existingMembers.filter((m) => m.id !== formData.id)
@@ -346,9 +306,9 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
})
return (
<form onSubmit={handleSubmit} className="space-y-8 max-w-4xl mx-auto p-6">
<form onSubmit={handleSubmit} className="w-full">
{errors.length > 0 && (
<div className="bg-destructive/15 text-destructive px-4 py-3 rounded-md border border-destructive/20">
<div className="bg-destructive/15 text-destructive px-4 py-3 rounded-md border border-destructive/20 mb-6">
<p className="font-bold mb-1"></p>
<ul className="list-disc list-inside text-sm">
{errors.map((err, index) => (
@@ -358,18 +318,28 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
</div>
)}
<div className="space-y-6">
{/* 基本信息和家庭关系 - 两列布局 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* 左列:基本信息 */}
<Card className={founderCardClass}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<User className="h-5 w-5" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<Tabs defaultValue="basic" value={hideTabs ? activeTab : undefined} className="w-full flex flex-col h-full">
{!hideTabs && (
<div className="flex-shrink-0">
<TabsList className="grid w-full grid-cols-4">
<TabsTrigger value="basic"></TabsTrigger>
<TabsTrigger value="family"></TabsTrigger>
<TabsTrigger value="details"></TabsTrigger>
<TabsTrigger value="media"></TabsTrigger>
</TabsList>
</div>
)}
<div className="flex-1 overflow-y-auto">
<TabsContent value="basic" className="space-y-6 mt-6 w-full">
<Card className={`w-full ${founderCardClass}`}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<User className="h-5 w-5" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex justify-center mb-4">
<ImageUpload
value={formData.avatarUrl}
@@ -379,7 +349,7 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="surname"> (Surname)</Label>
<Label htmlFor="surname"></Label>
<Input
id="surname"
value={formData.surname}
@@ -387,7 +357,7 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
/>
</div>
<div className="space-y-2">
<Label htmlFor="givenName"> (Given Name)</Label>
<Label htmlFor="givenName"></Label>
<Input
id="givenName"
value={formData.givenName}
@@ -419,11 +389,32 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="phone"></Label>
<Input
id="phone"
value={formData.phone || ""}
onChange={(e) => handleChange("phone", e.target.value)}
placeholder="请输入联系电话"
/>
</div>
<div className="space-y-2">
<Label htmlFor="occupation"></Label>
<Input
id="occupation"
value={formData.occupation || ""}
onChange={(e) => handleChange("occupation", e.target.value)}
placeholder="请输入职业"
/>
</div>
</CardContent>
</Card>
</TabsContent>
{/* 右列:家庭关系 */}
<Card className={founderCardClass}>
<TabsContent value="family" className="space-y-6 mt-6 w-full">
<Card className={`w-full ${founderCardClass}`}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<Users className="h-5 w-5" />
@@ -550,32 +541,18 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
<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
// 2. 排除子女
// 排除父母和子女
if (formData.fatherId === m.id || formData.motherId === m.id) return false
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
// 必须是同世系
if (m.generation !== (formData.generation || 1)) return false
// 必须是异性
if (m.gender === formData.gender) return false
return true
})
@@ -698,303 +675,241 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
</div>
</CardContent>
</Card>
</div>
</TabsContent>
{/* Traditional Names */}
<Card className={founderCardClass}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<Scroll className="h-5 w-5" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="courtesyName"></Label>
<Input
id="courtesyName"
value={formData.courtesyName || ""}
onChange={(e) => handleChange("courtesyName", e.target.value)}
placeholder="例如:伯虎"
<TabsContent value="details" className="space-y-6 mt-6 w-full">
<Card className={`w-full ${founderCardClass}`}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<Scroll className="h-5 w-5" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="courtesyName"></Label>
<Input
id="courtesyName"
value={formData.courtesyName || ""}
onChange={(e) => handleChange("courtesyName", e.target.value)}
placeholder="例如:伯虎"
/>
</div>
<div className="space-y-2">
<Label htmlFor="artName"></Label>
<Input
id="artName"
value={formData.artName || ""}
onChange={(e) => handleChange("artName", e.target.value)}
placeholder="例如:六如居士"
/>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="generationName"></Label>
<Input
id="generationName"
value={formData.generationName || ""}
onChange={(e) => handleChange("generationName", e.target.value)}
/>
</div>
<div className="space-y-2">
<Label htmlFor="posthumousName"></Label>
<Input
id="posthumousName"
value={formData.posthumousName || ""}
onChange={(e) => handleChange("posthumousName", e.target.value)}
/>
</div>
</div>
</CardContent>
</Card>
<Card className={`w-full ${founderCardClass}`}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<CalendarIcon className="h-5 w-5" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<DateInputWithLunar
id="birthDate"
label="出生日期"
value={formData.birthDate || ""}
isLunar={formData.isLunarDate || false}
onChange={(value) => handleChange("birthDate", value)}
showLunarToggle={false}
/>
<DateInputWithLunar
id="deathDate"
label="逝世日期"
value={formData.deathDate || ""}
isLunar={formData.isLunarDate || false}
onChange={(value) => handleChange("deathDate", value)}
showLunarToggle={false}
/>
</div>
<div className="space-y-2">
<Label htmlFor="artName"></Label>
<Input
id="artName"
value={formData.artName || ""}
onChange={(e) => handleChange("artName", e.target.value)}
placeholder="例如:六如居士"
/>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="generationName"></Label>
<Input
id="generationName"
value={formData.generationName || ""}
onChange={(e) => handleChange("generationName", e.target.value)}
/>
</div>
<div className="space-y-2">
<Label htmlFor="posthumousName"></Label>
<Input
id="posthumousName"
value={formData.posthumousName || ""}
onChange={(e) => handleChange("posthumousName", e.target.value)}
/>
</div>
</div>
</CardContent>
</Card>
{/* Dates & Places */}
<Card className={founderCardClass}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<CalendarIcon className="h-5 w-5" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<DateInputWithLunar
id="birthDate"
label="出生日期"
value={formData.birthDate || ""}
isLunar={formData.isLunarDate || false}
onChange={(value) => handleChange("birthDate", value)}
showLunarToggle={false}
/>
<DateInputWithLunar
id="deathDate"
label="逝世日期"
value={formData.deathDate || ""}
isLunar={formData.isLunarDate || false}
onChange={(value) => handleChange("deathDate", value)}
showLunarToggle={false}
/>
</div>
{/* 统一的公历/农历选择 */}
{(formData.birthDate || formData.deathDate) && (
<div className="space-y-2 pt-2">
<Label className="text-sm font-medium"></Label>
<RadioGroup
value={formData.isLunarDate ? "lunar" : "solar"}
onValueChange={(val) => handleChange("isLunarDate", val === "lunar")}
className="flex flex-row gap-6"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="solar" id="date-type-solar" />
<Label htmlFor="date-type-solar" className="text-sm font-normal cursor-pointer">
()
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="lunar" id="date-type-lunar" />
<Label htmlFor="date-type-lunar" className="text-sm font-normal cursor-pointer">
()
</Label>
</div>
</RadioGroup>
<p className="text-xs text-muted-foreground">
{formData.isLunarDate
? "💡 将按农历日期计算每年的纪念日,如生日、祭日等"
: "💡 将按公历日期计算每年的纪念日"}
</p>
</div>
)}
<div className="space-y-2">
<Label htmlFor="ancestralHome"></Label>
<Input
id="ancestralHome"
value={formData.ancestralHome || ""}
onChange={(e) => handleChange("ancestralHome", e.target.value)}
placeholder="例如:福建省泉州市"
/>
</div>
<div className="space-y-2">
<Label htmlFor="burialPlace"></Label>
<Input
id="burialPlace"
value={formData.burialPlace || ""}
onChange={(e) => handleChange("burialPlace", e.target.value)}
/>
</div>
</CardContent>
</Card>
{/* Contact Information */}
<Card className={founderCardClass}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<User className="h-5 w-5" />
</CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<Label htmlFor="phone"></Label>
<Input
id="phone"
type="tel"
value={formData.phone || ""}
onChange={(e) => handleChange("phone", e.target.value)}
placeholder="例如:13800138000"
/>
</div>
<div className="space-y-2">
<Label htmlFor="telephone"></Label>
<Input
id="telephone"
type="tel"
value={formData.telephone || ""}
onChange={(e) => handleChange("telephone", e.target.value)}
placeholder="例如:0592-1234567"
/>
</div>
<div className="space-y-2">
<Label htmlFor="email"></Label>
<Input
id="email"
type="email"
value={formData.email || ""}
onChange={(e) => handleChange("email", e.target.value)}
placeholder="例如:example@email.com"
/>
</div>
<div className="space-y-2 md:col-span-2">
<Label htmlFor="address"></Label>
<Input
id="address"
value={formData.address || ""}
onChange={(e) => handleChange("address", e.target.value)}
placeholder="例如:福建省厦门市思明区XX路XX号"
/>
</div>
</CardContent>
</Card>
{/* Biography */}
<Card className={founderCardClass}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<Scroll className="h-5 w-5" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<Textarea
className="min-h-[200px]"
placeholder="记录生平事迹..."
value={formData.bio || ""}
onChange={(e) => handleChange("bio", e.target.value)}
/>
</CardContent>
</Card>
{/* Tags */}
<Card className={founderCardClass}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<Scroll className="h-5 w-5" />
</CardTitle>
<CardDescription>便</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-3">
<div className="flex gap-2">
<Input
placeholder="输入标签,按回车添加..."
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault()
const input = e.currentTarget
const value = input.value.trim()
if (value && !(formData.tags || []).includes(value)) {
handleChange('tags', [...(formData.tags || []), value])
input.value = ''
}
}
}}
/>
</div>
{formData.tags && formData.tags.length > 0 && (
<div className="flex flex-wrap gap-2">
{formData.tags.map((tag, index) => (
<span
key={index}
className="inline-flex items-center gap-1 px-2 py-1 bg-primary/10 text-primary text-sm rounded-md border border-primary/20"
>
{tag}
<button
type="button"
onClick={() => {
const newTags = formData.tags?.filter((_, i) => i !== index) || []
handleChange('tags', newTags)
}}
className="ml-1 hover:text-destructive"
>
×
</button>
</span>
))}
{(formData.birthDate || formData.deathDate) && (
<div className="space-y-2 pt-2">
<Label className="text-sm font-medium"></Label>
<RadioGroup
value={formData.isLunarDate ? "lunar" : "solar"}
onValueChange={(val) => handleChange("isLunarDate", val === "lunar")}
className="flex flex-row gap-6"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="solar" id="date-type-solar" />
<Label htmlFor="date-type-solar" className="text-sm font-normal cursor-pointer">
()
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="lunar" id="date-type-lunar" />
<Label htmlFor="date-type-lunar" className="text-sm font-normal cursor-pointer">
()
</Label>
</div>
</RadioGroup>
</div>
)}
</div>
</CardContent>
</Card>
{/* Photo Gallery */}
<Card className={founderCardClass}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<Images className="h-5 w-5" />
</CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<PhotoGallery
photos={formData.photos}
photoIds={formData.photoIds}
onChange={handlePhotosChange}
/>
</CardContent>
</Card>
<div className="space-y-2">
<Label htmlFor="birthPlace"></Label>
<Input
id="birthPlace"
value={formData.birthPlace || ""}
onChange={(e) => handleChange("birthPlace", e.target.value)}
placeholder="请输入出生地"
/>
</div>
{/* Family Stories */}
<Card className={founderCardClass}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<BookOpen className="h-5 w-5" />
</CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<StoryManager
stories={formData.stories || []}
onChange={(stories) => handleChange("stories", stories)}
/>
</CardContent>
</Card>
<div className="space-y-2">
<Label htmlFor="deathPlace"></Label>
<Input
id="deathPlace"
value={formData.deathPlace || ""}
onChange={(e) => handleChange("deathPlace", e.target.value)}
placeholder="请输入逝世地"
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="address"></Label>
<Input
id="address"
value={formData.address || ""}
onChange={(e) => handleChange("address", e.target.value)}
placeholder="请输入居住地"
/>
</div>
<div className="flex justify-end gap-4 sticky bottom-4 bg-background/90 p-4 border-t border-border backdrop-blur rounded-lg">
<Button type="button" variant="outline" onClick={onCancel}>
</Button>
<Button type="submit" className="bg-primary text-primary-foreground hover:bg-primary/90">
</Button>
</div>
<div className="space-y-2">
<Label htmlFor="bio"></Label>
<Textarea
id="bio"
value={formData.bio || ""}
onChange={(e) => handleChange("bio", e.target.value)}
placeholder="请输入个人简介"
rows={4}
/>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="media" className="space-y-6 mt-6 w-full">
<Card className={`w-full ${founderCardClass}`}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<Images className="h-5 w-5" />
</CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<PhotoGallery
photos={formData.photos}
photoIds={formData.photoIds}
onChange={handlePhotosChange}
/>
</CardContent>
</Card>
<Card className={`w-full ${founderCardClass}`}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<BookOpen className="h-5 w-5" />
</CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<StoryManager
stories={formData.stories || []}
onChange={(stories) => handleChange("stories", stories)}
/>
</CardContent>
</Card>
<Card className={`w-full ${founderCardClass}`}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<Scroll className="h-5 w-5" />
</CardTitle>
<CardDescription>便</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-3">
<div className="flex gap-2">
<Input
placeholder="输入标签,按回车添加..."
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault()
const input = e.currentTarget
const value = input.value.trim()
if (value && !(formData.tags || []).includes(value)) {
handleChange('tags', [...(formData.tags || []), value])
input.value = ''
}
}
}}
/>
</div>
{formData.tags && formData.tags.length > 0 && (
<div className="flex flex-wrap gap-2">
{formData.tags.map((tag, index) => (
<span
key={index}
className="inline-flex items-center gap-1 px-2 py-1 bg-primary/10 text-primary text-sm rounded-md border border-primary/20"
>
{tag}
<button
type="button"
onClick={() => {
const newTags = formData.tags?.filter((_, i) => i !== index) || []
handleChange('tags', newTags)
}}
className="ml-1 hover:text-destructive"
>
×
</button>
</span>
))}
</div>
)}
</div>
</CardContent>
</Card>
</TabsContent>
</div>
</Tabs>
</form>
)
}