import React, { useEffect, useRef, useState } from 'react'; import { Button, Card, Col, Empty, Form, Input, Modal, QRCode, Row, Space, Spin, Typography, message } from 'antd'; import { CheckCircleOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons'; import type { PrivatePortraitProject, PrivatePortraitValidateSession } from '../../../types'; import { createPrivatePortraitProject, getPrivatePortraitProjects, getPrivatePortraitValidateSession } from '../../../api'; import PrivatePortraitProjectList from './ProjectList'; import PrivatePortraitProjectDetail from './ProjectDetail'; const VALIDATE_SUCCESS_STATUS = 'group_active'; const POLL_INTERVAL_FALLBACK = 2000; const RealPersonLibraryPanel: React.FC = () => { const [projects, setProjects] = useState([]); const [selected, setSelected] = useState(null); const [loading, setLoading] = useState(false); const [createOpen, setCreateOpen] = useState(false); const [creating, setCreating] = useState(false); const [createdProject, setCreatedProject] = useState(null); const [validateSession, setValidateSession] = useState(null); const [polling, setPolling] = useState(false); const [form] = Form.useForm(); const timerRef = useRef(null); const clearPollTimer = () => { if (timerRef.current) { window.clearInterval(timerRef.current); timerRef.current = null; } }; const resetCreateModal = () => { clearPollTimer(); setCreateOpen(false); setCreating(false); setPolling(false); setCreatedProject(null); setValidateSession(null); form.resetFields(); }; const loadProjects = async () => { setLoading(true); try { const res = await getPrivatePortraitProjects({ pageSize: 100, status: 'active' }); setProjects(res.items); setSelected((prev) => prev ? (res.items.find((item) => item.id === prev.id) || res.items[0] || null) : (res.items[0] || null)); } catch (e: any) { message.error(e?.message || '加载真人素材项目组失败'); } finally { setLoading(false); } }; useEffect(() => { loadProjects(); }, []); useEffect(() => () => clearPollTimer(), []); const finishCreateSuccess = async (projectId: string) => { clearPollTimer(); setPolling(false); message.success('真人认证完成,项目组已创建成功'); setCreateOpen(false); setValidateSession(null); setCreatedProject(null); form.resetFields(); const res = await getPrivatePortraitProjects({ pageSize: 100, status: 'active' }); setProjects(res.items); setSelected(res.items.find((item) => item.id === projectId) || res.items[0] || null); }; const startPolling = (sessionId: string, projectId: string, intervalMs: number) => { clearPollTimer(); setPolling(true); const run = async () => { try { const next = await getPrivatePortraitValidateSession(sessionId); setValidateSession(next); if (next.status === VALIDATE_SUCCESS_STATUS) { await finishCreateSuccess(projectId); return; } if (['callback_failed', 'failed', 'expired'].includes(next.status)) { clearPollTimer(); setPolling(false); message.error(next.errorMessage || '真人认证未完成,请重新创建项目组'); } } catch (e: any) { clearPollTimer(); setPolling(false); message.error(e?.message || '轮询真人认证状态失败'); } }; timerRef.current = window.setInterval(run, Math.max(1000, intervalMs || POLL_INTERVAL_FALLBACK)); void run(); }; const handleCreate = async () => { const values = await form.validateFields(); setCreating(true); try { const callbackRedirectUrl = `${window.location.origin}/private-portrait-authorized`; const res = await createPrivatePortraitProject({ ...values, callbackRedirectUrl }); setCreatedProject(res.project); setValidateSession(res.validateSession); message.success('请使用手机扫码完成人脸认证'); if (res.validateSession?.id) { startPolling(res.validateSession.id, res.project.id, res.pollIntervalMs || POLL_INTERVAL_FALLBACK); } } catch (e: any) { message.error(e?.message || '创建项目组认证二维码失败'); } finally { setCreating(false); } }; const h5Link = validateSession?.h5Link || ''; const isSuccess = validateSession?.status === VALIDATE_SUCCESS_STATUS; return (
真人素材库 创建项目组时先完成真人认证,认证成功后项目组才会正式创建并可上传素材。
{projects.length === 0 ? ( ) : ( )} {selected ? ( { setSelected(null); loadProjects(); }} onChanged={loadProjects} /> ) : ( 请先创建或选择一个真人素材项目组 )} 关闭] : undefined} onOk={validateSession ? undefined : handleCreate} okText="开始认证并创建" confirmLoading={creating} maskClosable={!polling} > {!validateSession ? (
点击后会生成真人认证二维码。手机扫码认证成功后,项目组才会出现在项目列表中。
) : ( {isSuccess ? ( ) : h5Link ? ( ) : ( )}
{createdProject?.name || '真人素材项目组'} {isSuccess ? '认证成功,项目组正在刷新' : '请使用手机扫码完成人脸认证,成功后回到电脑端查看项目组。'}
{/* {h5Link && !isSuccess && {h5Link}} */}
)}
); }; export default RealPersonLibraryPanel;