This commit is contained in:
freedakgmail
2025-11-30 19:10:30 +08:00
parent a05fb384a4
commit 86e889cc2a
16 changed files with 1555 additions and 763 deletions
+195 -131
View File
@@ -11,7 +11,7 @@ import { Textarea } from "@/components/ui/textarea"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { CalendarIcon, User, Scroll, Users, Images, BookOpen } from "lucide-react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { ChineseCard, ChineseCardContent, ChineseCardHeader, ChineseCardTitle, ChineseCardDescription } from "@/components/ui/chinese-card"
import { Switch } from "@/components/ui/switch"
import { ImageUpload } from "@/components/ui/image-upload"
import { PhotoGallery } from "./photo-gallery"
@@ -290,6 +290,8 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
spouseFatherName: formData.spouseFatherName,
spouseMotherName: formData.spouseMotherName,
phone: formData.phone,
telephone: formData.telephone,
email: formData.email,
occupation: formData.occupation,
courtesyName: formData.courtesyName,
artName: formData.artName,
@@ -298,8 +300,10 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
birthDate: formData.birthDate,
deathDate: formData.deathDate,
isLunarDate: formData.isLunarDate || false,
ancestralHome: formData.ancestralHome,
birthPlace: formData.birthPlace,
deathPlace: formData.deathPlace,
burialPlace: formData.burialPlace,
address: formData.address,
bio: formData.bio,
avatarUrl: formData.avatarUrl,
@@ -337,7 +341,7 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
{!hideTabs && (
<div className="flex-shrink-0">
<TabsList className="grid w-full grid-cols-4">
<TabsTrigger value="info"></TabsTrigger>
<TabsTrigger value="info"></TabsTrigger>
<TabsTrigger value="details"></TabsTrigger>
<TabsTrigger value="photos"></TabsTrigger>
<TabsTrigger value="stories"></TabsTrigger>
@@ -346,17 +350,17 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
)}
<div className="flex-1 overflow-y-auto">
{/* 详细信息Tab - 包含基本信息和家庭关系 */}
{/* 基本信息Tab - 包含基本信息和家庭关系 */}
<TabsContent value="info" 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">
<ChineseCard variant="default" cornerOrnament className="shadow-sm w-full">
<ChineseCardHeader>
<ChineseCardTitle className="flex items-center gap-2 text-lg font-serif">
<User className="h-5 w-5" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
</ChineseCardTitle>
</ChineseCardHeader>
<ChineseCardContent className="space-y-6">
{/* 头像和基本信息两栏布局 */}
<div className="grid grid-cols-1 lg:grid-cols-[200px_1fr] gap-8">
{/* 左侧:头像 */}
@@ -367,89 +371,88 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
/>
</div>
{/* 右侧:基本表单字段 */}
<div className="space-y-4">
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{/* 右侧:表单 */}
<div className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<Label htmlFor="surname"> <span className="text-destructive">*</span></Label>
<Label htmlFor="surname">
<span className="text-red-500">*</span>
</Label>
<Input
id="surname"
value={formData.surname}
value={formData.surname || ""}
onChange={(e) => handleChange("surname", e.target.value)}
placeholder="例如:李"
className={errors.includes("surname") ? "border-red-500" : ""}
/>
</div>
<div className="space-y-2">
<Label htmlFor="givenName"> <span className="text-destructive">*</span></Label>
<Label htmlFor="givenName">
<span className="text-red-500">*</span>
</Label>
<Input
id="givenName"
value={formData.givenName}
value={formData.givenName || ""}
onChange={(e) => handleChange("givenName", e.target.value)}
/>
</div>
<div className="space-y-2">
<Label htmlFor="gender"> <span className="text-destructive">*</span></Label>
<Select value={formData.gender} onValueChange={(val) => handleChange("gender", val)}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="MALE"></SelectItem>
<SelectItem value="FEMALE"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="generation"> <span className="text-destructive">*</span></Label>
<Input
id="generation"
type="number"
value={formData.generation}
onChange={(e) => handleChange("generation", Number.parseInt(e.target.value))}
placeholder="例如:明"
className={errors.includes("givenName") ? "border-red-500" : ""}
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="phone"></Label>
<div className="space-y-2">
<Label></Label>
<RadioGroup
value={formData.gender}
onValueChange={(val) => handleChange("gender", val)}
className="flex space-x-4"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="MALE" id="male" />
<Label htmlFor="male"></Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="FEMALE" id="female" />
<Label htmlFor="female"></Label>
</div>
</RadioGroup>
</div>
<div className="space-y-2">
<Label htmlFor="generation"> ()</Label>
<div className="flex items-center gap-4">
<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="请输入职业"
id="generation"
type="number"
value={formData.generation || ""}
onChange={(e) => handleChange("generation", parseInt(e.target.value) || 0)}
className="w-32"
min={1}
/>
<span className="text-sm text-muted-foreground"></span>
</div>
</div>
</div>
</div>
</CardContent>
</Card>
</ChineseCardContent>
</ChineseCard>
{/* 家庭关系 - 左右两个独立卡片 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* 左栏:父母 */}
<Card className={`w-full ${founderCardClass}`}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<ChineseCard variant="default" cornerOrnament className="shadow-sm w-full">
<ChineseCardHeader>
<ChineseCardTitle className="flex items-center gap-2 text-lg font-serif">
<Users className="h-5 w-5" />
</CardTitle>
</ChineseCardTitle>
{!isFounder && (
<CardDescription className="text-xs">
<ChineseCardDescription className="text-xs">
</CardDescription>
</ChineseCardDescription>
)}
</CardHeader>
<CardContent>
</ChineseCardHeader>
<ChineseCardContent>
<div className="space-y-3">
<div className="space-y-2">
<Label htmlFor="fatherId"></Label>
@@ -558,23 +561,23 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
)}
</div>
</div>
</CardContent>
</Card>
</ChineseCardContent>
</ChineseCard>
{/* 右栏:配偶和子女 */}
<Card className={`w-full ${founderCardClass}`}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<ChineseCard variant="default" cornerOrnament className="shadow-sm w-full">
<ChineseCardHeader>
<ChineseCardTitle className="flex items-center gap-2 text-lg font-serif">
<Users className="h-5 w-5" />
</CardTitle>
</ChineseCardTitle>
{!isFounder && (
<CardDescription className="text-xs">
<ChineseCardDescription className="text-xs">
</CardDescription>
</ChineseCardDescription>
)}
</CardHeader>
<CardContent>
</ChineseCardHeader>
<ChineseCardContent>
<div className="space-y-3">
<div className="space-y-2">
<Label htmlFor="spouseId"></Label>
@@ -697,8 +700,8 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
)}
</div>
</div>
</CardContent>
</Card>
</ChineseCardContent>
</ChineseCard>
</div>
</TabsContent>
@@ -706,14 +709,14 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
<TabsContent value="details" className="space-y-6 mt-6 w-full">
{/* 使用两列布局放置两个卡片 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card className={`w-full ${founderCardClass}`}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<ChineseCard variant="default" cornerOrnament className="shadow-sm w-full">
<ChineseCardHeader>
<ChineseCardTitle className="flex items-center gap-2 text-lg font-serif">
<Scroll className="h-5 w-5" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
</ChineseCardTitle>
</ChineseCardHeader>
<ChineseCardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="courtesyName"></Label>
@@ -752,17 +755,17 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
/>
</div>
</div>
</CardContent>
</Card>
</ChineseCardContent>
</ChineseCard>
<Card className={`w-full ${founderCardClass}`}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<ChineseCard variant="default" cornerOrnament className="shadow-sm w-full">
<ChineseCardHeader>
<ChineseCardTitle className="flex items-center gap-2 text-lg font-serif">
<CalendarIcon className="h-5 w-5" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
</ChineseCardTitle>
</ChineseCardHeader>
<ChineseCardContent className="space-y-4">
<div className="grid grid-cols-1 gap-4">
<DateInputWithLunar
id="birthDate"
@@ -781,20 +784,29 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
showLunarToggle={false}
/>
</div>
</CardContent>
</Card>
</ChineseCardContent>
</ChineseCard>
</div>
{/* 地点和简介卡片 */}
<Card className={`w-full ${founderCardClass}`}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<ChineseCard variant="default" cornerOrnament className="shadow-sm w-full">
<ChineseCardHeader>
<ChineseCardTitle className="flex items-center gap-2 text-lg font-serif">
<Scroll className="h-5 w-5" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
</ChineseCardTitle>
</ChineseCardHeader>
<ChineseCardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<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="birthPlace"></Label>
<Input
@@ -804,6 +816,18 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
placeholder="请输入出生地"
/>
</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>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="space-y-2">
<Label htmlFor="deathPlace"></Label>
<Input
@@ -814,12 +838,52 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
/>
</div>
<div className="space-y-2">
<Label htmlFor="address"></Label>
<Label htmlFor="burialPlace"></Label>
<Input
id="address"
value={formData.address || ""}
onChange={(e) => handleChange("address", e.target.value)}
placeholder="请输入居住地"
id="burialPlace"
value={formData.burialPlace || ""}
onChange={(e) => handleChange("burialPlace", 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>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<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="telephone"></Label>
<Input
id="telephone"
value={formData.telephone || ""}
onChange={(e) => handleChange("telephone", e.target.value)}
placeholder="请输入固定电话"
/>
</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="请输入电子邮箱"
/>
</div>
</div>
@@ -834,57 +898,57 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
rows={4}
/>
</div>
</CardContent>
</Card>
</ChineseCardContent>
</ChineseCard>
</TabsContent>
{/* 生平影像Tab */}
<TabsContent value="photos" 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">
<ChineseCard variant="default" cornerOrnament className="shadow-sm w-full">
<ChineseCardHeader>
<ChineseCardTitle className="flex items-center gap-2 text-lg font-serif">
<Images className="h-5 w-5" />
</CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
</ChineseCardTitle>
<ChineseCardDescription></ChineseCardDescription>
</ChineseCardHeader>
<ChineseCardContent>
<PhotoGallery
photos={formData.photos}
photoIds={formData.photoIds}
onChange={handlePhotosChange}
/>
</CardContent>
</Card>
</ChineseCardContent>
</ChineseCard>
</TabsContent>
{/* 家族故事Tab */}
<TabsContent value="stories" 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">
<ChineseCard variant="default" cornerOrnament className="shadow-sm w-full">
<ChineseCardHeader>
<ChineseCardTitle className="flex items-center gap-2 text-lg font-serif">
<BookOpen className="h-5 w-5" />
</CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
</ChineseCardTitle>
<ChineseCardDescription></ChineseCardDescription>
</ChineseCardHeader>
<ChineseCardContent>
<StoryManager
stories={formData.stories || []}
onChange={(stories) => handleChange("stories", stories)}
/>
</CardContent>
</Card>
</ChineseCardContent>
</ChineseCard>
<Card className={`w-full ${founderCardClass}`}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg font-serif">
<ChineseCard variant="default" cornerOrnament className="shadow-sm w-full">
<ChineseCardHeader>
<ChineseCardTitle 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">
</ChineseCardTitle>
<ChineseCardDescription>便</ChineseCardDescription>
</ChineseCardHeader>
<ChineseCardContent className="space-y-4">
<div className="space-y-3">
<div className="flex gap-2">
<Input
@@ -925,8 +989,8 @@ export function MemberForm({ initialData, existingMembers = [], onSubmit, onCanc
</div>
)}
</div>
</CardContent>
</Card>
</ChineseCardContent>
</ChineseCard>
</TabsContent>
</div>
</Tabs>
@@ -146,7 +146,7 @@ export function MemberVersionHistory({ memberId, treeId, onVersionSelect, lastUp
{/* 其他信息变更 */}
{changedOthers.length > 0 && (
<div>
<h4 className="text-xs font-semibold text-foreground mb-2"></h4>
<h4 className="text-xs font-semibold text-foreground mb-2"></h4>
<div className="space-y-2 text-xs">
{changedOthers.map(([field, change]) => (
<div key={field} className="flex items-start gap-2">