"use client" import { useState, useEffect, useCallback } from "react" import { DashboardShell } from "@/components/dashboard-shell" import { DashboardHeader } from "@/components/dashboard-header" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { Badge } from "@/components/ui/badge" import { Search, Edit, Eye, Loader2, Archive } from "lucide-react" import { AddOutfitDialog } from "@/components/outfits/add-outfit-dialog" import type { DisplayOutfit } from "@/components/outfits/add-outfit-dialog" import { DeleteConfirmationDialog } from "@/components/delete-confirmation-dialog" import { PublishConfirmationDialog } from "@/components/publish-confirmation-dialog" import { useToast } from "@/components/ui/use-toast" import { isSuperUser } from "@/lib/api/auth" import { getOutfits, deleteOutfit, publishOutfit, archiveOutfit } from "@/lib/api/outfits" import type { Outfit } from "@/lib/api/types" import Link from "next/link" function formatDate(dateStr: string): string { if (!dateStr) return "" try { return new Date(dateStr).toLocaleDateString("zh-CN", { year: "numeric", month: "2-digit", day: "2-digit", }) } catch { return dateStr } } function toDisplayOutfit(outfit: Outfit): DisplayOutfit { return { id: outfit.id, name: outfit.name, type: outfit.category || "", rarity: outfit.rarity || "", description: outfit.description || "", releaseDate: formatDate(outfit.publishedAt || outfit.createdAt || ""), status: outfit.status || "未发布", activatedCount: outfit.activeCardsCount || 0, image: outfit.imageUrl || "", } } export default function OutfitsPage() { const { toast } = useToast() const [outfits, setOutfits] = useState([]) const [searchTerm, setSearchTerm] = useState("") const [currentPage, setCurrentPage] = useState(1) const [totalItems, setTotalItems] = useState(0) const [selectedOutfit, setSelectedOutfit] = useState(null) const [isEditDialogOpen, setIsEditDialogOpen] = useState(false) const [loading, setLoading] = useState(true) const itemsPerPage = 10 const fetchOutfits = useCallback(async () => { try { setLoading(true) const response = await getOutfits({ page: currentPage, pageSize: itemsPerPage, search: searchTerm || undefined, }) setOutfits(response.items.map(toDisplayOutfit)) setTotalItems(response.total) } catch (error) { console.error("获取服装列表失败:", error) toast({ title: "获取失败", description: "无法获取服装列表,请稍后重试", variant: "destructive", }) } finally { setLoading(false) } }, [currentPage, searchTerm, toast]) useEffect(() => { fetchOutfits() }, [fetchOutfits]) const totalPages = Math.ceil(totalItems / itemsPerPage) const handleAddOutfit = (newOutfit: DisplayOutfit) => { fetchOutfits() toast({ title: "添加成功", description: `服装 ${newOutfit.name} 已成功添加`, }) } const handleEditOutfit = (updatedOutfit: DisplayOutfit) => { fetchOutfits() setSelectedOutfit(null) setIsEditDialogOpen(false) toast({ title: "更新成功", description: `服装 ${updatedOutfit.name} 已成功更新`, }) } const handleDeleteOutfit = async (outfitId: string) => { try { await deleteOutfit(outfitId) await fetchOutfits() toast({ title: "删除成功", description: "服装已成功删除", variant: "destructive", }) } catch (error) { console.error("删除服装失败:", error) toast({ title: "删除失败", description: "无法删除服装,请稍后重试", variant: "destructive", }) } } const handlePublishOutfit = async (outfitId: string, outfitName: string) => { try { await publishOutfit(outfitId) await fetchOutfits() toast({ title: "发布成功", description: `服装 "${outfitName}" 已成功发布`, }) } catch (error) { console.error("发布服装失败:", error) toast({ title: "发布失败", description: "无法发布服装,请稍后重试", variant: "destructive", }) } } const handleArchiveOutfit = async (outfitId: string, outfitName: string) => { try { await archiveOutfit(outfitId) await fetchOutfits() toast({ title: "归档成功", description: `服装 "${outfitName}" 已归档`, }) } catch (error) { console.error("归档服装失败:", error) toast({ title: "归档失败", description: "无法归档服装,请稍后重试", variant: "destructive", }) } } const openEditDialog = (outfit: DisplayOutfit) => { setSelectedOutfit(outfit) setIsEditDialogOpen(true) } return (
{ setSearchTerm(e.target.value) setCurrentPage(1) }} />
服装列表
管理洛天依可以使用的服装
{loading ? (
加载中...
) : ( ID 服装名称 类型 稀有度 发布日期 状态 激活数量 操作 {outfits.map((outfit) => ( {outfit.id} {outfit.name} {outfit.type} {outfit.rarity} {outfit.releaseDate || "-"} {outfit.status} {outfit.activatedCount} {outfit.status === "草稿" && ( handlePublishOutfit(outfit.id, outfit.name)} /> )} {outfit.status === "已发布" && ( )} {(outfit.status !== "已发布" || isSuperUser()) && ( <> handleDeleteOutfit(outfit.id)} /> )} ))} {outfits.length === 0 && ( 没有找到匹配的服装 )}
)}
显示 {outfits.length > 0 ? (currentPage - 1) * itemsPerPage + 1 : 0}- {Math.min(currentPage * itemsPerPage, totalItems)} 共 {totalItems} 个服装
{selectedOutfit && isEditDialogOpen && ( )}
) }