0.0.1.1
This commit is contained in:
+81
-11
@@ -1,7 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import Link from "next/link"
|
||||
import { BookOpen, Search, Bell, Settings, LogOut, User, ChevronDown, Plus } from "lucide-react"
|
||||
import { BookOpen, Search, Bell, Settings, LogOut, User, ChevronDown, Plus, Trash2 } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
||||
@@ -22,6 +22,7 @@ interface FamilyTree {
|
||||
id: string
|
||||
name: string
|
||||
description: string | null
|
||||
ownerId: string
|
||||
}
|
||||
|
||||
export function SiteHeader() {
|
||||
@@ -31,6 +32,7 @@ export function SiteHeader() {
|
||||
const [showResults, setShowResults] = useState(false)
|
||||
const [familyTrees, setFamilyTrees] = useState<FamilyTree[]>([])
|
||||
const [currentTree, setCurrentTree] = useState<FamilyTree | null>(null)
|
||||
const [deletingTreeId, setDeletingTreeId] = useState<string | null>(null)
|
||||
const searchRef = useRef<HTMLDivElement>(null)
|
||||
const router = useRouter()
|
||||
|
||||
@@ -49,11 +51,64 @@ export function SiteHeader() {
|
||||
return "U"
|
||||
}
|
||||
|
||||
// 删除家族树
|
||||
const handleDeleteTree = async (tree: FamilyTree, e: React.MouseEvent) => {
|
||||
e.stopPropagation() // 阻止触发选择事件
|
||||
|
||||
// 检查权限
|
||||
if (tree.ownerId !== session?.user?.id) {
|
||||
alert('只有家族树的创建者才能删除')
|
||||
return
|
||||
}
|
||||
|
||||
const confirmText = `确定要删除家族树"${tree.name}"吗?\n\n此操作将永久删除所有成员、活动日志和协作者数据,且不可撤销!`
|
||||
if (!confirm(confirmText)) {
|
||||
return
|
||||
}
|
||||
|
||||
// 二次确认
|
||||
const finalConfirm = prompt(`请输入家族树名称"${tree.name}"以确认删除:`)
|
||||
if (finalConfirm !== tree.name) {
|
||||
alert('名称不匹配,删除已取消')
|
||||
return
|
||||
}
|
||||
|
||||
setDeletingTreeId(tree.id)
|
||||
try {
|
||||
const response = await fetch(`/api/trees/${tree.id}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.error || '删除失败')
|
||||
}
|
||||
|
||||
// 刷新家族树列表
|
||||
await loadFamilyTrees()
|
||||
|
||||
// 如果删除的是当前树,跳转到首页
|
||||
if (currentTree?.id === tree.id) {
|
||||
window.location.href = '/'
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除家族树失败:', error)
|
||||
alert('删除失败:' + (error as Error).message)
|
||||
} finally {
|
||||
setDeletingTreeId(null)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取家族树列表
|
||||
const loadFamilyTrees = useCallback(() => {
|
||||
if (session?.user?.id) {
|
||||
fetch('/api/trees')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
throw new Error(`HTTP ${res.status}`)
|
||||
}
|
||||
return res.json()
|
||||
})
|
||||
.then(data => {
|
||||
if (data.trees) {
|
||||
setFamilyTrees(data.trees)
|
||||
@@ -72,7 +127,9 @@ export function SiteHeader() {
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => console.error('获取家族树失败:', err))
|
||||
.catch(err => {
|
||||
console.error('获取家族树列表失败:', err)
|
||||
})
|
||||
}
|
||||
}, [session?.user?.id])
|
||||
|
||||
@@ -128,12 +185,12 @@ export function SiteHeader() {
|
||||
{session && familyTrees.length > 0 && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" className="gap-2">
|
||||
<span className="max-w-[150px] truncate">{currentTree?.name || '选择家族树'}</span>
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
<Button variant="outline" className="gap-2 min-w-[200px]">
|
||||
<span className="flex-1 truncate text-left">{currentTree?.name || '选择家族树'}</span>
|
||||
<ChevronDown className="h-4 w-4 flex-shrink-0" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="w-56">
|
||||
<DropdownMenuContent align="start" className="w-[var(--radix-dropdown-menu-trigger-width)] min-w-[200px]">
|
||||
<DropdownMenuLabel>我的家族树</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
{familyTrees.map((tree) => (
|
||||
@@ -143,12 +200,25 @@ export function SiteHeader() {
|
||||
setCurrentTree(tree)
|
||||
router.push(`/?treeId=${tree.id}`)
|
||||
}}
|
||||
className="cursor-pointer"
|
||||
className="cursor-pointer flex items-center justify-between group"
|
||||
>
|
||||
<span className="flex-1 truncate">{tree.name}</span>
|
||||
{currentTree?.id === tree.id && (
|
||||
<span className="text-primary">✓</span>
|
||||
)}
|
||||
<div className="flex items-center gap-2">
|
||||
{currentTree?.id === tree.id && (
|
||||
<span className="text-primary">✓</span>
|
||||
)}
|
||||
{tree.ownerId === session?.user?.id && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-6 w-6 p-0 opacity-0 group-hover:opacity-100 transition-opacity hover:bg-destructive hover:text-destructive-foreground"
|
||||
onClick={(e) => handleDeleteTree(tree, e)}
|
||||
disabled={deletingTreeId === tree.id}
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
Reference in New Issue
Block a user