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
+341 -289
View File
@@ -1,14 +1,7 @@
"use client"
import { useEffect, useState } from "react"
import { useParams, useRouter } from "next/navigation"
import Link from "next/link"
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, TabsContent } from "@/components/ui/tabs"
import { ArrowLeft, Edit, Trash, Share2, Printer, CircleUser, CircleUserRound, Images, BookOpen, UserPlus, Camera } from "lucide-react"
import { ChineseCard, ChineseCardContent, ChineseCardHeader, ChineseCardTitle } from "@/components/ui/chinese-card"
import { ArrowLeft, Edit, Trash, Share2, Printer, CircleUser, CircleUserRound, Images, BookOpen, UserPlus, Camera, History } from "lucide-react"
import type { FamilyMember } from "@/types/family"
import { PhotoGallery } from "@/components/members/photo-gallery"
import { StoryManager } from "@/components/members/story-manager"
@@ -17,6 +10,15 @@ import { permissions } from "@/lib/permissions"
import { MemberNameWithStatus } from "@/components/member-name-with-status"
import { MemberVersionHistory } from "@/components/members/member-version-history"
import { useSession } from "next-auth/react"
import { useEffect, useState } from "react"
import { useParams, useRouter } from "next/navigation"
import Link from "next/link"
import { useFamily } from "@/context/family-context"
import { BackButton } from "@/components/ui/back-button"
import { SiteHeader } from "@/components/site-header"
import { MemberForm } from "@/components/members/member-form"
import { Button } from "@/components/ui/button"
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
export default function MemberProfilePage() {
const { id } = useParams()
@@ -40,8 +42,16 @@ export default function MemberProfilePage() {
const member = getMember(id as string)
const allMembers = Object.values(treeData.members)
// 始祖专用边框样式
const founderBorderClass = member?.isFounder ? "border-2 border-amber-400 shadow-md" : "border border-border"
// 判断是否已故
const isDead = !!member?.deathDate
// 根据在世/已故状态设置卡片样式
// 优先级:始祖 > 已故 > 在世
const cardStyle = member?.isFounder
? '!border-2 !border-amber-400 !bg-amber-50/30 dark:!bg-amber-900/20 shadow-md' // 始祖:金色边框(优先级最高)
: isDead
? '!border-2 !border-gray-400 dark:!border-gray-600 !bg-gray-50/50 dark:!bg-gray-900/30' // 已故:灰色深边框
: '!border-2 !border-amber-300/70 !bg-card dark:!border-amber-600/50' // 在世:浅金色边框
// 计算年龄或享年
const calculateAge = () => {
@@ -227,16 +237,7 @@ export default function MemberProfilePage() {
{/* 固定头部区域 */}
<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>
<BackButton onClick={() => setIsEditing(false)} label="返回" />
{/* Header Section */}
<div className="flex flex-col md:flex-row justify-between items-start gap-6 pb-4">
@@ -304,7 +305,7 @@ export default function MemberProfilePage() {
{/* Tab 导航栏 */}
<TabsList className="flex flex-wrap gap-2 bg-muted/70 p-1 w-fit">
<TabsTrigger value="info"></TabsTrigger>
<TabsTrigger value="info"></TabsTrigger>
<TabsTrigger value="details"></TabsTrigger>
<TabsTrigger value="photos"></TabsTrigger>
<TabsTrigger value="stories"></TabsTrigger>
@@ -359,16 +360,10 @@ export default function MemberProfilePage() {
{/* 固定头部区域 */}
<div className="flex-shrink-0 pb-4 border-b border-border">
{/* 返回按钮 */}
<div className="py-4">
<Button
variant="ghost"
onClick={() => router.push(`/members${typeof window !== 'undefined' ? window.location.search : ''}`)}
className="gap-2 pl-0 hover:bg-transparent hover:text-primary text-2xl font-bold"
>
<ArrowLeft className="h-6 w-6" />
</Button>
</div>
<BackButton
onClick={() => router.push(`/members${typeof window !== 'undefined' ? window.location.search : ''}`)}
label="返回"
/>
{/* Header Section */}
<div className="flex flex-col md:flex-row justify-between items-start gap-6 pb-4">
@@ -479,7 +474,7 @@ export default function MemberProfilePage() {
{/* Tab 导航栏 */}
<TabsList className="flex flex-wrap gap-2 bg-muted/70 p-1 w-fit">
<TabsTrigger value="info"></TabsTrigger>
<TabsTrigger value="info"></TabsTrigger>
<TabsTrigger value="details"></TabsTrigger>
<TabsTrigger value="photos"></TabsTrigger>
<TabsTrigger value="stories"></TabsTrigger>
@@ -511,304 +506,361 @@ export default function MemberProfilePage() {
{/* Tab 内容区域 - 可滚动 */}
<div className="flex-1 overflow-y-auto">
{/* 详细信息Tab - 包含基本信息和家庭关系 */}
{/* 基本信息Tab - 包含基本信息和家庭关系 */}
<TabsContent value="info" className="mt-6 space-y-6">
{/* 基本信息 */}
<div className={`bg-card rounded-lg ${founderBorderClass} p-6 shadow-sm w-full`}>
<h3 className="text-lg font-serif font-semibold mb-4"></h3>
<dl className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-x-8 gap-y-4 text-sm">
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium text-lg">{member.fullName}</dd>
</div>
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.gender === 'MALE' ? '男' : '女'}</dd>
</div>
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium"> {member.generation} </dd>
</div>
{member.phone && (
<ChineseCard variant="default" cornerOrnament className={`w-full shadow-sm ${cardStyle}`}>
<ChineseCardContent className="p-6">
<ChineseCardTitle className="text-lg font-serif font-semibold mb-4"></ChineseCardTitle>
<dl className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-x-8 gap-y-4 text-sm">
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">
<a href={`tel:${member.phone}`} className="hover:text-primary">{member.phone}</a>
</dd>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium text-lg">{member.fullName}</dd>
</div>
)}
{member.occupation && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.occupation}</dd>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.gender === 'MALE' ? '男' : '女'}</dd>
</div>
)}
</dl>
</div>
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium"> {member.generation} </dd>
</div>
{member.occupation && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.occupation}</dd>
</div>
)}
</dl>
</ChineseCardContent>
</ChineseCard>
{/* 家庭关系 */}
<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">
</h3>
<div className="grid gap-3 sm:grid-cols-2">
<div>
<p className="text-xs text-muted-foreground uppercase tracking-widest mb-1"></p>
{member.fatherId ? (
<Link
href={`/members/${member.fatherId}${typeof window !== 'undefined' ? window.location.search : ''}`}
className="text-lg font-serif font-bold hover:underline text-primary"
>
<MemberNameWithStatus
name={getMember(member.fatherId)?.fullName || member.fatherId}
isDead={!!getMember(member.fatherId)?.deathDate}
/>
</Link>
) : member.spouseFatherName ? (
<span className="text-lg font-serif font-bold text-foreground">
{member.spouseFatherName}
</span>
) : (
<span className="text-muted-foreground">-</span>
)}
</div>
<div>
<p className="text-xs text-muted-foreground uppercase tracking-widest mb-1"></p>
{member.motherId ? (
<Link
href={`/members/${member.motherId}${typeof window !== 'undefined' ? window.location.search : ''}`}
className="text-lg font-serif font-bold hover:underline text-primary"
>
<MemberNameWithStatus
name={getMember(member.motherId)?.fullName || member.motherId}
isDead={!!getMember(member.motherId)?.deathDate}
/>
</Link>
) : member.spouseMotherName ? (
<span className="text-lg font-serif font-bold text-foreground">
{member.spouseMotherName}
</span>
) : (
<span className="text-muted-foreground">-</span>
)}
</div>
</div>
</div>
<div className={`bg-card p-4 rounded-lg ${founderBorderClass}`}>
<h3 className="font-medium text-muted-foreground mb-3 text-sm uppercase tracking-wider">
</h3>
<div className="grid gap-4 sm:grid-cols-2">
<div>
<p className="text-xs text-muted-foreground uppercase tracking-widest mb-2"></p>
<div className="flex flex-wrap gap-2">
{member.spouseIds?.length > 0 ? (
member.spouseIds.map((spouseId) => (
<Link
href={`/members/${spouseId}${typeof window !== 'undefined' ? window.location.search : ''}`}
key={spouseId}
className="px-3 py-1 bg-muted rounded hover:bg-muted/80 text-sm font-medium"
>
<MemberNameWithStatus
name={getMember(spouseId)?.fullName || spouseId}
isDead={!!getMember(spouseId)?.deathDate}
/>
</Link>
))
<ChineseCard variant="default" cornerOrnament className={`rounded-lg ${cardStyle}`}>
<ChineseCardContent className="p-4">
<h3 className="font-medium text-muted-foreground mb-3 text-sm uppercase tracking-wider">
</h3>
<div className="grid gap-3 sm:grid-cols-2">
<div>
<p className="text-xs text-muted-foreground uppercase tracking-widest mb-1"></p>
{member.fatherId ? (
<Link
href={`/members/${member.fatherId}${typeof window !== 'undefined' ? window.location.search : ''}`}
className="text-lg font-serif font-bold hover:underline text-primary"
>
<MemberNameWithStatus
name={getMember(member.fatherId)?.fullName || member.fatherId}
isDead={!!getMember(member.fatherId)?.deathDate}
/>
</Link>
) : member.spouseFatherName ? (
<span className="text-lg font-serif font-bold text-foreground">
{member.spouseFatherName}
</span>
) : (
<span className="text-muted-foreground"></span>
<span className="text-muted-foreground">-</span>
)}
</div>
<div>
<p className="text-xs text-muted-foreground uppercase tracking-widest mb-1"></p>
{member.motherId ? (
<Link
href={`/members/${member.motherId}${typeof window !== 'undefined' ? window.location.search : ''}`}
className="text-lg font-serif font-bold hover:underline text-primary"
>
<MemberNameWithStatus
name={getMember(member.motherId)?.fullName || member.motherId}
isDead={!!getMember(member.motherId)?.deathDate}
/>
</Link>
) : member.spouseMotherName ? (
<span className="text-lg font-serif font-bold text-foreground">
{member.spouseMotherName}
</span>
) : (
<span className="text-muted-foreground">-</span>
)}
</div>
</div>
<div>
<p className="text-xs text-muted-foreground uppercase tracking-widest mb-2"></p>
<div className="flex flex-wrap gap-2">
{member.childrenIds?.length > 0 ? (
member.childrenIds.map((childId) => (
<Link
href={`/members/${childId}${typeof window !== 'undefined' ? window.location.search : ''}`}
key={childId}
className="px-3 py-1 bg-muted rounded hover:bg-muted/80 text-sm font-medium"
>
<MemberNameWithStatus
name={getMember(childId)?.fullName || childId}
isDead={!!getMember(childId)?.deathDate}
/>
</Link>
))
) : (
<span className="text-muted-foreground"></span>
)}
</ChineseCardContent>
</ChineseCard>
<ChineseCard variant="default" cornerOrnament className={`rounded-lg ${cardStyle}`}>
<ChineseCardContent className="p-4">
<h3 className="font-medium text-muted-foreground mb-3 text-sm uppercase tracking-wider">
</h3>
<div className="grid gap-4 sm:grid-cols-2">
<div>
<p className="text-xs text-muted-foreground uppercase tracking-widest mb-2"></p>
<div className="flex flex-wrap gap-2">
{member.spouseIds?.length > 0 ? (
member.spouseIds.map((spouseId) => (
<Link
href={`/members/${spouseId}${typeof window !== 'undefined' ? window.location.search : ''}`}
key={spouseId}
className="px-3 py-1 bg-muted rounded hover:bg-muted/80 text-sm font-medium"
>
<MemberNameWithStatus
name={getMember(spouseId)?.fullName || spouseId}
isDead={!!getMember(spouseId)?.deathDate}
/>
</Link>
))
) : (
<span className="text-muted-foreground"></span>
)}
</div>
</div>
<div>
<p className="text-xs text-muted-foreground uppercase tracking-widest mb-2"></p>
<div className="flex flex-wrap gap-2">
{member.childrenIds?.length > 0 ? (
member.childrenIds.map((childId) => (
<Link
href={`/members/${childId}${typeof window !== 'undefined' ? window.location.search : ''}`}
key={childId}
className="px-3 py-1 bg-muted rounded hover:bg-muted/80 text-sm font-medium"
>
<MemberNameWithStatus
name={getMember(childId)?.fullName || childId}
isDead={!!getMember(childId)?.deathDate}
/>
</Link>
))
) : (
<span className="text-muted-foreground"></span>
)}
</div>
</div>
</div>
</div>
</div>
</ChineseCardContent>
</ChineseCard>
</div>
</TabsContent>
{/* 详细资料Tab */}
<TabsContent value="details" className="mt-6 space-y-6">
<div className={`bg-card rounded-lg ${founderBorderClass} p-6 shadow-sm w-full`}>
<h3 className="text-lg font-serif font-semibold mb-4"></h3>
<dl className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-6 gap-x-8 gap-y-4 text-sm">
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium space-y-1">
<div className="flex flex-wrap items-center gap-2">
<span>{member.birthDate || "-"}</span>
{member.birthDate && member.isLunarDate && (
<span className="text-xs text-amber-600 bg-amber-50 px-2 py-0.5 rounded border border-amber-200">
</span>
)}
<ChineseCard variant="default" cornerOrnament className={`w-full shadow-sm ${cardStyle}`}>
<ChineseCardContent className="p-6">
<ChineseCardTitle className="text-lg font-serif font-semibold mb-4"></ChineseCardTitle>
<dl className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-6 gap-x-8 gap-y-4 text-sm">
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium space-y-1">
<div className="flex flex-wrap items-center gap-2">
<span>{member.birthDate || "-"}</span>
{member.birthDate && member.isLunarDate && (
<span className="text-xs text-amber-600 bg-amber-50 px-2 py-0.5 rounded border border-amber-200">
</span>
)}
</div>
<div className="flex flex-wrap items-center gap-2">
<span>{member.deathDate || "-"}</span>
{member.deathDate && member.isLunarDate && (
<span className="text-xs text-amber-600 bg-amber-50 px-2 py-0.5 rounded border border-amber-200">
</span>
)}
</div>
</dd>
</div>
{ageInfo && (
<div>
<dt className="text-muted-foreground mb-1">{ageInfo.type === 'deceased' ? '享年' : '年龄'}</dt>
<dd className="font-medium text-primary">{ageInfo.age} </dd>
</div>
<div className="flex flex-wrap items-center gap-2">
<span>{member.deathDate || "-"}</span>
{member.deathDate && member.isLunarDate && (
<span className="text-xs text-amber-600 bg-amber-50 px-2 py-0.5 rounded border border-amber-200">
</span>
)}
)}
{member.generationName && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.generationName}</dd>
</div>
</dd>
</div>
{ageInfo && (
<div>
<dt className="text-muted-foreground mb-1">{ageInfo.type === 'deceased' ? '享年' : '年龄'}</dt>
<dd className="font-medium text-primary">{ageInfo.age} </dd>
</div>
)}
{member.generationName && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.generationName}</dd>
</div>
)}
{member.courtesyName && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.courtesyName}</dd>
</div>
)}
{member.artName && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.artName}</dd>
</div>
)}
{member.posthumousName && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.posthumousName}</dd>
</div>
)}
{member.birthPlace && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.birthPlace}</dd>
</div>
)}
{member.deathPlace && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.deathPlace}</dd>
</div>
)}
{member.address && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.address}</dd>
</div>
)}
</dl>
</div>
)}
{member.courtesyName && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.courtesyName}</dd>
</div>
)}
{member.artName && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.artName}</dd>
</div>
)}
{member.posthumousName && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.posthumousName}</dd>
</div>
)}
{member.ancestralHome && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.ancestralHome}</dd>
</div>
)}
{member.birthPlace && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.birthPlace}</dd>
</div>
)}
{member.deathPlace && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.deathPlace}</dd>
</div>
)}
{member.burialPlace && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.burialPlace}</dd>
</div>
)}
{member.address && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.address}</dd>
</div>
)}
{member.occupation && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">{member.occupation}</dd>
</div>
)}
{member.phone && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">
<a href={`tel:${member.phone}`} className="hover:text-primary">{member.phone}</a>
</dd>
</div>
)}
{member.telephone && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">
<a href={`tel:${member.telephone}`} className="hover:text-primary">{member.telephone}</a>
</dd>
</div>
)}
{member.email && (
<div>
<dt className="text-muted-foreground mb-1"></dt>
<dd className="font-medium">
<a href={`mailto:${member.email}`} className="hover:text-primary">{member.email}</a>
</dd>
</div>
)}
</dl>
</ChineseCardContent>
</ChineseCard>
{/* 个人简介 */}
{member.bio && (
<div className={`prose prose-stone dark:prose-invert max-w-none bg-card p-6 rounded-lg ${founderBorderClass} shadow-sm w-full`}>
<h3 className="text-lg font-serif font-semibold mb-4"></h3>
<p className="whitespace-pre-line leading-relaxed">{member.bio}</p>
</div>
<ChineseCard variant="default" cornerOrnament className={`w-full shadow-sm ${cardStyle}`}>
<ChineseCardContent className="p-6">
<ChineseCardTitle className="text-lg font-serif font-semibold mb-4"></ChineseCardTitle>
<p className="whitespace-pre-line leading-relaxed">{member.bio}</p>
</ChineseCardContent>
</ChineseCard>
)}
{/* 标签 */}
{member.tags && member.tags.length > 0 && (
<div className={`bg-card rounded-lg ${founderBorderClass} p-6 shadow-sm`}>
<h3 className="text-lg font-serif font-semibold mb-4"></h3>
<div className="flex flex-wrap gap-2">
{member.tags.map((tag) => (
<span
key={tag}
className="px-3 py-1 bg-primary/10 text-primary text-sm font-medium rounded-md border border-primary/20"
>
{tag}
</span>
))}
</div>
</div>
<ChineseCard variant="default" cornerOrnament className={`shadow-sm ${cardStyle}`}>
<ChineseCardContent className="p-6">
<ChineseCardTitle className="text-lg font-serif font-semibold mb-4"></ChineseCardTitle>
<div className="flex flex-wrap gap-2">
{member.tags.map((tag) => (
<span
key={tag}
className="px-3 py-1 bg-primary/10 text-primary text-sm font-medium rounded-md border border-primary/20"
>
{tag}
</span>
))}
</div>
</ChineseCardContent>
</ChineseCard>
)}
</TabsContent>
{/* 生平影像Tab */}
<TabsContent value="photos" className="mt-6 space-y-6">
<div className={`bg-card p-6 rounded-lg ${founderBorderClass} shadow-sm w-full`}>
<h3 className="text-lg font-serif font-semibold mb-4 flex items-center gap-2">
<Images className="h-5 w-5 text-primary" />
</h3>
<PhotoGallery
photos={member.photos}
photoIds={member.photoIds}
onChange={async (photos) => {
await updateMember(member.id, { photos, photoIds: undefined })
setLastSaveTime(Date.now())
}}
readonly={false}
/>
</div>
</TabsContent>
{/* 家族故事Tab */}
<TabsContent value="stories" className="mt-6 space-y-6">
<div className={`bg-card p-6 rounded-lg ${founderBorderClass} shadow-sm w-full`}>
<h3 className="text-lg font-serif font-semibold mb-4 flex items-center gap-2">
<BookOpen className="h-5 w-5 text-primary" />
</h3>
{member.stories && member.stories.length > 0 ? (
<StoryManager
stories={member.stories}
onChange={async (stories) => {
await updateMember(member.id, { stories })
<ChineseCard variant="default" cornerOrnament className={`w-full shadow-sm ${cardStyle}`}>
<ChineseCardContent className="p-6">
<ChineseCardTitle className="text-lg font-serif font-semibold mb-4 flex items-center gap-2">
<Images className="h-5 w-5 text-primary" />
</ChineseCardTitle>
<PhotoGallery
photos={member.photos}
photoIds={member.photoIds}
onChange={async (photos) => {
await updateMember(member.id, { photos, photoIds: undefined })
setLastSaveTime(Date.now())
}}
readonly={false}
/>
) : (
<div className="text-sm text-muted-foreground text-center py-10">
</div>
)}
</div>
</ChineseCardContent>
</ChineseCard>
</TabsContent>
{/* 家族故事Tab */}
<TabsContent value="stories" className="mt-6 space-y-6">
<ChineseCard variant="default" cornerOrnament className={`w-full shadow-sm ${cardStyle}`}>
<ChineseCardContent className="p-6">
<ChineseCardTitle className="text-lg font-serif font-semibold mb-4 flex items-center gap-2">
<BookOpen className="h-5 w-5 text-primary" />
</ChineseCardTitle>
{member.stories && member.stories.length > 0 ? (
<StoryManager
stories={member.stories}
onChange={async (stories) => {
await updateMember(member.id, { stories })
setLastSaveTime(Date.now())
}}
readonly={false}
/>
) : (
<div className="text-sm text-muted-foreground text-center py-10">
</div>
)}
</ChineseCardContent>
</ChineseCard>
</TabsContent>
{/* 时间线Tab - 仅显示视图 */}
<TabsContent value="history" className="mt-6 space-y-6">
<div className={`bg-card rounded-lg ${founderBorderClass} p-6 shadow-sm w-full`}>
<h3 className="text-lg font-serif font-semibold mb-2"></h3>
<p className="text-sm text-muted-foreground">
{new Date(lastSaveTime).toLocaleString('zh-CN')}
</p>
</div>
<div className={`bg-card rounded-lg ${founderBorderClass} p-6 shadow-sm w-full`}>
<h3 className="text-lg font-serif font-semibold mb-4"></h3>
<MemberVersionHistory
memberId={member.id}
treeId={currentTree?.id || ''}
lastUpdated={lastSaveTime}
/>
</div>
<ChineseCard variant="default" cornerOrnament className={`w-full shadow-sm ${cardStyle}`}>
<ChineseCardContent className="p-6">
<ChineseCardTitle className="text-lg font-serif font-semibold mb-2"></ChineseCardTitle>
<p className="text-sm text-muted-foreground">
{new Date(lastSaveTime).toLocaleString('zh-CN')}
</p>
</ChineseCardContent>
</ChineseCard>
<ChineseCard variant="default" cornerOrnament className={`w-full shadow-sm ${cardStyle}`}>
<ChineseCardContent className="p-6">
<ChineseCardTitle className="text-lg font-serif font-semibold mb-4 flex items-center gap-2">
<History className="h-5 w-5 text-primary" />
</ChineseCardTitle>
<MemberVersionHistory
memberId={member.id}
treeId={currentTree?.id || ''}
lastUpdated={lastSaveTime}
/>
</ChineseCardContent>
</ChineseCard>
</TabsContent>
</div>
</Tabs>
+6 -13
View File
@@ -1,15 +1,17 @@
"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 { ArrowLeft, UserPlus } from "lucide-react"
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"
export default function NewMemberPage() {
const router = useRouter()
@@ -146,21 +148,12 @@ export default function NewMemberPage() {
{/* 固定头部区域 */}
<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>
<BackButton onClick={() => router.back()} />
{/* 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">
<div className={`h-24 w-24 rounded-lg bg-muted flex items-center justify-center border-2 ${isFirstMember ? 'border-amber-400 bg-amber-50/30' : 'border-primary/20'} overflow-hidden`}>
{isFirstMember ? (
<span className="text-4xl">👑</span>
) : (
@@ -208,7 +201,7 @@ export default function NewMemberPage() {
{/* Tab 导航栏 */}
<TabsList className="flex flex-wrap gap-2 bg-muted/70 p-1 w-fit">
<TabsTrigger value="info"></TabsTrigger>
<TabsTrigger value="info"></TabsTrigger>
<TabsTrigger value="details"></TabsTrigger>
<TabsTrigger value="photos"></TabsTrigger>
<TabsTrigger value="stories"></TabsTrigger>
+16 -9
View File
@@ -6,7 +6,7 @@ import { useFamily } from "@/context/family-context"
import { useRouter } from "next/navigation"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"
import { ChineseCard, ChineseCardContent } from "@/components/ui/chinese-card"
import { Search, Plus, Filter, X, Users, User } from "lucide-react"
import Link from "next/link"
import { AvatarDisplay } from "@/components/ui/avatar-display"
@@ -90,15 +90,22 @@ const MemberCard = memo(function MemberCard({
const father = member.fatherId ? allMembers.find(m => m.id === member.fatherId) : null
const mother = member.motherId ? allMembers.find(m => m.id === member.motherId) : null
// 判断是否已故
const isDead = !!member.deathDate
// 根据在世/已故状态设置卡片样式
// 优先级:始祖 > 已故 > 在世
const cardStyle = isFounder
? '!border-2 !border-amber-400 !bg-amber-50/30 dark:!bg-amber-900/20 shadow-md' // 始祖:金色边框(优先级最高)
: isDead
? '!border-2 !border-gray-400 dark:!border-gray-600 !bg-gray-50/50 dark:!bg-gray-900/30' // 已故:灰色深边框
: '!border-2 !border-amber-300/70 !bg-card dark:!border-amber-600/50' // 在世:浅金色边框
return (
<div className="relative group">
<Link href={`/members/${member.id}${typeof window !== 'undefined' ? window.location.search : ''}`}>
<Card className={`hover:border-primary transition-colors cursor-pointer h-full ${
isSpouse
? 'bg-pink-50/50 dark:bg-pink-950/20 border-pink-200/50 dark:border-pink-800/30'
: 'bg-blue-50/50 dark:bg-blue-950/20 border-blue-200/50 dark:border-blue-800/30'
}`}>
<CardContent className="px-3 py-2 flex items-center gap-5">
<ChineseCard variant="default" className={`hover:shadow-lg transition-all cursor-pointer h-full ${cardStyle}`}>
<ChineseCardContent className="px-4 py-3 flex items-center gap-5">
<div className="relative flex-shrink-0">
<AvatarDisplay
imageUrl={member.avatarUrl}
@@ -225,8 +232,8 @@ const MemberCard = memo(function MemberCard({
</div>
)}
</div>
</CardContent>
</Card>
</ChineseCardContent>
</ChineseCard>
</Link>
<Button
size="sm"