1
This commit is contained in:
@@ -120,7 +120,7 @@ const PrivatePortraitLibraryPanel: React.FC = () => {
|
||||
activeKey={activeKey}
|
||||
onChange={handleTabChange}
|
||||
items={items}
|
||||
destroyInactiveTabPane={false}
|
||||
destroyOnHidden={false}
|
||||
tabBarExtraContent={
|
||||
<div style={{ fontSize: 14, fontWeight: 700, color: '#4f46e5', marginTop: 8 }}>{quotaText}。真人/虚拟共用,图片/视频共用;音频暂不开放。</div>
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ interface Props {
|
||||
const PrivatePortraitProjectList: React.FC<Props> = ({ items, selectedId, onSelect }) => {
|
||||
if (!items.length) return <Empty description="暂无项目组" />;
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: '100%' }} size={10}>
|
||||
<Space orientation="vertical" style={{ width: '100%' }} size={10}>
|
||||
{items.map((project) => {
|
||||
const active = selectedId === project.id;
|
||||
return (
|
||||
|
||||
@@ -122,7 +122,14 @@ const RealPersonLibraryPanel: React.FC = () => {
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>真人素材库</Typography.Title>
|
||||
<Typography.Text type="secondary">创建项目组时先完成真人认证,认证成功后项目组才会正式创建并可上传素材。</Typography.Text>
|
||||
</div>
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={() => setCreateOpen(true)}>创建项目组</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => setCreateOpen(true)}
|
||||
style={{ borderRadius: 8, fontWeight: 600, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none' }}
|
||||
>
|
||||
创建项目组
|
||||
</Button>
|
||||
</div>
|
||||
<Row gutter={16}>
|
||||
<Col xs={24} lg={5}>
|
||||
@@ -155,7 +162,7 @@ const RealPersonLibraryPanel: React.FC = () => {
|
||||
onOk={validateSession ? undefined : handleCreate}
|
||||
okText="开始认证并创建"
|
||||
confirmLoading={creating}
|
||||
maskClosable={!polling}
|
||||
mask={{ closable: !polling }}
|
||||
>
|
||||
{!validateSession ? (
|
||||
<Form form={form} layout="vertical">
|
||||
|
||||
@@ -338,7 +338,7 @@ const VirtualMaterialPanel: React.FC = () => {
|
||||
<Card
|
||||
key={asset.id}
|
||||
hoverable
|
||||
bodyStyle={{ padding: 12 }}
|
||||
styles={{ body: { padding: 12 } }}
|
||||
style={{ borderRadius: 16, overflow: 'hidden', borderColor: '#eef2f7' }}
|
||||
cover={(
|
||||
<div style={{ height: 170, background: '#f8fafc', display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative' }}>
|
||||
@@ -392,7 +392,14 @@ const VirtualMaterialPanel: React.FC = () => {
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>虚拟素材库</Typography.Title>
|
||||
<Typography.Text type="secondary">可提前上传虚拟人像素材,后续生成视频时可直接使用。</Typography.Text>
|
||||
</div>
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={() => setCreateOpen(true)}>创建项目组</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => setCreateOpen(true)}
|
||||
style={{ borderRadius: 8, fontWeight: 600, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none' }}
|
||||
>
|
||||
创建项目组
|
||||
</Button>
|
||||
</div>
|
||||
<Row gutter={[10, 10]}>
|
||||
<Col xs={24} lg={5} >
|
||||
@@ -446,11 +453,24 @@ const VirtualMaterialPanel: React.FC = () => {
|
||||
title={selectedProject ? selectedProject.name : '素材资产'}
|
||||
extra={(
|
||||
<Space wrap>
|
||||
{/* <Button icon={<ReloadOutlined />} onClick={() => { loadProjects(); loadAssets(assetPage, assetPageSize); }} loading={assetLoading}>刷新</Button> */}
|
||||
<Button type="primary" icon={<UploadOutlined />} disabled={!selectedProjectId} onClick={() => setUploadOpen(true)}>上传图片/视频</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<UploadOutlined />}
|
||||
disabled={!selectedProjectId}
|
||||
onClick={() => setUploadOpen(true)}
|
||||
style={{ borderRadius: 8, fontWeight: 600, borderColor: 'transparent' }}
|
||||
>
|
||||
上传图片/视频
|
||||
</Button>
|
||||
{selectedProjectId && (
|
||||
<Popconfirm title="确认删除当前虚拟人像项目组吗?" onConfirm={handleDeleteProject}>
|
||||
<Button danger icon={<DeleteOutlined />}>删除项目组</Button>
|
||||
<Button
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
style={{ borderRadius: 8, fontWeight: 600 }}
|
||||
>
|
||||
删除项目组
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
)}
|
||||
</Space>
|
||||
@@ -464,14 +484,15 @@ const VirtualMaterialPanel: React.FC = () => {
|
||||
value={keyword}
|
||||
onChange={(e) => setKeyword(e.target.value)}
|
||||
onSearch={() => loadAssets(1, assetPageSize)}
|
||||
style={{ width: 240 }}
|
||||
style={{ width: 240, borderRadius: 8 }}
|
||||
enterButton
|
||||
/>
|
||||
<Select
|
||||
allowClear
|
||||
placeholder="素材状态"
|
||||
value={assetStatus}
|
||||
onChange={(value) => setAssetStatus(value)}
|
||||
style={{ width: 150 }}
|
||||
style={{ width: 140, borderRadius: 8 }}
|
||||
options={[
|
||||
{ value: 'Processing', label: '处理中' },
|
||||
{ value: 'Active', label: '可用' },
|
||||
@@ -483,13 +504,19 @@ const VirtualMaterialPanel: React.FC = () => {
|
||||
placeholder="素材类型"
|
||||
value={assetType}
|
||||
onChange={(value) => setAssetType(value)}
|
||||
style={{ width: 130 }}
|
||||
style={{ width: 120, borderRadius: 8 }}
|
||||
options={[
|
||||
{ value: 'Image', label: '图片' },
|
||||
{ value: 'Video', label: '视频' },
|
||||
]}
|
||||
/>
|
||||
<Button onClick={() => loadAssets(1, assetPageSize)}>筛选</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => loadAssets(1, assetPageSize)}
|
||||
style={{ borderRadius: 8, fontWeight: 600, borderColor: 'transparent' }}
|
||||
>
|
||||
筛选
|
||||
</Button>
|
||||
</Space>
|
||||
|
||||
<Spin spinning={assetLoading}>
|
||||
@@ -563,7 +590,7 @@ const VirtualMaterialPanel: React.FC = () => {
|
||||
</Space>
|
||||
</Modal>
|
||||
|
||||
<Modal title="素材预览" open={previewOpen} onCancel={() => setPreviewOpen(false)} footer={null} width={760} destroyOnClose>
|
||||
<Modal title="素材预览" open={previewOpen} onCancel={() => setPreviewOpen(false)} footer={null} width={760} destroyOnHidden>
|
||||
<div style={{ minHeight: 420, display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#0f172a', borderRadius: 12, overflow: 'hidden' }}>
|
||||
{previewType === 'Video' ? (
|
||||
<video src={previewUrl} controls autoPlay style={{ maxWidth: '100%', maxHeight: 520 }} />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { Button, Empty, Input, List, Modal, Select, Space, Spin, Tag, Typography, message } from 'antd';
|
||||
import { Button, Empty, Input, Modal, Select, Space, Spin, Tag, Typography, message } from 'antd';
|
||||
import { CheckOutlined, PictureOutlined, ReloadOutlined, SearchOutlined, VideoCameraOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
getPrivatePortraitProjects,
|
||||
@@ -251,33 +251,36 @@ const PrivatePortraitAssetPicker: React.FC<PrivatePortraitAssetPickerProps> = ({
|
||||
<Button size="small" icon={<ReloadOutlined />} onClick={loadProjects} loading={loadingProjects} />
|
||||
</Space>
|
||||
<Spin spinning={loadingProjects}>
|
||||
<List
|
||||
dataSource={projects}
|
||||
locale={{ emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无项目组" /> }}
|
||||
renderItem={(item) => (
|
||||
<List.Item
|
||||
onClick={() => setProjectId(item.id)}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
padding: '10px 12px',
|
||||
borderRadius: 10,
|
||||
marginBottom: 6,
|
||||
border: projectId === item.id ? '1px solid #8b5cf6' : '1px solid transparent',
|
||||
background: projectId === item.id ? '#f5f3ff' : '#fff',
|
||||
}}
|
||||
>
|
||||
<div style={{ width: '100%' }}>
|
||||
<Text strong ellipsis style={{ display: 'block' }}>{item.name}</Text>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
{/* {item.activeAssetCount || 0} */}
|
||||
{typeof item.activeImageAssetCount === 'number' || typeof item.activeVideoAssetCount === 'number'
|
||||
? ` 图 ${item.activeImageAssetCount || 0} / 视 ${item.activeVideoAssetCount || 0}`
|
||||
: ''}
|
||||
</Text>
|
||||
{projects.length === 0 ? (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无项目组" />
|
||||
) : (
|
||||
<div>
|
||||
{projects.map((item) => (
|
||||
<div
|
||||
key={item.id}
|
||||
onClick={() => setProjectId(item.id)}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
padding: '10px 12px',
|
||||
borderRadius: 10,
|
||||
marginBottom: 6,
|
||||
border: projectId === item.id ? '1px solid #8b5cf6' : '1px solid transparent',
|
||||
background: projectId === item.id ? '#f5f3ff' : '#fff',
|
||||
}}
|
||||
>
|
||||
<div style={{ width: '100%' }}>
|
||||
<Text strong ellipsis style={{ display: 'block' }}>{item.name}</Text>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
{/* {item.activeAssetCount || 0} */}
|
||||
{typeof item.activeImageAssetCount === 'number' || typeof item.activeVideoAssetCount === 'number'
|
||||
? ` 图 ${item.activeImageAssetCount || 0} / 视 ${item.activeVideoAssetCount || 0}`
|
||||
: ''}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Spin>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user