312 lines
12 KiB
TypeScript
312 lines
12 KiB
TypeScript
"use client"
|
||
|
||
import { ChineseCard, ChineseCardContent, ChineseCardHeader, ChineseCardTitle } from "@/components/ui/chinese-card"
|
||
import { useRouter, useSearchParams } from "next/navigation"
|
||
import { useFamily } from "@/context/family-context"
|
||
import { SiteHeader } from "@/components/site-header"
|
||
import { MemberForm } from "@/components/members/member-form"
|
||
import { Button } from "@/components/ui/button"
|
||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||
import { UserPlus } from "lucide-react"
|
||
import type { FamilyMember } from "@/types/family"
|
||
import { useMemo, useEffect, useState } from "react"
|
||
import { useSession } from "next-auth/react"
|
||
import { BackButton } from "@/components/ui/back-button"
|
||
import { useDialog } from "@/components/ui/alert-dialog-custom"
|
||
|
||
export default function NewMemberPage() {
|
||
const router = useRouter()
|
||
const searchParams = useSearchParams()
|
||
const { addMember, treeData, getMember } = useFamily()
|
||
const { showAlert, showConfirm } = useDialog()
|
||
const { data: session, status } = useSession()
|
||
const [activeTab, setActiveTab] = useState("info")
|
||
|
||
const existingMembers = Object.values(treeData.members)
|
||
const isFirstMember = existingMembers.length === 0
|
||
|
||
// 未登录时重定向到登录页
|
||
useEffect(() => {
|
||
if (status === 'unauthenticated') {
|
||
router.push('/auth/signin')
|
||
}
|
||
}, [status, router])
|
||
|
||
// 从URL参数获取预填信息
|
||
const initialData = useMemo(() => {
|
||
const fatherId = searchParams.get('fatherId')
|
||
const motherId = searchParams.get('motherId')
|
||
const spouseId = searchParams.get('spouseId')
|
||
const childId = searchParams.get('childId')
|
||
const generation = searchParams.get('generation')
|
||
const gender = searchParams.get('gender')
|
||
const surname = searchParams.get('surname')
|
||
const givenName = searchParams.get('givenName')
|
||
|
||
if (!fatherId && !motherId && !spouseId && !childId && !generation && !gender && !surname && !givenName) return undefined
|
||
|
||
const data: Partial<FamilyMember> = {}
|
||
|
||
// 处理姓名信息
|
||
if (surname) {
|
||
data.surname = surname
|
||
}
|
||
if (givenName) {
|
||
data.givenName = givenName
|
||
}
|
||
// 如果有姓和名,自动生成全名
|
||
if (surname || givenName) {
|
||
data.fullName = (surname || '') + (givenName || '')
|
||
}
|
||
|
||
// 处理父母信息
|
||
if (fatherId) {
|
||
data.fatherId = fatherId
|
||
}
|
||
|
||
if (motherId) {
|
||
data.motherId = motherId
|
||
}
|
||
|
||
// 如果只有父亲没有母亲,尝试自动获取母亲(父亲的配偶)
|
||
if (fatherId && !motherId) {
|
||
const father = getMember(fatherId)
|
||
console.log('Looking for mother (father\'s spouse):', { fatherId, father, spouseIds: father?.spouseIds })
|
||
if (father?.spouseIds && father.spouseIds.length > 0) {
|
||
data.motherId = father.spouseIds[0]
|
||
console.log('Found mother:', data.motherId)
|
||
}
|
||
}
|
||
|
||
// 如果只有母亲没有父亲,尝试自动获取父亲(母亲的配偶)
|
||
if (motherId && !fatherId) {
|
||
const mother = getMember(motherId)
|
||
console.log('Looking for father (mother\'s spouse):', { motherId, mother, spouseIds: mother?.spouseIds })
|
||
if (mother?.spouseIds && mother.spouseIds.length > 0) {
|
||
data.fatherId = mother.spouseIds[0]
|
||
console.log('Found father:', data.fatherId)
|
||
}
|
||
}
|
||
|
||
// 处理配偶信息
|
||
if (spouseId) {
|
||
data.spouseIds = [spouseId]
|
||
}
|
||
|
||
// 处理子女信息
|
||
const childrenIds = searchParams.getAll('childrenIds')
|
||
|
||
if (childrenIds.length > 0) {
|
||
data.childrenIds = childrenIds
|
||
} else if (childId) {
|
||
data.childrenIds = [childId]
|
||
}
|
||
|
||
// 处理世代
|
||
if (generation) {
|
||
data.generation = parseInt(generation)
|
||
}
|
||
|
||
// 处理性别
|
||
if (gender) {
|
||
data.gender = gender.toUpperCase() as 'MALE' | 'FEMALE'
|
||
}
|
||
|
||
return data
|
||
}, [searchParams, getMember])
|
||
|
||
// 检测添加成员的类型
|
||
const memberType = useMemo(() => {
|
||
const fatherId = searchParams.get('fatherId')
|
||
const motherId = searchParams.get('motherId')
|
||
const spouseId = searchParams.get('spouseId')
|
||
const childId = searchParams.get('childId')
|
||
const childrenIds = searchParams.getAll('childrenIds')
|
||
|
||
if (isFirstMember) return '始祖'
|
||
if (fatherId || motherId) return '子女'
|
||
if (spouseId) return '配偶'
|
||
if (childId || childrenIds.length > 0) return '父母'
|
||
|
||
return '成员'
|
||
}, [searchParams, isFirstMember])
|
||
|
||
// 判断是否是给始祖添加父母(新成员将成为新始祖)
|
||
const isAddingParentToFounder = useMemo(() => {
|
||
const childId = searchParams.get('childId')
|
||
if (!childId) return false
|
||
|
||
const child = getMember(childId)
|
||
return child?.generation === 1
|
||
}, [searchParams, getMember])
|
||
|
||
// 是否应该显示始祖样式
|
||
const showFounderStyle = isFirstMember || isAddingParentToFounder
|
||
|
||
const handleSave = async (newMember: FamilyMember) => {
|
||
try {
|
||
// 检查是否正在给始祖添加父母
|
||
const childId = searchParams.get('childId')
|
||
if (childId && memberType === '父母') {
|
||
const child = getMember(childId)
|
||
|
||
// 如果子女不存在(已被删除),提示用户
|
||
if (!child) {
|
||
await showAlert('目标成员已不存在,无法添加父母。请返回成员列表重新操作。', "提示")
|
||
const params = new URLSearchParams(window.location.search)
|
||
router.push(`/members?${params.toString()}`)
|
||
return
|
||
}
|
||
|
||
// 如果子女是第1代(始祖),需要确认
|
||
if (child.generation === 1) {
|
||
const confirmed = await showConfirm(
|
||
'您正在给始祖添加父母,这将导致全族代数重新计算。\n原第1代将变为第2代,以此类推。\n\n是否继续?',
|
||
"确认操作"
|
||
)
|
||
if (!confirmed) {
|
||
return
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果是第一个成员,自动设置为始祖
|
||
const memberData = isFirstMember ? {
|
||
...newMember,
|
||
generation: 1,
|
||
isFounder: true,
|
||
createdAt: new Date().toISOString(),
|
||
updatedAt: new Date().toISOString(),
|
||
} : {
|
||
...newMember,
|
||
createdAt: new Date().toISOString(),
|
||
updatedAt: new Date().toISOString(),
|
||
}
|
||
|
||
const result = await addMember(memberData)
|
||
|
||
// 构建跳转参数
|
||
const params = new URLSearchParams()
|
||
const treeId = searchParams.get('treeId')
|
||
if (treeId) params.set('treeId', treeId)
|
||
|
||
// 如果有提示信息(代数调整),显示给用户,等待用户确认后再跳转
|
||
if (result.message) {
|
||
await showAlert(result.message, "提示")
|
||
}
|
||
|
||
// 跳转到新成员的详情页
|
||
router.push(`/members/${result.member.id}?${params.toString()}`)
|
||
} catch (error) {
|
||
console.error('添加成员失败:', error)
|
||
await showAlert('添加成员失败: ' + (error as Error).message, "错误")
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className="min-h-screen flex flex-col bg-background">
|
||
<SiteHeader />
|
||
<main className="container mx-auto py-4 md:py-8 px-3 md:px-6">
|
||
<Tabs value={activeTab} onValueChange={setActiveTab} className="flex flex-col h-[calc(100vh-6rem)] md:h-[calc(100vh-8rem)]">
|
||
{/* 固定头部区域 */}
|
||
<div className="flex-shrink-0 pb-3 md:pb-4 border-b border-border">
|
||
{/* 返回按钮 */}
|
||
<BackButton onClick={() => router.back()} />
|
||
|
||
{/* Header Section */}
|
||
<div className="flex flex-col md:flex-row justify-between items-start gap-3 md:gap-6 pb-3 md:pb-4">
|
||
<div className="flex items-start gap-3 md:gap-6">
|
||
<div className={`h-14 w-14 md:h-24 md:w-24 rounded-lg bg-muted flex items-center justify-center border-2 ${showFounderStyle ? 'border-amber-400 bg-amber-50/30' : 'border-primary/20'} overflow-hidden flex-shrink-0`}>
|
||
{showFounderStyle ? (
|
||
<span className="text-2xl md:text-4xl">👑</span>
|
||
) : (
|
||
<UserPlus className="h-8 w-8 md:h-16 md:w-16 text-muted-foreground" />
|
||
)}
|
||
</div>
|
||
<div className="min-w-0 flex-1">
|
||
<div className="flex items-center gap-2 md:gap-3 mb-1 md:mb-2 flex-wrap">
|
||
<h1 className="text-lg md:text-3xl font-bold font-serif text-foreground flex items-center gap-1 md:gap-2">
|
||
{showFounderStyle && <span className="text-amber-600 text-lg md:text-2xl">👑</span>}
|
||
{isFirstMember ? '添加家族始祖' : isAddingParentToFounder ? '添加新始祖' : `添加${memberType}`}
|
||
</h1>
|
||
{showFounderStyle && (
|
||
<span className="px-2 md:px-3 py-0.5 md:py-1 bg-gradient-to-r from-amber-100 to-orange-100 text-amber-800 text-xs md:text-sm font-semibold rounded-full border-2 border-amber-300 shadow-sm">
|
||
始祖 · 1世
|
||
</span>
|
||
)}
|
||
</div>
|
||
<p className="text-xs md:text-base text-muted-foreground">
|
||
{isFirstMember
|
||
? '请认真填写始祖信息'
|
||
: `填写${memberType}信息`}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex gap-1.5 md:gap-2 flex-wrap">
|
||
<Button type="button" variant="outline" onClick={() => router.back()} className="text-xs md:text-sm h-7 md:h-9 px-2 md:px-4">
|
||
取消
|
||
</Button>
|
||
<Button
|
||
type="button"
|
||
className="bg-primary text-primary-foreground hover:bg-primary/90 text-xs md:text-sm h-7 md:h-9 px-2 md:px-4"
|
||
onClick={() => {
|
||
const form = document.querySelector('form')
|
||
if (form) {
|
||
form.requestSubmit()
|
||
}
|
||
}}
|
||
>
|
||
保存成员
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Tab 导航栏 */}
|
||
<TabsList className="grid grid-cols-4 md:flex md:flex-wrap gap-1 md:gap-2 bg-muted/70 p-1 w-full md:w-fit h-auto">
|
||
<TabsTrigger value="info" className="text-xs md:text-sm px-2 md:px-3 py-1.5">基本信息</TabsTrigger>
|
||
<TabsTrigger value="details" className="text-xs md:text-sm px-2 md:px-3 py-1.5">详细资料</TabsTrigger>
|
||
<TabsTrigger value="photos" className="text-xs md:text-sm px-2 md:px-3 py-1.5">生平影像</TabsTrigger>
|
||
<TabsTrigger value="stories" className="text-xs md:text-sm px-2 md:px-3 py-1.5">家族故事</TabsTrigger>
|
||
</TabsList>
|
||
|
||
{/* 始祖特殊提示框 - 移动端简化 */}
|
||
{isFirstMember && (
|
||
<div className="mt-3 md:mt-6 p-3 md:p-6 bg-gradient-to-br from-amber-50 via-orange-50 to-amber-50 border-2 border-amber-400 rounded-xl shadow-md">
|
||
<div className="flex items-start gap-2 md:gap-4">
|
||
<div className="flex-shrink-0 w-10 h-10 md:w-14 md:h-14 bg-gradient-to-br from-amber-200 to-orange-200 rounded-full flex items-center justify-center shadow-sm">
|
||
<span className="text-xl md:text-3xl">👑</span>
|
||
</div>
|
||
<div className="flex-1 min-w-0">
|
||
<h2 className="text-lg md:text-2xl font-serif font-bold text-amber-900 mb-1 md:mb-3">
|
||
立家族之根基
|
||
</h2>
|
||
<p className="text-sm md:text-base text-amber-800 leading-relaxed font-serif">
|
||
此为家族树之<span className="font-bold text-amber-900">始祖</span>,
|
||
<span className="font-bold text-amber-900">第一世</span>传人。
|
||
<span className="hidden md:inline"><br />血脉由此而始,世系因此而传。</span>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Tab 内容区域 - 可滚动 */}
|
||
<div className="flex-1 overflow-y-auto">
|
||
<MemberForm
|
||
initialData={initialData}
|
||
existingMembers={existingMembers}
|
||
onSubmit={handleSave}
|
||
onCancel={() => router.back()}
|
||
isFounder={isFirstMember}
|
||
hideTabs={true}
|
||
activeTab={activeTab}
|
||
/>
|
||
</div>
|
||
</Tabs>
|
||
</main>
|
||
</div>
|
||
)
|
||
}
|