'use client'; // 反馈审核抽屉:展示本条反馈 + 该用户历史反馈(就近参考),待审核态可采纳(发金币)或拒绝(填用户可见原因)。 // 已审核态只读回显。对接同事 PR#66 接口:POST .../approve、POST .../reject(.../handle 已废弃)。 import { useEffect, useState } from 'react'; import { App, Button, Drawer, Form, Image, Input, InputNumber, Segmented, Space, Spin, Tag, Typography, } from 'antd'; import { api, errMsg } from '@/lib/api'; import { canDo } from '@/lib/auth'; import { formatUtcTime } from '@/lib/format'; import { mediaUrl } from '@/lib/media'; import type { CursorPage, Feedback } from '@/lib/types'; const { Text, Paragraph } = Typography; const REWARD_MAX = 10000; // 对齐后端 FEEDBACK_REWARD_MAX_COINS interface Props { feedback: Feedback | null; open: boolean; onClose: () => void; onDone: () => void; // 审核成功后刷新列表 } // 待审核(同事后端初始态 pending;兼容历史 new 数据) const isPending = (s: string) => s === 'pending' || s === 'new'; const STATUS_META: Record = { pending: { label: '审核中', color: 'orange' }, new: { label: '待审核', color: 'orange' }, adopted: { label: '已采纳', color: 'green' }, rejected: { label: '未采纳', color: 'red' }, }; const statusTag = (s: string) => { const m = STATUS_META[s] ?? { label: s, color: 'default' }; return {m.label}; }; function FeedbackImages({ images }: { images: string[] | null }) { if (!images || !images.length) return 无截图; return ( {images.map((src, i) => ( ))} ); } export default function FeedbackHandleDrawer({ feedback, open, onClose, onDone }: Props) { const { message } = App.useApp(); const [form] = Form.useForm(); const [action, setAction] = useState<'approve' | 'reject'>('approve'); const [submitting, setSubmitting] = useState(false); const [history, setHistory] = useState([]); const [loadingHistory, setLoadingHistory] = useState(false); const editable = feedback != null && isPending(feedback.status) && canDo(['operator']); // 打开时:重置表单 + 拉该用户全部历史反馈(含本条,渲染时滤掉本条) useEffect(() => { if (!open || !feedback) return; setAction('approve'); form.resetFields(); let alive = true; setLoadingHistory(true); api .get>('/admin/api/feedbacks', { params: { user_id: feedback.user_id, limit: 50, sort_by: 'id', sort_order: 'desc' }, }) .then((r) => { if (alive) setHistory(r.data.items); }) .catch(() => { if (alive) setHistory([]); }) .finally(() => { if (alive) setLoadingHistory(false); }); return () => { alive = false; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [open, feedback?.id]); const submit = async () => { if (!feedback) return; const v = await form.validateFields(); setSubmitting(true); try { if (action === 'approve') { await api.post(`/admin/api/feedbacks/${feedback.id}/approve`, { reward_coins: v.reward_coins, note: v.note?.trim() || null, }); message.success(`已采纳,发放 ${v.reward_coins} 金币`); } else { await api.post(`/admin/api/feedbacks/${feedback.id}/reject`, { reason: v.reason.trim(), note: v.note?.trim() || null, }); message.success('已拒绝'); } onClose(); onDone(); } catch (e) { message.error(errMsg(e)); } finally { setSubmitting(false); } }; const others = feedback ? history.filter((h) => h.id !== feedback.id) : []; return ( {action === 'approve' ? '采纳并发金币' : '确认拒绝'} ) : null } > {feedback && ( {/* 本条反馈 */}
用户 {feedback.user_id} · {formatUtcTime(feedback.created_at)} {feedback.content || '-'}
{/* 已审核(或无权限):只读回显 */} {!editable && (
状态:{statusTag(feedback.status)} {feedback.reviewed_at ? ( · {formatUtcTime(feedback.reviewed_at)} ) : null}
{feedback.status === 'adopted' && (
奖励金币:{feedback.reward_coins ?? 0}
)} {feedback.status === 'rejected' && (
未采纳原因(用户可见): {feedback.reject_reason || (无)}
)}
审核备注:{feedback.review_note || (无)}
{isPending(feedback.status) && (
仅 operator / super_admin 可审核
)}
)} {/* 待审核:采纳 / 拒绝表单 */} {editable && (
setAction(v as 'approve' | 'reject')} options={[ { label: '采纳(发金币)', value: 'approve' }, { label: '拒绝', value: 'reject' }, ]} style={{ marginBottom: 16 }} /> {/* preserve=false:切换采纳/拒绝时卸载的字段从 store 移除,validateFields 不误校验另一侧 */}
{action === 'approve' ? ( <> ) : ( <> )}
)} {/* 该用户历史反馈 */}
该用户历史反馈{others.length ? `(${others.length})` : ''} {loadingHistory ? ( ) : others.length === 0 ? (
该用户暂无其它反馈
) : ( {others.map((h) => (
#{h.id} · {formatUtcTime(h.created_at)} {statusTag(h.status)}
{h.content || '-'} {h.status === 'adopted' && ( 采纳 · 奖励 {h.reward_coins ?? 0} 金币 {h.review_note ? ` · ${h.review_note}` : ''} )} {h.status === 'rejected' && ( 未采纳:{h.reject_reason || '-'} )}
))}
)}
)}
); }