import React, { useEffect, useState } from 'react'; import { Button, Card, Input, Space, Table, Tag, Typography, message } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import { ReloadOutlined, SearchOutlined } from '@ant-design/icons'; import { adminGetPrivatePortraitAssets, adminGetPrivatePortraitProjects } from '../api'; import type { PrivatePortraitAsset, PrivatePortraitProject } from '../types'; const { Title, Text } = Typography; const statusColor = (status?: string) => { if (status === 'Active' || status === 'active') return 'green'; if (status === 'Processing') return 'blue'; if (status === 'Failed' || status === 'failed' || status === 'delete_failed') return 'red'; if (status?.includes('deleted')) return 'default'; return 'default'; }; const AdminPrivatePortraitProjects: React.FC = () => { const [projects, setProjects] = useState([]); const [assets, setAssets] = useState([]); const [projectTotal, setProjectTotal] = useState(0); const [assetTotal, setAssetTotal] = useState(0); const [loadingProjects, setLoadingProjects] = useState(false); const [loadingAssets, setLoadingAssets] = useState(false); const [keyword, setKeyword] = useState(''); const [selectedProjectId, setSelectedProjectId] = useState(); const [projectPage, setProjectPage] = useState(1); const [assetPage, setAssetPage] = useState(1); const loadProjects = async () => { setLoadingProjects(true); try { const res = await adminGetPrivatePortraitProjects({ keyword: keyword.trim() || undefined, page: projectPage, pageSize: 20 }); setProjects(res.items || []); setProjectTotal(res.total || 0); } catch (err: any) { message.error(err?.message || '加载真人素材项目失败'); } finally { setLoadingProjects(false); } }; const loadAssets = async () => { setLoadingAssets(true); try { const res = await adminGetPrivatePortraitAssets({ projectId: selectedProjectId, keyword: keyword.trim() || undefined, page: assetPage, pageSize: 20 }); setAssets(res.items || []); setAssetTotal(res.total || 0); } catch (err: any) { message.error(err?.message || '加载真人素材失败'); } finally { setLoadingAssets(false); } }; useEffect(() => { loadProjects(); }, [projectPage]); useEffect(() => { loadAssets(); }, [selectedProjectId, assetPage]); const projectColumns: ColumnsType = [ { title: '项目名称', dataIndex: 'name', width: 180, render: (v, r) => }, { title: '用户ID', dataIndex: 'userId', width: 170, render: (v) => {v || '-'} }, { title: '状态', dataIndex: 'status', width: 100, render: (v) => {v} }, { title: 'ProjectName', dataIndex: 'remoteProjectName', width: 240, render: (v) => {v || '-'} }, { title: '素材数', dataIndex: 'assetCount', width: 100 }, { title: 'Active', dataIndex: 'activeAssetCount', width: 100 }, { title: '创建时间', dataIndex: 'createdAt', width: 170, render: (v) => v || '-' }, ]; const assetColumns: ColumnsType = [ { title: '素材', dataIndex: 'name', width: 180, render: (v, r) => v || r.remoteAssetId || '-' }, { title: '用户ID', dataIndex: 'userId', width: 170, render: (v) => {v || '-'} }, { title: '本地项目', dataIndex: 'projectName', width: 160 }, { title: 'AssetId', dataIndex: 'remoteAssetId', width: 240, render: (v) => {v} }, { title: 'GroupId', dataIndex: 'remoteGroupId', width: 240, render: (v) => {v} }, { title: 'ProjectName', dataIndex: 'remoteProjectName', width: 240, render: (v) => {v || '-'} }, { title: '类型', dataIndex: 'assetType', width: 90 }, { title: '状态', dataIndex: 'status', width: 110, render: (v) => {v} }, { title: '错误', dataIndex: 'errorMessage', width: 240, ellipsis: true, render: (v) => v || '-' }, { title: '创建时间', dataIndex: 'createdAt', width: 170, render: (v) => v || '-' }, ]; return (
真人素材库 只读排查页:查看用户项目组、Asset Group 与 Asset 状态。
} placeholder="搜索项目/素材" value={keyword} onChange={(e) => setKeyword(e.target.value)} onPressEnter={() => { setProjectPage(1); setAssetPage(1); loadProjects(); loadAssets(); }} style={{ width: 260 }} />
setSelectedProjectId(undefined)}>查看全部 : null} >
); }; export default AdminPrivatePortraitProjects;