0.3.0.6
This commit is contained in:
+150
-67
@@ -21,11 +21,11 @@ import { useSession } from "next-auth/react"
|
||||
export default function MemberProfilePage() {
|
||||
const { id } = useParams()
|
||||
const { treeData, currentTree, getMember, updateMember, deleteMember, isLoading } = useFamily()
|
||||
const role = currentTree?.currentUserRole
|
||||
const { showConfirm } = useDialog()
|
||||
const router = useRouter()
|
||||
const { data: session, status } = useSession()
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
const [editActiveTab, setEditActiveTab] = useState("basic")
|
||||
const [avatarBlobUrl, setAvatarBlobUrl] = useState<string | undefined>(undefined)
|
||||
const [isUploadingAvatar, setIsUploadingAvatar] = useState(false)
|
||||
const [lastSaveTime, setLastSaveTime] = useState(Date.now())
|
||||
@@ -61,6 +61,9 @@ export default function MemberProfilePage() {
|
||||
}
|
||||
|
||||
const ageInfo = calculateAge()
|
||||
|
||||
// 获取用户角色
|
||||
const role = currentTree?.currentUserRole
|
||||
|
||||
// Load avatar
|
||||
useEffect(() => {
|
||||
@@ -219,50 +222,128 @@ export default function MemberProfilePage() {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col bg-background">
|
||||
<SiteHeader />
|
||||
<div className="container mx-auto py-8">
|
||||
<div className="mb-6">
|
||||
<Button variant="ghost" onClick={() => setIsEditing(false)} className="mb-4">
|
||||
<ArrowLeft className="mr-2 h-4 w-4" /> 返回
|
||||
</Button>
|
||||
|
||||
{member.isFounder && (
|
||||
<div className="mb-6 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-4">
|
||||
<div className="flex-shrink-0 w-14 h-14 bg-gradient-to-br from-amber-200 to-orange-200 rounded-full flex items-center justify-center shadow-sm">
|
||||
<span className="text-3xl">👑</span>
|
||||
<main className="container mx-auto py-8 px-4 md:px-6">
|
||||
<Tabs value={editActiveTab} onValueChange={setEditActiveTab} className="flex flex-col h-[calc(100vh-8rem)]">
|
||||
{/* 固定头部区域 */}
|
||||
<div className="flex-shrink-0 pb-4 border-b border-border">
|
||||
{/* 返回按钮 */}
|
||||
<div className="py-4">
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => setIsEditing(false)}
|
||||
className="gap-2 pl-0 hover:bg-transparent hover:text-primary text-2xl font-bold"
|
||||
>
|
||||
<ArrowLeft className="h-6 w-6" />
|
||||
返回成员详情
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Header Section */}
|
||||
<div className="flex flex-col md:flex-row justify-between items-start gap-6 pb-4">
|
||||
<div className="flex items-start gap-6">
|
||||
<div className="h-24 w-24 rounded-lg bg-muted flex items-center justify-center border-2 border-primary/20 overflow-hidden">
|
||||
{member.avatarUrl ? (
|
||||
<img
|
||||
src={member.avatarUrl}
|
||||
alt={member.fullName}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
member.gender === 'FEMALE' ? (
|
||||
<CircleUserRound className="h-16 w-16 text-pink-400" />
|
||||
) : (
|
||||
<CircleUser className="h-16 w-16 text-blue-400" />
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h2 className="text-2xl font-serif font-bold text-amber-900 mb-3">
|
||||
编辑家族始祖
|
||||
</h2>
|
||||
<p className="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>传人。
|
||||
<br />
|
||||
请谨慎修改始祖信息,这将影响整个家族树的根基。
|
||||
<div>
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<h1 className="text-3xl font-bold font-serif text-foreground flex items-center gap-2">
|
||||
{member.isFounder && <span className="text-amber-600 text-2xl">👑</span>}
|
||||
编辑: {member.fullName}
|
||||
</h1>
|
||||
{member.isFounder ? (
|
||||
<span className="px-3 py-1 bg-gradient-to-r from-amber-100 to-orange-100 text-amber-800 text-sm font-semibold rounded-full border-2 border-amber-300 shadow-sm">
|
||||
始祖 · {member.generation}世
|
||||
</span>
|
||||
) : (
|
||||
<span className="px-2 py-1 bg-primary/10 text-primary text-sm font-medium rounded-full border border-primary/20">
|
||||
{member.generation}世
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-muted-foreground">
|
||||
{member.isFounder
|
||||
? '修改始祖信息将影响整个家族树'
|
||||
: '更新成员信息'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
<Button type="button" variant="outline" onClick={() => setIsEditing(false)}>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
className="bg-primary text-primary-foreground hover:bg-primary/90"
|
||||
onClick={() => {
|
||||
const form = document.querySelector('form')
|
||||
if (form) {
|
||||
form.requestSubmit()
|
||||
}
|
||||
}}
|
||||
>
|
||||
保存成员
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<h1 className="text-3xl font-serif font-bold mb-2">
|
||||
{member.isFounder ? '编辑家族始祖' : `编辑成员: ${member.fullName}`}
|
||||
</h1>
|
||||
<p className="text-muted-foreground">
|
||||
{member.isFounder
|
||||
? '修改始祖信息将影响整个家族树'
|
||||
: '更新成员信息'}
|
||||
</p>
|
||||
</div>
|
||||
<MemberForm
|
||||
initialData={member}
|
||||
existingMembers={allMembers}
|
||||
onSubmit={handleSave}
|
||||
onCancel={() => setIsEditing(false)}
|
||||
isFounder={member.isFounder}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Tab 导航栏 */}
|
||||
<TabsList className="flex flex-wrap gap-2 bg-muted/70 p-1 w-fit">
|
||||
<TabsTrigger value="basic">基本信息</TabsTrigger>
|
||||
<TabsTrigger value="family">家庭关系</TabsTrigger>
|
||||
<TabsTrigger value="details">详细资料</TabsTrigger>
|
||||
<TabsTrigger value="media">照片故事</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
{/* 始祖特殊提示框 */}
|
||||
{member.isFounder && (
|
||||
<div className="mt-6 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-4">
|
||||
<div className="flex-shrink-0 w-14 h-14 bg-gradient-to-br from-amber-200 to-orange-200 rounded-full flex items-center justify-center shadow-sm">
|
||||
<span className="text-3xl">👑</span>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h2 className="text-2xl font-serif font-bold text-amber-900 mb-3">
|
||||
编辑家族始祖
|
||||
</h2>
|
||||
<p className="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>传人。
|
||||
<br />
|
||||
请谨慎修改始祖信息,这将影响整个家族树的根基。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Tab 内容区域 - 可滚动 */}
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<MemberForm
|
||||
initialData={member}
|
||||
existingMembers={allMembers}
|
||||
onSubmit={handleSave}
|
||||
onCancel={() => setIsEditing(false)}
|
||||
isFounder={member.isFounder}
|
||||
hideTabs={true}
|
||||
activeTab={editActiveTab}
|
||||
/>
|
||||
</div>
|
||||
</Tabs>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -271,9 +352,9 @@ export default function MemberProfilePage() {
|
||||
<div className="min-h-screen flex flex-col bg-background">
|
||||
<SiteHeader />
|
||||
<main className="container mx-auto py-8 px-4 md:px-6">
|
||||
<Tabs defaultValue="family" className="flex flex-col">
|
||||
{/* 固定头部区域 - top-16 对应 SiteHeader 的 h-16 高度 */}
|
||||
<div className="sticky top-16 z-30 bg-background pb-4 -mx-4 px-4 md:-mx-6 md:px-6 border-b border-border -mt-8 pt-8">
|
||||
<Tabs defaultValue="family" className="flex flex-col h-[calc(100vh-8rem)]">
|
||||
{/* 固定头部区域 */}
|
||||
<div className="flex-shrink-0 pb-4 border-b border-border">
|
||||
{/* 返回按钮 */}
|
||||
<div className="py-4">
|
||||
<Button
|
||||
@@ -401,33 +482,34 @@ export default function MemberProfilePage() {
|
||||
<TabsTrigger value="stories">家族故事</TabsTrigger>
|
||||
<TabsTrigger value="history">时间记录</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
{/* 始祖特殊提示框 */}
|
||||
{member.isFounder && (
|
||||
<div className="mt-6 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-4">
|
||||
<div className="flex-shrink-0 w-14 h-14 bg-gradient-to-br from-amber-200 to-orange-200 rounded-full flex items-center justify-center shadow-sm">
|
||||
<span className="text-3xl">👑</span>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h2 className="text-2xl font-serif font-bold text-amber-900 mb-3">
|
||||
家族始祖
|
||||
</h2>
|
||||
<p className="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>传人。
|
||||
<br />
|
||||
血脉由此而始,世系因此而传。
|
||||
</p>
|
||||
|
||||
{/* 始祖特殊提示框 */}
|
||||
{member.isFounder && (
|
||||
<div className="mt-6 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-4">
|
||||
<div className="flex-shrink-0 w-14 h-14 bg-gradient-to-br from-amber-200 to-orange-200 rounded-full flex items-center justify-center shadow-sm">
|
||||
<span className="text-3xl">👑</span>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h2 className="text-2xl font-serif font-bold text-amber-900 mb-3">
|
||||
家族始祖
|
||||
</h2>
|
||||
<p className="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>传人。
|
||||
<br />
|
||||
血脉由此而始,世系因此而传。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Tab 内容区域 */}
|
||||
<TabsContent value="family" className="mt-6 space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{/* Tab 内容区域 - 可滚动 */}
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<TabsContent value="family" className="mt-6 space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className={`bg-card p-4 rounded-lg ${founderBorderClass}`}>
|
||||
<h3 className="font-medium text-muted-foreground mb-3 text-sm uppercase tracking-wider">
|
||||
父母
|
||||
@@ -723,6 +805,7 @@ export default function MemberProfilePage() {
|
||||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</div>
|
||||
</Tabs>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
+125
-41
@@ -5,9 +5,10 @@ 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 { ArrowLeft } from "lucide-react"
|
||||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import { ArrowLeft, UserPlus } from "lucide-react"
|
||||
import type { FamilyMember } from "@/types/family"
|
||||
import { useMemo, useEffect } from "react"
|
||||
import { useMemo, useEffect, useState } from "react"
|
||||
import { useSession } from "next-auth/react"
|
||||
|
||||
export default function NewMemberPage() {
|
||||
@@ -15,6 +16,7 @@ export default function NewMemberPage() {
|
||||
const searchParams = useSearchParams()
|
||||
const { addMember, treeData, getMember } = useFamily()
|
||||
const { data: session, status } = useSession()
|
||||
const [activeTab, setActiveTab] = useState("basic")
|
||||
|
||||
// 未登录时重定向到登录页
|
||||
useEffect(() => {
|
||||
@@ -95,6 +97,22 @@ export default function NewMemberPage() {
|
||||
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 handleSave = async (newMember: FamilyMember) => {
|
||||
try {
|
||||
// 如果是第一个成员,自动设置为始祖
|
||||
@@ -123,50 +141,116 @@ export default function NewMemberPage() {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col bg-background">
|
||||
<SiteHeader />
|
||||
<div className="container mx-auto py-8">
|
||||
<div className="mb-6">
|
||||
<Button variant="ghost" onClick={() => router.back()} className="mb-4">
|
||||
<ArrowLeft className="mr-2 h-4 w-4" /> 返回
|
||||
</Button>
|
||||
|
||||
{isFirstMember && (
|
||||
<div className="mb-6 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-4">
|
||||
<div className="flex-shrink-0 w-14 h-14 bg-gradient-to-br from-amber-200 to-orange-200 rounded-full flex items-center justify-center shadow-sm">
|
||||
<span className="text-3xl">👑</span>
|
||||
<main className="container mx-auto py-8 px-4 md:px-6">
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="flex flex-col h-[calc(100vh-8rem)]">
|
||||
{/* 固定头部区域 */}
|
||||
<div className="flex-shrink-0 pb-4 border-b border-border">
|
||||
{/* 返回按钮 */}
|
||||
<div className="py-4">
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => router.back()}
|
||||
className="gap-2 pl-0 hover:bg-transparent hover:text-primary text-2xl font-bold"
|
||||
>
|
||||
<ArrowLeft className="h-6 w-6" />
|
||||
返回
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Header Section */}
|
||||
<div className="flex flex-col md:flex-row justify-between items-start gap-6 pb-4">
|
||||
<div className="flex items-start gap-6">
|
||||
<div className="h-24 w-24 rounded-lg bg-muted flex items-center justify-center border-2 border-primary/20 overflow-hidden">
|
||||
{isFirstMember ? (
|
||||
<span className="text-4xl">👑</span>
|
||||
) : (
|
||||
<UserPlus className="h-16 w-16 text-muted-foreground" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h2 className="text-2xl font-serif font-bold text-amber-900 mb-3">
|
||||
立家族之根基
|
||||
</h2>
|
||||
<p className="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>传人。
|
||||
<br />
|
||||
血脉由此而始,世系因此而传。
|
||||
<div>
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<h1 className="text-3xl font-bold font-serif text-foreground flex items-center gap-2">
|
||||
{isFirstMember && <span className="text-amber-600 text-2xl">👑</span>}
|
||||
{isFirstMember ? '添加家族始祖' : `添加家族${memberType}`}
|
||||
</h1>
|
||||
{isFirstMember && (
|
||||
<span className="px-3 py-1 bg-gradient-to-r from-amber-100 to-orange-100 text-amber-800 text-sm font-semibold rounded-full border-2 border-amber-300 shadow-sm">
|
||||
始祖 · 1世
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-muted-foreground">
|
||||
{isFirstMember
|
||||
? '请认真填写始祖信息,这将是家族历史的起点'
|
||||
: `填写${memberType}信息,完善家族树`}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
<Button type="button" variant="outline" onClick={() => router.back()}>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
className="bg-primary text-primary-foreground hover:bg-primary/90"
|
||||
onClick={() => {
|
||||
const form = document.querySelector('form')
|
||||
if (form) {
|
||||
form.requestSubmit()
|
||||
}
|
||||
}}
|
||||
>
|
||||
保存成员
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<h1 className="text-3xl font-serif font-bold mb-2">
|
||||
{isFirstMember ? '添加家族始祖' : '新增家族成员'}
|
||||
</h1>
|
||||
<p className="text-muted-foreground">
|
||||
{isFirstMember
|
||||
? '请认真填写始祖信息,这将是家族历史的起点'
|
||||
: '填写成员信息,完善家族树'}
|
||||
</p>
|
||||
</div>
|
||||
<MemberForm
|
||||
initialData={initialData}
|
||||
existingMembers={existingMembers}
|
||||
onSubmit={handleSave}
|
||||
onCancel={() => router.back()}
|
||||
isFounder={isFirstMember}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Tab 导航栏 */}
|
||||
<TabsList className="flex flex-wrap gap-2 bg-muted/70 p-1 w-fit">
|
||||
<TabsTrigger value="basic">基本信息</TabsTrigger>
|
||||
<TabsTrigger value="family">家庭关系</TabsTrigger>
|
||||
<TabsTrigger value="details">详细资料</TabsTrigger>
|
||||
<TabsTrigger value="media">照片故事</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
{/* 始祖特殊提示框 */}
|
||||
{isFirstMember && (
|
||||
<div className="mt-6 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-4">
|
||||
<div className="flex-shrink-0 w-14 h-14 bg-gradient-to-br from-amber-200 to-orange-200 rounded-full flex items-center justify-center shadow-sm">
|
||||
<span className="text-3xl">👑</span>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h2 className="text-2xl font-serif font-bold text-amber-900 mb-3">
|
||||
立家族之根基
|
||||
</h2>
|
||||
<p className="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>传人。
|
||||
<br />
|
||||
血脉由此而始,世系因此而传。
|
||||
</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>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user