0.3.1.2
This commit is contained in:
+240
-237
@@ -6,6 +6,7 @@ import Image from "next/image"
|
||||
import dynamic from "next/dynamic"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { ChineseCard, ChineseCardContent, ChineseCardHeader, ChineseCardTitle, ChineseCardDescription, ChineseDivider } from "@/components/ui/chinese-card"
|
||||
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"
|
||||
import { SiteHeader } from "@/components/site-header"
|
||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
@@ -458,58 +459,58 @@ export default function DashboardPage() {
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
{/* 第一个卡片:家族成员 + 在世/已故 */}
|
||||
<Link href={currentTree?.id ? `/members?treeId=${currentTree.id}` : '#'}>
|
||||
<Card className="relative overflow-hidden border border-border/50 shadow-sm bg-gradient-to-br from-card to-card/50 backdrop-blur transition-all hover:shadow-lg hover:border-primary/30 group cursor-pointer">
|
||||
<CardContent className="p-5">
|
||||
<ChineseCard variant="default" cornerOrnament className="chinese-stat-card transition-all hover:shadow-lg group cursor-pointer h-full">
|
||||
<ChineseCardContent className="p-5 pt-6">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<p className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">家族成员</p>
|
||||
<div className="text-primary/70 bg-primary/10 p-2.5 rounded-lg group-hover:bg-primary/20 transition-colors">
|
||||
<Users className="h-5 w-5" />
|
||||
<div className="chinese-seal chinese-seal-sm">
|
||||
<Users className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col space-y-1.5">
|
||||
<span className="text-3xl font-serif font-bold text-foreground tracking-tight">{stats.totalMembers} 人</span>
|
||||
<ChineseDivider variant="simple" className="my-2" />
|
||||
<div className="flex items-center gap-3 text-sm text-muted-foreground/80 font-medium">
|
||||
<span className="text-green-600">在世 {stats.livingMembers}</span>
|
||||
<span className="text-muted-foreground">·</span>
|
||||
<span className="text-gray-600">已故 {stats.deceasedMembers}</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
<div className="absolute bottom-0 left-0 right-0 h-1 bg-gradient-to-r from-primary/50 via-primary to-primary/50 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</Card>
|
||||
</ChineseCardContent>
|
||||
</ChineseCard>
|
||||
</Link>
|
||||
|
||||
{/* 第二个卡片:繁衍代数 + 记录年代 + 平均寿命 */}
|
||||
<Link href={currentTree?.id ? `/timeline?treeId=${currentTree.id}` : '#'}>
|
||||
<Card className="relative overflow-hidden border border-border/50 shadow-sm bg-gradient-to-br from-card to-card/50 backdrop-blur transition-all hover:shadow-lg hover:border-primary/30 group cursor-pointer">
|
||||
<CardContent className="p-5">
|
||||
<ChineseCard variant="default" cornerOrnament className="chinese-stat-card transition-all hover:shadow-lg group cursor-pointer h-full">
|
||||
<ChineseCardContent className="p-5 pt-6">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<p className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">家族历史</p>
|
||||
<div className="text-primary/70 bg-primary/10 p-2.5 rounded-lg group-hover:bg-primary/20 transition-colors">
|
||||
<Clock className="h-5 w-5" />
|
||||
<div className="chinese-seal chinese-seal-sm">
|
||||
<Clock className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col space-y-1.5">
|
||||
<span className="text-3xl font-serif font-bold text-foreground tracking-tight">{stats.maxGeneration} 代</span>
|
||||
<ChineseDivider variant="simple" className="my-2" />
|
||||
<div className="flex items-center gap-3 text-sm text-muted-foreground/80 font-medium">
|
||||
<span>跨越 {stats.yearsSpan} 年</span>
|
||||
<span className="text-muted-foreground">·</span>
|
||||
<span>均寿 {stats.averageLifespan > 0 ? `${stats.averageLifespan}岁` : '-'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
<div className="absolute bottom-0 left-0 right-0 h-1 bg-gradient-to-r from-primary/50 via-primary to-primary/50 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</Card>
|
||||
</ChineseCardContent>
|
||||
</ChineseCard>
|
||||
</Link>
|
||||
|
||||
{/* 第三个卡片:性别比例 */}
|
||||
<Link href={currentTree?.id ? `/members?treeId=${currentTree.id}` : '#'}>
|
||||
<Card className="relative overflow-hidden border border-border/50 shadow-sm bg-gradient-to-br from-card to-card/50 backdrop-blur transition-all hover:shadow-lg hover:border-primary/30 group cursor-pointer">
|
||||
<CardContent className="p-5">
|
||||
<ChineseCard variant="default" cornerOrnament className="chinese-stat-card transition-all hover:shadow-lg group cursor-pointer h-full">
|
||||
<ChineseCardContent className="p-5 pt-6">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<p className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">性别比例</p>
|
||||
<div className="text-primary/70 bg-primary/10 p-2.5 rounded-lg group-hover:bg-primary/20 transition-colors">
|
||||
<Users className="h-5 w-5" />
|
||||
<div className="chinese-seal chinese-seal-sm">
|
||||
<Users className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col space-y-1.5">
|
||||
@@ -522,13 +523,13 @@ export default function DashboardPage() {
|
||||
{stats.maleCount > 0 ? (stats.femaleCount / stats.maleCount).toFixed(2) : '0'}
|
||||
</span>
|
||||
</div>
|
||||
<ChineseDivider variant="simple" className="my-2" />
|
||||
<div className="text-center text-sm text-muted-foreground/80 font-medium">
|
||||
男性 {stats.maleCount} · 女性 {stats.femaleCount}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
<div className="absolute bottom-0 left-0 right-0 h-1 bg-gradient-to-r from-primary/50 via-primary to-primary/50 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</Card>
|
||||
</ChineseCardContent>
|
||||
</ChineseCard>
|
||||
</Link>
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
@@ -538,10 +539,9 @@ export default function DashboardPage() {
|
||||
<div className="flex items-center justify-between mb-4 flex-shrink-0">
|
||||
<TabsList className="bg-muted/50">
|
||||
<TabsTrigger value="photos">照片展示</TabsTrigger>
|
||||
<TabsTrigger value="recent">最近更新</TabsTrigger>
|
||||
<TabsTrigger value="anniversaries">近期纪念日</TabsTrigger>
|
||||
<TabsTrigger value="recent">最近动态</TabsTrigger>
|
||||
<TabsTrigger value="statistics">统计图表</TabsTrigger>
|
||||
<TabsTrigger value="migration">迁徙记录</TabsTrigger>
|
||||
<TabsTrigger value="migration">籍贯记录</TabsTrigger>
|
||||
</TabsList>
|
||||
<Link href={currentTree?.id ? `/members?treeId=${currentTree.id}` : '/members'}>
|
||||
<Button variant="ghost" size="sm" className="text-muted-foreground gap-1">
|
||||
@@ -552,15 +552,15 @@ export default function DashboardPage() {
|
||||
|
||||
{/* 照片展示标签页 */}
|
||||
<TabsContent value="photos" className="mt-0 flex-1 overflow-y-auto">
|
||||
<Card className="border-none shadow-sm bg-card/50 backdrop-blur">
|
||||
<CardHeader>
|
||||
<CardTitle className="font-serif flex items-center gap-2">
|
||||
<ChineseCard variant="scroll" className="shadow-sm">
|
||||
<ChineseCardHeader className="pt-6">
|
||||
<ChineseCardTitle className="flex items-center gap-2">
|
||||
<Images className="h-5 w-5 text-primary" />
|
||||
家族影像
|
||||
</CardTitle>
|
||||
<CardDescription>珍藏的家族照片,记录美好时光</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
</ChineseCardTitle>
|
||||
<ChineseCardDescription>珍藏的家族照片,记录美好时光</ChineseCardDescription>
|
||||
</ChineseCardHeader>
|
||||
<ChineseCardContent>
|
||||
{allPhotos.length > 0 ? (
|
||||
<div className="columns-2 md:columns-3 lg:columns-4 xl:columns-5 gap-4 space-y-4">
|
||||
{allPhotos.map((photo, index) => (
|
||||
@@ -622,21 +622,22 @@ export default function DashboardPage() {
|
||||
<p className="text-sm text-muted-foreground/70 mt-1">在成员详情页可以添加照片</p>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</ChineseCardContent>
|
||||
</ChineseCard>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="recent" className="mt-0 flex-1 overflow-y-auto">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div className="space-y-6">
|
||||
<Card className="border-none shadow-sm bg-primary/5">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-primary font-serif">
|
||||
<ChineseCard variant="plaque" className="shadow-sm">
|
||||
<ChineseCardHeader>
|
||||
<ChineseCardTitle className="flex items-center gap-2 text-primary">
|
||||
<Calendar className="h-5 w-5" />
|
||||
本月祭祖/纪念
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-4">
|
||||
</ChineseCardTitle>
|
||||
<ChineseCardDescription>本月需要纪念的家族成员生日与忌日</ChineseCardDescription>
|
||||
</ChineseCardHeader>
|
||||
<ChineseCardContent className="grid gap-4">
|
||||
{monthlyAnniversaries.length > 0 ? (
|
||||
monthlyAnniversaries.map((anniversary, index) => {
|
||||
const yearsAgo = new Date().getFullYear() - new Date(anniversary.date).getFullYear()
|
||||
@@ -688,17 +689,197 @@ export default function DashboardPage() {
|
||||
<p className="text-sm text-foreground font-light tracking-wide">此月无忆</p>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</ChineseCardContent>
|
||||
</ChineseCard>
|
||||
|
||||
<ChineseCard variant="scroll" className="shadow-sm">
|
||||
<ChineseCardHeader className="pt-6">
|
||||
<ChineseCardTitle className="flex items-center gap-2">
|
||||
<Clock className="h-5 w-5 text-primary" />
|
||||
未来三月纪念日
|
||||
</ChineseCardTitle>
|
||||
<ChineseCardDescription>即将到来的生日与忌日提醒</ChineseCardDescription>
|
||||
</ChineseCardHeader>
|
||||
<ChineseCardContent>
|
||||
<div className="space-y-3">
|
||||
{(() => {
|
||||
const now = new Date()
|
||||
const threeMonthsLater = new Date(now.getFullYear(), now.getMonth() + 3, now.getDate())
|
||||
const members = Object.values(treeData.members)
|
||||
const upcomingEvents: Array<{
|
||||
member: any
|
||||
type: 'birth' | 'death'
|
||||
date: Date
|
||||
originalDate: string
|
||||
isLunar: boolean
|
||||
lunarDisplay?: string
|
||||
month: number
|
||||
day: number
|
||||
}> = []
|
||||
|
||||
members.forEach(member => {
|
||||
// 生日
|
||||
if (member.birthDate) {
|
||||
const birthDate = new Date(member.birthDate)
|
||||
let thisYearBirth: Date
|
||||
let lunarDisplay: string | undefined
|
||||
|
||||
// 检查是否按农历计算
|
||||
if (member.isLunarDate) {
|
||||
// 农历生日:计算今年对应的公历日期
|
||||
const lunarInfo = solar2lunar(birthDate)
|
||||
if (lunarInfo) {
|
||||
const thisYearLunar = lunar2solar(
|
||||
now.getFullYear(),
|
||||
lunarInfo.lunarMonth,
|
||||
lunarInfo.lunarDay,
|
||||
lunarInfo.isLeap
|
||||
)
|
||||
if (thisYearLunar) {
|
||||
thisYearBirth = thisYearLunar
|
||||
lunarDisplay = `${lunarInfo.monthName}${lunarInfo.dayName}`
|
||||
} else {
|
||||
thisYearBirth = new Date(now.getFullYear(), birthDate.getMonth(), birthDate.getDate())
|
||||
}
|
||||
} else {
|
||||
thisYearBirth = new Date(now.getFullYear(), birthDate.getMonth(), birthDate.getDate())
|
||||
}
|
||||
} else {
|
||||
// 公历生日
|
||||
thisYearBirth = new Date(now.getFullYear(), birthDate.getMonth(), birthDate.getDate())
|
||||
}
|
||||
|
||||
if (thisYearBirth >= now && thisYearBirth <= threeMonthsLater) {
|
||||
upcomingEvents.push({
|
||||
member,
|
||||
type: 'birth',
|
||||
date: thisYearBirth,
|
||||
originalDate: member.birthDate,
|
||||
isLunar: member.isLunarDate || false,
|
||||
lunarDisplay,
|
||||
month: thisYearBirth.getMonth() + 1,
|
||||
day: thisYearBirth.getDate()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 忌日
|
||||
if (member.deathDate) {
|
||||
const deathDate = new Date(member.deathDate)
|
||||
let thisYearDeath: Date
|
||||
let lunarDisplay: string | undefined
|
||||
|
||||
// 检查是否按农历计算
|
||||
if (member.isLunarDate) {
|
||||
// 农历忌日:计算今年对应的公历日期
|
||||
const lunarInfo = solar2lunar(deathDate)
|
||||
if (lunarInfo) {
|
||||
const thisYearLunar = lunar2solar(
|
||||
now.getFullYear(),
|
||||
lunarInfo.lunarMonth,
|
||||
lunarInfo.lunarDay,
|
||||
lunarInfo.isLeap
|
||||
)
|
||||
if (thisYearLunar) {
|
||||
thisYearDeath = thisYearLunar
|
||||
lunarDisplay = `${lunarInfo.monthName}${lunarInfo.dayName}`
|
||||
} else {
|
||||
thisYearDeath = new Date(now.getFullYear(), deathDate.getMonth(), deathDate.getDate())
|
||||
}
|
||||
} else {
|
||||
thisYearDeath = new Date(now.getFullYear(), deathDate.getMonth(), deathDate.getDate())
|
||||
}
|
||||
} else {
|
||||
// 公历忌日
|
||||
thisYearDeath = new Date(now.getFullYear(), deathDate.getMonth(), deathDate.getDate())
|
||||
}
|
||||
|
||||
if (thisYearDeath >= now && thisYearDeath <= threeMonthsLater) {
|
||||
upcomingEvents.push({
|
||||
member,
|
||||
type: 'death',
|
||||
date: thisYearDeath,
|
||||
originalDate: member.deathDate,
|
||||
isLunar: member.isLunarDate || false,
|
||||
lunarDisplay,
|
||||
month: thisYearDeath.getMonth() + 1,
|
||||
day: thisYearDeath.getDate()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
upcomingEvents.sort((a, b) => a.date.getTime() - b.date.getTime())
|
||||
|
||||
return upcomingEvents.length > 0 ? (
|
||||
upcomingEvents.slice(0, 5).map((event, index) => {
|
||||
const yearsAgo = now.getFullYear() - new Date(event.originalDate).getFullYear()
|
||||
const daysUntil = Math.ceil((event.date.getTime() - now.getTime()) / (1000 * 60 * 60 * 24))
|
||||
return (
|
||||
<Link href={`/members/${event.member.id}${currentTree?.id ? `?treeId=${currentTree.id}` : ''}`} key={`${event.member.id}-${event.type}-${index}`}>
|
||||
<div className="flex items-center gap-3 p-2 rounded-lg bg-background/60 border border-border/50 hover:bg-background transition-colors cursor-pointer">
|
||||
{event.isLunar && event.lunarDisplay ? (
|
||||
// 农历日期框 - 红色主题
|
||||
<div className="flex flex-col items-center justify-center w-12 h-12 rounded-lg bg-gradient-to-br from-red-50 to-amber-50 border border-red-200/50 text-red-800 font-serif shadow-sm">
|
||||
<span className="text-[9px] leading-tight text-center px-1">
|
||||
{event.lunarDisplay}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
// 公历日期框 - 蓝色主题
|
||||
<div className="flex flex-col items-center justify-center w-12 h-12 rounded-lg bg-gradient-to-br from-blue-50 to-sky-50 border border-blue-200/50 text-blue-800 font-serif shadow-sm">
|
||||
<span className="text-xs font-medium">
|
||||
{event.month}月
|
||||
</span>
|
||||
<span className="font-bold text-sm leading-tight">{event.day}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-sm flex items-center gap-2">
|
||||
<MemberNameWithStatus
|
||||
name={event.member.fullName}
|
||||
isDead={!!event.member.deathDate}
|
||||
/> {event.type === 'birth' ? '诞辰' : '忌日'}
|
||||
{event.isLunar && (
|
||||
<span className="text-[10px] text-amber-600 bg-amber-50 px-1.5 py-0.5 rounded border border-amber-200">
|
||||
农历
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
第 {event.member.generation} 世 · {yearsAgo} 年
|
||||
{event.type === 'death' && ' · 已故'}
|
||||
{event.isLunar && event.lunarDisplay && (
|
||||
<span className="ml-1">· {event.lunarDisplay}</span>
|
||||
)}
|
||||
<span className="ml-1 text-primary">· {daysUntil}天后</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})
|
||||
) : (
|
||||
<div className="text-center py-8">
|
||||
<p className="text-sm text-foreground font-light tracking-wide">来日无期</p>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
</div>
|
||||
</ChineseCardContent>
|
||||
</ChineseCard>
|
||||
</div>
|
||||
|
||||
<Card className="border-none shadow-sm bg-card/50 backdrop-blur">
|
||||
<CardHeader>
|
||||
<CardTitle className="font-serif">近期修谱动态</CardTitle>
|
||||
<CardDescription>记录家族成员的最新变动与修订</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ScrollArea className="h-[400px] pr-4">
|
||||
<ChineseCard variant="default" cornerOrnament className="shadow-sm">
|
||||
<ChineseCardHeader>
|
||||
<ChineseCardTitle className="flex items-center gap-2">
|
||||
<BarChart3 className="h-5 w-5 text-primary" />
|
||||
近期修谱动态
|
||||
</ChineseCardTitle>
|
||||
<ChineseCardDescription>记录家族成员的最新变动与修订</ChineseCardDescription>
|
||||
</ChineseCardHeader>
|
||||
<ChineseCardContent>
|
||||
<ScrollArea className="h-[600px] pr-4">
|
||||
<div className="space-y-4">
|
||||
{recentActivities.length > 0 ? (
|
||||
recentActivities.map((activity, i) => (
|
||||
@@ -804,189 +985,11 @@ export default function DashboardPage() {
|
||||
)}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</ChineseCardContent>
|
||||
</ChineseCard>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
{/* 近期纪念日标签页 */}
|
||||
<TabsContent value="anniversaries" className="mt-0 flex-1 overflow-y-auto">
|
||||
<Card className="border-none shadow-sm bg-card/50 backdrop-blur">
|
||||
<CardHeader>
|
||||
<CardTitle className="font-serif">近期纪念日</CardTitle>
|
||||
<CardDescription>未来三个月内的生日和忌日</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{(() => {
|
||||
const now = new Date()
|
||||
const threeMonthsLater = new Date(now.getFullYear(), now.getMonth() + 3, now.getDate())
|
||||
const members = Object.values(treeData.members)
|
||||
const upcomingEvents: Array<{
|
||||
member: any
|
||||
type: 'birth' | 'death'
|
||||
date: Date
|
||||
originalDate: string
|
||||
isLunar: boolean
|
||||
lunarDisplay?: string
|
||||
month: number
|
||||
day: number
|
||||
}> = []
|
||||
|
||||
members.forEach(member => {
|
||||
// 生日
|
||||
if (member.birthDate) {
|
||||
const birthDate = new Date(member.birthDate)
|
||||
let thisYearBirth: Date
|
||||
let lunarDisplay: string | undefined
|
||||
|
||||
// 检查是否按农历计算
|
||||
if (member.isLunarDate) {
|
||||
// 农历生日:计算今年对应的公历日期
|
||||
const lunarInfo = solar2lunar(birthDate)
|
||||
if (lunarInfo) {
|
||||
const thisYearLunar = lunar2solar(
|
||||
now.getFullYear(),
|
||||
lunarInfo.lunarMonth,
|
||||
lunarInfo.lunarDay,
|
||||
lunarInfo.isLeap
|
||||
)
|
||||
if (thisYearLunar) {
|
||||
thisYearBirth = thisYearLunar
|
||||
lunarDisplay = `${lunarInfo.monthName}${lunarInfo.dayName}`
|
||||
} else {
|
||||
thisYearBirth = new Date(now.getFullYear(), birthDate.getMonth(), birthDate.getDate())
|
||||
}
|
||||
} else {
|
||||
thisYearBirth = new Date(now.getFullYear(), birthDate.getMonth(), birthDate.getDate())
|
||||
}
|
||||
} else {
|
||||
// 公历生日
|
||||
thisYearBirth = new Date(now.getFullYear(), birthDate.getMonth(), birthDate.getDate())
|
||||
}
|
||||
|
||||
if (thisYearBirth >= now && thisYearBirth <= threeMonthsLater) {
|
||||
upcomingEvents.push({
|
||||
member,
|
||||
type: 'birth',
|
||||
date: thisYearBirth,
|
||||
originalDate: member.birthDate,
|
||||
isLunar: member.isLunarDate || false,
|
||||
lunarDisplay,
|
||||
month: thisYearBirth.getMonth() + 1,
|
||||
day: thisYearBirth.getDate()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 忌日
|
||||
if (member.deathDate) {
|
||||
const deathDate = new Date(member.deathDate)
|
||||
let thisYearDeath: Date
|
||||
let lunarDisplay: string | undefined
|
||||
|
||||
// 检查是否按农历计算
|
||||
if (member.isLunarDate) {
|
||||
// 农历忌日:计算今年对应的公历日期
|
||||
const lunarInfo = solar2lunar(deathDate)
|
||||
if (lunarInfo) {
|
||||
const thisYearLunar = lunar2solar(
|
||||
now.getFullYear(),
|
||||
lunarInfo.lunarMonth,
|
||||
lunarInfo.lunarDay,
|
||||
lunarInfo.isLeap
|
||||
)
|
||||
if (thisYearLunar) {
|
||||
thisYearDeath = thisYearLunar
|
||||
lunarDisplay = `${lunarInfo.monthName}${lunarInfo.dayName}`
|
||||
} else {
|
||||
thisYearDeath = new Date(now.getFullYear(), deathDate.getMonth(), deathDate.getDate())
|
||||
}
|
||||
} else {
|
||||
thisYearDeath = new Date(now.getFullYear(), deathDate.getMonth(), deathDate.getDate())
|
||||
}
|
||||
} else {
|
||||
// 公历忌日
|
||||
thisYearDeath = new Date(now.getFullYear(), deathDate.getMonth(), deathDate.getDate())
|
||||
}
|
||||
|
||||
if (thisYearDeath >= now && thisYearDeath <= threeMonthsLater) {
|
||||
upcomingEvents.push({
|
||||
member,
|
||||
type: 'death',
|
||||
date: thisYearDeath,
|
||||
originalDate: member.deathDate,
|
||||
isLunar: member.isLunarDate || false,
|
||||
lunarDisplay,
|
||||
month: thisYearDeath.getMonth() + 1,
|
||||
day: thisYearDeath.getDate()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
upcomingEvents.sort((a, b) => a.date.getTime() - b.date.getTime())
|
||||
|
||||
return upcomingEvents.length > 0 ? (
|
||||
upcomingEvents.map((event, index) => {
|
||||
const yearsAgo = now.getFullYear() - new Date(event.originalDate).getFullYear()
|
||||
return (
|
||||
<Link href={`/members/${event.member.id}${currentTree?.id ? `?treeId=${currentTree.id}` : ''}`} key={`${event.member.id}-${event.type}-${index}`}>
|
||||
<Card className="hover:border-primary transition-colors cursor-pointer">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
{event.isLunar && event.lunarDisplay ? (
|
||||
// 农历日期框 - 红色主题
|
||||
<div className="flex flex-col items-center justify-center w-14 h-14 rounded-lg bg-gradient-to-br from-red-50 to-amber-50 border-2 border-red-200/50 text-red-800 font-serif shadow-sm">
|
||||
<span className="text-[10px] leading-tight text-center px-1">
|
||||
{event.lunarDisplay}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
// 公历日期框 - 蓝色主题
|
||||
<div className="flex flex-col items-center justify-center w-14 h-14 rounded-lg bg-gradient-to-br from-blue-50 to-sky-50 border-2 border-blue-200/50 text-blue-800 font-serif shadow-sm">
|
||||
<span className="text-xs font-medium">
|
||||
{event.month}月
|
||||
</span>
|
||||
<span className="font-bold text-xl leading-tight">{event.day}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-sm flex items-center gap-2">
|
||||
<MemberNameWithStatus
|
||||
name={event.member.fullName}
|
||||
isDead={!!event.member.deathDate}
|
||||
/> {event.type === 'birth' ? '诞辰' : '忌日'}
|
||||
{event.isLunar && (
|
||||
<span className="text-[10px] text-amber-600 bg-amber-50 px-1.5 py-0.5 rounded border border-amber-200">
|
||||
农历
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
第 {event.member.generation} 世 · {yearsAgo} 年
|
||||
{event.type === 'death' && ' · 已故'}
|
||||
{event.isLunar && event.lunarDisplay && (
|
||||
<span className="ml-1">· {event.lunarDisplay}</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
)
|
||||
})
|
||||
) : (
|
||||
<div className="col-span-full text-center py-16">
|
||||
<p className="text-sm text-foreground font-light tracking-wide">来日无期</p>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
{/* 统计图表标签页 */}
|
||||
<TabsContent value="statistics" className="mt-0 flex-1 overflow-y-auto">
|
||||
@@ -995,12 +998,12 @@ export default function DashboardPage() {
|
||||
|
||||
{/* 迁徙记录标签页 */}
|
||||
<TabsContent value="migration" className="mt-0 flex-1 overflow-y-auto">
|
||||
<Card className="border-none shadow-sm bg-card/50 backdrop-blur">
|
||||
<CardHeader>
|
||||
<CardTitle className="font-serif">家族迁徙记录</CardTitle>
|
||||
<CardDescription>记录家族成员的籍贯分布</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChineseCard variant="bamboo" className="shadow-sm">
|
||||
<ChineseCardHeader>
|
||||
<ChineseCardTitle>家族籍贯记录</ChineseCardTitle>
|
||||
<ChineseCardDescription>记录家族成员的籍贯分布</ChineseCardDescription>
|
||||
</ChineseCardHeader>
|
||||
<ChineseCardContent>
|
||||
<div className="space-y-6">
|
||||
{locationGroups.length > 0 ? (
|
||||
locationGroups.map(([location, locationMembers]) => (
|
||||
@@ -1025,12 +1028,12 @@ export default function DashboardPage() {
|
||||
))
|
||||
) : (
|
||||
<div className="text-center py-16">
|
||||
<p className="text-sm text-foreground font-light tracking-wide">未有迁徙</p>
|
||||
<p className="text-sm text-foreground font-light tracking-wide">未有籍贯记录</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</ChineseCardContent>
|
||||
</ChineseCard>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user