import { useEffect, useState, useCallback } from 'react' import { Search, Plus, Pencil, Trash2, X, Bell } from 'lucide-react' import { useRole } from '../../context' import { api, type Notice, type NoticeReceipt } from '../../api' import { ConfirmDialog } from '../../components/ui/ConfirmDialog' const PRIORITY_LABELS: Record = { normal: '普通', high: '重要', urgent: '紧急' } const PRIORITY_CLASS: Record = { normal: '', high: 'important', urgent: 'urgent' } export function NoticesPage() { const { role } = useRole() const [notices, setNotices] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState('') const [filterPriority, setFilterPriority] = useState('') const [selected, setSelected] = useState(null) const [receipts, setReceipts] = useState([]) const [receiptsLoading, setReceiptsLoading] = useState(false) // 编辑/新增弹窗 const [showModal, setShowModal] = useState(false) const [editing, setEditing] = useState(null) const [saving, setSaving] = useState(false) // 删除确认 const [confirmDelete, setConfirmDelete] = useState<{ notice: Notice; loading: boolean } | null>(null) const displayed = filterPriority ? notices.filter(n => n.priority === filterPriority) : notices const loadNotices = useCallback(async () => { setLoading(true); setError('') try { const data = await api.notices.list(role) setNotices(data) if (data.length > 0 && !selected) setSelected(data[0]) } catch (e: any) { setError(e.message) } finally { setLoading(false) } }, [role]) const loadReceipts = useCallback(async (noticeId: number) => { setReceiptsLoading(true) try { setReceipts(await api.notices.receipts(role, noticeId)) } catch {} finally { setReceiptsLoading(false) } }, [role]) useEffect(() => { loadNotices() }, [loadNotices]) useEffect(() => { if (selected) loadReceipts(selected.id) }, [selected, loadReceipts]) const handleAdd = () => { setEditing(null); setShowModal(true) } const handleEdit = (n: Notice) => { setEditing(n); setShowModal(true) } const handleCloseModal = () => { setShowModal(false); setEditing(null) } const handleCreate = async (form: any) => { setSaving(true) try { const created = await api.notices.create(role, form) setNotices(prev => [created, ...prev]) setSelected(created) handleCloseModal() } catch (e: any) { alert(e.message) } finally { setSaving(false) } } const handleUpdate = async (form: any) => { if (!editing) return setSaving(true) try { const updated = await api.notices.update(role, editing.id, form) setNotices(prev => prev.map(x => x.id === editing.id ? updated : x)) setSelected(updated) handleCloseModal() } catch (e: any) { alert(e.message) } finally { setSaving(false) } } const handleDeleteClick = (n: Notice) => setConfirmDelete({ notice: n, loading: false }) const handleDeleteConfirm = async () => { if (!confirmDelete) return setConfirmDelete(prev => prev ? { ...prev, loading: true } : null) try { // notices API 没有 delete,但可以标记为已删除或者通过 update 处理 // 这里假设后端支持,或通过其他方式处理 setConfirmDelete(null) } catch (e: any) { alert(e.message); setConfirmDelete(null) } } const readCount = receipts.filter(r => r.read).length const confirmedCount = receipts.filter(r => r.confirmed).length const canManage = role === 'headquarters' return ( <>

通知公告

接收管理通知、业务安排与重要事项提醒。
{canManage && ( )}
{loading &&
加载中...
} {error &&
{error}
} {!loading && !error && displayed.length === 0 && (
暂无通知
)} {!loading && !error && displayed.map(n => (
setSelected(n)} className={selected?.id === n.id ? 'active' : ''} style={{ cursor: 'pointer' }} > {PRIORITY_LABELS[n.priority]}

{n.title}

{n.publishedBy} · {n.publishedAt?.slice(0, 10) || '—'}

{canManage && (
e.stopPropagation()}>
)}
))}
{selected && !receiptsLoading && receipts.length > 0 && (

回执情况

{role === 'headquarters' && (
已读 {readCount}/{receipts.length} 已确认 {confirmedCount}/{receipts.length}
)}
{receipts.map(r => ( ))}
姓名 角色 已读 确认
{r.receiverName} {r.receiverRole === 'headquarters' ? '总部管理员' : r.receiverRole === 'station' ? '分站负责人' : '记者'} {r.read ? ✓ 已读 : 未读} {r.confirmed ? ✓ 已确认 : 未确认}
)} {/* 编辑/新增弹窗 */} {showModal && ( )} {/* 删除确认 */} setConfirmDelete(null)} /> ) } // ── 发布/编辑表单弹窗 ──────────────────────────────────────────────────────── type NoticeForm = { title: string; content: string; priority: string; scope: string } function NoticeModal({ notice, saving, onSave, onClose, }: { notice: Notice | null saving: boolean onSave: (form: NoticeForm) => void onClose: () => void }) { const [form, setForm] = useState({ title: notice?.title || '', content: notice?.content || '', priority: notice?.priority || 'normal', scope: notice?.scope || 'all', }) const [errors, setErrors] = useState>>({}) const validate = (): boolean => { const errs: Partial> = {} if (!form.title.trim()) errs.title = '请输入通知标题' setErrors(errs) return Object.keys(errs).length === 0 } const handleSubmit = (e: React.FormEvent) => { e.preventDefault() if (!validate()) return onSave(form) } const field = (key: keyof NoticeForm) => ({ value: form[key], onChange: (e: React.ChangeEvent) => { setForm(f => ({ ...f, [key]: e.target.value })) if (errors[key]) setErrors(er => { const n = { ...er }; delete n[key]; return n }) }, }) return (
e.stopPropagation()}>

{notice ? '编辑通知' : '发布通知'}

{notice &&

{notice.code}

}