"use client"; import { useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Card, CardContent } from "@/components/ui/card"; import { Pencil, Trash2, Plus, ExternalLink, Globe, } from "lucide-react"; interface Bookmark { id: number; title: string; url: string; description: string; icon: string; category: string; sortOrder: number; } export function BookmarkManager() { const [bookmarks, setBookmarks] = useState([]); const [categories, setCategories] = useState([]); const [loading, setLoading] = useState(true); const [dialogOpen, setDialogOpen] = useState(false); const [editing, setEditing] = useState(null); const [title, setTitle] = useState(""); const [url, setUrl] = useState(""); const [description, setDescription] = useState(""); const [icon, setIcon] = useState(""); const [category, setCategory] = useState(""); const [sortOrder, setSortOrder] = useState(0); async function fetchBookmarks() { setLoading(true); try { const res = await fetch("/api/proxy/bookmarks"); if (!res.ok) throw new Error("failed to fetch"); const data = await res.json(); setBookmarks(data.bookmarks || []); setCategories(data.categories || []); } catch { setBookmarks([]); setCategories([]); } finally { setLoading(false); } } useEffect(() => { fetchBookmarks(); }, []); function openAdd() { setEditing(null); setTitle(""); setUrl(""); setDescription(""); setIcon(""); setCategory(""); setSortOrder(0); setDialogOpen(true); } function openEdit(bm: Bookmark) { setEditing(bm); setTitle(bm.title); setUrl(bm.url); setDescription(bm.description); setIcon(bm.icon); setCategory(bm.category); setSortOrder(bm.sortOrder); setDialogOpen(true); } async function handleSave() { const payload = { title, url, description, icon, category: category || "默认", sortOrder, }; try { const res = editing ? await fetch(`/api/proxy/bookmarks/${editing.id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }) : await fetch("/api/proxy/bookmarks", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); if (!res.ok) throw new Error("save failed"); setDialogOpen(false); fetchBookmarks(); } catch { alert("保存失败,请检查输入"); } } async function handleDelete(id: number) { if (!confirm("确定要删除这个书签吗?")) return; try { const res = await fetch(`/api/proxy/bookmarks/${id}`, { method: "DELETE", }); if (!res.ok) throw new Error("delete failed"); fetchBookmarks(); } catch { alert("删除失败"); } } const grouped = bookmarks.reduce>((acc, bm) => { const cat = bm.category || "默认"; if (!acc[cat]) acc[cat] = []; acc[cat].push(bm); return acc; }, {}); if (loading) { return

加载中...

; } return (

共 {bookmarks.length} 个书签

{bookmarks.length === 0 ? (

还没有书签

) : ( Object.entries(grouped).map(([cat, items]) => (

{cat}

{items.map((bm) => (
{bm.icon ? ( { (e.target as HTMLImageElement).style.display = "none"; }} /> ) : ( )}
{bm.title} {bm.description && (

{bm.description}

)}
))}
)) )} {editing ? "编辑书签" : "添加书签"}
setTitle(e.target.value)} placeholder="例如:GitHub" />
setUrl(e.target.value)} placeholder="https://github.com" />
setDescription(e.target.value)} placeholder="简短描述" />
setCategory(e.target.value)} placeholder="默认" list="categories" /> {categories.map((c) => (
setSortOrder(Number(e.target.value)) } />
setIcon(e.target.value)} placeholder="https://example.com/favicon.ico" />
); }