/** * P18 待审核任务页面。 * * 展示 status='pending_review' 的任务,支持重新分配和关闭操作。 */ import React, { useEffect, useState, useCallback } from "react"; import { Table, Card, Typography, Button, Space, InputNumber, Tag, Tooltip, Modal, Input, Drawer, message, } from "antd"; import { ReloadOutlined, ExclamationCircleOutlined, AuditOutlined, SwapOutlined, CloseCircleOutlined, } from "@ant-design/icons"; import type { ColumnsType } from "antd/es/table"; import dayjs from "dayjs"; import { fetchPendingReviews, reassignTask, closeTask, fetchMemberTransferHistory, type PendingReviewItem, type PendingReviewQuery, type TransferLogItem, } from "../api/taskEngine"; import { useAuthStore } from "../store/authStore"; const { Title, Text } = Typography; const { TextArea } = Input; function formatTime(raw: string | null): string { if (!raw) return "—"; return dayjs(raw).format("YYYY-MM-DD HH:mm"); } const PendingReview: React.FC = () => { const user = useAuthStore((s) => s.user); const isSuperAdmin = user?.roles?.includes("super_admin") ?? false; const [items, setItems] = useState([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(false); const [query, setQuery] = useState({ page: 1, page_size: 20 }); // 重新分配弹窗 const [reassignVisible, setReassignVisible] = useState(false); const [reassignTaskId, setReassignTaskId] = useState(null); const [toAssistantId, setToAssistantId] = useState(null); const [reassigning, setReassigning] = useState(false); // 关闭弹窗 const [closeVisible, setCloseVisible] = useState(false); const [closeTaskId, setCloseTaskId] = useState(null); const [closeReason, setCloseReason] = useState(""); const [closing, setClosing] = useState(false); // 转移历史抽屉 const [historyVisible, setHistoryVisible] = useState(false); const [historyMemberId, setHistoryMemberId] = useState(null); const [historyItems, setHistoryItems] = useState([]); const [historyLoading, setHistoryLoading] = useState(false); const load = useCallback(async () => { setLoading(true); try { const data = await fetchPendingReviews(query); setItems(data.items); setTotal(data.total); } catch { message.error("加载待审核任务失败"); } finally { setLoading(false); } }, [query]); useEffect(() => { load(); }, [load]); const handleReassign = async () => { if (!reassignTaskId || !toAssistantId) return; setReassigning(true); try { await reassignTask(reassignTaskId, toAssistantId); message.success("重新分配成功"); setReassignVisible(false); setToAssistantId(null); load(); } catch { message.error("重新分配失败"); } finally { setReassigning(false); } }; const handleClose = async () => { if (!closeTaskId || !closeReason.trim()) return; setClosing(true); try { await closeTask(closeTaskId, closeReason.trim()); message.success("任务已关闭"); setCloseVisible(false); setCloseReason(""); load(); } catch { message.error("关闭任务失败"); } finally { setClosing(false); } }; const showHistory = async (memberId: number) => { setHistoryMemberId(memberId); setHistoryVisible(true); setHistoryLoading(true); try { const data = await fetchMemberTransferHistory(memberId); setHistoryItems(data); } catch { message.error("加载转移历史失败"); } finally { setHistoryLoading(false); } }; const columns: ColumnsType = [ { title: "创建时间", dataIndex: "created_at", key: "created_at", width: 160, render: (v: string) => formatTime(v), }, { title: "门店", dataIndex: "site_name", key: "site_name", width: 120, render: (v: string, r) => v || `#${r.site_id}`, }, { title: "客户", key: "member", width: 140, render: (_: unknown, r) => ( showHistory(r.member_id)}> {r.member_name || `会员#${r.member_id}`} ), }, { title: "当前助教", key: "assistant", width: 120, render: (_: unknown, r) => r.assistant_name || `#${r.assistant_id}`, }, { title: "任务类型", dataIndex: "task_type_label", key: "type", width: 120, render: (v: string) => {v || "未知"}, }, { title: "转移次数", dataIndex: "transfer_count", key: "tc", width: 90, render: (v: number) => ( = 2 ? "red" : "default"} icon={v >= 2 ? : undefined}> {v} ), }, { title: "优先级分", dataIndex: "priority_score", key: "score", width: 90, render: (v: number | null) => v != null ? v.toFixed(2) : "—", }, ]; // 超级管理员才显示操作列 if (isSuperAdmin) { columns.push({ title: "操作", key: "action", width: 180, fixed: "right", render: (_: unknown, r) => ( ), }); } return (
<AuditOutlined style={{ marginRight: 8 }} /> 待审核任务
setQuery((q) => ({ ...q, site_id: (v as number) ?? undefined, page: 1 }))} /> rowKey="id" columns={columns} dataSource={items} loading={loading} size="small" scroll={{ x: 1100 }} pagination={{ current: query.page, pageSize: query.page_size, total, showSizeChanger: true, showTotal: (t) => `共 ${t} 条`, onChange: (page, pageSize) => setQuery((q) => ({ ...q, page, page_size: pageSize })), }} /> {/* 重新分配弹窗 */} { setReassignVisible(false); setToAssistantId(null); }} confirmLoading={reassigning} okButtonProps={{ disabled: !toAssistantId }} > 请输入目标助教 ID: setToAssistantId(v)} /> 提示:POOL 为空时无法自动推荐候选助教。如需强制指定,操作将标记为 manual_override。 {/* 关闭任务弹窗 */} { setCloseVisible(false); setCloseReason(""); }} confirmLoading={closing} okButtonProps={{ disabled: !closeReason.trim(), danger: true }} okText="确认关闭" > 请填写关闭原因: