import React, { useMemo, useState, useEffect } from 'react'; import { Card, Col, Row, Tabs, Typography, message } from 'antd'; import { useSearchParams } from 'react-router-dom'; import { getPrivatePortraitConfig, getPrivatePortraitProjects, getPrivatePortraitVirtualConfig, getPrivatePortraitVirtualProjects } from '../../../api'; import type { PrivatePortraitConfig, PrivatePortraitProject } from '../../../types'; import RealPersonLibraryPanel from './RealPersonLibraryPanel'; import VirtualMaterialPanel from './VirtualMaterialPanel'; const { Title, Text, Paragraph } = Typography; type PrivatePortraitTabKey = 'real_person' | 'aigc_virtual'; const normalizeTabKey = (value?: string | null): PrivatePortraitTabKey => ( value === 'aigc_virtual' ? 'aigc_virtual' : 'real_person' ); const PrivatePortraitLibraryPanel: React.FC = () => { const [searchParams, setSearchParams] = useSearchParams(); const [activeKey, setActiveKey] = useState(() => normalizeTabKey(searchParams.get('portraitTab'))); const [config, setConfig] = useState(null); const [projects, setProjects] = useState([]); const [selectedProjectId, setSelectedProjectId] = useState(); useEffect(() => { const loadData = async () => { try { if (activeKey === 'aigc_virtual') { const [configRes, projectsRes] = await Promise.all([ getPrivatePortraitVirtualConfig(), getPrivatePortraitVirtualProjects({ page: 1, pageSize: 100, status: 'active' }), ]); setConfig(configRes); const projectList = projectsRes.items || []; setProjects(projectList); setSelectedProjectId((prev) => prev && projectList.some((item) => item.id === prev) ? prev : projectList[0]?.id); } else { const [configRes, projectsRes] = await Promise.all([ getPrivatePortraitConfig(), getPrivatePortraitProjects({ pageSize: 100, status: 'active' }), ]); setConfig(configRes); const projectList = projectsRes.items || []; setProjects(projectList); setSelectedProjectId((prev) => prev && projectList.some((item) => item.id === prev) ? prev : projectList[0]?.id); } } catch (err: any) { message.error(err?.message || '加载数据失败'); } }; void loadData(); }, [activeKey]); const selectedProject = useMemo( () => projects.find((item) => item.id === selectedProjectId) || null, [projects, selectedProjectId], ); const quotaText = useMemo(() => { if (!config) return '额度加载中'; return `已用 ${config.usedAssetCount || 0} / ${config.assetLimit || 0} 个素材,剩余 ${config.remainingAssetCount || 0}`; }, [config]); const items = useMemo(() => [ { key: 'real_person', label: '真人素材库', children: , }, { key: 'aigc_virtual', label: '虚拟素材库', children: , }, ], []); const handleTabChange = (key: string) => { const nextKey = normalizeTabKey(key); setActiveKey(nextKey); const nextParams = new URLSearchParams(searchParams); nextParams.set('filterType', 'private_portrait'); nextParams.set('portraitTab', nextKey); setSearchParams(nextParams, { replace: true }); }; return (
{/*
私域素材库 统一管理真人素材和虚拟素材。真人素材需先完成人脸认证后才可上传素材,虚拟素材如需使用需先上传。
*/} {/* 素材总额度
{quotaText}
真人/虚拟共用,图片/视频共用;音频暂不开放。
项目组
{projects.length}
{activeKey === 'aigc_virtual' ? '虚拟素材如需使用需先上传' : '真人项目组需完成人脸认证后才可上传素材。'}
当前项目素材
{selectedProject?.assetCount || 0}
仅 入库成功 状态素材可在 AI 创作中引用。
*/} {quotaText}。真人/虚拟共用,图片/视频共用;音频暂不开放。
} /> ); }; export default PrivatePortraitLibraryPanel;