爆款/拆镜生成简化3个步骤 | 项目生成可携带附件控制

This commit is contained in:
2026-07-21 14:01:08 +08:00
parent 40efcf55cf
commit 79c09151ba
60 changed files with 4250 additions and 924 deletions
@@ -21,7 +21,7 @@ import {
PlayCircleOutlined,
VideoCameraOutlined,
} from '@ant-design/icons';
import { useNavigate, useParams } from 'react-router-dom';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { getAdminHotOpeningTaskDetail, getAdminShotProjectDetail } from '../api';
import type { ReplicationProjectDetailOut, ReplicationStepOut } from '../types';
import { formatDate } from '../utils/formatDate';
@@ -36,7 +36,7 @@ interface AdminReplicationProjectDetailProps {
moduleType?: ReplicationModuleType;
}
const STEP_ORDER = [
const V1_STEP_ORDER = [
'material_input',
'image_prompt_optimize',
'image_generate',
@@ -44,6 +44,12 @@ const STEP_ORDER = [
'video_generate',
];
const V2_STEP_ORDER = [
'material_input',
'video_prompt_optimize',
'video_generate',
];
const STEP_DESCRIPTIONS: Record<string, string> = {
material_input: '参考素材、项目名称和核心内容点',
image_prompt_optimize: '图片 AI 提词优化结果',
@@ -104,6 +110,8 @@ const renderPromptText = (value?: string | null, empty = '暂无提词') => {
const AdminReplicationProjectDetail: React.FC<AdminReplicationProjectDetailProps> = ({ moduleType = 'shot_replicate' }) => {
const { projectId } = useParams<{ projectId: string }>();
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const flowVersion: 'v1' | 'v2' = searchParams.get('flow_version') === 'v2' ? 'v2' : 'v1';
const [detail, setDetail] = useState<ReplicationProjectDetailOut | null>(null);
const [loading, setLoading] = useState(false);
@@ -112,15 +120,15 @@ const AdminReplicationProjectDetail: React.FC<AdminReplicationProjectDetailProps
setLoading(true);
try {
const res = moduleType === 'hot_opening_replicate'
? await getAdminHotOpeningTaskDetail(projectId)
: await getAdminShotProjectDetail(projectId);
? await getAdminHotOpeningTaskDetail(projectId, flowVersion)
: await getAdminShotProjectDetail(projectId, flowVersion);
setDetail(res);
} catch (e: any) {
message.error(e?.message || '加载复刻项目详情失败');
} finally {
setLoading(false);
}
}, [moduleType, projectId]);
}, [flowVersion, moduleType, projectId]);
useEffect(() => {
load();
@@ -134,8 +142,10 @@ const AdminReplicationProjectDetail: React.FC<AdminReplicationProjectDetailProps
const moduleValue = detail?.module || moduleType;
const moduleName = getModuleLabel(moduleValue);
const isV2 = detail?.flowVersion === 'v2';
const effectiveStepOrder = isV2 ? V2_STEP_ORDER : V1_STEP_ORDER;
const stepItems = useMemo(() => STEP_ORDER.map(code => {
const stepItems = useMemo(() => effectiveStepOrder.map(code => {
const step = stepsByCode[code];
let status: 'wait' | 'process' | 'finish' | 'error' = 'wait';
if (step?.status === 'completed') status = 'finish';
@@ -146,7 +156,7 @@ const AdminReplicationProjectDetail: React.FC<AdminReplicationProjectDetailProps
description: step ? <StatusTag status={step.status} /> : '未创建',
status,
};
}), [stepsByCode]);
}), [effectiveStepOrder, stepsByCode]);
const defaultActiveKeys = useMemo(() => buildDefaultActiveKeys(detail, stepsByCode), [detail, stepsByCode]);
@@ -242,7 +252,7 @@ const AdminReplicationProjectDetail: React.FC<AdminReplicationProjectDetailProps
},
{
key: 'video_prompt_optimize',
label: <StepHeader index={4} stepCode="video_prompt_optimize" step={videoPromptStep} current={detail.currentStepCode === 'video_prompt_optimize'} />,
label: <StepHeader index={isV2 ? 2 : 4} stepCode="video_prompt_optimize" step={videoPromptStep} current={detail.currentStepCode === 'video_prompt_optimize'} />,
children: (
<Space direction="vertical" size={16} style={{ width: '100%' }}>
<Descriptions column={3} bordered size="small">
@@ -265,7 +275,7 @@ const AdminReplicationProjectDetail: React.FC<AdminReplicationProjectDetailProps
},
{
key: 'video_generate',
label: <StepHeader index={5} stepCode="video_generate" step={videoGenerateStep} current={detail.currentStepCode === 'video_generate'} />,
label: <StepHeader index={isV2 ? 3 : 5} stepCode="video_generate" step={videoGenerateStep} current={detail.currentStepCode === 'video_generate'} />,
children: (
<Space direction="vertical" size={16} style={{ width: '100%' }}>
<Descriptions column={3} bordered size="small">
@@ -300,6 +310,9 @@ const AdminReplicationProjectDetail: React.FC<AdminReplicationProjectDetailProps
),
},
];
const visibleCollapseItems = isV2
? collapseItems.filter(item => !['image_prompt_optimize', 'image_generate'].includes(String(item.key)))
: collapseItems;
return (
<div style={{ padding: 24 }}>
@@ -322,6 +335,8 @@ const AdminReplicationProjectDetail: React.FC<AdminReplicationProjectDetailProps
<Descriptions.Item label="用户名">{detail.userName || '-'}</Descriptions.Item>
<Descriptions.Item label="标题">{detail.title || '-'}</Descriptions.Item>
<Descriptions.Item label="当前步骤"><Tooltip title={detail.currentStepCode || ''}>{getStepCodeLabel(detail.currentStepCode)}</Tooltip></Descriptions.Item>
<Descriptions.Item label="流程版本"><Tag color={isV2 ? 'blue' : 'default'}>{String(detail.flowVersion || 'v1').toUpperCase()}</Tag></Descriptions.Item>
<Descriptions.Item label="步骤数量">{detail.stepCount || effectiveStepOrder.length}</Descriptions.Item>
<Descriptions.Item label="状态"><StatusTag status={detail.status} /></Descriptions.Item>
<Descriptions.Item label="创建时间">{safeDate(detail.createdAt)}</Descriptions.Item>
<Descriptions.Item label="更新时间">{safeDate(detail.updatedAt)}</Descriptions.Item>
@@ -336,9 +351,11 @@ const AdminReplicationProjectDetail: React.FC<AdminReplicationProjectDetailProps
<Card title="最终结果预览">
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: 16 }}>
<Card size="small" title="最终图片">
<MediaPreview type="image" url={detail.finalImageUrl || detail.imageGeneration?.resultImageUrl} height={220} emptyDescription="暂无最终图片" />
</Card>
{!isV2 ? (
<Card size="small" title="最终图片">
<MediaPreview type="image" url={detail.finalImageUrl || detail.imageGeneration?.resultImageUrl} height={220} emptyDescription="暂无最终图片" />
</Card>
) : null}
<Card size="small" title="最终视频封面">
<MediaPreview type="image" url={detail.finalVideoCoverUrl || detail.videoGeneration?.resultVideoCoverUrl} height={220} emptyDescription="暂无最终视频封面" />
</Card>
@@ -348,7 +365,7 @@ const AdminReplicationProjectDetail: React.FC<AdminReplicationProjectDetailProps
</div>
</Card>
<Collapse defaultActiveKey={defaultActiveKeys} items={collapseItems} />
<Collapse defaultActiveKey={defaultActiveKeys} items={visibleCollapseItems} />
<Collapse
items={[