"use client" import { useEffect, useState } from "react" import Link from "next/link" import { useRouter } from "next/navigation" import { Plus, Megaphone } from "lucide-react" import { useTranslations } from "next-intl" import { Button } from "@/shared/components/ui/button" import { EmptyState } from "@/shared/components/ui/empty-state" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/shared/components/ui/select" import { AnnouncementCard } from "./announcement-card" import { AnnouncementPagination } from "./announcement-pagination" import { useAnnouncementsService } from "./announcements-service-context" import type { Announcement, AnnouncementStatus } from "../types" type Filter = "all" | AnnouncementStatus /** * 公告列表组件。 * * 过滤模式:纯服务端过滤。 * - Select 切换时更新 URL `?status=`,触发 RSC 重新渲染 * - 父页面根据 `?status=` 查询并传入 `announcements` prop * - 组件不再做客户端二次过滤,避免双重过滤逻辑冗余 * * P2-4: 删除了死 prop `detailHrefBuilder`,仅保留 `detailHrefPrefix`(Server Component 安全)。 * P1-2: 用户端(canManage=false)时调用 `getReadStatus` 批量获取已读状态, * 传入卡片做已读/未读视觉区分。 * P2-6: 新增分页支持。传入 `pagination` prop 时在列表底部渲染 `AnnouncementPagination`。 * v3: pagination 不再接收 `buildPageHref` 函数(Server Component 不能传函数), * 改为传 `basePath` + `statusFilter`,由 AnnouncementPagination 自行构建 URL。 */ export function AnnouncementList({ announcements, canManage, createHref, detailHrefPrefix, initialStatus, pagination, }: { announcements: Announcement[] canManage?: boolean createHref?: string detailHrefPrefix?: string initialStatus?: Filter pagination?: { page: number pageSize: number total: number basePath: string statusFilter?: string } }) { const t = useTranslations("announcements") const router = useRouter() const service = useAnnouncementsService() const filter: Filter = initialStatus ?? "all" // P1-2: 用户端批量获取已读状态 const [readStatus, setReadStatus] = useState>({}) useEffect(() => { if (canManage) return if (announcements.length === 0) return let cancelled = false void service .getReadStatus(announcements.map((a) => a.id)) .then((res) => { if (cancelled) return if (res.success && res.data) { setReadStatus(res.data) } }) .catch(() => { // 静默处理,已读状态不影响主流程 }) return () => { cancelled = true } }, [announcements, canManage, service]) const filterOptions: { value: Filter; label: string }[] = [ { value: "all", label: t("filter.all") }, { value: "published", label: t("filter.published") }, { value: "draft", label: t("filter.draft") }, { value: "archived", label: t("filter.archived") }, ] const handleFilterChange = (value: string): void => { const params = new URLSearchParams() if (value !== "all") params.set("status", value) const qs = params.toString() router.replace(qs ? `?${qs}` : "?") } return (
{canManage && createHref ? ( ) : null}
{announcements.length === 0 ? ( ) : (
{announcements.map((a) => ( ))}
{pagination ? ( ) : null}
)}
) }