生成任务兼容图片类型

This commit is contained in:
2026-05-27 13:26:28 +08:00
parent ec89a7d87e
commit 6d76d36522
4 changed files with 280 additions and 54 deletions
+2 -2
View File
@@ -298,7 +298,7 @@ export async function adminUpdateGenerationStatus(
}
export async function adminGenerateVideo(
recordId: string, aspectRatio: string, resolution: string
recordId: string, aspectRatio: string, resolution: string, image_size: string
): Promise<void> {
await api.post(`/admin/generation-records/${recordId}/generate`, { aspect_ratio: aspectRatio, resolution });
await api.post(`/admin/generation-records/${recordId}/generate`, { aspect_ratio: aspectRatio, resolution, image_size });
}
@@ -5,7 +5,7 @@ import {
import {
PlayCircleOutlined, EyeOutlined, ClockCircleOutlined, CheckCircleOutlined,
LoadingOutlined, CloseCircleOutlined, SearchOutlined, VideoCameraOutlined,
ExclamationCircleOutlined,
ExclamationCircleOutlined, FileImageOutlined
} from '@ant-design/icons';
import { getAdminGenerationRecords, adminUpdateGenerationStatus, adminGenerateVideo } from '../api';
import type { AdminGenerationRecord } from '../types';
@@ -32,7 +32,7 @@ const AdminGenerationRecords: React.FC = () => {
const [filterUserId, setFilterUserId] = useState<string>('');
const [preview, setPreview] = useState<AdminGenerationRecord | null>(null);
const [updating, setUpdating] = useState<string | null>(null);
const [genModal, setGenModal] = useState<{ record: AdminGenerationRecord; ratio: string; resolution: string } | null>(null);
const [genModal, setGenModal] = useState<{ record: AdminGenerationRecord; ratio: string; resolution: string; image_size: string } | null>(null);
const load = async () => {
setLoading(true);
@@ -63,7 +63,12 @@ const AdminGenerationRecords: React.FC = () => {
videoTokensUsed: item.videoTokensUsed || 0,
errorMessage: item.errorMessage,
createdAt: item.createdAt,
generatedAt: item.generatedAt,
genType: item.genType,
imageSize: item.imageSize,
imageUrl: item.imageUrl,
imageTokensUsed: item.imageTokensUsed,
imageProportion: item.imageProportion,
imagePx: item.imagePx,
})));
setTotal(res.total);
} catch {
@@ -92,7 +97,7 @@ const AdminGenerationRecords: React.FC = () => {
if (!genModal) return;
setUpdating(genModal.record.id);
try {
await adminGenerateVideo(genModal.record.id, genModal.ratio, genModal.resolution);
await adminGenerateVideo(genModal.record.id, genModal.ratio, genModal.resolution, genModal.image_size);
message.success('已提交视频生成');
setGenModal(null);
load();
@@ -117,6 +122,12 @@ const AdminGenerationRecords: React.FC = () => {
title: '项目', dataIndex: 'projectName', width: 120, ellipsis: true,
render: (v: string) => <Typography.Text style={{ fontSize: 13 }}>{v}</Typography.Text>,
},
{
title: '类型', dataIndex: 'genType', width: 60, ellipsis: true,
render: (v: string) => <Typography.Text style={{ fontSize: 13 }}>
{v==='video' ? "视频" : "图片"}
</Typography.Text>,
},
{
title: '提示词', key: 'prompt', ellipsis: true,
render: (_: any, r: AdminGenerationRecord) => (
@@ -130,13 +141,22 @@ const AdminGenerationRecords: React.FC = () => {
{
title: '参数', key: 'params', width: 140,
render: (_: any, r: AdminGenerationRecord) => (
r.duration ? (
<Space size={4} wrap>
<Tag>{r.duration}s</Tag>
<Tag>{r.aspectRatio}</Tag>
<Tag>{r.resolution}</Tag>
</Space>
) : <Tag color="default"></Tag>
r.genType === 'video'?
(r.duration ? (
<Space size={4} wrap>
<Tag>{r.duration}s</Tag>
<Tag>{r.aspectRatio}</Tag>
<Tag>{r.resolution}</Tag>
</Space>
) : <Tag color="default"></Tag>)
:
(r.imageSize ? (
<Space size={4} wrap>
<Tag>{r.imageSize}</Tag>
<Tag>{r.imageProportion}</Tag>
<Tag>{r.imagePx}</Tag>
</Space>
) : <Tag color="default"></Tag>)
),
},
{
@@ -147,7 +167,7 @@ const AdminGenerationRecords: React.FC = () => {
<div style={{ color: '#f59e0b' }}>: {r.textCreditsCost}</div>
)}
{r.creditsCost > 0 && (
<div style={{ color: '#6366f1' }}>: {r.creditsCost}</div>
<div style={{ color: '#6366f1' }}>{r.genType === 'video'? "视频" : "图片"}: {r.creditsCost}</div>
)}
{r.textCreditsCost === 0 && r.creditsCost === 0 && (
<Typography.Text style={{ color: '#94a3b8' }}>0</Typography.Text>
@@ -193,16 +213,16 @@ const AdminGenerationRecords: React.FC = () => {
)}
{r.status === 'failed' && (
<Button size="small" type="primary" danger loading={updating === r.id}
onClick={() => setGenModal({ record: r, ratio: r.aspectRatio || '16:9', resolution: r.resolution || '720p' })}>
onClick={() => setGenModal({ record: r, ratio: r.aspectRatio || '16:9', resolution: r.resolution || '720p', image_size: r.imageSize })}>
</Button>
)}
{r.status === 'prompt_optimized' && (
<>
<Button size="small" type="primary" loading={updating === r.id}
onClick={() => setGenModal({ record: r, ratio: '16:9', resolution: '720p' })}
onClick={() => setGenModal({ record: r, ratio: '16:9', resolution: '720p', image_size: r.imageSize })}
style={{ background: '#6366f1', border: 'none' }}>
{r.genType === 'video'? "视频" : "图片"}
</Button>
<Button size="small" danger loading={updating === r.id}
onClick={() => {
@@ -328,7 +348,7 @@ const AdminGenerationRecords: React.FC = () => {
<Typography.Text style={{ fontSize: 11, color: '#94a3b8' }}> ({preview.textTokensUsed} tokens)</Typography.Text>
</div>
<div style={{ flex: 1 }}>
<Typography.Text style={{ fontSize: 11, color: '#94a3b8', display: 'block' }}></Typography.Text>
<Typography.Text style={{ fontSize: 11, color: '#94a3b8', display: 'block' }}>{preview.genType === 'image'? '图片' : '视频'}</Typography.Text>
<Typography.Text strong style={{ fontSize: 14, color: '#6366f1' }}>{preview.creditsCost}</Typography.Text>
{preview.videoTokensUsed > 0 && (
<Typography.Text style={{ fontSize: 11, color: '#94a3b8' }}> ({preview.videoTokensUsed} tokens)</Typography.Text>
@@ -341,23 +361,47 @@ const AdminGenerationRecords: React.FC = () => {
</div>
{/* Video Params */}
{preview.duration ? (
<div style={{ display: 'flex', gap: 16, padding: 12, borderRadius: 10, background: '#f8f9fc' }}>
{[
{ label: '时长', value: `${preview.duration}` },
{ label: '比例', value: preview.aspectRatio },
{ label: '分辨率', value: preview.resolution },
].map((item, i) => (
<div key={i} style={{ flex: 1 }}>
<Typography.Text style={{ fontSize: 11, color: '#94a3b8', display: 'block' }}>{item.label}</Typography.Text>
<Typography.Text strong style={{ fontSize: 14 }}>{item.value}</Typography.Text>
</div>
))}
</div>
) : (
<div style={{ padding: 12, borderRadius: 10, background: '#f8f9fc', textAlign: 'center' }}>
<Tag color="default"></Tag>
</div>
{preview.genType === 'video' && (
preview.duration ? (
<div style={{ display: 'flex', gap: 16, padding: 12, borderRadius: 10, background: '#f8f9fc' }}>
{[
{ label: '时长', value: `${preview.duration}` },
{ label: '比例', value: preview.aspectRatio },
{ label: '分辨率', value: preview.resolution },
].map((item, i) => (
<div key={i} style={{ flex: 1 }}>
<Typography.Text style={{ fontSize: 11, color: '#94a3b8', display: 'block' }}>{item.label}</Typography.Text>
<Typography.Text strong style={{ fontSize: 14 }}>{item.value}</Typography.Text>
</div>
))}
</div>
) : (
<div style={{ padding: 12, borderRadius: 10, background: '#f8f9fc', textAlign: 'center' }}>
<Tag color="default"></Tag>
</div>
)
)}
{/* Image Params */}
{preview.genType === 'image' && (
preview.imageSize ? (
<div style={{ display: 'flex', gap: 16, padding: 12, borderRadius: 10, background: '#f8f9fc' }}>
{[
{ label: '尺寸', value: preview.imagePx },
{ label: '比例', value: preview.imageProportion },
{ label: '分辨率', value: preview.imageSize },
].map((item, i) => (
<div key={i} style={{ flex: 1 }}>
<Typography.Text style={{ fontSize: 11, color: '#94a3b8', display: 'block' }}>{item.label}</Typography.Text>
<Typography.Text strong style={{ fontSize: 14 }}>{item.value}</Typography.Text>
</div>
))}
</div>
) : (
<div style={{ padding: 12, borderRadius: 10, background: '#f8f9fc', textAlign: 'center' }}>
<Tag color="default"></Tag>
</div>
)
)}
{/* Reference images */}
@@ -398,6 +442,18 @@ const AdminGenerationRecords: React.FC = () => {
</div>
)}
{/* Image preview */}
{preview.status === 'completed' && preview.imageUrl && preview.genType === 'image' && (
<div>
<Typography.Text style={{ fontSize: 12, color: '#94a3b8', display: 'block', marginBottom: 6 }}></Typography.Text>
<img
// src={apiUrl(preview.imageUrl)}
src={`http://ceshi.apiforeign.minzhong.cn${preview.imageUrl}`}
style={{ width: '100%', minHeight: 100, borderRadius: 12, background: '#000' }}
/>
</div>
)}
{/* Error message */}
{preview.status === 'failed' && preview.errorMessage && (
<div style={{ padding: 12, borderRadius: 10, background: 'rgba(239,68,68,0.04)', border: '1px solid rgba(239,68,68,0.15)' }}>
@@ -416,7 +472,7 @@ const AdminGenerationRecords: React.FC = () => {
{/* Generate modal */}
<Modal
title={<Space><PlayCircleOutlined /></Space>}
title={<Space>{genModal && (genModal.record.genType === 'video' ? <PlayCircleOutlined /> : <FileImageOutlined />)}{genModal && (genModal.record.genType === 'video' ? '生成视频' : '生成图片')}</Space>}
open={!!genModal}
onCancel={() => setGenModal(null)}
onOk={handleGenerate}
@@ -427,24 +483,45 @@ const AdminGenerationRecords: React.FC = () => {
>
{genModal && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, marginTop: 16 }}>
<div style={{ padding: 12, borderRadius: 10, background: '#f8f9fc' }}>
<Typography.Text style={{ fontSize: 11, color: '#94a3b8', display: 'block' }}></Typography.Text>
<Typography.Text strong>{genModal.record.duration || 5}s</Typography.Text>
</div>
<div>
<Typography.Text style={{ fontSize: 12, color: '#64748b', display: 'block', marginBottom: 6 }}></Typography.Text>
<Select value={genModal.ratio} onChange={(v) => setGenModal(prev => prev ? { ...prev, ratio: v } : null)}
style={{ width: '100%' }}
options={['16:9', '4:3', '1:1', '3:4', '9:16', '21:9'].map(r => ({ value: r, label: r }))}
/>
</div>
<div>
<Typography.Text style={{ fontSize: 12, color: '#64748b', display: 'block', marginBottom: 6 }}></Typography.Text>
<Select value={genModal.resolution} onChange={(v) => setGenModal(prev => prev ? { ...prev, resolution: v } : null)}
style={{ width: '100%' }}
options={['480p', '720p', '1080p'].map(r => ({ value: r, label: r }))}
/>
</div>
{genModal.record.genType === 'video' ?
<>
<div style={{ padding: 12, borderRadius: 10, background: '#f8f9fc' }}>
<Typography.Text style={{ fontSize: 11, color: '#94a3b8', display: 'block' }}></Typography.Text>
<Typography.Text strong>{genModal.record.duration || 5}s</Typography.Text>
</div>
<div>
<Typography.Text style={{ fontSize: 12, color: '#64748b', display: 'block', marginBottom: 6 }}></Typography.Text>
<Select value={genModal.ratio} onChange={(v) => setGenModal(prev => prev ? { ...prev, ratio: v } : null)}
style={{ width: '100%' }}
options={['16:9', '4:3', '1:1', '3:4', '9:16', '21:9'].map(r => ({ value: r, label: r }))}
/>
</div>
<div>
<Typography.Text style={{ fontSize: 12, color: '#64748b', display: 'block', marginBottom: 6 }}></Typography.Text>
<Select value={genModal.resolution} onChange={(v) => setGenModal(prev => prev ? { ...prev, resolution: v } : null)}
style={{ width: '100%' }}
options={['480p', '720p', '1080p'].map(r => ({ value: r, label: r }))}
/>
</div>
</> :
<>
<div style={{ padding: 12, borderRadius: 10, background: '#f8f9fc' }}>
<Typography.Text style={{ fontSize: 11, color: '#94a3b8', display: 'block' }}></Typography.Text>
<Typography.Text strong>{genModal.record.imagePx}</Typography.Text>
</div>
<div style={{ padding: 12, borderRadius: 10, background: '#f8f9fc' }}>
<Typography.Text style={{ fontSize: 11, color: '#94a3b8', display: 'block' }}></Typography.Text>
<Typography.Text strong>{genModal.record.imageProportion}</Typography.Text>
</div>
<div>
<Typography.Text style={{ fontSize: 12, color: '#64748b', display: 'block', marginBottom: 6 }}></Typography.Text>
<Select value={genModal.image_size} onChange={(v) => setGenModal(prev => prev ? { ...prev, image_size: v } : null)}
style={{ width: '100%' }}
options={['2K', '4K'].map(r => ({ value: r, label: r }))}
/>
</div>
</> }
</div>
)}
</Modal>
+6
View File
@@ -168,4 +168,10 @@ export interface AdminGenerationRecord {
errorMessage?: string;
createdAt: string;
generatedAt?: string;
genType?: string,
imageSize?: string,
imageUrl?: string,
imageTokensUsed?: number,
imageProportion?: string,
imagePx?: string,
}