拆镜复刻

This commit is contained in:
孙佳艺
2026-06-24 17:36:17 +08:00
parent 7f1b18314e
commit 28c6e2c772
12 changed files with 323 additions and 300 deletions
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

+1 -1
View File
@@ -28,7 +28,7 @@
}
})();
</script>
<script type="module" crossorigin src="/assets/index-DTCz7Wbv.js"></script>
<script type="module" crossorigin src="/assets/index-BwL6llN9.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-eLRg4pQk.css">
</head>
<body>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

@@ -29,7 +29,7 @@ function isRecord(value: unknown): value is Record<string, JsonValue> {
}
function ReadonlyBlock({ value }: { value: JsonValue }) {
const text = stringifySchemaValue(value);
const text = value === 'true' ? '是' : value === 'false' ? '否' : stringifySchemaValue(value);
return shouldUseSchemaTextArea(value) ? (
<TextArea value={text} rows={Math.min(6, Math.max(2, Math.ceil(text.length / 42)))} disabled style={{ borderRadius: 8, color: '#64748b' }} />
) : (
@@ -466,7 +466,7 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
title={title}
open={open}
onCancel={onCancel}
width={980}
width={Math.min(window.innerWidth - 40, 1400)}
destroyOnHidden
footer={[
<Button key="cancel" onClick={onCancel} disabled={loading} style={{ borderRadius: 8, border: '1px solid rgba(99, 102, 241, 0.2)', color: '#6366f1' }}>
@@ -538,12 +538,13 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
<div
style={{
display: 'grid',
gridTemplateColumns: `repeat(${Math.max(frames.length, 1)}, minmax(42px, 1fr))`,
gridTemplateColumns: `repeat(${Math.max(frames.length, 1)}, minmax(0, 1fr))`,
height: 86,
overflow: 'hidden',
borderRadius: 12,
background: '#fff',
position: 'relative',
gap: 1,
}}
>
{frameLoading && frames.length === 0 ? (
@@ -588,10 +589,10 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
<span
style={{
position: 'absolute',
left: 4,
left: 0,
bottom: 3,
color: '#fff',
fontSize: 11,
color: '#ffffffff',
fontSize: 10,
textShadow: '0 1px 3px rgba(0,0,0,.7)',
}}
>
+79 -162
View File
@@ -17,6 +17,8 @@ import {
import bg1 from '../assets/bg1.png';
import bg2 from '../assets/bg2.png';
import text from '../assets/testb.png';
import {
@@ -540,7 +542,7 @@ const AIChatPage: React.FC = () => {
setHeight(width);
};
// ==================== 事处理函数 ====================
// ==================== 事处理函数 ====================
const handleNewChat = () => {
// 生成唯一ID(使用时间戳)
@@ -1006,7 +1008,7 @@ const AIChatPage: React.FC = () => {
style={{
flex: 1,
overflowY: 'auto',
paddingBottom: 24,
// paddingBottom: 24,
paddingRight: 8,
// backgroundImage: `url(${bg1})`,
// backgroundRepeat: 'no-repeat',
@@ -1084,11 +1086,22 @@ const AIChatPage: React.FC = () => {
boxShadow: '0 4px 20px rgba(99, 102, 241, 0.08), 0 1px 3px rgba(0,0,0,0.04)',
position: 'relative',
border: '1px solid rgba(99, 102, 241, 0.06)',
}}
>
<div style={{ margin: 4, fontSize: 11, color: '#999', textAlign: 'left', display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' }}>
<span>{msg.createdAt?.replace('T', ' ').split('.')[0]}</span>
<span style={{
background: msg.genType === 'image' ? 'rgba(34, 197, 94, 0.12)' : 'rgba(249, 115, 22, 0.12)',
color: msg.genType === 'image' ? '#22c55e' : '#f97316',
padding: '2px 8px',
borderRadius: 12,
fontWeight: 500,
fontSize: 11,
marginLeft: 8
}}>
{msg.genType === 'image' ? '图片生成' : '视频生成'}
</span>
</div>
{/* 删除按钮 - 右上角 */}
<div style={{ position: 'absolute', top: 8, right: 8, zIndex: 100 }}>
@@ -1197,174 +1210,78 @@ const AIChatPage: React.FC = () => {
</div> */}
{/* 根据 status 显示不同内容 */}
{/* 生成中 - 显示加载动画 */}
{msg.status === 'generating' && (
<div style={{
height: 200,
marginBottom: 10,
marginTop: 12,
borderRadius: 8,
overflow: 'hidden',
position: 'relative',
background: 'linear-gradient(135deg, #e0e7ff 0%, #f3e8ff 50%, #e0e7ff 100%)',
}}>
<div style={{
position: 'absolute',
top: 12,
left: 12,
display: 'flex',
alignItems: 'center',
gap: 8,
}}>
<div style={{
width: 24,
height: 24,
border: '2px solid #c7d2fe',
borderTopColor: '#6366f1',
borderRadius: '50%',
animation: 'spin 1s linear infinite'
}} />
<span style={{ fontSize: 13, color: '#6366f1' }}>...</span>
</div>
<div style={{
position: 'absolute',
inset: 0,
background: 'linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.3) 50%, transparent 100%)',
animation: 'shimmer 2s infinite'
}} />
</div>
)}
{/* 生成失败 - 显示失败提示 */}
{msg.status === 'failed' && (
<div style={{
height: 200,
marginBottom: 10,
marginTop: 12,
borderRadius: 8,
overflow: 'hidden',
position: 'relative',
backgroundColor: '#fafafa',
border: '1px dashed #e2e8f0',
}}>
<div style={{
position: 'absolute',
inset: 0,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: 12
}}>
<div style={{
width: 48,
height: 48,
borderRadius: '50%',
background: '#fff2f0',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
<WarningOutlined style={{ color: '#ff4d4f', fontSize: 22 }} />
</div>
<span style={{ fontSize: 14, color: '#94a3b8' }}></span>
{/* <span style={{ fontSize: 12, color: '#cbd5e1' }}>请重试</span> */}
</div>
</div>
)}
{/* 已完成 - 显示媒体内容 */}
{msg.status === 'completed' && (
<div style={{ display: 'flex', gridTemplateColumns: 'repeat(auto-fill, minmax(150px, 1fr))', gap: 8, marginBottom: 10, marginTop: 12, width: '100%', }}>
<div
onClick={() => {
setPreviewUrl(msg.genType === 'image' ? msg.imageUrl : msg.videoUrl);
setPreviewType(msg.genType === 'image' ? 'image' : 'video');
setPreviewVisible(true);
}}
style={{ width: '50%', cursor: 'pointer', overflow: 'hidden', borderRadius: 8, position: 'relative', height: 200 }}
>
{msg.genType === 'image' ? (
<img
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}/static${msg.imageUrl}&w=300&p=50`}
alt={msg.name}
style={{ width: '100%', height: '100%', borderRadius: 8, objectFit: 'contain', backgroundColor: '#f1f5f9', transition: 'transform 0.2s' }}
onMouseEnter={(e) => { e.currentTarget.style.transform = 'scale(1.05)'; }}
onMouseLeave={(e) => { e.currentTarget.style.transform = 'scale(1)'; }}
/>
{(msg.status === 'generating' || msg.status === 'failed' || msg.status === 'completed') && (
<div style={{ display: 'flex', gap: 16, marginBottom: 16, marginTop: 16, width: '100%', alignItems: 'flex-start',
}}>
<div style={{ width: '50%', overflow: 'hidden', borderRadius: 12, position: 'relative', height: 220, border: '1px solid rgba(99, 102, 241, 0.1)', boxShadow: '0 4px 12px rgba(99, 102, 241, 0.08)', display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'linear-gradient(135deg, #f8fafc 0%, #eef2ff 100%)' }}>
{msg.status === 'generating' ? (
<>
<div style={{ position: 'absolute', top: 12, left: 12, display: 'flex', alignItems: 'center', gap: 8, zIndex: 2 }}>
{/* <div style={{ width: 20, height: 20, border: '2px solid #c7d2fe', borderTopColor: '#6366f1', borderRadius: '50%', animation: 'spin 1s linear infinite' }} /> */}
</div>
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.6) 50%, transparent 100%)', animation: 'shimmer 2s infinite' }} />
<div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 16 }}>
<div style={{ width: 64, height: 64, borderRadius: '50%', background: 'rgba(99, 102, 241, 0.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '0 0 30px rgba(99, 102, 241, 0.2)' }}>
<div style={{ width: 48, height: 48, border: '3px solid #c7d2fe', borderTopColor: '#6366f1', borderRadius: '50%', animation: 'spin 1s linear infinite' }} />
</div>
<span style={{ fontSize: 12, color: '#6366f1', fontWeight: 500 }}>...</span>
{/* <div style={{ display: 'flex', gap: 4 }}>
<div style={{ width: 6, height: 6, borderRadius: '50%', background: '#6366f1', animation: 'pulse 1.5s ease-in-out infinite' }} />
<div style={{ width: 6, height: 6, borderRadius: '50%', background: '#818cf8', animation: 'pulse 1.5s ease-in-out 0.2s infinite' }} />
<div style={{ width: 6, height: 6, borderRadius: '50%', background: '#a5b4fc', animation: 'pulse 1.5s ease-in-out 0.4s infinite' }} />
</div> */}
</div>
</>
) : msg.status === 'failed' ? (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12 }}>
<div style={{ width: 48, height: 48, borderRadius: '50%', background: 'rgba(254, 226, 226, 0.8)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<WarningOutlined style={{ color: '#ef4444', fontSize: 22 }} />
</div>
<span style={{ fontSize: 14, color: '#94a3b8' }}></span>
</div>
) : (
<>
<img
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}/static${msg.videoCoverUrl}&w=300&p=50`}
alt={msg.name}
style={{ width: '100%', height: '100%', borderRadius: 8, objectFit: 'contain', backgroundColor: '#f1f5f9', transition: 'transform 0.2s' }}
onMouseEnter={(e) => { e.currentTarget.style.transform = 'scale(1.05)'; }}
onMouseLeave={(e) => { e.currentTarget.style.transform = 'scale(1)'; }}
/>
{/* 视频播放按钮 */}
<div style={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 56,
height: 56,
background: 'rgba(0,0,0,0.6)',
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
pointerEvents: 'none',
boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
}}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="#fff">
<path d="M8 5v14l11-7z" />
</svg>
</div>
{msg.genType === 'image' ? (
<img src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}/static${msg.imageUrl}&w=300&p=50`} alt={msg.name} style={{ width: '100%', height: '100%', borderRadius: 12, objectFit: 'contain', cursor: 'pointer', transition: 'transform 0.3s ease' }} onClick={() => { setPreviewUrl(msg.imageUrl); setPreviewType('image'); setPreviewVisible(true); }} onMouseEnter={(e) => { e.currentTarget.style.transform = 'scale(1.05)'; }} onMouseLeave={(e) => { e.currentTarget.style.transform = 'scale(1)'; }} />
) : (
<>
<img src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}/static${msg.videoCoverUrl}&w=300&p=50`} alt={msg.name} style={{ width: '100%', height: '100%', borderRadius: 12, objectFit: 'contain', cursor: 'pointer', transition: 'transform 0.3s ease' }} onClick={() => { setPreviewUrl(msg.videoUrl); setPreviewType('video'); setPreviewVisible(true); }} onMouseEnter={(e) => { e.currentTarget.style.transform = 'scale(1.05)'; }} onMouseLeave={(e) => { e.currentTarget.style.transform = 'scale(1)'; }} />
<div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', width: 56, height: 56, background: 'rgba(99, 102, 241, 0.85)', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', pointerEvents: 'none', boxShadow: '0 4px 20px rgba(99, 102, 241, 0.4)' }}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="#fff"><path d="M8 5v14l11-7z" /></svg>
</div>
</>
)}
</>
)}
</div>
<div style={{ width: '50%', }}>
<p style={{ marginBottom: 20 ,height: 100, overflow: 'auto', padding: 0, margin: 0 }}>
{msg.originalPrompt}
</p>
<div style={{fontSize: 11, color: '#999', textAlign: 'left', display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' }}>
{/* 引擎标签 */}
<span >
{/* <SettingsOutlined style={{ fontSize: 12 }} /> */}
{msg.engineSnapshot.name}
</span>
{/* 参数标签 */}
<span >
{/* <LayoutGridOutlined style={{ fontSize: 12 }} /> */}
{msg.genType === 'image'
? `${msg.imageProportion || ''} · ${msg.imagePx || ''} · ${msg.imageSize || ''}`
: `${msg.duration || ''}s · ${msg.aspectRatio || ''} · ${msg.resolution || ''}`
}
</span>
<span style={{ marginLeft: 8 }}>{msg.creditsCost}</span>
{msg.mediaReferences && msg.mediaReferences.length > 0 && (
<span
style={{ marginLeft: 20, color: '#6366f1', cursor: 'pointer' }}
onClick={(e) => {
e.stopPropagation();
const target = e.currentTarget as HTMLElement;
const rect = target.getBoundingClientRect();
setAttachmentPopupPosition({
x: rect.left,
y: rect.top - 10
});
setAttachmentPopupMessageId(msg.id);
setAttachmentPopupVisible(true);
}}
>
</span>
)}
<div style={{ width: '50%', display: 'flex', flexDirection: 'column', gap: 12 }}>
<div style={{ background: 'linear-gradient(135deg, rgba(248,250,252,0.8) 0%, rgba(238,242,255,0.6) 100%)', borderRadius: 12, padding: 12, border: '1px solid rgba(99, 102, 241, 0.08)', flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<p style={{width: '100%' ,height: 150, overflow: 'auto', padding: 0, marginTop: 0, marginBottom: 0, lineHeight: 2, color: '#475569', fontSize: 13 }}>{msg.originalPrompt}</p>
</div>
<div style={{ fontSize: 12, color: '#64748b', textAlign: 'left', display: 'flex', flexWrap: 'wrap', gap: 12, alignItems: 'center', justifyContent: 'space-between',marginBottom: 12 }}>
<span style={{
// background: 'rgba(99, 102, 241, 0.08)',
borderRadius: 16, color: '#6366f1', fontWeight: 500 }}>{msg.engineSnapshot.name}</span>
<span style={{
// background: 'rgba(99, 102, 241, 0.08)',
borderRadius: 16, color: '#64748b' }}>{msg.genType === 'image' ? `${msg.imageProportion || ''} · ${msg.imagePx || ''} · ${msg.imageSize || ''}` : `${msg.duration || ''}s · ${msg.aspectRatio || ''} · ${msg.resolution || ''}`}</span>
<span style={{
// background: 'rgba(239, 68, 68, 0.08)',
borderRadius: 16, color: '#ef4444', fontWeight: 500 }}>{msg.creditsCost}</span>
</div>
</div>
</div>
)}
<div style={{ textAlign: 'right' }}>
{msg.mediaReferences && msg.mediaReferences.length > 0 && (
<span style={{ padding: '4px 12px', borderRadius: 16, color: '#6366f1', cursor: 'pointer', fontWeight: 500, border: '1px solid rgba(99, 102, 241, 0.2)', background: 'rgba(99, 102, 241, 0.04)' }} onClick={(e) => { e.stopPropagation(); const target = e.currentTarget as HTMLElement; const rect = target.getBoundingClientRect(); setAttachmentPopupPosition({ x: rect.left, y: rect.top - 10 }); setAttachmentPopupMessageId(msg.id); setAttachmentPopupVisible(true); }}></span>
)}
</div>
</div>
+19 -5
View File
@@ -80,13 +80,27 @@ function InitialInfo() {
// 当apiSteps更新时,逆向遍历找到第一个已完成或失败的步骤并展开
useEffect(() => {
const completedFailedKeys = new Set<string>();
activeKey.forEach(key => {
const step = steps.find(s => String(s.childId) === key);
if (step && (step.status === 'completed' || step.status === 'failed')) {
completedFailedKeys.add(key);
}
});
for (let i = steps.length - 1; i >= 0; i--) {
if (steps[i].status === 'completed' || steps[i].status === 'failed') {
setActiveKey([String(steps[i].childId)]);
return;
completedFailedKeys.add(String(steps[i].childId));
break;
}
}
setActiveKey([]);
if (completedFailedKeys.size > 0) {
setActiveKey(Array.from(completedFailedKeys));
} else {
setActiveKey([]);
}
}, [apiSteps]);
// console.log('steps', steps);
@@ -198,7 +212,7 @@ function InitialInfo() {
if (fifthStep && fifthStep.status !== 'completed' && fifthStep.status !== 'failed') {
// 第五步未完成,启动轮询
if (!pollingTimer) {
const timer = setInterval(pollTaskDetail, 30000);
const timer = setInterval(pollTaskDetail, 15000);
setPollingTimer(timer);
} else {
}
@@ -551,7 +565,7 @@ function InitialInfo() {
className="replication-form"
style={{
flex: 1,
minWidth: '400px',
minWidth: '450px',
background: 'rgba(255,255,255,0.85)',
backdropFilter: 'blur(20px)',
borderRadius: 20,
@@ -422,7 +422,7 @@ const GenerateConver: React.FC = () => {
className="replication-container"
style={{
margin: '-24px -32px -32px',
height: 'calc(100vh - 32px)',
height: 'calc(100vh - 34px)',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'stretch',
@@ -431,6 +431,7 @@ const GenerateConver: React.FC = () => {
position: 'relative',
// padding: '20px 32px',
boxSizing: 'border-box',
}}
>
@@ -1411,7 +1412,7 @@ const GenerateConver: React.FC = () => {
},
}}
style={{ fontSize: 13 }}
scroll={{ y: 380 }}
scroll={{ y: 350 }}
/>
</div>
</div>
+60 -7
View File
@@ -138,7 +138,6 @@ function RemoveInfo() {
useEffect(() => {
refreshPageData();
// 组件卸载时清除轮询
return () => {
if (pollingIntervalRef.current) {
clearInterval(pollingIntervalRef.current);
@@ -147,6 +146,34 @@ function RemoveInfo() {
};
}, [refreshPageData]);
useEffect(() => {
if (!taskDetail) return;
if (taskDetail.analysisStatus === 'processing') {
console.log("开始轮询");
pollingIntervalRef.current = window.setInterval(async () => {
try {
const res = await getShotReplicationDetail(creatID);
setTaskDetail(res);
} catch {
}
}, 3000);
} else {
if (pollingIntervalRef.current) {
clearInterval(pollingIntervalRef.current);
pollingIntervalRef.current = null;
}
}
return () => {
if (pollingIntervalRef.current) {
clearInterval(pollingIntervalRef.current);
pollingIntervalRef.current = null;
}
};
}, [taskDetail?.analysisStatus, creatID]);
const handleGenerate = (segmentId: string, segmentName: string) => {
setCurrentSegment(segmentId);
setCurrentSegmentName(segmentName);
@@ -197,19 +224,45 @@ function RemoveInfo() {
return;
}
setLoading(true);
try {
const params = {
const params = {
target_project_name: productName.trim(),
core_content_point: productSellingPoint.trim(),
material_image_url: buildAssetUrl(productImage),
idempotency_key: `replication_${Date.now()}`,
};
console.log(currentSegment,params);
setLoading(true);
try {
await removeCreate(currentSegment, params);
message.success('视频生成任务创建成功');
handleCloseDrawer();
await fetchSegments();
Removelist(creatID).then((res: any) => {
console.log('123123123123',res);
const targetItem = res.items.find((item: any) => item.id === currentSegment);
console.log(targetItem);
message.loading('创建中...', 3);
setTimeout(() => {
navigate(`/removelens/${targetItem.moduleProjectId}/removefenbu`);
}, 3000);
});
// fetchSegments().then((res: any) => {
// console.log('123123123123',res);
// // const targetItem = res.items.find((item: any) => item.id === currentSegment);
// // console.log(targetItem);
// });
// if (targetItem?.moduleProjectId) {
// message.loading('跳转中...', 1.5);
//
// }
} catch (err: any) {
message.error(err?.message || '创建失败,请重试');
} finally {
@@ -518,7 +571,7 @@ function RemoveInfo() {
style={{
margin: '-24px -32px -32px',
borderRadius: 20,
minHeight: 'calc(100vh - 32px)',
minHeight: 'calc(100vh - 34px)',
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
@@ -851,7 +904,7 @@ function RemoveInfo() {
styles={{
body: { padding: '24px', background: 'linear-gradient(180deg, #f8fafc 0%, #eef2ff 100%)' },
header: { background: '#fff', borderBottom: '1px solid rgba(99, 102, 241, 0.1)', padding: '20px 24px' },
content: { borderRadius: '20px 0 0 20px', overflow: 'hidden' },
section: { borderRadius: '20px 0 0 20px', overflow: 'hidden' },
}}
>
<div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
+25 -2
View File
@@ -3,6 +3,7 @@ import { Button, Modal, Input, Table, Upload, Popconfirm, message } from 'antd';
import { FileTextOutlined, CloudUploadOutlined } from '@ant-design/icons';
import { useNavigate } from 'react-router-dom';
import { uploadVideo, createShotReplication, getShotReplicationList } from '../api';
import bg1 from '../assets/bg1.png';
export default function VideoFrameExtractor() {
const navigate = useNavigate();
@@ -43,6 +44,24 @@ export default function VideoFrameExtractor() {
return false;
}
const duration = await new Promise<number>((resolve) => {
const video = document.createElement('video');
video.preload = 'metadata';
video.onloadedmetadata = () => {
resolve(video.duration);
URL.revokeObjectURL(video.src);
};
video.onerror = () => {
resolve(0);
};
video.src = URL.createObjectURL(file);
});
if (duration > 60) {
setError('视频时长不能超过 1分钟');
return false;
}
cleanupResources();
setLoading(true);
@@ -135,11 +154,15 @@ export default function VideoFrameExtractor() {
style={{
margin: '-24px -32px -32px',
borderRadius: 20,
minHeight: 'calc(100vh - 32px)',
minHeight: 'calc(100vh - 34px)',
overflow: 'auto',
background: 'linear-gradient(135deg, #f8fafc 0%, #eef2ff 50%, #f0f9ff 100%)',
position: 'relative',
padding: '20px 32px',
backgroundImage: `url(${bg1})`,
backgroundRepeat: 'no-repeat',
backgroundSize: '100% 100%',
backgroundPosition: 'center',
}}
>
@@ -285,7 +308,7 @@ export default function VideoFrameExtractor() {
</p>
<p style={{ fontSize: 13, color: '#64748b', margin: 0 }}>
MP4MOV 100MB 3
MP4MOV 100MB 1
</p>
</>
)}
+19 -5
View File
@@ -80,13 +80,27 @@ function InitialInfo() {
// 当apiSteps更新时,逆向遍历找到第一个已完成或失败的步骤并展开
useEffect(() => {
const completedFailedKeys = new Set<string>();
activeKey.forEach(key => {
const step = steps.find(s => String(s.childId) === key);
if (step && (step.status === 'completed' || step.status === 'failed')) {
completedFailedKeys.add(key);
}
});
for (let i = steps.length - 1; i >= 0; i--) {
if (steps[i].status === 'completed' || steps[i].status === 'failed') {
setActiveKey([String(steps[i].childId)]);
return;
completedFailedKeys.add(String(steps[i].childId));
break;
}
}
setActiveKey([]);
if (completedFailedKeys.size > 0) {
setActiveKey(Array.from(completedFailedKeys));
} else {
setActiveKey([]);
}
}, [apiSteps]);
@@ -441,7 +455,7 @@ function InitialInfo() {
<Button
type="text"
icon={<ArrowLeftOutlined />}
onClick={() => navigate('/removelens')}
onClick={() => window.history.back()}
style={{
color: '#64748b',
borderRadius: 10,
@@ -552,7 +566,7 @@ function InitialInfo() {
className="replication-form"
style={{
flex: 1,
minWidth: '400px',
minWidth: '450px',
background: 'rgba(255,255,255,0.85)',
backdropFilter: 'blur(20px)',
borderRadius: 20,