/* global React */ const { useState: useT, useEffect: useTE, useRef: useTR } = React; /* ===================================================== Global Toast host + showToast() usage: window.showToast({ type: 'success', msg: '奏折已呈' }) ===================================================== */ const ToastIcons = { success: ( ), error: ( ), info: ( ), warning: ( ), }; function ToastHost() { const [items, setItems] = useT([]); useTE(() => { window.showToast = ({ type = 'success', msg = '', duration = 3000 } = {}) => { const id = Date.now() + Math.random(); setItems(x => [...x, { id, type, msg }]); setTimeout(() => setItems(x => x.map(i => i.id === id ? { ...i, leaving: true } : i)), duration); setTimeout(() => setItems(x => x.filter(i => i.id !== id)), duration + 220); }; }, []); return (
{items.map(t => (
{ToastIcons[t.type]} {t.msg}
))}
); } /* ===================================================== Confirm Modal (controlled) ===================================================== */ function ConfirmModal({ open, title = '朕问你', message, confirmText = '确认', cancelText = '朕再想想', danger = false, requireTyping, onConfirm, onCancel }) { const [typed, setTyped] = useT(''); useTE(() => { if (open) setTyped(''); }, [open]); if (!open) return null; const canConfirm = !requireTyping || typed.trim() === requireTyping; return (
e.stopPropagation()}> {danger && (
)} {danger ? 'DANGER · 慎之' : 'CONFIRM · 请示'}

{title}

{message}

{requireTyping && (
setTyped(e.target.value)}/>
)}
); } /* ===================================================== Info Modal — "朕曰" ===================================================== */ function InfoModal({ open, title = '朕曰', children, onClose }) { if (!open) return null; return (
e.stopPropagation()}>

{title}

{children}
); } /* ===================================================== Loading helpers ===================================================== */ function SealSpinner({ size = 48, text }) { return (
{text && {text}}
); } function FullscreenLoading({ text = '稍候,待朕过目……' }) { return (
); } function PageTopBar() { return
; } function LetterSkeleton() { return (
); } /* ===================================================== Empty State ===================================================== */ function EmptyState({ icon, title, subtitle, cta, onCta }) { return (
{icon}

{title}

{subtitle}

{cta && }
); } /* ===================================================== Form Field (with states) ===================================================== */ function TextField({ label, required, state = 'default', hint, error, ...rest }) { const cls = `bx-field ${state === 'success' ? 'bx-field--success' : ''} ${state === 'error' ? 'bx-field--error' : ''}`; return ( ); } /* ===================================================== 违规词提示条 · 写信 / 评论共用 blocked: 必须修改才能提交的违规词 [{word,category}] warnings: 仅提醒、不阻止提交的敏感词 [{word,category}] ===================================================== */ function SensitiveNotice({ blocked = [], warnings = [] }) { if (blocked.length === 0 && warnings.length === 0) return null; return (
{blocked.length > 0 && (
! 检测到 {blocked.length} 个违规词,须修改后方可呈递
{blocked.map((b, i) => ( {b.word} ))}

这些词可能触发抖音限流/封禁,请删去或换种说法。如要留联系方式,可在被陛下选中后私下沟通。

)} {warnings.length > 0 && (
· 另有 {warnings.length} 个敏感词,建议斟酌(不影响呈递)
{warnings.map((w, i) => ( {w.word} ))}
)}
); } Object.assign(window, { ToastHost, ConfirmModal, InfoModal, SealSpinner, FullscreenLoading, PageTopBar, LetterSkeleton, EmptyState, TextField, SensitiveNotice });