import { useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { toast } from "sonner"; import { archiveAnnouncement, createAnnouncement, fetchAnnouncements, updateAnnouncement, type AnnouncementCategory, type PortalAnnouncement } from "../api/announcements"; import { PageHeader } from "../components/common/PageHeader"; import { Button } from "../components/ui/button"; import { Input } from "../components/ui/input"; import { Label } from "../components/ui/label"; import { Tabs, TabsList, TabsTrigger } from "../components/ui/tabs"; import { Textarea } from "../components/ui/textarea"; import { formatDisplayDate, formatDisplayDateTime } from "../lib/displayFormat"; function toChinaEndOfDayIso(value: string) { if (!value) return null; return new Date(`${value}T23:59:59+08:00`).toISOString(); } function acknowledgementModeLabel(category: AnnouncementCategory, requiresAcknowledgement: boolean) { return category === "policy" || requiresAcknowledgement ? "制度签收" : "普通公告"; } function formatSummary(item: PortalAnnouncement) { const summary = item.acknowledgementSummary; if (!summary?.required) return "无需签收"; return `已签 ${summary.acknowledgedCount}/${summary.totalActiveEmployees} · 待签 ${summary.pendingCount}`; } export function PortalAnnouncementsPage() { const queryClient = useQueryClient(); const [title, setTitle] = useState(""); const [summary, setSummary] = useState(""); const [linkUrl, setLinkUrl] = useState(""); const [priority, setPriority] = useState("100"); const [mode, setMode] = useState("general"); const [acknowledgementStatement, setAcknowledgementStatement] = useState("我已阅读并理解该制度,确认按公司制度执行。"); const [acknowledgementDueDate, setAcknowledgementDueDate] = useState(""); const listQuery = useQuery({ queryKey: ["announcements", "manage"], queryFn: () => fetchAnnouncements(true) }); const refresh = () => queryClient.invalidateQueries({ queryKey: ["announcements"] }); const isPolicy = mode === "policy"; const createMutation = useMutation({ mutationFn: () => createAnnouncement({ title, summary, linkUrl: linkUrl || null, priority: Number(priority || 100), category: mode, requiresAcknowledgement: isPolicy, acknowledgementStatement: isPolicy ? acknowledgementStatement.trim() : null, acknowledgementDueAt: isPolicy ? toChinaEndOfDayIso(acknowledgementDueDate) : null, }), onSuccess: async () => { setTitle(""); setSummary(""); setLinkUrl(""); setPriority("100"); setMode("general"); setAcknowledgementStatement("我已阅读并理解该制度,确认按公司制度执行。"); setAcknowledgementDueDate(""); await refresh(); toast.success("公告已创建"); }, onError: () => { toast.error("公告创建失败,请检查填写内容"); }, }); const archiveMutation = useMutation({ mutationFn: archiveAnnouncement, onSuccess: async () => { await refresh(); toast.success("公告已归档"); }, }); const toggleMutation = useMutation({ mutationFn: ({ id, enabled }: { id: string; enabled: boolean }) => updateAnnouncement(id, { enabled }), onSuccess: async () => { await refresh(); toast.success("公告状态已更新"); }, }); const handleCreate = () => { if (!title.trim() || !summary.trim()) { toast.error("请填写标题和摘要"); return; } if (isPolicy && !acknowledgementStatement.trim()) { toast.error("制度签收公告必须填写签收声明"); return; } createMutation.mutate(); }; return (

新建公告

制度内容变化时请新建公告版本,不直接覆盖已签收内容。

setMode(value as AnnouncementCategory)}> 普通公告 制度签收
setTitle(event.target.value)} placeholder="例如:费用报销制度签收" />