爆款开头复刻样式修改
This commit is contained in:
@@ -588,7 +588,7 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ marginTop: -54, padding: '0 8px 0' }}>
|
<div style={{ marginTop: 0, padding: '0 8px 0' }}>
|
||||||
<Slider
|
<Slider
|
||||||
range
|
range
|
||||||
min={0}
|
min={0}
|
||||||
@@ -616,7 +616,7 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
|
|||||||
marginTop: 0,
|
marginTop: 0,
|
||||||
// backgroundColor: '#fff',
|
// backgroundColor: '#fff',
|
||||||
// border: '3px solid #6366f1',
|
// border: '3px solid #6366f1',
|
||||||
boxShadow: '0 2px 8px rgba(99, 102, 241, 0.3)',
|
// boxShadow: '0 2px 8px rgba(99, 102, 241, 0.3)',
|
||||||
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -830,7 +830,7 @@ const AIChatPage: React.FC = () => {
|
|||||||
// ==================== 渲染 ====================
|
// ==================== 渲染 ====================
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout style={{ height: 'calc(100vh - 90px)', background: '#fafafa', overflow: 'auto' }}>
|
<Layout style={{ height: 'calc(100vh - 90px)', overflow: 'auto' }}>
|
||||||
{/* 左侧边栏 - 对话列表(已隐藏,保留代码) */}
|
{/* 左侧边栏 - 对话列表(已隐藏,保留代码) */}
|
||||||
{false && (
|
{false && (
|
||||||
<Sider
|
<Sider
|
||||||
@@ -917,12 +917,12 @@ const AIChatPage: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 主内容区 */}
|
{/* 主内容区 */}
|
||||||
<Layout style={{ flex: 1 }}>
|
<Layout style={{ flex: 1, background: '#ffffffff' }}>
|
||||||
{/* 头部 - 显示对话标题和模型信息 */}
|
{/* 头部 - 显示对话标题和模型信息 */}
|
||||||
<Header
|
<Header
|
||||||
style={{
|
style={{
|
||||||
background: '#fff',
|
background: '#fff',
|
||||||
padding: '0 24px',
|
// padding: '0 24px',
|
||||||
borderBottom: '1px solid #f0f0f0',
|
borderBottom: '1px solid #f0f0f0',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -945,10 +945,9 @@ const AIChatPage: React.FC = () => {
|
|||||||
<Content
|
<Content
|
||||||
style={{
|
style={{
|
||||||
margin: 0,
|
margin: 0,
|
||||||
background: '#fafafa',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
padding: 24,
|
// padding: 24,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useEffect, useState, useLayoutEffect, useRef, useCallback } from 'react';
|
import React, { useEffect, useState, useLayoutEffect, useRef, useCallback } from 'react';
|
||||||
import { Button, Empty, Input, Select, Space, Typography, Tag, message, Upload, Modal, Progress, Table } from 'antd';
|
import { Button, Empty, Input, Select, Space, Typography, Tag, message, Upload, Modal, Progress, Table, DatePicker } from 'antd';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
import {
|
import {
|
||||||
SearchOutlined,
|
SearchOutlined,
|
||||||
FilterOutlined,
|
FilterOutlined,
|
||||||
@@ -32,6 +33,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
const [previewVisible, setPreviewVisible] = useState(false);
|
const [previewVisible, setPreviewVisible] = useState(false);
|
||||||
const [previewItem, setPreviewItem] = useState<any>(null);
|
const [previewItem, setPreviewItem] = useState<any>(null);
|
||||||
const videoRef = React.createRef<HTMLVideoElement>();
|
const videoRef = React.createRef<HTMLVideoElement>();
|
||||||
|
const [selectedDate, setSelectedDate] = useState<string>('');
|
||||||
|
|
||||||
// 批量上传相关状态
|
// 批量上传相关状态
|
||||||
const [uploadModalVisible, setUploadModalVisible] = useState(false);
|
const [uploadModalVisible, setUploadModalVisible] = useState(false);
|
||||||
@@ -52,7 +54,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
authStatus: 'pending' | 'authorized' | 'failed';
|
authStatus: 'pending' | 'authorized' | 'failed';
|
||||||
}[]>([]);
|
}[]>([]);
|
||||||
const [accountIdInput, setAccountIdInput] = useState('');
|
const [accountIdInput, setAccountIdInput] = useState('');
|
||||||
|
|
||||||
const [oauthList, setOauthList] = useState<any[]>([]);
|
const [oauthList, setOauthList] = useState<any[]>([]);
|
||||||
const [oauthLoading, setOauthLoading] = useState(false);
|
const [oauthLoading, setOauthLoading] = useState(false);
|
||||||
const [oauthTotal, setOauthTotal] = useState(0);
|
const [oauthTotal, setOauthTotal] = useState(0);
|
||||||
@@ -60,7 +62,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
const [oauthPage, setOauthPage] = useState(1);
|
const [oauthPage, setOauthPage] = useState(1);
|
||||||
const [oauthPageSize, setOauthPageSize] = useState(10);
|
const [oauthPageSize, setOauthPageSize] = useState(10);
|
||||||
const [oauthSelectOpen, setOauthSelectOpen] = useState(false);
|
const [oauthSelectOpen, setOauthSelectOpen] = useState(false);
|
||||||
|
|
||||||
const [batchUploadProgress, setBatchUploadProgress] = useState<{
|
const [batchUploadProgress, setBatchUploadProgress] = useState<{
|
||||||
itemId: string;
|
itemId: string;
|
||||||
status: 'pending' | 'uploading' | 'success' | 'error';
|
status: 'pending' | 'uploading' | 'success' | 'error';
|
||||||
@@ -290,7 +292,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
|
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
|
||||||
transition: 'transform 0.2s, box-shadow 0.2s',
|
transition: 'transform 0.2s, box-shadow 0.2s',
|
||||||
}}
|
}}
|
||||||
onClick={!isSelectionMode ? onClick : undefined}
|
onClick={!isSelectionMode ? onClick : undefined}
|
||||||
@@ -307,7 +309,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
{/* 媒体内容 - 当isLoading为true时开始渲染,加载完成后显示 */}
|
{/* 媒体内容 - 当isLoading为true时开始渲染,加载完成后显示 */}
|
||||||
{((mediaType === 'video' && item.videoCoverUrl) || mediaType === 'image') && (isLoading || isLoaded) && !isError && (
|
{((mediaType === 'video' && item.videoCoverUrl) || mediaType === 'image') && (isLoading || isLoaded) && !isError && (
|
||||||
<div style={{
|
<div style={{
|
||||||
@@ -664,7 +666,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
const res = await getOAuthList({ page, page_size: pageSize });
|
const res = await getOAuthList({ page, page_size: pageSize });
|
||||||
const data = res?.data || res;
|
const data = res?.data || res;
|
||||||
console.log(res);
|
console.log(res);
|
||||||
setOauthList(data|| []);
|
setOauthList(data || []);
|
||||||
setOauthTotal(res.pagination.total || 0);
|
setOauthTotal(res.pagination.total || 0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载授权列表失败:', error);
|
console.error('加载授权列表失败:', error);
|
||||||
@@ -678,7 +680,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
const handleStartBatchUpload = async () => {
|
const handleStartBatchUpload = async () => {
|
||||||
setUploading(true);
|
setUploading(true);
|
||||||
const uploadProgressMap: { [key: string]: { status: 'pending' | 'uploading' | 'success' | 'error'; message: string } } = {};
|
const uploadProgressMap: { [key: string]: { status: 'pending' | 'uploading' | 'success' | 'error'; message: string } } = {};
|
||||||
|
|
||||||
for (const itemId of selectedItems) {
|
for (const itemId of selectedItems) {
|
||||||
uploadProgressMap[itemId] = { status: 'pending', message: '' };
|
uploadProgressMap[itemId] = { status: 'pending', message: '' };
|
||||||
}
|
}
|
||||||
@@ -771,6 +773,17 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 日期选择器变化处理函数
|
||||||
|
const handleDateChange = (dateString: string) => {
|
||||||
|
console.log(dateString);
|
||||||
|
|
||||||
|
console.log('qweqwe');
|
||||||
|
|
||||||
|
setSelectedDate(dateString);
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
let parameters = '';
|
let parameters = '';
|
||||||
@@ -779,27 +792,70 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
} else {
|
} else {
|
||||||
parameters = `?gen_type=${filterMedia}&page=${Pagebreak.page}&page_size=${Pagebreak.pageSize}`;
|
parameters = `?gen_type=${filterMedia}&page=${Pagebreak.page}&page_size=${Pagebreak.pageSize}`;
|
||||||
}
|
}
|
||||||
gethistory(parameters).then((res: any) => {
|
|
||||||
const data = Array.isArray(res) ? res : (res?.groups || []);
|
|
||||||
data.forEach(group => {
|
|
||||||
group.page = 1;
|
|
||||||
});
|
|
||||||
// 如果是第一页,替换数据;否则追加数据
|
|
||||||
if (Pagebreak.page === 1) {
|
|
||||||
setRecordList(data);
|
|
||||||
} else {
|
|
||||||
setRecordList(prev => [...prev, ...data]);
|
|
||||||
}
|
|
||||||
|
|
||||||
setTotalnumber(res?.totalDays || 0);
|
if (selectedDate) {
|
||||||
}).catch((err) => {
|
let parameters = ``;
|
||||||
if (Pagebreak.page === 1) {
|
|
||||||
setRecordList([]);
|
if (filterType === 'project') {
|
||||||
|
parameters = `${selectedDate}?gen_type=${filterMedia}&history_source=generation_record&page=${Pagebreak.page}&page_size=${Pagebreak.pageSize}`;
|
||||||
|
} else {
|
||||||
|
parameters = `${selectedDate}?gen_type=${filterMedia}&page=${Pagebreak.page}&page_size=${Pagebreak.pageSize}`;
|
||||||
}
|
}
|
||||||
}).finally(() => {
|
gethistoryItems(parameters).then((res: any) => {
|
||||||
setLoading(false);
|
const data = Array.isArray(res) ? res : (res?.items || []);
|
||||||
});
|
let recordList = [{
|
||||||
}, [filterType, filterMedia, Pagebreak.page]);
|
generatedDate: res.generatedDate,
|
||||||
|
items: data,
|
||||||
|
total: res.total,
|
||||||
|
page: res.page,
|
||||||
|
}];
|
||||||
|
console.log('data', data);
|
||||||
|
if (recordList && recordList[0].items.length > 0) {
|
||||||
|
setRecordList(recordList);
|
||||||
|
|
||||||
|
}else{
|
||||||
|
setRecordList([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('recordList', recordList);
|
||||||
|
|
||||||
|
}).catch((err) => {
|
||||||
|
}).finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
} catch (err) {
|
||||||
|
} finally {
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gethistory(parameters).then((res: any) => {
|
||||||
|
const data = Array.isArray(res) ? res : (res?.groups || []);
|
||||||
|
data.forEach(group => {
|
||||||
|
group.page = 1;
|
||||||
|
});
|
||||||
|
console.log('qweqweqwe', data);
|
||||||
|
|
||||||
|
// 如果是第一页,替换数据;否则追加数据
|
||||||
|
if (Pagebreak.page === 1) {
|
||||||
|
setRecordList(data);
|
||||||
|
} else {
|
||||||
|
setRecordList(prev => [...prev, ...data]);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTotalnumber(res?.totalDays || 0);
|
||||||
|
}).catch((err) => {
|
||||||
|
if (Pagebreak.page === 1) {
|
||||||
|
setRecordList([]);
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}, [filterType, filterMedia, Pagebreak.page, selectedDate]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setPreTestLoading(true);
|
setPreTestLoading(true);
|
||||||
@@ -878,7 +934,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
}, [filterType, filterMedia]);
|
}, [filterType, filterMedia]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div style={{ minHeight: 'calc(100vh - 90px)', background: '#ffffffff' , overflowY: 'auto'}} >
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
{/* <div style={{ marginBottom: 20 }}>
|
{/* <div style={{ marginBottom: 20 }}>
|
||||||
<Typography.Title level={3} style={{ margin: '0 0 4px', color: '#1a1a2e', fontWeight: 700 }}>
|
<Typography.Title level={3} style={{ margin: '0 0 4px', color: '#1a1a2e', fontWeight: 700 }}>
|
||||||
@@ -1047,6 +1103,23 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
>
|
>
|
||||||
图片
|
图片
|
||||||
</Button>
|
</Button>
|
||||||
|
<DatePicker
|
||||||
|
picker="date"
|
||||||
|
value={selectedDate ? dayjs(selectedDate) : undefined}
|
||||||
|
onChange={(date, dateString) => handleDateChange(dateString || '')}
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
style={{ width: 160, borderRadius: 8, border: '1px solid #e2e8f0' }}
|
||||||
|
placeholder="选择日期"
|
||||||
|
/>
|
||||||
|
{selectedDate && (
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
onClick={() => handleDateChange('')}
|
||||||
|
style={{ color: '#94a3b8', fontSize: 12 }}
|
||||||
|
>
|
||||||
|
清除
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1292,7 +1365,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
<Typography.Text strong style={{ fontSize: 14, color: '#475569', marginBottom: 8, display: 'block' }}>
|
<Typography.Text strong style={{ fontSize: 14, color: '#475569', marginBottom: 8, display: 'block' }}>
|
||||||
选择授权账户(可多选)
|
选择授权账户(可多选)
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
value={selectedOauthItems}
|
value={selectedOauthItems}
|
||||||
@@ -1301,12 +1374,12 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
const selectedAccounts = oauthList
|
const selectedAccounts = oauthList
|
||||||
.filter(item => value.includes(String(item.id)))
|
.filter(item => value.includes(String(item.id)))
|
||||||
.map(item => ({ accountId: String(item.accountId), authStatus: 'authorized' as const }));
|
.map(item => ({ accountId: String(item.accountId), authStatus: 'authorized' as const }));
|
||||||
|
|
||||||
const textAccounts = accountIdInput.split('\n')
|
const textAccounts = accountIdInput.split('\n')
|
||||||
.map(line => line.trim())
|
.map(line => line.trim())
|
||||||
.filter(line => line.length > 0)
|
.filter(line => line.length > 0)
|
||||||
.map(id => ({ accountId: id, authStatus: 'pending' as const }));
|
.map(id => ({ accountId: id, authStatus: 'pending' as const }));
|
||||||
|
|
||||||
const mergedAccounts = [...selectedAccounts, ...textAccounts];
|
const mergedAccounts = [...selectedAccounts, ...textAccounts];
|
||||||
const seen = new Set<string>();
|
const seen = new Set<string>();
|
||||||
const finalAccounts = mergedAccounts.filter(a => {
|
const finalAccounts = mergedAccounts.filter(a => {
|
||||||
@@ -1314,7 +1387,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
seen.add(a.accountId);
|
seen.add(a.accountId);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
setAccountIdList(finalAccounts);
|
setAccountIdList(finalAccounts);
|
||||||
}}
|
}}
|
||||||
placeholder="点击选择授权账户"
|
placeholder="点击选择授权账户"
|
||||||
@@ -1417,24 +1490,24 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
<Typography.Text strong style={{ fontSize: 14, color: '#475569', marginBottom: 8, display: 'block', marginTop: 16 }}>
|
<Typography.Text strong style={{ fontSize: 14, color: '#475569', marginBottom: 8, display: 'block', marginTop: 16 }}>
|
||||||
粘贴账户ID(每行一个)
|
粘贴账户ID(每行一个)
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
|
|
||||||
<Input.TextArea
|
<Input.TextArea
|
||||||
value={accountIdInput}
|
value={accountIdInput}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
setAccountIdInput(value);
|
setAccountIdInput(value);
|
||||||
|
|
||||||
const ids = value.split('\n')
|
const ids = value.split('\n')
|
||||||
.map(line => line.trim())
|
.map(line => line.trim())
|
||||||
.filter(line => line.length > 0);
|
.filter(line => line.length > 0);
|
||||||
|
|
||||||
const uniqueIds = [...new Set(ids)];
|
const uniqueIds = [...new Set(ids)];
|
||||||
const textAccounts = uniqueIds.map(id => ({ accountId: id, authStatus: 'pending' as const }));
|
const textAccounts = uniqueIds.map(id => ({ accountId: id, authStatus: 'pending' as const }));
|
||||||
|
|
||||||
const oauthAccounts = oauthList
|
const oauthAccounts = oauthList
|
||||||
.filter(item => selectedOauthItems.includes(String(item.id)))
|
.filter(item => selectedOauthItems.includes(String(item.id)))
|
||||||
.map(item => ({ accountId: String(item.accountId), authStatus: 'authorized' as const }));
|
.map(item => ({ accountId: String(item.accountId), authStatus: 'authorized' as const }));
|
||||||
|
|
||||||
const mergedAccounts = [...oauthAccounts, ...textAccounts];
|
const mergedAccounts = [...oauthAccounts, ...textAccounts];
|
||||||
const seen = new Set<string>();
|
const seen = new Set<string>();
|
||||||
const finalAccounts = mergedAccounts.filter(a => {
|
const finalAccounts = mergedAccounts.filter(a => {
|
||||||
@@ -1442,7 +1515,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
seen.add(a.accountId);
|
seen.add(a.accountId);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
setAccountIdList(finalAccounts);
|
setAccountIdList(finalAccounts);
|
||||||
}}
|
}}
|
||||||
placeholder="粘贴账户ID,每行一个,例如:
|
placeholder="粘贴账户ID,每行一个,例如:
|
||||||
@@ -1458,7 +1531,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
<Typography.Text style={{ fontSize: 13, color: '#64748b', marginBottom: 8, display: 'block' }}>
|
<Typography.Text style={{ fontSize: 13, color: '#64748b', marginBottom: 8, display: 'block' }}>
|
||||||
已导入 {accountIdList.length} 个账户
|
已导入 {accountIdList.length} 个账户
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
|
|
||||||
<Table
|
<Table
|
||||||
dataSource={accountIdList.map((item, index) => ({ ...item, key: index }))}
|
dataSource={accountIdList.map((item, index) => ({ ...item, key: index }))}
|
||||||
columns={[
|
columns={[
|
||||||
@@ -1493,7 +1566,7 @@ const GeneratedRecord: React.FC = () => {
|
|||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
danger
|
danger
|
||||||
|
|||||||
@@ -165,10 +165,10 @@ function InitialInfo() {
|
|||||||
|
|
||||||
// 监听合并后的 steps 数据,判断第五步状态并启动/停止轮询
|
// 监听合并后的 steps 数据,判断第五步状态并启动/停止轮询
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
// 检查第五步的状态(索引 4)
|
// 检查第五步的状态(索引 4)
|
||||||
const fifthStep = steps[4];
|
const fifthStep = steps[4];
|
||||||
|
|
||||||
if (fifthStep && fifthStep.status !== 'completed' && fifthStep.status !== 'failed') {
|
if (fifthStep && fifthStep.status !== 'completed' && fifthStep.status !== 'failed') {
|
||||||
// 第五步未完成,启动轮询
|
// 第五步未完成,启动轮询
|
||||||
if (!pollingTimer) {
|
if (!pollingTimer) {
|
||||||
@@ -259,7 +259,7 @@ function InitialInfo() {
|
|||||||
if (!creatID) {
|
if (!creatID) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
getReplicationDetail(creatID).then((res: any) => {
|
getReplicationDetail(creatID).then((res: any) => {
|
||||||
setTaskDetail(res);
|
setTaskDetail(res);
|
||||||
if (res.steps) {
|
if (res.steps) {
|
||||||
@@ -272,7 +272,7 @@ function InitialInfo() {
|
|||||||
// 刷新任务详情(用于点击下一步后更新数据)
|
// 刷新任务详情(用于点击下一步后更新数据)
|
||||||
const refreshTaskDetail = () => {
|
const refreshTaskDetail = () => {
|
||||||
if (!creatID) return;
|
if (!creatID) return;
|
||||||
|
|
||||||
getReplicationDetail(creatID).then((res: any) => {
|
getReplicationDetail(creatID).then((res: any) => {
|
||||||
setTaskDetail(res);
|
setTaskDetail(res);
|
||||||
if (res.steps) {
|
if (res.steps) {
|
||||||
@@ -294,7 +294,7 @@ function InitialInfo() {
|
|||||||
refreshTaskDetail();
|
refreshTaskDetail();
|
||||||
}).catch((error: any) => {
|
}).catch((error: any) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const newcreateimage = () => {
|
const newcreateimage = () => {
|
||||||
// console.log('下一步:', stepId);
|
// console.log('下一步:', stepId);
|
||||||
let params = {
|
let params = {
|
||||||
@@ -303,22 +303,22 @@ function InitialInfo() {
|
|||||||
image_px: "2048x2048",
|
image_px: "2048x2048",
|
||||||
image_size: "2K"
|
image_size: "2K"
|
||||||
}
|
}
|
||||||
|
|
||||||
gettwo(taskDetail.id, steps[1].id.toString(), params).then((res: any) => {
|
gettwo(taskDetail.id, steps[1].id.toString(), params).then((res: any) => {
|
||||||
refreshTaskDetail();
|
refreshTaskDetail();
|
||||||
|
|
||||||
}).catch((error: any) => {
|
}).catch((error: any) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 下一步按钮点击处理
|
// 下一步按钮点击处理
|
||||||
const handleNextStep = (stepId: number) => {
|
const handleNextStep = (stepId: number) => {
|
||||||
// 获取引擎 ID 和视频参数
|
// 获取引擎 ID 和视频参数
|
||||||
const engineId = countType;
|
const engineId = countType;
|
||||||
const videoParams = {
|
const videoParams = {
|
||||||
engine_id : countType,
|
engine_id: countType,
|
||||||
duration: videoDuration,
|
duration: videoDuration,
|
||||||
aspect_ratio: videoAspectRatio,
|
aspect_ratio: videoAspectRatio,
|
||||||
resolution: videoResolution,
|
resolution: videoResolution,
|
||||||
@@ -333,12 +333,12 @@ function InitialInfo() {
|
|||||||
// 这里可以添加下一步的逻辑,比如调用接口等
|
// 这里可以添加下一步的逻辑,比如调用接口等
|
||||||
};
|
};
|
||||||
|
|
||||||
const createvideo = (stepId: number,engineId: string) => {
|
const createvideo = (stepId: number, engineId: string) => {
|
||||||
let params = {
|
let params = {
|
||||||
engine_id: engineId,
|
engine_id: engineId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getfour(taskDetail.id, stepId.toString(), params).then((res: any) => {
|
getfour(taskDetail.id, stepId.toString(), params).then((res: any) => {
|
||||||
// 重新获取任务详情以更新数据
|
// 重新获取任务详情以更新数据
|
||||||
refreshTaskDetail();
|
refreshTaskDetail();
|
||||||
@@ -349,8 +349,8 @@ function InitialInfo() {
|
|||||||
let params = {
|
let params = {
|
||||||
engine_id: steps[2].engineId,
|
engine_id: steps[2].engineId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getfour(taskDetail.id, steps[2].id.toString(), params).then((res: any) => {
|
getfour(taskDetail.id, steps[2].id.toString(), params).then((res: any) => {
|
||||||
// 重新获取任务详情以更新数据
|
// 重新获取任务详情以更新数据
|
||||||
refreshTaskDetail();
|
refreshTaskDetail();
|
||||||
@@ -362,51 +362,112 @@ function InitialInfo() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div style={{ minHeight: 'calc(100vh - 90px)', background: '#f8fafc' }}>
|
<div
|
||||||
<div style={{ height: '100%' }}>
|
className="initialinfo-container"
|
||||||
|
style={{
|
||||||
|
margin: '-24px -32px -32px',
|
||||||
|
|
||||||
<div style={{ height: '100%', display: 'flex', justifyContent: 'space-between', gap: '2%' }}>
|
height: 'calc(100vh - 34px)',
|
||||||
<div style={{ width: '70%', background: '#fff', borderRadius: 12, overflowY: 'auto', maxHeight: 'calc(100vh - 90px)' }}>
|
// background: 'linear-gradient(135deg, #f8fafc 0%, #eef2ff 50%, #f0f9ff 100%)',
|
||||||
|
position: 'relative',
|
||||||
|
// padding: '20px 32px',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
overflow: 'hidden',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
|
||||||
<div style={{ borderBottom: '1px solid #e2e8f0', width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '14px', boxSizing: 'border-box' }}>
|
|
||||||
<Button
|
<div style={{ flex: 1, display: 'flex', justifyContent: 'space-between', alignItems: 'stretch', gap: '2%', position: 'relative', zIndex: 1, minHeight: 0 }}>
|
||||||
type="text"
|
|
||||||
icon={<ArrowLeftOutlined />}
|
<div
|
||||||
onClick={() => navigate(-1)}
|
className="replication-preview"
|
||||||
style={{ color: '#64748b' }}
|
style={{
|
||||||
>
|
flex: 4,
|
||||||
返回
|
background: 'rgba(255,255,255,0.85)',
|
||||||
</Button>
|
backdropFilter: 'blur(20px)',
|
||||||
<h3 style={{ margin: 0 }}>
|
borderRadius: 20,
|
||||||
|
overflow: 'hidden',
|
||||||
|
border: '1px solid rgba(99, 102, 241, 0.1)',
|
||||||
|
boxShadow: '0 8px 32px rgba(99, 102, 241, 0.08)',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
minHeight: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
|
||||||
|
<div style={{ borderBottom: '1px solid rgba(99, 102, 241, 0.08)', width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '16px 24px', background: 'rgba(255,255,255,0.6)', backdropFilter: 'blur(10px)', boxSizing: 'border-box' }}>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
icon={<ArrowLeftOutlined />}
|
||||||
|
onClick={() => navigate(-1)}
|
||||||
|
style={{
|
||||||
|
color: '#64748b',
|
||||||
|
borderRadius: 10,
|
||||||
|
fontSize: 13,
|
||||||
|
height: 32,
|
||||||
|
transition: 'all 0.25s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
返回
|
||||||
|
</Button>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||||
|
<div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #6366f1, #8b5cf6, transparent)', borderRadius: 1 }} />
|
||||||
|
|
||||||
|
<h3 style={{ margin: 0, fontSize: 16, fontWeight: 700, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
|
||||||
爆款开头复刻 - 任务详情
|
爆款开头复刻 - 任务详情
|
||||||
</h3>
|
</h3>
|
||||||
<Button type="primary" ghost style={{ borderRadius: 6, padding: '4px 12px', fontSize: 12, borderColor: '#6366f1', color: '#6366f1' }} onClick={() => setIsModalOpen(true)}>
|
<div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #8b5cf6, #6366f1, transparent)', borderRadius: 1 }} />
|
||||||
创作记录
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
ghost
|
||||||
|
style={{
|
||||||
|
borderRadius: 10,
|
||||||
|
padding: '4px 16px',
|
||||||
|
fontSize: 13,
|
||||||
|
background: 'rgba(99, 102, 241, 0.1)',
|
||||||
|
borderColor: 'rgba(99, 102, 241, 0.2)',
|
||||||
|
color: '#6366f1',
|
||||||
|
}}
|
||||||
|
onClick={() => setIsModalOpen(true)}
|
||||||
|
>
|
||||||
|
创作记录
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ flex: 1, overflowY: 'auto' }}>
|
||||||
<div className="material_box">
|
<div className="material_box">
|
||||||
<div style={{ display: 'flex', gap: 24, flexWrap: 'wrap', alignItems: 'flex-start' }}>
|
<div style={{ display: 'flex', gap: 24, flexWrap: 'wrap', alignItems: 'flex-start' }}>
|
||||||
<div style={{ flex: 1, minWidth: 250 }}>
|
<div style={{ flex: 1, minWidth: 250 }}>
|
||||||
<span style={{ fontSize: 14, fontWeight: 500, color: '#374151', marginBottom: 8, display: 'block' }}>视频</span>
|
<span style={{ fontSize: 14, fontWeight: 600, color: '#1e293b', marginBottom: 10, display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
<div className="medio_box" style={{ aspectRatio: '16/9', background: '#f1f5f9', borderRadius: 8, overflow: 'hidden' }}>
|
<span style={{ width: 3, height: 14, background: 'linear-gradient(180deg, #6366f1, #8b5cf6)', borderRadius: 2, display: 'inline-block' }} />
|
||||||
|
视频
|
||||||
|
</span>
|
||||||
|
<div className="medio_box" style={{ aspectRatio: '16/9', background: 'linear-gradient(135deg, rgba(248,250,252,0.8) 0%, rgba(238,242,255,0.6) 100%)', borderRadius: 14, overflow: 'hidden', boxShadow: '0 4px 12px rgba(99, 102, 241, 0.06)', border: '1px solid rgba(99, 102, 241, 0.08)' }}>
|
||||||
{taskDetail?.material?.materialVideoUrl ? (
|
{taskDetail?.material?.materialVideoUrl ? (
|
||||||
<video
|
<video
|
||||||
controls
|
controls
|
||||||
src={taskDetail.material.materialVideoUrl}
|
src={taskDetail.material.materialVideoUrl}
|
||||||
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#94a3b8', fontSize: 12 }}>暂无视频</div>
|
<div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#94a3b8', fontSize: 13 }}>暂无视频</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="product_img" style={{ flex: 1, minWidth: 250 }}>
|
<div className="product_img" style={{ flex: 1, minWidth: 250 }}>
|
||||||
<span style={{ fontSize: 14, fontWeight: 500, color: '#374151', marginBottom: 8, display: 'block' }}>产品图片</span>
|
<span style={{ fontSize: 14, fontWeight: 600, color: '#1e293b', marginBottom: 10, display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
<div className="medio_box" style={{ aspectRatio: '1/1', background: '#f1f5f9', borderRadius: 8, overflow: 'hidden' }}>
|
<span style={{ width: 3, height: 14, background: 'linear-gradient(180deg, #6366f1, #8b5cf6)', borderRadius: 2, display: 'inline-block' }} />
|
||||||
|
产品图片
|
||||||
|
</span>
|
||||||
|
<div className="medio_box" style={{ aspectRatio: '1/1', background: 'linear-gradient(135deg, rgba(248,250,252,0.8) 0%, rgba(238,242,255,0.6) 100%)', borderRadius: 14, overflow: 'hidden', boxShadow: '0 4px 12px rgba(99, 102, 241, 0.06)', border: '1px solid rgba(99, 102, 241, 0.08)' }}>
|
||||||
{taskDetail?.material?.materialImageUrl ? (
|
{taskDetail?.material?.materialImageUrl ? (
|
||||||
<img src={taskDetail.material.materialImageUrl} alt="" style={{ width: '100%', height: '100%', objectFit: 'contain' }} />
|
<img src={taskDetail.material.materialImageUrl} alt="" style={{ width: '100%', height: '100%', objectFit: 'contain' }} />
|
||||||
) : (
|
) : (
|
||||||
<div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#94a3b8', fontSize: 12 }}>暂无图片</div>
|
<div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#94a3b8', fontSize: 13 }}>暂无图片</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -414,12 +475,15 @@ function InitialInfo() {
|
|||||||
{taskDetail?.finalImageUrl && (
|
{taskDetail?.finalImageUrl && (
|
||||||
<div style={{ marginTop: 16 }}>
|
<div style={{ marginTop: 16 }}>
|
||||||
<div>
|
<div>
|
||||||
<span style={{ fontSize: 14, fontWeight: 500, color: '#374151', marginBottom: 8, display: 'block' }}>生成图片</span>
|
<span style={{ fontSize: 14, fontWeight: 600, color: '#1e293b', marginBottom: 10, display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
<div className="medio_box" style={{ aspectRatio: '1/1', background: '#f1f5f9', borderRadius: 8, overflow: 'hidden' }}>
|
<span style={{ width: 3, height: 14, background: 'linear-gradient(180deg, #6366f1, #8b5cf6)', borderRadius: 2, display: 'inline-block' }} />
|
||||||
<img
|
生成图片
|
||||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.finalImageUrl}`}
|
</span>
|
||||||
alt=""
|
<div className="medio_box" style={{ aspectRatio: '1/1', background: 'linear-gradient(135deg, rgba(248,250,252,0.8) 0%, rgba(238,242,255,0.6) 100%)', borderRadius: 14, overflow: 'hidden', boxShadow: '0 4px 12px rgba(99, 102, 241, 0.06)', border: '1px solid rgba(99, 102, 241, 0.08)' }}>
|
||||||
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
<img
|
||||||
|
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.finalImageUrl}`}
|
||||||
|
alt=""
|
||||||
|
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -428,12 +492,15 @@ function InitialInfo() {
|
|||||||
{taskDetail?.finalVideoUrl && (
|
{taskDetail?.finalVideoUrl && (
|
||||||
<div style={{ marginTop: 16 }}>
|
<div style={{ marginTop: 16 }}>
|
||||||
<div>
|
<div>
|
||||||
<span style={{ fontSize: 14, fontWeight: 500, color: '#374151', marginBottom: 8, display: 'block' }}>生成视频</span>
|
<span style={{ fontSize: 14, fontWeight: 600, color: '#1e293b', marginBottom: 10, display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
<div className="medio_box" style={{ aspectRatio: '16/9', background: '#f1f5f9', borderRadius: 8, overflow: 'hidden' }}>
|
<span style={{ width: 3, height: 14, background: 'linear-gradient(180deg, #6366f1, #8b5cf6)', borderRadius: 2, display: 'inline-block' }} />
|
||||||
<video
|
生成视频
|
||||||
controls
|
</span>
|
||||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.finalVideoUrl}`}
|
<div className="medio_box" style={{ aspectRatio: '16/9', background: 'linear-gradient(135deg, rgba(248,250,252,0.8) 0%, rgba(238,242,255,0.6) 100%)', borderRadius: 14, overflow: 'hidden', boxShadow: '0 4px 12px rgba(99, 102, 241, 0.06)', border: '1px solid rgba(99, 102, 241, 0.08)' }}>
|
||||||
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
<video
|
||||||
|
controls
|
||||||
|
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.finalVideoUrl}`}
|
||||||
|
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -441,9 +508,30 @@ function InitialInfo() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ width: '28%', minWidth: '400px', background: '#fff', borderRadius: 12, overflowY: 'auto', maxHeight: 'calc(100vh - 90px)' }}>
|
</div>
|
||||||
|
|
||||||
<div style={{ padding: '14px' }}> 生成步骤</div>
|
<div
|
||||||
|
className="replication-form"
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
minWidth: '400px',
|
||||||
|
background: 'rgba(255,255,255,0.85)',
|
||||||
|
backdropFilter: 'blur(20px)',
|
||||||
|
borderRadius: 20,
|
||||||
|
overflow: 'hidden',
|
||||||
|
border: '1px solid rgba(99, 102, 241, 0.1)',
|
||||||
|
boxShadow: '0 8px 32px rgba(99, 102, 241, 0.08)',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
minHeight: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
|
||||||
|
<div style={{ padding: '16px 24px', borderBottom: '1px solid rgba(99, 102, 241, 0.08)', display: 'flex', alignItems: 'center', gap: 10, background: 'rgba(255,255,255,0.6)', backdropFilter: 'blur(10px)' }}>
|
||||||
|
<div style={{ width: 4, height: 18, background: 'linear-gradient(180deg, #6366f1 0%, #8b5cf6 100%)', borderRadius: 2 }} />
|
||||||
|
<span style={{ fontSize: 15, fontWeight: 700, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>生成步骤</span>
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: 1, overflowY: 'auto', padding: '8px 16px 16px' }}>
|
||||||
<Collapse
|
<Collapse
|
||||||
defaultActiveKey={['']}
|
defaultActiveKey={['']}
|
||||||
ghost
|
ghost
|
||||||
@@ -484,25 +572,25 @@ function InitialInfo() {
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
children: (
|
children: (
|
||||||
<div style={{ padding: '16px', background: '#f8fafc', borderRadius: 8, marginTop: 8 }}>
|
<div style={{ padding: '16px', background: 'linear-gradient(135deg, rgba(248,250,252,0.6) 0%, rgba(238,242,255,0.4) 100%)', borderRadius: 12, marginTop: 8, border: '1px solid rgba(99, 102, 241, 0.06)' }}>
|
||||||
{/* 步骤1: 原始素材 */}
|
{/* 步骤1: 原始素材 */}
|
||||||
{step.childId === 1 && (
|
{step.childId === 1 && (
|
||||||
<>
|
<>
|
||||||
<div style={{ display: 'flex', gap: 20, marginBottom: 20, alignItems: 'flex-start', flexWrap: 'wrap' }}>
|
<div style={{ display: 'flex', gap: 20, marginBottom: 20, alignItems: 'flex-start', flexWrap: 'wrap' }}>
|
||||||
<div style={{ flex: 1, minWidth: 200 }}>
|
<div style={{ flex: 1, minWidth: 200 }}>
|
||||||
<Text style={{ color: '#64748b', fontSize: 13, marginBottom: 10, display: 'block', fontWeight: 500 }}>原视频</Text>
|
<Text style={{ color: '#475569', fontSize: 13, marginBottom: 10, display: 'block', fontWeight: 500 }}>原视频</Text>
|
||||||
<div style={{ height: 180, borderRadius: 10, background: '#f1f5f9', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
<div style={{ height: 180, borderRadius: 12, background: 'linear-gradient(135deg, rgba(248,250,252,0.8) 0%, rgba(238,242,255,0.6) 100%)', display: 'flex', alignItems: 'center', justifyContent: 'center', border: '1px solid rgba(99, 102, 241, 0.08)', boxShadow: '0 2px 8px rgba(99, 102, 241, 0.04)' }}>
|
||||||
{taskDetail?.material?.materialVideoUrl ? (
|
{taskDetail?.material?.materialVideoUrl ? (
|
||||||
<video
|
<video
|
||||||
src={taskDetail.material.materialVideoUrl}
|
src={taskDetail.material.materialVideoUrl}
|
||||||
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', borderRadius: 10 }}
|
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', borderRadius: 10 }}
|
||||||
controls
|
controls
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ textAlign: 'center', color: '#94a3b8' }}>
|
<div style={{ textAlign: 'center', color: '#94a3b8' }}>
|
||||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" style={{ marginBottom: 8 }}>
|
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" style={{ marginBottom: 8 }}>
|
||||||
<rect x="2" y="2" width="20" height="16" rx="2" ry="2"/>
|
<rect x="2" y="2" width="20" height="16" rx="2" ry="2" />
|
||||||
<path d="M8 12l4 2 4-2"/>
|
<path d="M8 12l4 2 4-2" />
|
||||||
</svg>
|
</svg>
|
||||||
<div style={{ fontSize: 13 }}>暂无视频</div>
|
<div style={{ fontSize: 13 }}>暂无视频</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -513,17 +601,17 @@ function InitialInfo() {
|
|||||||
<Text style={{ color: '#64748b', fontSize: 13, marginBottom: 10, display: 'block', fontWeight: 500 }}>产品图片</Text>
|
<Text style={{ color: '#64748b', fontSize: 13, marginBottom: 10, display: 'block', fontWeight: 500 }}>产品图片</Text>
|
||||||
<div style={{ height: 180, borderRadius: 10, background: '#f1f5f9', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
<div style={{ height: 180, borderRadius: 10, background: '#f1f5f9', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
{taskDetail?.material?.materialImageUrl ? (
|
{taskDetail?.material?.materialImageUrl ? (
|
||||||
<img
|
<img
|
||||||
src={taskDetail.material.materialImageUrl}
|
src={taskDetail.material.materialImageUrl}
|
||||||
alt="产品图片"
|
alt="产品图片"
|
||||||
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', borderRadius: 10 }}
|
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', borderRadius: 10 }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ textAlign: 'center', color: '#94a3b8' }}>
|
<div style={{ textAlign: 'center', color: '#94a3b8' }}>
|
||||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" style={{ marginBottom: 8 }}>
|
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" style={{ marginBottom: 8 }}>
|
||||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
|
||||||
<circle cx="8.5" cy="8.5" r="1.5" fill="currentColor"/>
|
<circle cx="8.5" cy="8.5" r="1.5" fill="currentColor" />
|
||||||
<path d="M21 15l-5-5L5 21"/>
|
<path d="M21 15l-5-5L5 21" />
|
||||||
</svg>
|
</svg>
|
||||||
<div style={{ fontSize: 13 }}>暂无图片</div>
|
<div style={{ fontSize: 13 }}>暂无图片</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -531,30 +619,33 @@ function InitialInfo() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{/* 步骤2: 生成提示词 */}
|
{/* 步骤2: 生成提示词 */}
|
||||||
{step.childId === 2 && (
|
{step.childId === 2 && (
|
||||||
<>
|
<>
|
||||||
{/* <Text style={{ color: '#475569', fontSize: 13, lineHeight: 1.8, whiteSpace: 'pre-wrap' }}>
|
<div style={{ padding: '12px 14px', background: 'rgba(255,255,255,0.6)', borderRadius: 10, border: '1px solid rgba(99, 102, 241, 0.08)', marginBottom: 4 }}>
|
||||||
{taskDetail?.generationPrompt || '暂无提示词'}
|
<Text style={{ color: '#475569', fontSize: 13, lineHeight: 1.8, whiteSpace: 'pre-wrap' }}>
|
||||||
</Text> */}
|
{step?.output?.payload?.prompt || '暂无提示词'}
|
||||||
<Text style={{ color: '#475569', fontSize: 13, lineHeight: 1.8, whiteSpace: 'pre-wrap' }}>
|
</Text>
|
||||||
{/* {taskDetail?.generationPrompt || '暂无提示词'} */}
|
</div>
|
||||||
{step?.output?.payload?.prompt || '暂无提示词'}
|
|
||||||
</Text>
|
|
||||||
<Space style={{ marginTop: 16, gap: 12, width: '100%' }}>
|
<Space style={{ marginTop: 16, gap: 12, width: '100%' }}>
|
||||||
<Button
|
<Button
|
||||||
type="default"
|
type="default"
|
||||||
icon={<EditOutlined />}
|
icon={<EditOutlined />}
|
||||||
onClick={() => handleOpenModal(step?.output?.payload?.prompt, 'image', step.id)}
|
onClick={() => handleOpenModal(step?.output?.payload?.prompt, 'image', step.id)}
|
||||||
style={{ flex: 1, borderRadius: 8, borderColor: '#6366f1', color: '#6366f1', height: 36 }}
|
style={{ flex: 1, borderRadius: 10, borderColor: 'rgba(99, 102, 241, 0.3)', color: '#6366f1', height: 36, fontWeight: 500, background: 'rgba(99, 102, 241, 0.04)' }}
|
||||||
disabled={step.status !== 'completed'}
|
disabled={step.status !== 'completed'}
|
||||||
>
|
>
|
||||||
修改提示词
|
修改提示词
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => { message.info('正在生成图片,请稍候...'); createimage(step.id); }} type="primary" style={{ flex: 1, borderRadius: 8, background: '#6366f1', borderColor: '#6366f1', height: 36 }} disabled={step.status !== 'completed'}>
|
<Button
|
||||||
|
onClick={() => { message.info('正在生成图片,请稍候...'); createimage(step.id); }}
|
||||||
|
type="primary"
|
||||||
|
style={{ flex: 1, borderRadius: 10, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', height: 36, fontWeight: 500, boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }}
|
||||||
|
disabled={step.status !== 'completed'}
|
||||||
|
>
|
||||||
下一步:生成图片
|
下一步:生成图片
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
@@ -563,19 +654,39 @@ function InitialInfo() {
|
|||||||
{/* 步骤3: 生成产品融合图 */}
|
{/* 步骤3: 生成产品融合图 */}
|
||||||
{step.childId === 3 && (
|
{step.childId === 3 && (
|
||||||
<>
|
<>
|
||||||
<div style={{ borderRadius: 8, overflow: 'hidden', marginBottom: 16, background: '#f1f5f9' }}>
|
<div style={{ borderRadius: 12, overflow: 'hidden', marginBottom: 16, background: 'linear-gradient(135deg, rgba(248,250,252,0.8) 0%, rgba(238,242,255,0.6) 100%)', border: '1px solid rgba(99, 102, 241, 0.08)', boxShadow: '0 2px 8px rgba(99, 102, 241, 0.04)', position: 'relative' }}>
|
||||||
{/* {step.output.result.result_image_url} */}
|
|
||||||
|
|
||||||
|
|
||||||
{taskDetail.finalImageUrl ? (
|
{taskDetail.finalImageUrl ? (
|
||||||
<img src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.finalImageUrl}`} alt="融合图" style={{ width: '100%', height: 180, objectFit: 'cover' }} />
|
<img src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.finalImageUrl}`} alt="融合图" style={{ width: '100%', height: 180, objectFit: 'cover' }} />
|
||||||
) : (
|
) : (
|
||||||
<div style={{ width: '100%', height: 180, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#94a3b8', fontSize: 14 }}>暂无融合图</div>
|
<div style={{ width: '100%', height: 180, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#94a3b8', fontSize: 14 }}>暂无融合图</div>
|
||||||
)}
|
)}
|
||||||
|
<Button
|
||||||
|
type="default"
|
||||||
|
icon={<EditOutlined />}
|
||||||
|
onClick={() => newcreateimage()}
|
||||||
|
disabled={step.status !== 'completed'}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 8,
|
||||||
|
left: 8,
|
||||||
|
borderRadius: 8,
|
||||||
|
borderColor: 'rgba(99, 102, 241, 0.3)',
|
||||||
|
color: '#6366f1',
|
||||||
|
height: 30,
|
||||||
|
fontWeight: 500,
|
||||||
|
background: 'rgba(255,255,255,0.85)',
|
||||||
|
backdropFilter: 'blur(4px)',
|
||||||
|
padding: '0 12px',
|
||||||
|
fontSize: 12,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
重新生成
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{/* 引擎选择器 */}
|
{/* 引擎选择器和视频参数设置 */}
|
||||||
<div style={{ marginBottom: 12 }}>
|
<p style={{marginBottom:6,fontSize: 14, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>视频参数选择:</p>
|
||||||
<div style={{ position: 'relative', display: 'inline-block' }}>
|
<div style={{ width:'100%', display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
|
||||||
|
<div style={{ flex: 1, position: 'relative', display: 'inline-block' }}>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowEngineModal(!showEngineModal);
|
setShowEngineModal(!showEngineModal);
|
||||||
@@ -583,12 +694,12 @@ function InitialInfo() {
|
|||||||
}}
|
}}
|
||||||
className="image-settings-trigger"
|
className="image-settings-trigger"
|
||||||
style={{
|
style={{
|
||||||
minWidth: 200,
|
width: '100%',
|
||||||
padding: '4px 12px',
|
padding: '4px 12px',
|
||||||
height: 34,
|
height: 34,
|
||||||
borderRadius: 8,
|
borderRadius: 10,
|
||||||
border: 'none',
|
border: '1px solid rgba(99, 102, 241, 0.15)',
|
||||||
backgroundColor: '#f1f5f9',
|
backgroundColor: 'rgba(255,255,255,0.7)',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -596,7 +707,7 @@ function InitialInfo() {
|
|||||||
transition: 'all 0.2s',
|
transition: 'all 0.2s',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SettingOutlined style={{ fontSize: 14, color: '#64748b' }} />
|
<SettingOutlined style={{ fontSize: 14, color: '#6366f1' }} />
|
||||||
<Text style={{
|
<Text style={{
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
@@ -612,7 +723,7 @@ function InitialInfo() {
|
|||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
bottom: 'calc(100% + 8px)',
|
bottom: 'calc(100% + 8px)',
|
||||||
left: -20,
|
left: -10,
|
||||||
width: 350,
|
width: 350,
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
borderRadius: 16,
|
borderRadius: 16,
|
||||||
@@ -702,11 +813,9 @@ function InitialInfo() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 视频参数设置 */}
|
{/* 视频参数设置 */}
|
||||||
<div style={{ marginBottom: 16 }}>
|
<div style={{ flex: 1, position: 'relative', display: 'inline-block' }}>
|
||||||
<div style={{ position: 'relative', display: 'inline-block' }}>
|
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowVideoSettingsModal(!showVideoSettingsModal);
|
setShowVideoSettingsModal(!showVideoSettingsModal);
|
||||||
@@ -714,12 +823,12 @@ function InitialInfo() {
|
|||||||
}}
|
}}
|
||||||
className="image-settings-trigger"
|
className="image-settings-trigger"
|
||||||
style={{
|
style={{
|
||||||
minWidth: 220,
|
width: '100%',
|
||||||
padding: '4px 12px',
|
padding: '4px 12px',
|
||||||
height: 34,
|
height: 34,
|
||||||
borderRadius: 8,
|
borderRadius: 10,
|
||||||
border: 'none',
|
border: '1px solid rgba(99, 102, 241, 0.15)',
|
||||||
backgroundColor: '#f1f5f9',
|
backgroundColor: 'rgba(255,255,255,0.7)',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -727,7 +836,7 @@ function InitialInfo() {
|
|||||||
transition: 'all 0.2s',
|
transition: 'all 0.2s',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LayoutOutlined style={{ fontSize: 14, color: '#64748b' }} />
|
<LayoutOutlined style={{ fontSize: 14, color: '#6366f1' }} />
|
||||||
<Text style={{
|
<Text style={{
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
@@ -743,7 +852,7 @@ function InitialInfo() {
|
|||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
bottom: 'calc(100% + 8px)',
|
bottom: 'calc(100% + 8px)',
|
||||||
left: -20,
|
left: -160,
|
||||||
width: 350,
|
width: 350,
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
borderRadius: 16,
|
borderRadius: 16,
|
||||||
@@ -935,41 +1044,40 @@ function InitialInfo() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Space style={{ width: '100%', gap: 12 }}>
|
<Button
|
||||||
<Button type="default" icon={<EditOutlined />} style={{ flex: 1, borderRadius: 8, borderColor: '#6366f1', color: '#6366f1', height: 36 }
|
type="primary"
|
||||||
} onClick={() => newcreateimage()} disabled={step.status !== 'completed'}>
|
style={{ width: '100%', borderRadius: 10, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', height: 36, fontWeight: 500, boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }}
|
||||||
重新生成
|
onClick={() => { message.info('正在生成视频提示词,请稍候...'); handleNextStep(step.id); }}
|
||||||
</Button>
|
disabled={step.status !== 'completed'}
|
||||||
<Button
|
>
|
||||||
type="primary"
|
下一步:生成视频提示词
|
||||||
style={{ flex: 1, borderRadius: 8, background: '#6366f1', borderColor: '#6366f1', height: 36 }}
|
</Button>
|
||||||
onClick={() => { message.info('正在生成视频提示词,请稍候...'); handleNextStep(step.id); }}
|
|
||||||
disabled={step.status !== 'completed'}
|
|
||||||
>
|
|
||||||
下一步:生成视频提示词
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{/* 步骤4: 生成视频提示词 */}
|
{/* 步骤4: 生成视频提示词 */}
|
||||||
{step.childId === 4 && (
|
{step.childId === 4 && (
|
||||||
<>
|
<>
|
||||||
<Text style={{ color: '#475569', fontSize: 13, lineHeight: 1.8, whiteSpace: 'pre-wrap' }}>
|
<div style={{ padding: '12px 14px', background: 'rgba(255,255,255,0.6)', borderRadius: 10, border: '1px solid rgba(99, 102, 241, 0.08)', marginBottom: 4 }}>
|
||||||
{/* {taskDetail?.videoPrompt || '暂无视频提示词'} */}
|
<Text style={{ color: '#475569', fontSize: 13, lineHeight: 1.8, whiteSpace: 'pre-wrap' }}>
|
||||||
{step?.output?.payload?.finalPrompt || '暂无提示词'}
|
{step?.output?.payload?.finalPrompt || '暂无提示词'}
|
||||||
|
</Text>
|
||||||
</Text>
|
</div>
|
||||||
<Space style={{ marginTop: 16, gap: 12, width: '100%' }}>
|
<Space style={{ marginTop: 16, gap: 12, width: '100%' }}>
|
||||||
<Button
|
<Button
|
||||||
type="default"
|
type="default"
|
||||||
icon={<EditOutlined />}
|
icon={<EditOutlined />}
|
||||||
onClick={() => handleOpenModal(step?.output?.payload?.promptSchema, 'video', step.id)}
|
onClick={() => handleOpenModal(step?.output?.payload?.promptSchema, 'video', step.id)}
|
||||||
style={{ flex: 1, borderRadius: 8, borderColor: '#6366f1', color: '#6366f1', height: 36 }}
|
style={{ flex: 1, borderRadius: 10, borderColor: 'rgba(99, 102, 241, 0.3)', color: '#6366f1', height: 36, fontWeight: 500, background: 'rgba(99, 102, 241, 0.04)' }}
|
||||||
disabled={step.status !== 'completed'}
|
disabled={step.status !== 'completed'}
|
||||||
>
|
>
|
||||||
修改提示词
|
修改提示词
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => { message.info('正在生成视频,请稍候...'); createvideo(step.id,step.engineId); }} type="primary" style={{ flex: 1, borderRadius: 8, background: '#6366f1', borderColor: '#6366f1', height: 36 }} disabled={step.status !== 'completed'}>
|
<Button
|
||||||
|
onClick={() => { message.info('正在生成视频,请稍候...'); createvideo(step.id, step.engineId); }}
|
||||||
|
type="primary"
|
||||||
|
style={{ flex: 1, borderRadius: 10, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', height: 36, fontWeight: 500, boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }}
|
||||||
|
disabled={step.status !== 'completed'}
|
||||||
|
>
|
||||||
下一步:生成视频
|
下一步:生成视频
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
@@ -978,7 +1086,7 @@ function InitialInfo() {
|
|||||||
{/* 步骤5: 生成最终视频 */}
|
{/* 步骤5: 生成最终视频 */}
|
||||||
{step.childId === 5 && (
|
{step.childId === 5 && (
|
||||||
<>
|
<>
|
||||||
<div style={{ borderRadius: 8, overflow: 'hidden', marginBottom: 16, position: 'relative', background: '#f1f5f9' }}>
|
<div style={{ borderRadius: 12, overflow: 'hidden', marginBottom: 16, position: 'relative', background: 'linear-gradient(135deg, rgba(248,250,252,0.8) 0%, rgba(238,242,255,0.6) 100%)', border: '1px solid rgba(99, 102, 241, 0.08)', boxShadow: '0 2px 8px rgba(99, 102, 241, 0.04)' }}>
|
||||||
{step.status === 'completed' && taskDetail?.finalVideoUrl ? (
|
{step.status === 'completed' && taskDetail?.finalVideoUrl ? (
|
||||||
<video src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.finalVideoUrl}`} style={{ width: '100%', height: 180, objectFit: 'cover' }} controls />
|
<video src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.finalVideoUrl}`} style={{ width: '100%', height: 180, objectFit: 'cover' }} controls />
|
||||||
) : step.status === 'processing' ? (
|
) : step.status === 'processing' ? (
|
||||||
@@ -993,13 +1101,19 @@ function InitialInfo() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Space style={{ width: '100%', gap: 12 }}>
|
<Space style={{ width: '100%', gap: 12 }}>
|
||||||
<Button onClick={() => agincreatevideo()} type="default" icon={<EditOutlined />} style={{ flex: 1, borderRadius: 8, borderColor: '#6366f1', color: '#6366f1', height: 36 }} disabled={step.status !== 'completed'}>
|
<Button
|
||||||
|
onClick={() => agincreatevideo()}
|
||||||
|
type="default"
|
||||||
|
icon={<EditOutlined />}
|
||||||
|
style={{ flex: 1, borderRadius: 10, borderColor: 'rgba(99, 102, 241, 0.3)', color: '#6366f1', height: 36, fontWeight: 500, background: 'rgba(99, 102, 241, 0.04)' }}
|
||||||
|
disabled={step.status !== 'completed'}
|
||||||
|
>
|
||||||
重新生成
|
重新生成
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
icon={<DownloadOutlined />}
|
icon={<DownloadOutlined />}
|
||||||
style={{ flex: 1, borderRadius: 8, background: '#6366f1', borderColor: '#6366f1', height: 36 }}
|
style={{ flex: 1, borderRadius: 10, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', height: 36, fontWeight: 500, boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }}
|
||||||
disabled={step.status !== 'completed'}
|
disabled={step.status !== 'completed'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const videoUrl = `${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail?.finalVideoUrl}&download=1`;
|
const videoUrl = `${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail?.finalVideoUrl}&download=1`;
|
||||||
@@ -1025,21 +1139,33 @@ function InitialInfo() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Modal
|
<Modal
|
||||||
title={currentType === 'video' ? '修改视频提示词' : '修改提示词'}
|
title={
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||||
|
<div style={{ width: 4, height: 18, background: 'linear-gradient(180deg, #6366f1 0%, #8b5cf6 100%)', borderRadius: 2 }} />
|
||||||
|
<span style={{ fontSize: 15, fontWeight: 700, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
|
||||||
|
{currentType === 'video' ? '修改视频提示词' : '修改提示词'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
open={modalVisible}
|
open={modalVisible}
|
||||||
onCancel={() => { if (!promptSaving) setModalVisible(false); }}
|
onCancel={() => { if (!promptSaving) setModalVisible(false); }}
|
||||||
footer={[
|
footer={[
|
||||||
<Button key="cancel" onClick={() => setModalVisible(false)} disabled={promptSaving}>取消</Button>,
|
<Button key="cancel" onClick={() => setModalVisible(false)} disabled={promptSaving} style={{ borderRadius: 8 }}>取消</Button>,
|
||||||
<Button key="confirm" type="primary" onClick={handleConfirm} loading={promptSaving} disabled={promptSaving}>确认</Button>,
|
<Button key="confirm" type="primary" onClick={handleConfirm} loading={promptSaving} disabled={promptSaving} style={{ borderRadius: 8, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none' }}>确认</Button>,
|
||||||
]}
|
]}
|
||||||
width={800}
|
width={800}
|
||||||
|
style={{ borderRadius: 16 }}
|
||||||
|
styles={{
|
||||||
|
header: { background: 'rgba(255,255,255,0.6)', backdropFilter: 'blur(10px)', borderBottom: '1px solid rgba(99, 102, 241, 0.08)', padding: '16px 24px' },
|
||||||
|
body: { background: 'linear-gradient(135deg, #f8fafc 0%, #eef2ff 50%, #f0f9ff 100%)', padding: '20px 24px' },
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{currentType === 'image' ? (
|
{currentType === 'image' ? (
|
||||||
<TextArea
|
<TextArea
|
||||||
value={promptText}
|
value={promptText}
|
||||||
onChange={(e) => setPromptText(e.target.value)}
|
onChange={(e) => setPromptText(e.target.value)}
|
||||||
rows={8}
|
rows={8}
|
||||||
style={{ width: '100%', height: 300 }}
|
style={{ width: '100%', height: 300, borderRadius: 10, border: '1px solid rgba(99, 102, 241, 0.15)', background: 'rgba(255,255,255,0.8)' }}
|
||||||
placeholder="请输入提示词"
|
placeholder="请输入提示词"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@@ -1050,171 +1176,186 @@ function InitialInfo() {
|
|||||||
</Modal>
|
</Modal>
|
||||||
{/* 创作记录弹窗 */}
|
{/* 创作记录弹窗 */}
|
||||||
<Modal
|
<Modal
|
||||||
title="创作记录"
|
title={
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||||
|
<div style={{ width: 4, height: 18, background: 'linear-gradient(180deg, #6366f1 0%, #8b5cf6 100%)', borderRadius: 2 }} />
|
||||||
|
<span style={{ fontSize: 15, fontWeight: 700, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
|
||||||
|
创作记录
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
open={isModalOpen}
|
open={isModalOpen}
|
||||||
onCancel={() => setIsModalOpen(false)}
|
onCancel={() => setIsModalOpen(false)}
|
||||||
width={800}
|
width={850}
|
||||||
footer={null}
|
footer={null}
|
||||||
style={{ borderRadius: 12 }}
|
style={{ borderRadius: 16 }}
|
||||||
|
styles={{
|
||||||
|
header: { background: 'rgba(255,255,255,0.6)', backdropFilter: 'blur(10px)', borderBottom: '1px solid rgba(99, 102, 241, 0.08)', padding: '16px 24px' },
|
||||||
|
body: { background: 'linear-gradient(135deg, #f8fafc 0%, #eef2ff 50%, #f0f9ff 100%)', },
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{/* 搜索区域 */}
|
{/* 搜索区域 */}
|
||||||
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16 }}>
|
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16 }}>
|
||||||
<Input
|
<Input
|
||||||
placeholder="搜索产品名称"
|
placeholder="搜索产品名称"
|
||||||
style={{ width: 200, borderRadius: 6, marginRight: 8 }}
|
style={{ width: 200, borderRadius: 8, marginRight: 8, border: '1px solid rgba(99, 102, 241, 0.15)', background: 'rgba(255,255,255,0.8)' }}
|
||||||
/>
|
/>
|
||||||
<Button type="primary" style={{ borderRadius: 6 }}>
|
<Button type="primary" style={{ borderRadius: 8, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }}>
|
||||||
搜索
|
搜索
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 表格 */}
|
{/* 表格 */}
|
||||||
<Table
|
<Table
|
||||||
columns={[
|
columns={[
|
||||||
{
|
{
|
||||||
title: '产品名称',
|
title: '产品名称',
|
||||||
dataIndex: 'targetProjectName',
|
dataIndex: 'targetProjectName',
|
||||||
key: 'targetProjectName',
|
key: 'targetProjectName',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
key: 'status',
|
key: 'status',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
render: (text: string, record: any) => {
|
render: (text: string, record: any) => {
|
||||||
const status = text;
|
const status = text;
|
||||||
const currentStepCode = record.current_step_code || record.currentStepCode;
|
const currentStepCode = record.current_step_code || record.currentStepCode;
|
||||||
|
|
||||||
const getStatusText = () => {
|
const getStatusText = () => {
|
||||||
if (currentStepCode === 'image_prompt_optimize') {
|
if (currentStepCode === 'image_prompt_optimize') {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'waiting_user':
|
case 'waiting_user':
|
||||||
return '等待融合图生成';
|
return { text: '等待融合图生成', color: '#f59e0b' };
|
||||||
case 'processing':
|
case 'processing':
|
||||||
return '图片提示词生成中';
|
return { text: '图片提示词生成中', color: '#f59e0b' };
|
||||||
case 'completed':
|
case 'completed':
|
||||||
return '图片提示词生成成功';
|
return { text: '图片提示词生成成功', color: '#10b981' };
|
||||||
case 'failed':
|
case 'failed':
|
||||||
return '图片提示词生成失败';
|
return { text: '图片提示词生成失败', color: '#ef4444' };
|
||||||
default:
|
default:
|
||||||
return status || '-';
|
return { text: status || '-', color: '#666' };
|
||||||
}
|
}
|
||||||
} else if (currentStepCode === 'image_generate') {
|
} else if (currentStepCode === 'image_generate') {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'waiting_user':
|
case 'waiting_user':
|
||||||
return '等待生成视频提示词';
|
return { text: '等待生成视频提示词', color: '#f59e0b' };
|
||||||
case 'processing':
|
case 'processing':
|
||||||
return '融合图生成中';
|
return { text: '融合图生成中', color: '#f59e0b' };
|
||||||
case 'completed':
|
case 'completed':
|
||||||
return '融合图生成成功';
|
return { text: '融合图生成成功', color: '#10b981' };
|
||||||
case 'failed':
|
case 'failed':
|
||||||
return '融合图生成失败';
|
return { text: '融合图生成失败', color: '#ef4444' };
|
||||||
default:
|
default:
|
||||||
return status || '-';
|
return { text: status || '-', color: '#666' };
|
||||||
}
|
}
|
||||||
} else if (currentStepCode === 'video_prompt_optimize') {
|
} else if (currentStepCode === 'video_prompt_optimize') {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'waiting_user':
|
case 'waiting_user':
|
||||||
return '等待最终视频生成';
|
return { text: '等待最终视频生成', color: '#f59e0b' };
|
||||||
case 'processing':
|
case 'processing':
|
||||||
return '视频提示词生成中';
|
return { text: '视频提示词生成中', color: '#f59e0b' };
|
||||||
case 'completed':
|
case 'completed':
|
||||||
return '视频提示词生成成功';
|
return { text: '视频提示词生成成功', color: '#10b981' };
|
||||||
case 'failed':
|
case 'failed':
|
||||||
return '视频提示词生成失败';
|
return { text: '视频提示词生成失败', color: '#ef4444' };
|
||||||
default:
|
default:
|
||||||
return status || '-';
|
return { text: status || '-', color: '#666' };
|
||||||
}
|
}
|
||||||
} else if (currentStepCode === 'video_generate') {
|
} else if (currentStepCode === 'video_generate') {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'waiting_user':
|
case 'waiting_user':
|
||||||
return '';
|
return { text: '', color: '#666' };
|
||||||
case 'processing':
|
case 'processing':
|
||||||
return '最终视频生成中';
|
return { text: '最终视频生成中', color: '#f59e0b' };
|
||||||
case 'completed':
|
case 'completed':
|
||||||
return '最终视频生成成功';
|
return { text: '任务完成', color: '#10b981' };
|
||||||
case 'failed':
|
case 'failed':
|
||||||
return '最终视频生成失败';
|
return { text: '最终视频生成失败', color: '#ef4444' };
|
||||||
default:
|
default:
|
||||||
return status || '-';
|
return { text: status || '-', color: '#666' };
|
||||||
}
|
}
|
||||||
} else if (currentStepCode === 'material_input') {
|
} else if (currentStepCode === 'material_input') {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'waiting_user':
|
case 'waiting_user':
|
||||||
return '等待生成图片提示词';
|
return { text: '等待生成图片提示词', color: '#f59e0b' };
|
||||||
case 'processing':
|
case 'processing':
|
||||||
return '素材处理中';
|
return { text: '素材处理中', color: '#f59e0b' };
|
||||||
case 'completed':
|
case 'completed':
|
||||||
return '素材上传成功';
|
return { text: '素材上传成功', color: '#10b981' };
|
||||||
case 'failed':
|
case 'failed':
|
||||||
return '素材上传失败';
|
return { text: '素材上传失败', color: '#ef4444' };
|
||||||
default:
|
default:
|
||||||
return status || '-';
|
return { text: status || '-', color: '#666' };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const statusMap: Record<string, string> = {
|
const statusMap: Record<string, { text: string; color: string }> = {
|
||||||
'pending': '子任务待处理',
|
'pending': { text: '子任务待处理', color: '#f59e0b' },
|
||||||
'waiting_user': '等待用户确认或触发',
|
'waiting_user': { text: '等待用户确认或触发', color: '#f59e0b' },
|
||||||
'processing': '子任务处理中',
|
'processing': { text: '子任务处理中', color: '#f59e0b' },
|
||||||
'completed': '子任务完成',
|
'completed': { text: '子任务完成', color: '#10b981' },
|
||||||
'failed': '子任务失败',
|
'failed': { text: '子任务失败', color: '#ef4444' },
|
||||||
'cancelled': '子任务取消',
|
'cancelled': { text: '子任务取消', color: '#94a3b8' },
|
||||||
};
|
};
|
||||||
return statusMap[status] || status || '-';
|
return statusMap[status] || { text: status || '-', color: '#666' };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return getStatusText();
|
const result = getStatusText() as { text: string; color: string };
|
||||||
},
|
if (typeof result === 'string') {
|
||||||
},
|
return <span style={{ fontSize: 12, color: '#666', fontWeight: 500 }}>{result}</span>;
|
||||||
{
|
}
|
||||||
title: '创建时间',
|
return <span style={{ fontSize: 12, color: result.color, fontWeight: 500 }}>{result.text}</span>;
|
||||||
dataIndex: 'createdAt',
|
},
|
||||||
key: 'createdAt',
|
},
|
||||||
align: 'center',
|
{
|
||||||
|
title: '创建时间',
|
||||||
render: (text: string) => {
|
dataIndex: 'createdAt',
|
||||||
if (!text) return '-';
|
key: 'createdAt',
|
||||||
const date = new Date(text);
|
align: 'center',
|
||||||
const year = date.getFullYear();
|
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
render: (text: string) => {
|
||||||
const day = String(date.getDate()).padStart(2, '0');
|
if (!text) return '-';
|
||||||
const hours = String(date.getHours()).padStart(2, '0');
|
const date = new Date(text);
|
||||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
const year = date.getFullYear();
|
||||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
},
|
const hours = String(date.getHours()).padStart(2, '0');
|
||||||
},
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||||
{
|
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||||
title: '操作',
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||||
key: 'action',
|
},
|
||||||
align: 'center',
|
},
|
||||||
|
{
|
||||||
render: (_, record) => (
|
title: '操作',
|
||||||
<button
|
key: 'action',
|
||||||
|
align: 'center',
|
||||||
|
|
||||||
|
render: (_, record) => (
|
||||||
|
<button
|
||||||
onClick={() => navigate(`/initial/${record.id}/initialinfo`)}
|
onClick={() => navigate(`/initial/${record.id}/initialinfo`)}
|
||||||
style={{ color: '#6366f1', textDecoration: 'none', fontSize: 12, border: 'none', background: 'none', cursor: 'pointer' }}
|
style={{ color: '#6366f1', textDecoration: 'none', fontSize: 12, border: 'none', background: 'rgba(99, 102, 241, 0.08)', padding: '4px 12px', borderRadius: 8, cursor: 'pointer', transition: 'all 0.2s', fontWeight: 500 }}
|
||||||
>
|
>
|
||||||
查看详情
|
查看详情
|
||||||
</button>
|
</button>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
dataSource={tableData}
|
dataSource={tableData}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
pagination={{
|
pagination={{
|
||||||
current: currentPage,
|
current: currentPage,
|
||||||
pageSize: pageSize,
|
pageSize: pageSize,
|
||||||
total: total,
|
total: total,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
showQuickJumper: true,
|
showQuickJumper: true,
|
||||||
showTotal: (total) => `共 ${total} 条`,
|
showTotal: (total) => `共 ${total} 条`,
|
||||||
onChange: handlePageChange,
|
onChange: handlePageChange,
|
||||||
}}
|
}}
|
||||||
style={{ fontSize: 13 }}
|
style={{ fontSize: 13 }}
|
||||||
scroll={{ y: 350 }}
|
scroll={{ y: 350 }}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { Button, Drawer, Input, Popconfirm, Spin, Table, Tag, Tooltip, Upload, message } from 'antd';
|
import { Button, Drawer, Input, Modal, Popconfirm, Spin, Table, Tag, Tooltip, Upload, message } from 'antd';
|
||||||
import { ArrowLeftOutlined, PlusOutlined, XOutlined } from '@ant-design/icons';
|
import { ArrowLeftOutlined, PlusOutlined, XOutlined } from '@ant-design/icons';
|
||||||
import { createRemoveLens, getShotReplicationDetail, Removelist, removeCreate, splitCustom, uploadImage } from '../api';
|
import { createRemoveLens, getShotReplicationDetail, Removelist, removeCreate, splitCustom, uploadImage } from '../api';
|
||||||
import VideoTrimPicker from '../components/VideoTrimPicker';
|
import VideoTrimPicker from '../components/VideoTrimPicker';
|
||||||
@@ -23,6 +23,10 @@ function RemoveInfo() {
|
|||||||
const [drawerVisible, setDrawerVisible] = useState(false);
|
const [drawerVisible, setDrawerVisible] = useState(false);
|
||||||
const [trimModalVisible, setTrimModalVisible] = useState(false);
|
const [trimModalVisible, setTrimModalVisible] = useState(false);
|
||||||
const [currentSegment, setCurrentSegment] = useState<string | null>(null);
|
const [currentSegment, setCurrentSegment] = useState<string | null>(null);
|
||||||
|
const [currentSegmentName, setCurrentSegmentName] = useState<string | null>(null);
|
||||||
|
const [previewVideoUrl, setPreviewVideoUrl] = useState<string | null>(null);
|
||||||
|
const [previewModalVisible, setPreviewModalVisible] = useState(false);
|
||||||
|
const previewVideoRef = useRef<HTMLVideoElement>(null);
|
||||||
const [productName, setProductName] = useState('');
|
const [productName, setProductName] = useState('');
|
||||||
const [productSellingPoint, setProductSellingPoint] = useState('');
|
const [productSellingPoint, setProductSellingPoint] = useState('');
|
||||||
const [productImage, setProductImage] = useState('');
|
const [productImage, setProductImage] = useState('');
|
||||||
@@ -31,6 +35,7 @@ function RemoveInfo() {
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [splitLoading, setSplitLoading] = useState(false);
|
const [splitLoading, setSplitLoading] = useState(false);
|
||||||
const [autoSplitLoading, setAutoSplitLoading] = useState(false);
|
const [autoSplitLoading, setAutoSplitLoading] = useState(false);
|
||||||
|
const pollingIntervalRef = useRef<number | null>(null);
|
||||||
|
|
||||||
const videoUrl = useMemo(() => buildAssetUrl(taskDetail?.videoUrl), [taskDetail?.videoUrl]);
|
const videoUrl = useMemo(() => buildAssetUrl(taskDetail?.videoUrl), [taskDetail?.videoUrl]);
|
||||||
|
|
||||||
@@ -58,7 +63,69 @@ function RemoveInfo() {
|
|||||||
if (!creatID) return;
|
if (!creatID) return;
|
||||||
try {
|
try {
|
||||||
const res = await Removelist(creatID);
|
const res = await Removelist(creatID);
|
||||||
setTableData(res.items || []);
|
const items = res.items || [];
|
||||||
|
setTableData(items);
|
||||||
|
|
||||||
|
// 检查是否需要开启轮询
|
||||||
|
const needsPolling = items.some(
|
||||||
|
(item: any) =>
|
||||||
|
item.moduleProjectCurrentStepCode !== 'video_generate' &&
|
||||||
|
item.moduleProjectStatus !== 'completed' &&
|
||||||
|
item.moduleProjectStatus !== 'failed'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (needsPolling) {
|
||||||
|
// 如果没有轮询在运行,则开启轮询
|
||||||
|
if (!pollingIntervalRef.current) {
|
||||||
|
pollingIntervalRef.current = window.setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const pollRes = await Removelist(creatID);
|
||||||
|
const pollItems = pollRes.items || [];
|
||||||
|
|
||||||
|
// 只更新未完成的项目
|
||||||
|
setTableData((prevData) =>
|
||||||
|
prevData.map((prevItem) => {
|
||||||
|
const pollItem = pollItems.find((p: any) => p.id === prevItem.id);
|
||||||
|
if (pollItem) {
|
||||||
|
// 只有当原项目未完成时才更新
|
||||||
|
if (
|
||||||
|
prevItem.moduleProjectCurrentStepCode !== 'video_generate' ||
|
||||||
|
(prevItem.moduleProjectStatus !== 'completed' && prevItem.moduleProjectStatus !== 'failed')
|
||||||
|
) {
|
||||||
|
return pollItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prevItem;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// 检查是否所有项目都已完成
|
||||||
|
const allCompleted = pollItems.every(
|
||||||
|
(item: any) =>
|
||||||
|
item.moduleProjectCurrentStepCode === 'video_generate' ||
|
||||||
|
item.moduleProjectStatus === 'completed' ||
|
||||||
|
item.moduleProjectStatus === 'failed'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (allCompleted) {
|
||||||
|
// 停止轮询
|
||||||
|
if (pollingIntervalRef.current) {
|
||||||
|
clearInterval(pollingIntervalRef.current);
|
||||||
|
pollingIntervalRef.current = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('轮询获取拆镜列表失败:', error);
|
||||||
|
}
|
||||||
|
}, 30000); // 30秒轮询
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果不需要轮询,停止现有轮询
|
||||||
|
if (pollingIntervalRef.current) {
|
||||||
|
clearInterval(pollingIntervalRef.current);
|
||||||
|
pollingIntervalRef.current = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
message.error('获取拆镜列表失败');
|
message.error('获取拆镜列表失败');
|
||||||
}
|
}
|
||||||
@@ -70,10 +137,19 @@ function RemoveInfo() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refreshPageData();
|
refreshPageData();
|
||||||
|
|
||||||
|
// 组件卸载时清除轮询
|
||||||
|
return () => {
|
||||||
|
if (pollingIntervalRef.current) {
|
||||||
|
clearInterval(pollingIntervalRef.current);
|
||||||
|
pollingIntervalRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
}, [refreshPageData]);
|
}, [refreshPageData]);
|
||||||
|
|
||||||
const handleGenerate = (segmentId: string) => {
|
const handleGenerate = (segmentId: string, segmentName: string) => {
|
||||||
setCurrentSegment(segmentId);
|
setCurrentSegment(segmentId);
|
||||||
|
setCurrentSegmentName(segmentName);
|
||||||
setDrawerVisible(true);
|
setDrawerVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -81,6 +157,7 @@ function RemoveInfo() {
|
|||||||
setDrawerVisible(false);
|
setDrawerVisible(false);
|
||||||
setCurrentSegment(null);
|
setCurrentSegment(null);
|
||||||
setProductName('');
|
setProductName('');
|
||||||
|
setCurrentSegmentName('');
|
||||||
setProductSellingPoint('');
|
setProductSellingPoint('');
|
||||||
setProductImage('');
|
setProductImage('');
|
||||||
};
|
};
|
||||||
@@ -218,13 +295,26 @@ function RemoveInfo() {
|
|||||||
width: 150,
|
width: 150,
|
||||||
align: 'center' as const,
|
align: 'center' as const,
|
||||||
render: (_: any, record: any) => (
|
render: (_: any, record: any) => (
|
||||||
<div style={{ position: 'relative', width: 120, height: 80, borderRadius: 6, overflow: 'hidden', background: '#f1f5f9' }}>
|
<div style={{ position: 'relative', width: 120, height: 80, borderRadius: 6, overflow: 'hidden', background: '#f1f5f9', cursor: 'pointer' }}
|
||||||
|
onClick={() => {
|
||||||
|
console.log('video clicked:', record.segmentVideoUrl);
|
||||||
|
if (record.segmentVideoUrl) {
|
||||||
|
setPreviewVideoUrl(buildAssetUrl(record.segmentVideoUrl));
|
||||||
|
setPreviewModalVisible(true);
|
||||||
|
}
|
||||||
|
}}>
|
||||||
{record.segmentVideoUrl ? (
|
{record.segmentVideoUrl ? (
|
||||||
<video
|
<>
|
||||||
controls
|
<video
|
||||||
src={buildAssetUrl(record.segmentVideoUrl)}
|
src={buildAssetUrl(record.segmentVideoUrl)}
|
||||||
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
|
style={{ width: '100%', height: '100%', objectFit: 'cover', pointerEvents: 'none' }}
|
||||||
/>
|
/>
|
||||||
|
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(0,0,0,0.3)' }}>
|
||||||
|
<div style={{ width: 32, height: 32, borderRadius: '50%', background: 'rgba(255,255,255,0.9)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
|
<div style={{ width: 0, height: 0, borderTop: '8px solid transparent', borderBottom: '8px solid transparent', borderLeft: '14px solid #6366f1', marginLeft: 2 }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#94a3b8', fontSize: 12 }}>
|
<div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#94a3b8', fontSize: 12 }}>
|
||||||
{record.splitStatus === 'failed' ? '切割失败' : '切割中'}
|
{record.splitStatus === 'failed' ? '切割失败' : '切割中'}
|
||||||
@@ -238,15 +328,7 @@ function RemoveInfo() {
|
|||||||
width: 500,
|
width: 500,
|
||||||
align: 'left' as const,
|
align: 'left' as const,
|
||||||
render: (_: any, record: any) => {
|
render: (_: any, record: any) => {
|
||||||
console.log('视频内容列数据:', {
|
|
||||||
record,
|
|
||||||
sourceMode: record.source_mode,
|
|
||||||
sourceMode2: record.sourceMode,
|
|
||||||
analysisStatus: record.analysis_status,
|
|
||||||
analysisStatus2: record.analysisStatus,
|
|
||||||
segmentContent: record.segmentContent,
|
|
||||||
lastError: record.lastError,
|
|
||||||
});
|
|
||||||
|
|
||||||
// AI建议的片段直接显示内容,不经过分析状态判断
|
// AI建议的片段直接显示内容,不经过分析状态判断
|
||||||
if (record.source_mode === 'ai_suggestion' || record.sourceMode === 'ai_suggestion') {
|
if (record.source_mode === 'ai_suggestion' || record.sourceMode === 'ai_suggestion') {
|
||||||
@@ -296,91 +378,95 @@ function RemoveInfo() {
|
|||||||
render: (text: string, record: any) => {
|
render: (text: string, record: any) => {
|
||||||
const status = text;
|
const status = text;
|
||||||
const currentStepCode = record.moduleProjectCurrentStepCode || record.currentStepCode;
|
const currentStepCode = record.moduleProjectCurrentStepCode || record.currentStepCode;
|
||||||
console.log('currentStepCode',status, currentStepCode);
|
|
||||||
|
|
||||||
const getStatusText = () => {
|
const getStatusText = () => {
|
||||||
if (currentStepCode === 'image_prompt_optimize') {
|
if (currentStepCode === 'image_prompt_optimize') {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'waiting_user':
|
case 'waiting_user':
|
||||||
return '等待融合图生成';
|
return { text: '等待融合图生成', color: '#f59e0b' };
|
||||||
case 'processing':
|
case 'processing':
|
||||||
return '图片提示词生成中';
|
return { text: '图片提示词生成中', color: '#f59e0b' };
|
||||||
case 'completed':
|
case 'completed':
|
||||||
return '图片提示词生成成功';
|
return { text: '图片提示词生成成功', color: '#10b981' };
|
||||||
case 'failed':
|
case 'failed':
|
||||||
return '图片提示词生成失败';
|
return { text: '图片提示词生成失败', color: '#ef4444' };
|
||||||
default:
|
default:
|
||||||
return status || '-';
|
return { text: status || '-', color: '#666' };
|
||||||
}
|
}
|
||||||
} else if (currentStepCode === 'image_generate') {
|
} else if (currentStepCode === 'image_generate') {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'waiting_user':
|
case 'waiting_user':
|
||||||
return '等待生成视频提示词';
|
return { text: '等待生成视频提示词', color: '#f59e0b' };
|
||||||
case 'processing':
|
case 'processing':
|
||||||
return '融合图生成中';
|
return { text: '融合图生成中', color: '#f59e0b' };
|
||||||
case 'completed':
|
case 'completed':
|
||||||
return '融合图生成成功';
|
return { text: '融合图生成成功', color: '#10b981' };
|
||||||
case 'failed':
|
case 'failed':
|
||||||
return '融合图生成失败';
|
return { text: '融合图生成失败', color: '#ef4444' };
|
||||||
default:
|
default:
|
||||||
return status || '-';
|
return status || '-';
|
||||||
}
|
}
|
||||||
} else if (currentStepCode === 'video_prompt_optimize') {
|
} else if (currentStepCode === 'video_prompt_optimize') {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'waiting_user':
|
case 'waiting_user':
|
||||||
return '等待最终视频生成';
|
return { text: '等待最终视频生成', color: '#f59e0b' };
|
||||||
case 'processing':
|
case 'processing':
|
||||||
return '视频提示词生成中';
|
return { text: '视频提示词生成中', color: '#f59e0b' };
|
||||||
case 'completed':
|
case 'completed':
|
||||||
return '视频提示词生成成功';
|
return { text: '视频提示词生成成功', color: '#10b981' };
|
||||||
case 'failed':
|
case 'failed':
|
||||||
return '视频提示词生成失败';
|
return { text: '视频提示词生成失败', color: '#ef4444' };
|
||||||
default:
|
default:
|
||||||
return status || '-';
|
return { text: status || '-', color: '#666' };
|
||||||
}
|
}
|
||||||
} else if (currentStepCode === 'video_generate') {
|
} else if (currentStepCode === 'video_generate') {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'waiting_user':
|
case 'waiting_user':
|
||||||
return '';
|
return { text: '', color: '#666' };
|
||||||
case 'processing':
|
case 'processing':
|
||||||
return '最终视频生成中';
|
return { text: '最终视频生成中', color: '#f59e0b' };
|
||||||
case 'completed':
|
case 'completed':
|
||||||
return '最终视频生成成功';
|
return { text: '任务完成', color: '#10b981' };
|
||||||
case 'failed':
|
case 'failed':
|
||||||
return '最终视频生成失败';
|
return { text: '最终视频生成失败', color: '#ef4444' };
|
||||||
default:
|
default:
|
||||||
return status || '-';
|
return { text: status || '-', color: '#666' };
|
||||||
}
|
}
|
||||||
} else if (currentStepCode === 'material_input') {
|
} else if (currentStepCode === 'material_input') {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'waiting_user':
|
case 'waiting_user':
|
||||||
return '等待生成图片提示词';
|
return { text: '等待生成图片提示词', color: '#f59e0b' };
|
||||||
case 'processing':
|
case 'processing':
|
||||||
return '素材处理中';
|
return { text: '素材处理中', color: '#f59e0b' };
|
||||||
case 'completed':
|
case 'completed':
|
||||||
return '素材上传成功';
|
return { text: '素材上传成功', color: '#10b981' };
|
||||||
case 'failed':
|
case 'failed':
|
||||||
return '素材上传失败';
|
return { text: '素材上传失败', color: '#ef4444' };
|
||||||
default:
|
default:
|
||||||
return status || '-';
|
return { text: status || '-', color: '#666' };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!record.moduleProjectId) {
|
if (!record.moduleProjectId) {
|
||||||
return '待生成任务';
|
return { text: '待生成任务', color: '#94a3b8' };
|
||||||
}
|
}
|
||||||
const statusMap: Record<string, string> = {
|
const statusMap: Record<string, { text: string; color: string }> = {
|
||||||
'pending': '子任务待处理',
|
'pending': { text: '子任务待处理', color: '#f59e0b' },
|
||||||
'waiting_user': '等待用户确认或触发',
|
'waiting_user': { text: '等待用户确认或触发', color: '#f59e0b' },
|
||||||
'processing': '子任务处理中',
|
'processing': { text: '子任务处理中', color: '#f59e0b' },
|
||||||
'completed': '子任务完成',
|
'completed': { text: '子任务完成', color: '#10b981' },
|
||||||
'failed': '子任务失败',
|
'failed': { text: '子任务失败', color: '#ef4444' },
|
||||||
'cancelled': '子任务取消',
|
'cancelled': { text: '子任务取消', color: '#94a3b8' },
|
||||||
};
|
};
|
||||||
return statusMap[status] || status || '-';
|
const result = statusMap[status] || { text: status || '-', color: '#666' };
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return getStatusText();
|
const result = getStatusText() as { text: string; color: string };
|
||||||
|
if (typeof result === 'string') {
|
||||||
|
return <span style={{ fontSize: 12, color: '#666', fontWeight: 500 }}>{result}</span>;
|
||||||
|
}
|
||||||
|
return <span style={{ fontSize: 12, color: result.color, fontWeight: 500 }}>{result.text}</span>;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
@@ -407,16 +493,16 @@ function RemoveInfo() {
|
|||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
onClick={() => navigate(`/removelens/${record.moduleProjectId}/removefenbu`)}
|
onClick={() => navigate(`/removelens/${record.moduleProjectId}/removefenbu`)}
|
||||||
style={{ color: '#656efa', fontSize: 12, padding: 0, display: 'flex', alignItems: 'center', gap: 4 }}
|
style={{ color: '#10b981', fontSize: 12, padding: 0, display: 'flex', alignItems: 'center', gap: 4 }}
|
||||||
>
|
>
|
||||||
详情任务
|
详情任务
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
onClick={() => handleGenerate(String(record.id))}
|
onClick={() => handleGenerate(String(record.id), record.segmentName)}
|
||||||
disabled={!canCreateReplication(record)}
|
disabled={!canCreateReplication(record)}
|
||||||
style={{ color: canCreateReplication(record) ? '#656efa' : '#94a3b8', fontSize: 12, padding: 0, display: 'flex', alignItems: 'center', gap: 4 }}
|
style={{ color: canCreateReplication(record) ? '#f59e0b' : '#94a3b8', fontSize: 12, padding: 0, display: 'flex', alignItems: 'center', gap: 4 }}
|
||||||
>
|
>
|
||||||
{canCreateReplication(record) ? '视频生成' : '待切割完成'}
|
{canCreateReplication(record) ? '视频生成' : '待切割完成'}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -430,76 +516,124 @@ function RemoveInfo() {
|
|||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
minHeight: 'calc(100vh - 90px)',
|
margin: '-24px -32px -32px',
|
||||||
|
borderRadius: 20,
|
||||||
|
minHeight: 'calc(100vh - 32px)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
|
background: 'linear-gradient(135deg, #f8fafc 0%, #eef2ff 50%, #f0f9ff 100%)',
|
||||||
|
position: 'relative',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ background: '#fff' }}>
|
{/* 背景装饰 */}
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
<div style={{ position: 'absolute', top: -100, right: -100, width: 300, height: 300, background: 'radial-gradient(circle, rgba(99,102,241,0.08) 0%, transparent 70%)', borderRadius: '50%' }} />
|
||||||
<Button
|
<div style={{ position: 'absolute', bottom: -100, left: -100, width: 300, height: 300, background: 'radial-gradient(circle, rgba(167,139,250,0.08) 0%, transparent 70%)', borderRadius: '50%' }} />
|
||||||
type="text"
|
|
||||||
icon={<ArrowLeftOutlined />}
|
{/* 标题栏 */}
|
||||||
onClick={() => navigate(-1)}
|
<div style={{ padding: '20px 32px', display: 'flex', alignItems: 'center', gap: 16, position: 'relative', zIndex: 10 }}>
|
||||||
style={{ fontSize: 16, color: '#666' }}
|
<Button
|
||||||
/>
|
type="text"
|
||||||
|
icon={<ArrowLeftOutlined />}
|
||||||
|
onClick={() => navigate(-1)}
|
||||||
|
style={{
|
||||||
|
fontSize: 16,
|
||||||
|
color: '#6366f1',
|
||||||
|
background: 'rgba(99, 102, 241, 0.1)',
|
||||||
|
borderRadius: 12,
|
||||||
|
padding: '8px 16px',
|
||||||
|
transition: 'all 0.2s ease',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.background = 'rgba(99, 102, 241, 0.15)';
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.background = 'rgba(99, 102, 241, 0.1)';
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
|
||||||
|
<div style={{ width: 40, height: 2, background: 'linear-gradient(90deg, transparent, #6366f1, #8b5cf6, transparent)', borderRadius: 1 }} />
|
||||||
|
<h1 style={{ fontSize: 22, fontWeight: 700, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', margin: 0 }}>
|
||||||
|
AI视频拆镜工作台
|
||||||
|
</h1>
|
||||||
|
<div style={{ width: 40, height: 2, background: 'linear-gradient(90deg, transparent, #8b5cf6, #6366f1, transparent)', borderRadius: 1 }} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{taskDetail ? (
|
{taskDetail ? (
|
||||||
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'space-between', overflow: 'hidden' }}>
|
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'space-between', overflow: 'hidden', padding: '0 32px 32px' }}>
|
||||||
<div style={{ flex: 1, background: '#fff', borderRadius: 12, display: 'flex', alignItems: 'center', gap: 30, }}>
|
{/* 视频总结卡片 */}
|
||||||
{/* <div style={{height: '100%', display: 'flex', alignItems: 'center', gap: 30,}}> */}
|
<div
|
||||||
<div style={{ position: 'relative', width: 280, height: 160, borderRadius: 8, overflow: 'hidden', flexShrink: 0 }}>
|
style={{
|
||||||
<video
|
flex: 1,
|
||||||
controls
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0.6) 100%)',
|
||||||
src={videoUrl}
|
backdropFilter: 'blur(20px)',
|
||||||
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
|
borderRadius: 20,
|
||||||
/>
|
display: 'flex',
|
||||||
</div>
|
alignItems: 'center',
|
||||||
|
gap: 30,
|
||||||
|
padding: 24,
|
||||||
|
border: '1px solid rgba(99, 102, 241, 0.1)',
|
||||||
|
boxShadow: '0 8px 32px rgba(99, 102, 241, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.8)',
|
||||||
|
position: 'relative',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* 卡片装饰 */}
|
||||||
|
<div style={{ position: 'absolute', top: -50, right: -50, width: 200, height: 200, background: 'radial-gradient(circle, rgba(99,102,241,0.05) 0%, transparent 70%)', borderRadius: '50%' }} />
|
||||||
|
|
||||||
|
<div style={{ position: 'relative', width: 280, height: 160, borderRadius: 12, overflow: 'hidden', flexShrink: 0, boxShadow: '0 4px 16px rgba(0,0,0,0.1)' }}>
|
||||||
|
<video
|
||||||
|
controls
|
||||||
|
src={videoUrl}
|
||||||
|
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }}>
|
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'space-between', position: 'relative', zIndex: 1 }}>
|
||||||
<div>
|
<div>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
<h2 style={{ fontSize: 20, fontWeight: 600, marginBottom: 16 }}>视频总结</h2>
|
<h2 style={{ fontSize: 20, fontWeight: 600, marginBottom: 16, background: 'linear-gradient(135deg, #1e293b 0%, #334155 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>视频总结</h2>
|
||||||
<div style={{ textAlign: 'right' }}>
|
<div style={{ textAlign: 'right' }}>
|
||||||
<span style={{ color: '#999', fontSize: 12 }}>上传时间: {taskDetail.createdAt}</span>
|
<span style={{ color: '#94a3b8', fontSize: 12 }}>上传时间: {taskDetail.createdAt ? new Date(taskDetail.createdAt).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }) : '-'}</span>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: 'flex', marginBottom: 12 }}>
|
|
||||||
<span style={{ color: '#999', fontSize: 14, marginRight: 12, width: 80, flexShrink: 0 }}>产品名称:</span>
|
|
||||||
<span style={{ color: '#333', fontWeight: 500 }}>{taskDetail.title}</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: 'flex', marginBottom: 12 }}>
|
|
||||||
<span style={{ color: '#999', fontSize: 14, marginRight: 12, width: 80, flexShrink: 0 }}>受众群体:</span>
|
|
||||||
{taskDetail.analysisStatus === 'processing' ? (
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
||||||
<Spin size="small" tip="分析中" style={{ color: '#f59e0b' }} />
|
|
||||||
</div>
|
|
||||||
) : taskDetail.analysisStatus === 'completed' ? (
|
|
||||||
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
|
||||||
{(taskDetail.originalVideoAudience?.split('、') || []).map((point: string, index: number) => (
|
|
||||||
<Tag key={index} color="purple" style={{ fontSize: 12 }}>
|
|
||||||
{point}
|
|
||||||
</Tag>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
<div style={{ display: 'flex' }}>
|
|
||||||
<span style={{ color: '#999', fontSize: 14, marginRight: 12, width: 80, flexShrink: 0 }}>视频内容:</span>
|
|
||||||
{taskDetail.analysisStatus === 'processing' ? (
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
||||||
<Spin size="small" tip="分析中" style={{ color: '#f59e0b' }} />
|
|
||||||
</div>
|
|
||||||
) : taskDetail.analysisStatus === 'completed' ? (
|
|
||||||
<span style={{ color: '#333' }}>{taskDetail.originalVideoContent}</span>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div style={{ display: 'flex', marginBottom: 12 }}>
|
||||||
|
<span style={{ color: '#64748b', fontSize: 14, marginRight: 12, width: 80, flexShrink: 0 }}>产品名称:</span>
|
||||||
|
<span style={{ color: '#1e293b', fontWeight: 500 }}>{taskDetail.title}</span>
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex', marginBottom: 12 }}>
|
||||||
|
<span style={{ color: '#64748b', fontSize: 14, marginRight: 12, width: 80, flexShrink: 0 }}>受众群体:</span>
|
||||||
|
{taskDetail.analysisStatus === 'processing' ? (
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
|
<Spin size="small" style={{ color: '#f59e0b' }} />
|
||||||
|
<span style={{ fontSize: 14, color: '#f59e0b' }}>分析中</span>
|
||||||
|
</div>
|
||||||
|
) : taskDetail.analysisStatus === 'completed' ? (
|
||||||
|
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
||||||
|
{(taskDetail.originalVideoAudience?.split('、') || []).map((point: string, index: number) => (
|
||||||
|
<Tag key={index} style={{ fontSize: 12, background: 'rgba(99, 102, 241, 0.1)', color: '#6366f1', border: 'none', borderRadius: 20 }}>
|
||||||
|
{point}
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex' }}>
|
||||||
|
<span style={{ color: '#64748b', fontSize: 14, marginRight: 12, width: 80, flexShrink: 0 }}>视频内容:</span>
|
||||||
|
{taskDetail.analysisStatus === 'processing' ? (
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
|
<Spin size="small" style={{ color: '#f59e0b' }} />
|
||||||
|
<span style={{ fontSize: 14, color: '#f59e0b' }}>分析中</span>
|
||||||
|
</div>
|
||||||
|
) : taskDetail.analysisStatus === 'completed' ? (
|
||||||
|
<span style={{ color: '#475569', lineHeight: 1.6 }}>{taskDetail.originalVideoContent}</span>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* </div> */}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: 'flex', gap: 16, marginBottom: 20, marginTop: 20 }}>
|
<div style={{ display: 'flex', gap: 16, marginBottom: 20, marginTop: 20 }}>
|
||||||
@@ -509,11 +643,23 @@ function RemoveInfo() {
|
|||||||
disabled={!videoUrl || splitLoading}
|
disabled={!videoUrl || splitLoading}
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
height: 48,
|
height: 52,
|
||||||
borderRadius: 8,
|
borderRadius: 12,
|
||||||
borderColor: '#6366f1',
|
border: '2px solid rgba(99, 102, 241, 0.3)',
|
||||||
color: '#6366f1',
|
color: '#6366f1',
|
||||||
fontWeight: 500,
|
fontWeight: 600,
|
||||||
|
fontSize: 15,
|
||||||
|
background: 'rgba(255, 255, 255, 0.9)',
|
||||||
|
backdropFilter: 'blur(10px)',
|
||||||
|
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.borderColor = '#6366f1';
|
||||||
|
e.currentTarget.style.boxShadow = '0 4px 20px rgba(99, 102, 241, 0.2)';
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.borderColor = 'rgba(99, 102, 241, 0.3)';
|
||||||
|
e.currentTarget.style.boxShadow = 'none';
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
手动
|
手动
|
||||||
@@ -524,65 +670,141 @@ function RemoveInfo() {
|
|||||||
cancelText="取消"
|
cancelText="取消"
|
||||||
onConfirm={handleAutoGenerate}
|
onConfirm={handleAutoGenerate}
|
||||||
>
|
>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', flex: 1 }}>
|
<Button
|
||||||
<Button
|
type="primary"
|
||||||
type="primary"
|
loading={autoSplitLoading}
|
||||||
loading={autoSplitLoading}
|
disabled={autoSplitLoading}
|
||||||
disabled={autoSplitLoading}
|
style={{
|
||||||
style={{
|
flex: 1,
|
||||||
flex: 1,
|
height: 52,
|
||||||
height: 48,
|
borderRadius: 12,
|
||||||
borderRadius: 8,
|
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a855f7 100%)',
|
||||||
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
fontWeight: 600,
|
||||||
fontWeight: 500,
|
fontSize: 15,
|
||||||
border: 'none',
|
border: 'none',
|
||||||
}}
|
boxShadow: '0 4px 20px rgba(99, 102, 241, 0.3)',
|
||||||
>
|
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
{autoSplitButtonText}
|
position: 'relative',
|
||||||
{taskDetail?.aiSuggestions && taskDetail.aiSuggestions.length > 0 && (
|
overflow: 'hidden',
|
||||||
<Tooltip
|
}}
|
||||||
title={
|
onMouseEnter={(e) => {
|
||||||
<div>
|
e.currentTarget.style.transform = 'translateY(-2px)';
|
||||||
<div style={{ fontWeight: 600, marginBottom: 8, color: '#6366f1' }}>AI拆分方案</div>
|
e.currentTarget.style.boxShadow = '0 6px 25px rgba(99, 102, 241, 0.4)';
|
||||||
{(() => {
|
}}
|
||||||
console.log('ai_suggestions:', taskDetail?.ai_suggestions);
|
onMouseLeave={(e) => {
|
||||||
console.log('ai_suggestions2:', taskDetail?.aiSuggestions);
|
e.currentTarget.style.transform = 'translateY(0)';
|
||||||
const suggestions = taskDetail?.ai_suggestions || taskDetail?.aiSuggestions || [];
|
e.currentTarget.style.boxShadow = '0 4px 20px rgba(99, 102, 241, 0.3)';
|
||||||
return suggestions.map((item: any) => (
|
}}
|
||||||
<div key={item.index} style={{ marginBottom: 8, fontSize: 13, lineHeight: 1.5 }}>
|
>
|
||||||
<span style={{ fontWeight: 500, color: '#6366f1' }}>{item.index}.</span>
|
<span style={{ position: 'relative', zIndex: 1 }}>{autoSplitButtonText}</span>
|
||||||
<span style={{ marginLeft: 4, color: '#000000ff' }}>{item.timeNode}</span>
|
{/* 按钮光效 */}
|
||||||
<br />
|
<div style={{
|
||||||
<span style={{ color: '#666' }}>{item.content}</span>
|
position: 'absolute',
|
||||||
</div>
|
top: 0,
|
||||||
));
|
left: '-100%',
|
||||||
})()}
|
width: '100%',
|
||||||
</div>
|
height: '100%',
|
||||||
}
|
background: 'linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent)',
|
||||||
placement="top"
|
transition: 'left 0.5s ease'
|
||||||
trigger="hover"
|
}}
|
||||||
overlayInnerStyle={{ maxWidth: 400, background: '#fff', border: '1px solid #e5e7eb', borderRadius: 12, padding: 12 }}
|
onMouseEnter={(e) => {
|
||||||
>
|
e.currentTarget.style.left = '100%';
|
||||||
<div style={{ borderRadius: 8, background: 'rgba(99, 102, 241, 0.1)', color: '#ebebeb', fontSize: 13, fontWeight: 500, cursor: 'pointer' }}>
|
}} />
|
||||||
(AI 镜头拆分方案)
|
{taskDetail?.aiSuggestions && taskDetail.aiSuggestions.length > 0 && (
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
<div>
|
||||||
|
<div style={{ fontWeight: 600, marginBottom: 8, color: '#6366f1' }}>AI拆分方案</div>
|
||||||
|
{(() => {
|
||||||
|
const suggestions = taskDetail?.ai_suggestions || taskDetail?.aiSuggestions || [];
|
||||||
|
return suggestions.map((item: any) => (
|
||||||
|
<div key={item.index} style={{ marginBottom: 8, fontSize: 13, lineHeight: 1.5 }}>
|
||||||
|
<span style={{ fontWeight: 500, color: '#6366f1' }}>{item.index}.</span>
|
||||||
|
<span style={{ marginLeft: 4, color: '#000000ff' }}>{item.timeNode}</span>
|
||||||
|
<br />
|
||||||
|
<span style={{ color: '#666' }}>{item.content}</span>
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
})()}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
}
|
||||||
|
placement="top"
|
||||||
|
trigger="hover"
|
||||||
|
>
|
||||||
|
<span style={{ marginLeft: 8, fontSize: 13, opacity: 0.9 }}>(AI 镜头拆分方案)</span>
|
||||||
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ flex: 2, background: '#fff', borderRadius: 12, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 2,
|
||||||
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.9) 0%, rgba(255,255,255,0.7) 100%)',
|
||||||
|
backdropFilter: 'blur(20px)',
|
||||||
|
borderRadius: 20,
|
||||||
|
overflow: 'hidden',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
border: '1px solid rgba(99, 102, 241, 0.1)',
|
||||||
|
boxShadow: '0 8px 32px rgba(99, 102, 241, 0.08)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ padding: '16px 24px', borderBottom: '1px solid rgba(99, 102, 241, 0.1)', background: 'linear-gradient(135deg, rgba(99,102,241,0.02) 0%, transparent 100%)' }}>
|
||||||
|
<h3 style={{ fontSize: 16, fontWeight: 600, color: '#1e293b', margin: 0 }}>拆镜片段列表</h3>
|
||||||
|
<span style={{ fontSize: 12, color: '#64748b', marginLeft: 12 }}>共 {tableData.length} 个片段</span>
|
||||||
|
</div>
|
||||||
<Table
|
<Table
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={tableData}
|
dataSource={tableData}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
bordered={false}
|
bordered={false}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
scroll={{ y: 450 }}
|
scroll={{ y: 300 }}
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
components={{
|
||||||
|
body: {
|
||||||
|
row: ({ className, style, ...rest }) => (
|
||||||
|
<tr
|
||||||
|
{...rest}
|
||||||
|
className={className}
|
||||||
|
style={{
|
||||||
|
...style,
|
||||||
|
transition: 'all 0.2s ease',
|
||||||
|
borderBottom: '1px solid rgba(99, 102, 241, 0.05)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
cell: ({ className, style, ...rest }) => (
|
||||||
|
<td
|
||||||
|
{...rest}
|
||||||
|
className={className}
|
||||||
|
style={{
|
||||||
|
...style,
|
||||||
|
padding: '16px 24px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
cell: ({ className, style, ...rest }) => (
|
||||||
|
<th
|
||||||
|
{...rest}
|
||||||
|
className={className}
|
||||||
|
style={{
|
||||||
|
...style,
|
||||||
|
background: 'rgba(99, 102, 241, 0.03)',
|
||||||
|
color: '#64748b',
|
||||||
|
fontWeight: 500,
|
||||||
|
fontSize: 13,
|
||||||
|
padding: '16px 24px',
|
||||||
|
borderBottom: 'none',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -607,15 +829,16 @@ function RemoveInfo() {
|
|||||||
<Drawer
|
<Drawer
|
||||||
title={
|
title={
|
||||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%' }}>
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%' }}>
|
||||||
<span>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||||
<span style={{ color: '#6366f1' }}>智能视频复刻</span>
|
<div style={{ width: 4, height: 20, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', borderRadius: 2 }} />
|
||||||
<span style={{ color: '#6366f1' }}>-片段{currentSegment}</span>
|
<span style={{ fontSize: 18, fontWeight: 600, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>智能视频复刻</span>
|
||||||
</span>
|
{currentSegmentName && <span style={{ fontSize: 14, color: '#64748b' }}>- {currentSegmentName}</span>}
|
||||||
|
</div>
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
icon={<XOutlined />}
|
icon={<XOutlined />}
|
||||||
onClick={handleCloseDrawer}
|
onClick={handleCloseDrawer}
|
||||||
style={{ padding: 0 }}
|
style={{ padding: 0, color: '#64748b', fontSize: 14 }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -625,75 +848,88 @@ function RemoveInfo() {
|
|||||||
open={drawerVisible}
|
open={drawerVisible}
|
||||||
size={480}
|
size={480}
|
||||||
styles={{
|
styles={{
|
||||||
body: { padding: '24px' },
|
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' },
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
|
||||||
<div>
|
<div style={{ background: '#fff', borderRadius: 12, padding: 16, border: '1px solid rgba(99, 102, 241, 0.1)' }}>
|
||||||
<label style={{ fontWeight: 400, color: '#333', marginBottom: 12, display: 'block' }}>
|
<label style={{ fontWeight: 500, color: '#334155', marginBottom: 8, display: 'block', fontSize: 14 }}>
|
||||||
产品白底图 <span style={{ color: '#ff4d4f' }}>*</span>
|
产品白底图 <span style={{ color: '#ef4444' }}>*</span>
|
||||||
</label>
|
</label>
|
||||||
<div style={{ display: 'flex', gap: 16 }}>
|
<div style={{ display: 'flex', gap: 12 }}>
|
||||||
<Upload
|
<Upload
|
||||||
listType="picture-card"
|
listType="picture-card"
|
||||||
onChange={handleProductImageChange}
|
onChange={handleProductImageChange}
|
||||||
beforeUpload={beforeUploadProductImage}
|
beforeUpload={beforeUploadProductImage}
|
||||||
maxCount={1}
|
maxCount={1}
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
style={{ width: 140, height: 140 }}
|
style={{ width: 120, height: 120, borderRadius: 8, border: '2px dashed rgba(99, 102, 241, 0.3)', background: 'rgba(99, 102, 241, 0.02)' }}
|
||||||
>
|
>
|
||||||
{!productImage && (
|
{!productImage && (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6 }}>
|
||||||
<PlusOutlined style={{ fontSize: 24, color: '#999' }} />
|
<PlusOutlined style={{ fontSize: 20, color: '#6366f1' }} />
|
||||||
<span style={{ fontSize: 12, color: '#999' }}>产品图 *</span>
|
<span style={{ fontSize: 12, color: '#64748b' }}>上传产品图</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Upload>
|
</Upload>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div style={{ background: '#fff', borderRadius: 12, padding: 16, border: '1px solid rgba(99, 102, 241, 0.1)' }}>
|
||||||
<label style={{ fontWeight: 500, color: '#333', marginBottom: 12, display: 'block' }}>
|
<label style={{ fontWeight: 500, color: '#334155', marginBottom: 8, display: 'block', fontSize: 14 }}>
|
||||||
产品名称
|
产品名称 <span style={{ color: '#ef4444' }}>*</span>
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
value={productName}
|
value={productName}
|
||||||
onChange={(e) => setProductName(e.target.value)}
|
onChange={(e) => setProductName(e.target.value)}
|
||||||
placeholder="请输入产品名称"
|
placeholder="请输入产品名称"
|
||||||
style={{ height: 48, borderRadius: 8 }}
|
style={{ height: 44, borderRadius: 8, border: '1px solid rgba(99, 102, 241, 0.2)' }}
|
||||||
maxLength={10}
|
maxLength={10}
|
||||||
showCount
|
showCount
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div style={{ background: '#fff', borderRadius: 12, padding: 16, border: '1px solid rgba(99, 102, 241, 0.1)' }}>
|
||||||
<label style={{ fontWeight: 500, color: '#333', marginBottom: 12, display: 'block' }}>
|
<label style={{ fontWeight: 500, color: '#334155', marginBottom: 8, display: 'block', fontSize: 14 }}>
|
||||||
产品卖点
|
产品卖点 <span style={{ color: '#ef4444' }}>*</span>
|
||||||
</label>
|
</label>
|
||||||
<TextArea
|
<TextArea
|
||||||
value={productSellingPoint}
|
value={productSellingPoint}
|
||||||
onChange={(e) => setProductSellingPoint(e.target.value)}
|
onChange={(e) => setProductSellingPoint(e.target.value)}
|
||||||
placeholder="请输入产品卖点"
|
placeholder="请输入产品卖点"
|
||||||
style={{ borderRadius: 8 }}
|
style={{ borderRadius: 8, border: '1px solid rgba(99, 102, 241, 0.2)' }}
|
||||||
maxLength={100}
|
maxLength={100}
|
||||||
showCount
|
showCount
|
||||||
rows={3}
|
rows={3}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: 'flex', gap: 16, marginTop: 24 }}>
|
<div style={{ display: 'flex', gap: 12, marginTop: 8 }}>
|
||||||
<Button
|
<Button
|
||||||
type="default"
|
type="primary"
|
||||||
onClick={handleManualGenerate}
|
onClick={handleManualGenerate}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
height: 48,
|
height: 48,
|
||||||
borderRadius: 8,
|
borderRadius: 10,
|
||||||
borderColor: '#6366f1',
|
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||||
color: '#6366f1',
|
fontWeight: 600,
|
||||||
fontWeight: 500,
|
fontSize: 15,
|
||||||
|
border: 'none',
|
||||||
|
boxShadow: '0 4px 15px rgba(99, 102, 241, 0.3)',
|
||||||
|
transition: 'all 0.2s ease',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.boxShadow = '0 6px 20px rgba(99, 102, 241, 0.4)';
|
||||||
|
e.currentTarget.style.transform = 'translateY(-1px)';
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.boxShadow = '0 4px 15px rgba(99, 102, 241, 0.3)';
|
||||||
|
e.currentTarget.style.transform = 'translateY(0)';
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{loading ? '生成中...' : '手动生成'}
|
{loading ? '生成中...' : '手动生成'}
|
||||||
@@ -701,6 +937,42 @@ function RemoveInfo() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
title={
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||||
|
<div style={{ width: 4, height: 20, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', borderRadius: 2 }} />
|
||||||
|
<span style={{ fontSize: 16, fontWeight: 600, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>视频预览</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
open={previewModalVisible}
|
||||||
|
onCancel={() => setPreviewModalVisible(false)}
|
||||||
|
afterClose={() => {
|
||||||
|
if (previewVideoRef.current) {
|
||||||
|
previewVideoRef.current.pause();
|
||||||
|
previewVideoRef.current.currentTime = 0;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
footer={null}
|
||||||
|
width={640}
|
||||||
|
centered
|
||||||
|
styles={{
|
||||||
|
body: { padding: 24, background: '#0f172a' },
|
||||||
|
header: { background: '#1e293b', borderBottom: 'none', padding: '20px 24px' },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ borderRadius: 12, overflow: 'hidden', boxShadow: '0 8px 32px rgba(0,0,0,0.3)' }}>
|
||||||
|
<video
|
||||||
|
ref={previewVideoRef}
|
||||||
|
controls
|
||||||
|
src={previewVideoUrl || ''}
|
||||||
|
style={{ width: '100%', maxHeight: 420, objectFit: 'contain', backgroundColor: '#000' }}
|
||||||
|
playsInline
|
||||||
|
webkit-playsinline
|
||||||
|
autoPlay
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,6 @@ export default function VideoFrameExtractor() {
|
|||||||
|
|
||||||
await createShotReplication(params);
|
await createShotReplication(params);
|
||||||
message.success('任务创建成功');
|
message.success('任务创建成功');
|
||||||
// 清空上传内容和输入框
|
|
||||||
cleanupResources();
|
cleanupResources();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
message.error('任务创建失败,请重试');
|
message.error('任务创建失败,请重试');
|
||||||
@@ -108,194 +107,194 @@ export default function VideoFrameExtractor() {
|
|||||||
fetchList(page, size);
|
fetchList(page, size);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 打开弹窗时获取列表数据
|
|
||||||
const handleOpenModal = () => {
|
const handleOpenModal = () => {
|
||||||
setIsModalOpen(true);
|
setIsModalOpen(true);
|
||||||
fetchList(1, 10);
|
fetchList(1, 10);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ minHeight: 'calc(100vh - 90px)', overflow: 'auto', background: 'linear-gradient(135deg, #f5f3ff 0%, #fdf2f8 100%)', padding: '20px' }}>
|
<div
|
||||||
<div style={{ maxWidth: 1200, margin: '0 auto' }}>
|
style={{
|
||||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 24 }}>
|
margin: '-24px -32px -32px',
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
borderRadius: 20,
|
||||||
<div>
|
minHeight: 'calc(100vh - 32px)',
|
||||||
<div style={{
|
overflow: 'auto',
|
||||||
width: 36,
|
background: 'linear-gradient(135deg, #f8fafc 0%, #eef2ff 50%, #f0f9ff 100%)',
|
||||||
height: 36,
|
position: 'relative',
|
||||||
background: 'linear-gradient(135deg, #6366f1 0%, #ec4899 100%)',
|
padding: '20px 32px',
|
||||||
borderRadius: 10,
|
}}
|
||||||
display: 'flex',
|
>
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
color: 'white',
|
<div style={{ maxWidth: 1200, margin: '50px auto', position: 'relative', zIndex: 10 }}>
|
||||||
fontSize: 18,
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 32 }}>
|
||||||
fontWeight: 600
|
<div style={{ display: 'flex', alignItems: 'center', gap: 16,marginBottom: 50 }}>
|
||||||
}}>
|
<div style={{ width: 40, height: 2, background: 'linear-gradient(90deg, transparent, #6366f1, #8b5cf6, transparent)', borderRadius: 1 }} />
|
||||||
✨
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<h1 style={{
|
<h1 style={{
|
||||||
fontSize: 22,
|
fontSize: 24,
|
||||||
fontWeight: 600,
|
fontWeight: 700,
|
||||||
margin: 0,
|
margin: 0,
|
||||||
color: '#1e293b'
|
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||||
|
WebkitBackgroundClip: 'text',
|
||||||
|
WebkitTextFillColor: 'transparent',
|
||||||
|
backgroundClip: 'text'
|
||||||
}}>
|
}}>
|
||||||
拆镜复刻
|
AI视频拆镜工作台
|
||||||
</h1>
|
</h1>
|
||||||
<p style={{ fontSize: 13, color: '#94a3b8', margin: 4 }}>
|
<p style={{ fontSize: 13, color: '#64748b', margin: '4px 0 0 0' }}>
|
||||||
一键拆解画面分镜,助力仿拍或创作
|
一键拆解画面分镜,助力仿拍或创作
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div style={{ width: 40, height: 2, background: 'linear-gradient(90deg, transparent, #8b5cf6, #6366f1, transparent)', borderRadius: 1 }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<Button
|
||||||
|
type="text"
|
||||||
|
icon={<FileTextOutlined />}
|
||||||
onClick={handleOpenModal}
|
onClick={handleOpenModal}
|
||||||
style={{
|
style={{
|
||||||
padding: '10px 20px',
|
|
||||||
background: 'white',
|
|
||||||
border: '1px solid #e2e8f0',
|
|
||||||
borderRadius: 8,
|
|
||||||
cursor: 'pointer',
|
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: '#6366f1',
|
color: '#6366f1',
|
||||||
|
background: 'rgba(99, 102, 241, 0.1)',
|
||||||
|
borderRadius: 12,
|
||||||
|
padding: '10px 20px',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: 8,
|
gap: 8,
|
||||||
boxShadow: '0 2px 8px rgba(0,0,0,0.06)'
|
transition: 'all 0.2s ease',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.background = 'rgba(99, 102, 241, 0.15)';
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.background = 'rgba(99, 102, 241, 0.1)';
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FileTextOutlined />
|
创作记录
|
||||||
<span>创作记录</span>
|
</Button>
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: 32 }}>
|
|
||||||
<div style={{ textAlign: 'center' }}>
|
|
||||||
<div style={{
|
|
||||||
width: 56,
|
|
||||||
height: 56,
|
|
||||||
background: 'linear-gradient(135deg, #e0e7ff 0%, #fce7f3 100%)',
|
|
||||||
borderRadius: 16,
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
margin: '0 auto 16px'
|
|
||||||
}}>
|
|
||||||
<span style={{ fontSize: 28 }}>🎬</span>
|
|
||||||
</div>
|
|
||||||
<h2 style={{
|
|
||||||
fontSize: 22,
|
|
||||||
fontWeight: 600,
|
|
||||||
margin: 0,
|
|
||||||
background: 'linear-gradient(135deg, #6366f1 0%, #ec4899 100%)',
|
|
||||||
WebkitBackgroundClip: 'text',
|
|
||||||
WebkitTextFillColor: 'transparent',
|
|
||||||
backgroundClip: 'text'
|
|
||||||
}}>
|
|
||||||
上传视频开始创作
|
|
||||||
</h2>
|
|
||||||
<p style={{ fontSize: 14, color: '#888', margin: '8px 0 0 0' }}>
|
|
||||||
支持 MP4、MOV 格式视频,最大支持 100MB
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ width: '100%', maxWidth: 720, margin: '0 auto' }}>
|
<div style={{ width: '100%', maxWidth: 720, margin: '0 auto' }}>
|
||||||
{error && (
|
{error && (
|
||||||
<div style={{
|
<div style={{
|
||||||
backgroundColor: '#fff5f5',
|
background: 'linear-gradient(135deg, rgba(239, 68, 68, 0.1) 0%, rgba(239, 68, 68, 0.05) 100%)',
|
||||||
border: '1px solid #ffccc7',
|
backdropFilter: 'blur(10px)',
|
||||||
borderRadius: 8,
|
border: '1px solid rgba(239, 68, 68, 0.2)',
|
||||||
padding: '12px 16px',
|
borderRadius: 12,
|
||||||
marginBottom: 20,
|
padding: '14px 20px',
|
||||||
color: '#d93026',
|
marginBottom: 24,
|
||||||
|
color: '#ef4444',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
textAlign: 'center'
|
textAlign: 'center',
|
||||||
|
fontWeight: 500,
|
||||||
}}>
|
}}>
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!videoUrl && (
|
{!videoUrl && (
|
||||||
<Upload.Dragger
|
<div
|
||||||
accept="video/*"
|
|
||||||
beforeUpload={handleFileChange}
|
|
||||||
showUploadList={false}
|
|
||||||
disabled={loading}
|
|
||||||
style={{
|
style={{
|
||||||
background: loading ? '#f3f4f6' : '#faf5ff',
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0.85) 100%)',
|
||||||
border: `2px dashed ${loading ? '#d1d5db' : '#c4b5fd'}`,
|
backdropFilter: 'blur(20px)',
|
||||||
borderRadius: 24,
|
borderRadius: 24,
|
||||||
padding: '60px 20px',
|
border: '1px solid rgba(99, 102, 241, 0.1)',
|
||||||
cursor: loading ? 'not-allowed' : 'pointer',
|
boxShadow: '0 8px 32px rgba(99, 102, 241, 0.08)',
|
||||||
|
position: 'relative',
|
||||||
|
overflow: 'hidden',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{loading ? (
|
<div style={{ position: 'absolute', top: -50, right: -50, width: 200, height: 200, background: 'radial-gradient(circle, rgba(99,102,241,0.05) 0%, transparent 70%)', borderRadius: '50%' }} />
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
|
||||||
<div style={{
|
<Upload.Dragger
|
||||||
width: 100,
|
accept="video/*"
|
||||||
height: 100,
|
beforeUpload={handleFileChange}
|
||||||
margin: '0 auto 20px',
|
showUploadList={false}
|
||||||
background: '#e5e7eb',
|
disabled={loading}
|
||||||
borderRadius: 24,
|
style={{
|
||||||
display: 'flex',
|
background: loading ? 'rgba(243, 244, 246, 0.3)' : 'transparent',
|
||||||
alignItems: 'center',
|
border: `2px dashed ${loading ? 'rgba(209, 213, 219, 0.5)' : 'rgba(99, 102, 241, 0.3)'}`,
|
||||||
justifyContent: 'center'
|
borderRadius: 24,
|
||||||
}}>
|
padding: '56px 20px',
|
||||||
|
cursor: loading ? 'not-allowed' : 'pointer',
|
||||||
|
margin: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
||||||
<div style={{
|
<div style={{
|
||||||
width: 40,
|
width: 100,
|
||||||
height: 40,
|
height: 100,
|
||||||
border: '4px solid #6366f1',
|
margin: '0 auto 20px',
|
||||||
borderTopColor: 'transparent',
|
background: 'linear-gradient(135deg, rgba(99, 102, 241, 0.1) 0%, rgba(139, 92, 246, 0.1) 100%)',
|
||||||
borderRadius: '50%',
|
borderRadius: 24,
|
||||||
animation: 'spin 1s linear infinite'
|
display: 'flex',
|
||||||
}} />
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center'
|
||||||
|
}}>
|
||||||
|
<div style={{
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
border: '4px solid #6366f1',
|
||||||
|
borderTopColor: 'transparent',
|
||||||
|
borderRadius: '50%',
|
||||||
|
animation: 'spin 1s linear infinite'
|
||||||
|
}} />
|
||||||
|
</div>
|
||||||
|
<p style={{ fontSize: 16, color: '#6366f1', margin: '0 0 8px 0', fontWeight: 600 }}>
|
||||||
|
上传中...
|
||||||
|
</p>
|
||||||
|
<p style={{ fontSize: 13, color: '#64748b', margin: 0 }}>
|
||||||
|
请稍候,视频正在上传
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p style={{ fontSize: 16, color: '#6366f1', margin: '0 0 8px 0', fontWeight: 500 }}>
|
) : (
|
||||||
上传中...
|
<>
|
||||||
</p>
|
<div style={{
|
||||||
<p style={{ fontSize: 13, color: '#999', margin: 0 }}>
|
width: 100,
|
||||||
请稍候,视频正在上传
|
height: 100,
|
||||||
</p>
|
margin: '0 auto 20px',
|
||||||
</div>
|
background: 'linear-gradient(135deg, rgba(99, 102, 241, 0.1) 0%, rgba(139, 92, 246, 0.1) 100%)',
|
||||||
) : (
|
borderRadius: 24,
|
||||||
<>
|
display: 'flex',
|
||||||
<div style={{
|
alignItems: 'center',
|
||||||
width: 100,
|
justifyContent: 'center'
|
||||||
height: 100,
|
}}>
|
||||||
margin: '0 auto 20px',
|
<CloudUploadOutlined style={{ fontSize: 40, color: '#6366f1' }} />
|
||||||
background: 'linear-gradient(135deg, #e0e7ff 0%, #fce7f3 100%)',
|
</div>
|
||||||
borderRadius: 24,
|
<p style={{ fontSize: 16, color: '#1e293b', margin: '0 0 8px 0', fontWeight: 600 }}>
|
||||||
display: 'flex',
|
点击或拖拽到此上传视频文件
|
||||||
alignItems: 'center',
|
</p>
|
||||||
justifyContent: 'center'
|
<p style={{ fontSize: 13, color: '#64748b', margin: 0 }}>
|
||||||
}}>
|
支持 MP4、MOV 格式视频,最大支持 100MB 的文件,支持上传 3 分钟内的视频
|
||||||
<CloudUploadOutlined style={{ fontSize: 40, color: '#818cf8' }} />
|
</p>
|
||||||
</div>
|
</>
|
||||||
<p style={{ fontSize: 16, color: '#333', margin: '0 0 8px 0', fontWeight: 500 }}>
|
)}
|
||||||
点击或拖拽到此上传视频文件
|
</Upload.Dragger>
|
||||||
</p>
|
</div>
|
||||||
<p style={{ fontSize: 13, color: '#999', margin: 0 }}>
|
|
||||||
支持 MP4、MOV 格式视频,最大支持 100MB 的文件,支持上传 3 分钟内的视频
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Upload.Dragger>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{videoUrl && (
|
{videoUrl && (
|
||||||
<div style={{
|
<div
|
||||||
position: 'relative',
|
style={{
|
||||||
borderRadius: 24,
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0.85) 100%)',
|
||||||
padding: 4,
|
backdropFilter: 'blur(20px)',
|
||||||
background: 'linear-gradient(135deg, #6366f1 0%, #ec4899 100%)'
|
borderRadius: 24,
|
||||||
}}>
|
border: '1px solid rgba(99, 102, 241, 0.1)',
|
||||||
|
boxShadow: '0 8px 32px rgba(99, 102, 241, 0.08)',
|
||||||
|
overflow: 'hidden',
|
||||||
|
position: 'relative',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ position: 'absolute', top: -50, right: -50, width: 200, height: 200, background: 'radial-gradient(circle, rgba(99,102,241,0.05) 0%, transparent 70%)', borderRadius: '50%' }} />
|
||||||
|
|
||||||
<div style={{
|
<div style={{
|
||||||
borderRadius: 22,
|
borderRadius: 22,
|
||||||
background: '#ffffff',
|
background: '#ffffff',
|
||||||
padding: '24px'
|
padding: '24px',
|
||||||
|
position: 'relative',
|
||||||
|
zIndex: 1,
|
||||||
}}>
|
}}>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="确定要删除吗?"
|
title="确定要删除吗?"
|
||||||
@@ -310,16 +309,27 @@ export default function VideoFrameExtractor() {
|
|||||||
right: 20,
|
right: 20,
|
||||||
width: 36,
|
width: 36,
|
||||||
height: 36,
|
height: 36,
|
||||||
border: '1px solid #e5e7eb',
|
border: '1px solid rgba(99, 102, 241, 0.2)',
|
||||||
background: '#fff',
|
background: 'rgba(99, 102, 241, 0.05)',
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: '#666',
|
color: '#6366f1',
|
||||||
zIndex: 10
|
zIndex: 10,
|
||||||
|
transition: 'all 0.2s ease',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.background = 'rgba(239, 68, 68, 0.1)';
|
||||||
|
e.currentTarget.style.borderColor = 'rgba(239, 68, 68, 0.3)';
|
||||||
|
e.currentTarget.style.color = '#ef4444';
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.background = 'rgba(99, 102, 241, 0.05)';
|
||||||
|
e.currentTarget.style.borderColor = 'rgba(99, 102, 241, 0.2)';
|
||||||
|
e.currentTarget.style.color = '#6366f1';
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
🗑️
|
🗑️
|
||||||
@@ -339,21 +349,21 @@ export default function VideoFrameExtractor() {
|
|||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
maxHeight: 320,
|
maxHeight: 320,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
background: '#000'
|
background: '#000',
|
||||||
|
boxShadow: '0 4px 16px rgba(0,0,0,0.1)'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 产品名称输入框 */}
|
|
||||||
<div style={{ marginBottom: 24 }}>
|
<div style={{ marginBottom: 24 }}>
|
||||||
<label style={{
|
<label style={{
|
||||||
display: 'block',
|
display: 'block',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
color: '#374151',
|
color: '#334155',
|
||||||
marginBottom: 8
|
marginBottom: 8
|
||||||
}}>
|
}}>
|
||||||
产品名称
|
产品名称 <span style={{ color: '#ef4444' }}>*</span>
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
placeholder="请输入产品名称"
|
placeholder="请输入产品名称"
|
||||||
@@ -361,9 +371,16 @@ export default function VideoFrameExtractor() {
|
|||||||
onChange={(e) => setProductName(e.target.value)}
|
onChange={(e) => setProductName(e.target.value)}
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: 40,
|
height: 44,
|
||||||
borderRadius: 8,
|
borderRadius: 10,
|
||||||
borderColor: '#e5e7eb'
|
border: '1px solid rgba(99, 102, 241, 0.2)',
|
||||||
|
transition: 'all 0.2s ease',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.borderColor = 'rgba(99, 102, 241, 0.4)';
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.borderColor = 'rgba(99, 102, 241, 0.2)';
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -375,28 +392,34 @@ export default function VideoFrameExtractor() {
|
|||||||
style={{
|
style={{
|
||||||
padding: '14px 56px',
|
padding: '14px 56px',
|
||||||
background: videoUrl && productName.trim() && !loading
|
background: videoUrl && productName.trim() && !loading
|
||||||
? 'linear-gradient(135deg, #6366f1 0%, #ec4899 100%)'
|
? 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a855f7 100%)'
|
||||||
: '#e2e8f0',
|
: '#e2e8f0',
|
||||||
color: 'white',
|
color: 'white',
|
||||||
border: 'none',
|
border: 'none',
|
||||||
borderRadius: 14,
|
borderRadius: 12,
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
cursor: videoUrl && productName.trim() && !loading ? 'pointer' : 'not-allowed',
|
cursor: videoUrl && productName.trim() && !loading ? 'pointer' : 'not-allowed',
|
||||||
boxShadow: videoUrl && productName.trim() && !loading ? '0 6px 20px rgba(99, 102, 241, 0.35)' : 'none',
|
boxShadow: videoUrl && productName.trim() && !loading ? '0 6px 20px rgba(99, 102, 241, 0.3)' : 'none',
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: 10
|
gap: 10,
|
||||||
|
transition: 'all 0.3s ease',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
if (videoUrl && productName.trim() && !loading) {
|
||||||
|
e.currentTarget.style.transform = 'translateY(-2px)';
|
||||||
|
e.currentTarget.style.boxShadow = '0 8px 25px rgba(99, 102, 241, 0.4)';
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
if (videoUrl && productName.trim() && !loading) {
|
||||||
|
e.currentTarget.style.transform = 'translateY(0)';
|
||||||
|
e.currentTarget.style.boxShadow = '0 6px 20px rgba(99, 102, 241, 0.3)';
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span>{loading ? '创建中...' : '创建'}</span>
|
<span>{loading ? '创建中...' : '创建'}</span>
|
||||||
<span style={{
|
|
||||||
fontSize: 12,
|
|
||||||
opacity: 0.85,
|
|
||||||
fontWeight: 400
|
|
||||||
}}>
|
|
||||||
{/* 预计消耗 10 个积分 */}
|
|
||||||
</span>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -406,91 +429,168 @@ export default function VideoFrameExtractor() {
|
|||||||
<canvas ref={canvasRef} style={{ display: 'none' }} />
|
<canvas ref={canvasRef} style={{ display: 'none' }} />
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
title="创作记录"
|
title={
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||||
|
<div style={{ width: 4, height: 20, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', borderRadius: 2 }} />
|
||||||
|
<span style={{ fontSize: 16, fontWeight: 600, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>创作记录</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
open={isModalOpen}
|
open={isModalOpen}
|
||||||
onCancel={() => setIsModalOpen(false)}
|
onCancel={() => setIsModalOpen(false)}
|
||||||
width={800}
|
width={800}
|
||||||
footer={null}
|
footer={null}
|
||||||
|
styles={{
|
||||||
|
body: { background: 'linear-gradient(180deg, #f8fafc 0%, #eef2ff 100%)' },
|
||||||
|
header: { background: '#fff', borderBottom: '1px solid rgba(99, 102, 241, 0.1)', padding: '20px 24px' },
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16 }}>
|
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16, gap: 8 }}>
|
||||||
<Input
|
<Input
|
||||||
placeholder="搜索产品名称"
|
placeholder="搜索产品名称"
|
||||||
style={{ width: 200, borderRadius: 6, marginRight: 8 }}
|
style={{
|
||||||
|
width: 200,
|
||||||
|
borderRadius: 10,
|
||||||
|
border: '1px solid rgba(99, 102, 241, 0.2)',
|
||||||
|
height: 40,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Button type="primary" style={{ borderRadius: 6 }}>
|
<Button
|
||||||
|
type="primary"
|
||||||
|
style={{
|
||||||
|
borderRadius: 10,
|
||||||
|
height: 40,
|
||||||
|
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||||
|
border: 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
搜索
|
搜索
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Table
|
<div
|
||||||
columns={[
|
style={{
|
||||||
// {
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.9) 0%, rgba(255,255,255,0.7) 100%)',
|
||||||
// title: '产品图片',
|
backdropFilter: 'blur(20px)',
|
||||||
// dataIndex: 'image',
|
borderRadius: 16,
|
||||||
// key: 'image',
|
overflow: 'hidden',
|
||||||
// width: 90,
|
border: '1px solid rgba(99, 102, 241, 0.1)',
|
||||||
// render: (image: string) => (
|
boxShadow: '0 8px 32px rgba(99, 102, 241, 0.08)',
|
||||||
// <img
|
}}
|
||||||
// src={image}
|
>
|
||||||
// alt="产品图片"
|
<Table
|
||||||
// style={{ width: 50, height: 50, objectFit: 'cover', borderRadius: 6 }}
|
columns={[
|
||||||
// />
|
{
|
||||||
// ),
|
title: '产品名称',
|
||||||
// },
|
dataIndex: 'title',
|
||||||
{
|
key: 'title',
|
||||||
title: '产品名称',
|
render: (text: string) => (
|
||||||
dataIndex: 'title',
|
<span style={{ fontSize: 14, color: '#1e293b', fontWeight: 500 }}>{text}</span>
|
||||||
key: 'title',
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '创建时间',
|
||||||
dataIndex: 'status',
|
dataIndex: 'createdAt',
|
||||||
key: 'status',
|
key: 'createdAt',
|
||||||
},
|
render: (text: string) => {
|
||||||
{
|
if (!text) return '-';
|
||||||
title: '创建时间',
|
const date = new Date(text);
|
||||||
dataIndex: 'createdAt',
|
const year = date.getFullYear();
|
||||||
key: 'createdAt',
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
render: (text: string) => {
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
if (!text) return '-';
|
const hours = String(date.getHours()).padStart(2, '0');
|
||||||
const date = new Date(text);
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||||
const year = date.getFullYear();
|
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
return <span style={{ fontSize: 13, color: '#64748b' }}>{`${year}-${month}-${day} ${hours}:${minutes}:${seconds}`}</span>;
|
||||||
const day = String(date.getDate()).padStart(2, '0');
|
},
|
||||||
const hours = String(date.getHours()).padStart(2, '0');
|
},
|
||||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
{
|
||||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
title: '操作',
|
||||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
key: 'action',
|
||||||
},
|
align: 'center',
|
||||||
},
|
render: (record) => (
|
||||||
{
|
<Button
|
||||||
title: '操作',
|
type="text"
|
||||||
key: 'action',
|
onClick={() => navigate(`/removelens/${record.id}/removeinfo`)}
|
||||||
render: (record) => (
|
style={{
|
||||||
<button
|
color: '#6366f1',
|
||||||
onClick={() => navigate(`/removelens/${record.id}/removeinfo`)}
|
fontSize: 13,
|
||||||
style={{ color: '#6366f1', textDecoration: 'none', fontSize: 13, border: 'none', background: 'none', cursor: 'pointer' }}
|
padding: '4px 12px',
|
||||||
>
|
borderRadius: 6,
|
||||||
查看详情
|
background: 'rgba(99, 102, 241, 0.1)',
|
||||||
</button>
|
}}
|
||||||
),
|
onMouseEnter={(e) => {
|
||||||
},
|
e.currentTarget.style.background = 'rgba(99, 102, 241, 0.15)';
|
||||||
]}
|
}}
|
||||||
dataSource={tableData}
|
onMouseLeave={(e) => {
|
||||||
rowKey="id"
|
e.currentTarget.style.background = 'rgba(99, 102, 241, 0.1)';
|
||||||
pagination={{
|
}}
|
||||||
current: currentPage,
|
>
|
||||||
pageSize: pageSize,
|
查看详情
|
||||||
total: total,
|
</Button>
|
||||||
showSizeChanger: true,
|
),
|
||||||
showQuickJumper: true,
|
},
|
||||||
showTotal: (total) => `共 ${total} 条`,
|
]}
|
||||||
onChange: handlePageChange,
|
dataSource={tableData}
|
||||||
}}
|
rowKey="id"
|
||||||
style={{ fontSize: 13 }}
|
pagination={{
|
||||||
scroll={{ y: 350 }}
|
current: currentPage,
|
||||||
|
pageSize: pageSize,
|
||||||
/>
|
total: total,
|
||||||
|
showSizeChanger: true,
|
||||||
|
showQuickJumper: true,
|
||||||
|
showTotal: (total) => `共 ${total} 条`,
|
||||||
|
onChange: handlePageChange,
|
||||||
|
style: {
|
||||||
|
padding: '16px 24px',
|
||||||
|
borderTop: '1px solid rgba(99, 102, 241, 0.1)',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
style={{ fontSize: 13 }}
|
||||||
|
scroll={{ y: 350 }}
|
||||||
|
components={{
|
||||||
|
body: {
|
||||||
|
row: ({ className, style, ...rest }) => (
|
||||||
|
<tr
|
||||||
|
{...rest}
|
||||||
|
className={className}
|
||||||
|
style={{
|
||||||
|
...style,
|
||||||
|
transition: 'all 0.2s ease',
|
||||||
|
borderBottom: '1px solid rgba(99, 102, 241, 0.05)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
cell: ({ className, style, ...rest }) => (
|
||||||
|
<td
|
||||||
|
{...rest}
|
||||||
|
className={className}
|
||||||
|
style={{
|
||||||
|
...style,
|
||||||
|
padding: '16px 24px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
cell: ({ className, style, ...rest }) => (
|
||||||
|
<th
|
||||||
|
{...rest}
|
||||||
|
className={className}
|
||||||
|
style={{
|
||||||
|
...style,
|
||||||
|
background: 'rgba(99, 102, 241, 0.03)',
|
||||||
|
color: '#64748b',
|
||||||
|
fontWeight: 500,
|
||||||
|
fontSize: 13,
|
||||||
|
padding: '16px 24px',
|
||||||
|
borderBottom: 'none',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { Button, Typography, Collapse, Space, Modal, Input, Table, message } from 'antd';
|
import { Button, Typography, Collapse, Space, Modal, Input, Table, message } from 'antd';
|
||||||
import { ArrowLeftOutlined, PlayCircleOutlined, CheckCircleOutlined, EditOutlined, DownloadOutlined, SettingOutlined, LayoutOutlined } from '@ant-design/icons';
|
import { ArrowLeftOutlined, PlayCircleOutlined, CheckCircleOutlined, EditOutlined, DownloadOutlined, SettingOutlined, LayoutOutlined } from '@ant-design/icons';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import {getShotReplicationList, removeDetail, removeone, removetwo, removethree, removefour, getEngine, updateShotImagePrompt, updateShotVideoPromptSchema } from '../api/index';
|
import { getShotReplicationList, removeDetail, removeone, removetwo, removethree, removefour, getEngine, updateShotImagePrompt, updateShotVideoPromptSchema } from '../api/index';
|
||||||
import VideoPromptSchemaEditor from '../components/VideoPromptSchemaEditor';
|
import VideoPromptSchemaEditor from '../components/VideoPromptSchemaEditor';
|
||||||
import './css/InitialInfo.css';
|
import './css/InitialInfo.css';
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ function InitialInfo() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
console.log('creatID', creatID);
|
console.log('creatID', creatID);
|
||||||
|
|
||||||
if (creatID) {
|
if (creatID) {
|
||||||
removeDetail(creatID).then((res: any) => {
|
removeDetail(creatID).then((res: any) => {
|
||||||
setTaskDetail(res);
|
setTaskDetail(res);
|
||||||
@@ -164,10 +164,10 @@ function InitialInfo() {
|
|||||||
|
|
||||||
// 监听合并后的 steps 数据,判断第五步状态并启动/停止轮询
|
// 监听合并后的 steps 数据,判断第五步状态并启动/停止轮询
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
// 检查第五步的状态(索引 4)
|
// 检查第五步的状态(索引 4)
|
||||||
const fifthStep = steps[4];
|
const fifthStep = steps[4];
|
||||||
|
|
||||||
if (fifthStep && fifthStep.status !== 'completed' && fifthStep.status !== 'failed') {
|
if (fifthStep && fifthStep.status !== 'completed' && fifthStep.status !== 'failed') {
|
||||||
// 第五步未完成,启动轮询
|
// 第五步未完成,启动轮询
|
||||||
if (!pollingTimer) {
|
if (!pollingTimer) {
|
||||||
@@ -189,7 +189,7 @@ function InitialInfo() {
|
|||||||
const handleOpenModal = (prompt?: any, type?: string, stepId?: string | number) => {
|
const handleOpenModal = (prompt?: any, type?: string, stepId?: string | number) => {
|
||||||
setCurrentType(type || 'image');
|
setCurrentType(type || 'image');
|
||||||
setEditingPromptStepId(stepId ? String(stepId) : '');
|
setEditingPromptStepId(stepId ? String(stepId) : '');
|
||||||
|
|
||||||
if (type === 'video' && typeof prompt === 'object') {
|
if (type === 'video' && typeof prompt === 'object') {
|
||||||
setFormData(prompt);
|
setFormData(prompt);
|
||||||
setPromptText('');
|
setPromptText('');
|
||||||
@@ -197,7 +197,7 @@ function InitialInfo() {
|
|||||||
setPromptText(prompt || '');
|
setPromptText(prompt || '');
|
||||||
setFormData({});
|
setFormData({});
|
||||||
}
|
}
|
||||||
|
|
||||||
setModalVisible(true);
|
setModalVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -258,7 +258,7 @@ function InitialInfo() {
|
|||||||
if (!creatID) {
|
if (!creatID) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeDetail(creatID).then((res: any) => {
|
removeDetail(creatID).then((res: any) => {
|
||||||
setTaskDetail(res);
|
setTaskDetail(res);
|
||||||
if (res.steps) {
|
if (res.steps) {
|
||||||
@@ -271,7 +271,7 @@ function InitialInfo() {
|
|||||||
// 刷新任务详情(用于点击下一步后更新数据)
|
// 刷新任务详情(用于点击下一步后更新数据)
|
||||||
const refreshTaskDetail = () => {
|
const refreshTaskDetail = () => {
|
||||||
if (!creatID) return;
|
if (!creatID) return;
|
||||||
|
|
||||||
removeDetail(creatID).then((res: any) => {
|
removeDetail(creatID).then((res: any) => {
|
||||||
setTaskDetail(res);
|
setTaskDetail(res);
|
||||||
if (res.steps) {
|
if (res.steps) {
|
||||||
@@ -281,13 +281,13 @@ function InitialInfo() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
const createone = (stepId: number) => {
|
const createone = (stepId: number) => {
|
||||||
|
|
||||||
removeone(taskDetail.id, stepId.toString()).then((res: any) => {
|
removeone(taskDetail.id, stepId.toString()).then((res: any) => {
|
||||||
// 重新获取任务详情以更新数据
|
// 重新获取任务详情以更新数据
|
||||||
refreshTaskDetail();
|
refreshTaskDetail();
|
||||||
}).catch((error: any) => {
|
}).catch((error: any) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const createimage = (stepId: number) => {
|
const createimage = (stepId: number) => {
|
||||||
let params = {
|
let params = {
|
||||||
@@ -301,7 +301,7 @@ function InitialInfo() {
|
|||||||
refreshTaskDetail();
|
refreshTaskDetail();
|
||||||
}).catch((error: any) => {
|
}).catch((error: any) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const newcreateimage = () => {
|
const newcreateimage = () => {
|
||||||
// console.log('下一步:', stepId);
|
// console.log('下一步:', stepId);
|
||||||
let params = {
|
let params = {
|
||||||
@@ -310,22 +310,22 @@ function InitialInfo() {
|
|||||||
image_px: "2048x2048",
|
image_px: "2048x2048",
|
||||||
image_size: "2K"
|
image_size: "2K"
|
||||||
}
|
}
|
||||||
|
|
||||||
removetwo(taskDetail.id, steps[1].id.toString(), params).then((res: any) => {
|
removetwo(taskDetail.id, steps[1].id.toString(), params).then((res: any) => {
|
||||||
refreshTaskDetail();
|
refreshTaskDetail();
|
||||||
|
|
||||||
}).catch((error: any) => {
|
}).catch((error: any) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 下一步按钮点击处理
|
// 下一步按钮点击处理
|
||||||
const handleNextStep = (stepId: number) => {
|
const handleNextStep = (stepId: number) => {
|
||||||
// 获取引擎 ID 和视频参数
|
// 获取引擎 ID 和视频参数
|
||||||
const engineId = countType;
|
const engineId = countType;
|
||||||
const videoParams = {
|
const videoParams = {
|
||||||
engine_id : countType,
|
engine_id: countType,
|
||||||
duration: videoDuration,
|
duration: videoDuration,
|
||||||
aspect_ratio: videoAspectRatio,
|
aspect_ratio: videoAspectRatio,
|
||||||
resolution: videoResolution,
|
resolution: videoResolution,
|
||||||
@@ -340,12 +340,12 @@ function InitialInfo() {
|
|||||||
// 这里可以添加下一步的逻辑,比如调用接口等
|
// 这里可以添加下一步的逻辑,比如调用接口等
|
||||||
};
|
};
|
||||||
|
|
||||||
const createvideo = (stepId: number,engineId: string) => {
|
const createvideo = (stepId: number, engineId: string) => {
|
||||||
let params = {
|
let params = {
|
||||||
engine_id: engineId,
|
engine_id: engineId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
removefour(taskDetail.id, stepId.toString(), params).then((res: any) => {
|
removefour(taskDetail.id, stepId.toString(), params).then((res: any) => {
|
||||||
// 重新获取任务详情以更新数据
|
// 重新获取任务详情以更新数据
|
||||||
refreshTaskDetail();
|
refreshTaskDetail();
|
||||||
@@ -356,8 +356,8 @@ function InitialInfo() {
|
|||||||
let params = {
|
let params = {
|
||||||
engine_id: steps[2].engineId,
|
engine_id: steps[2].engineId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
removefour(taskDetail.id, steps[2].id.toString(), params).then((res: any) => {
|
removefour(taskDetail.id, steps[2].id.toString(), params).then((res: any) => {
|
||||||
// 重新获取任务详情以更新数据
|
// 重新获取任务详情以更新数据
|
||||||
refreshTaskDetail();
|
refreshTaskDetail();
|
||||||
@@ -369,76 +369,141 @@ function InitialInfo() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div style={{ minHeight: 'calc(100vh - 90px)', background: '#f8fafc' }}>
|
<div
|
||||||
<div style={{ height: '100%' }}>
|
className="initialinfo-container"
|
||||||
<div style={{ height: '100%', display: 'flex', justifyContent: 'space-between', gap: '2%' }}>
|
style={{
|
||||||
<div style={{ width: '70%', background: '#fff', borderRadius: 12, overflowY: 'auto', maxHeight: 'calc(100vh - 90px)' }}>
|
margin: '-24px -32px -32px',
|
||||||
<div style={{ borderBottom: '1px solid #e2e8f0', width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '14px', boxSizing: 'border-box' }}>
|
height: 'calc(100vh - 34px)',
|
||||||
<Button
|
position: 'relative',
|
||||||
type="text"
|
boxSizing: 'border-box',
|
||||||
icon={<ArrowLeftOutlined />}
|
overflow: 'hidden',
|
||||||
onClick={() => navigate(-1)}
|
display: 'flex',
|
||||||
style={{ color: '#64748b' }}
|
flexDirection: 'column',
|
||||||
>
|
}}
|
||||||
返回
|
>
|
||||||
</Button>
|
{/* 背景装饰 */}
|
||||||
<h3 style={{ margin: 0 }}>
|
<div style={{ position: 'absolute', top: -100, right: -100, width: 300, height: 300, background: 'radial-gradient(circle, rgba(99,102,241,0.08) 0%, transparent 70%)', borderRadius: '50%', pointerEvents: 'none' }} />
|
||||||
|
<div style={{ position: 'absolute', bottom: -100, left: -100, width: 300, height: 300, background: 'radial-gradient(circle, rgba(167,139,250,0.08) 0%, transparent 70%)', borderRadius: '50%', pointerEvents: 'none' }} />
|
||||||
|
|
||||||
|
<div style={{ flex: 1, display: 'flex', justifyContent: 'space-between', alignItems: 'stretch', gap: '2%', position: 'relative', zIndex: 1, minHeight: 0 }}>
|
||||||
|
<div
|
||||||
|
className="replication-preview"
|
||||||
|
style={{
|
||||||
|
flex: 4,
|
||||||
|
background: 'rgba(255,255,255,0.85)',
|
||||||
|
backdropFilter: 'blur(20px)',
|
||||||
|
borderRadius: 20,
|
||||||
|
overflow: 'hidden',
|
||||||
|
border: '1px solid rgba(99, 102, 241, 0.1)',
|
||||||
|
boxShadow: '0 8px 32px rgba(99, 102, 241, 0.08)',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
minHeight: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ borderBottom: '1px solid rgba(99, 102, 241, 0.08)', width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '16px 24px', background: 'rgba(255,255,255,0.6)', backdropFilter: 'blur(10px)', boxSizing: 'border-box' }}>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
icon={<ArrowLeftOutlined />}
|
||||||
|
onClick={() => navigate(-1)}
|
||||||
|
style={{
|
||||||
|
color: '#64748b',
|
||||||
|
borderRadius: 10,
|
||||||
|
fontSize: 13,
|
||||||
|
height: 32,
|
||||||
|
transition: 'all 0.25s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
返回
|
||||||
|
</Button>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||||
|
<div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #6366f1, #8b5cf6, transparent)', borderRadius: 1 }} />
|
||||||
|
|
||||||
|
<h3 style={{ margin: 0, fontSize: 16, fontWeight: 700, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
|
||||||
拆镜复刻 - 任务详情
|
拆镜复刻 - 任务详情
|
||||||
</h3>
|
</h3>
|
||||||
<Button type="primary" ghost style={{ borderRadius: 6, padding: '4px 12px', fontSize: 12, borderColor: '#6366f1', color: '#6366f1' }} onClick={() => setIsModalOpen(true)}>
|
<div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #8b5cf6, #6366f1, transparent)', borderRadius: 1 }} />
|
||||||
创作记录
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
ghost
|
||||||
|
style={{
|
||||||
|
borderRadius: 10,
|
||||||
|
padding: '4px 16px',
|
||||||
|
fontSize: 13,
|
||||||
|
background: 'rgba(99, 102, 241, 0.1)',
|
||||||
|
borderColor: 'rgba(99, 102, 241, 0.2)',
|
||||||
|
color: '#6366f1',
|
||||||
|
}}
|
||||||
|
onClick={() => setIsModalOpen(true)}
|
||||||
|
>
|
||||||
|
创作记录
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: 1, overflowY: 'auto' }}>
|
||||||
<div className="material_box">
|
<div className="material_box">
|
||||||
<div style={{ display: 'flex', gap: 24, flexWrap: 'wrap', alignItems: 'flex-start' }}>
|
<div style={{ display: 'flex', gap: 24, flexWrap: 'wrap', alignItems: 'flex-start' }}>
|
||||||
<div style={{ flex: 1, minWidth: 250 }}>
|
<div style={{ flex: 1, minWidth: 250 }}>
|
||||||
<span style={{ fontSize: 14, fontWeight: 500, color: '#374151', marginBottom: 8, display: 'block' }}>视频</span>
|
<span style={{ fontSize: 14, fontWeight: 600, color: '#1e293b', marginBottom: 10, display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
<div className="medio_box" style={{ aspectRatio: '16/9', background: '#f1f5f9', borderRadius: 8, overflow: 'hidden' }}>
|
<span style={{ width: 3, height: 14, background: 'linear-gradient(180deg, #6366f1, #8b5cf6)', borderRadius: 2, display: 'inline-block' }} />
|
||||||
|
视频
|
||||||
|
</span>
|
||||||
|
<div className="medio_box" style={{ aspectRatio: '16/9', background: 'linear-gradient(135deg, rgba(248,250,252,0.8) 0%, rgba(238,242,255,0.6) 100%)', borderRadius: 14, overflow: 'hidden', boxShadow: '0 4px 12px rgba(99, 102, 241, 0.06)', border: '1px solid rgba(99, 102, 241, 0.08)' }}>
|
||||||
{taskDetail?.material?.materialVideoUrl ? (
|
{taskDetail?.material?.materialVideoUrl ? (
|
||||||
<video
|
<video
|
||||||
controls
|
controls
|
||||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.material.materialVideoUrl}`}
|
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.material.materialVideoUrl}`}
|
||||||
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#94a3b8', fontSize: 12 }}>暂无视频</div>
|
<div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#94a3b8', fontSize: 13 }}>暂无视频</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="product_img" style={{ flex: 1, minWidth: 250 }}>
|
<div className="product_img" style={{ flex: 1, minWidth: 250 }}>
|
||||||
<span style={{ fontSize: 14, fontWeight: 500, color: '#374151', marginBottom: 8, display: 'block' }}>产品图片</span>
|
<span style={{ fontSize: 14, fontWeight: 600, color: '#1e293b', marginBottom: 10, display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
<div className="medio_box" style={{ aspectRatio: '1/1', background: '#f1f5f9', borderRadius: 8, overflow: 'hidden' }}>
|
<span style={{ width: 3, height: 14, background: 'linear-gradient(180deg, #6366f1, #8b5cf6)', borderRadius: 2, display: 'inline-block' }} />
|
||||||
|
产品图片
|
||||||
|
</span>
|
||||||
|
<div className="medio_box" style={{ aspectRatio: '1/1', background: 'linear-gradient(135deg, rgba(248,250,252,0.8) 0%, rgba(238,242,255,0.6) 100%)', borderRadius: 14, overflow: 'hidden', boxShadow: '0 4px 12px rgba(99, 102, 241, 0.06)', border: '1px solid rgba(99, 102, 241, 0.08)' }}>
|
||||||
{taskDetail?.material?.materialImageUrl ? (
|
{taskDetail?.material?.materialImageUrl ? (
|
||||||
<img src={taskDetail.material.materialImageUrl} alt="" style={{ width: '100%', height: '100%', objectFit: 'contain' }} />
|
<img src={taskDetail.material.materialImageUrl} alt="" style={{ width: '100%', height: '100%', objectFit: 'contain' }} />
|
||||||
) : (
|
) : (
|
||||||
<div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#94a3b8', fontSize: 12 }}>暂无图片</div>
|
<div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#94a3b8', fontSize: 13 }}>暂无图片</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{taskDetail?.finalImageUrl && (
|
{taskDetail?.finalImageUrl && (
|
||||||
<div style={{ marginTop: 16 }}>
|
<div style={{ marginTop: 24 }}>
|
||||||
<div>
|
<div>
|
||||||
<span style={{ fontSize: 14, fontWeight: 500, color: '#374151', marginBottom: 8, display: 'block' }}>生成图片</span>
|
<span style={{ fontSize: 14, fontWeight: 600, color: '#1e293b', marginBottom: 10, display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
<div className="medio_box" style={{ aspectRatio: '1/1', background: '#f1f5f9', borderRadius: 8, overflow: 'hidden' }}>
|
<span style={{ width: 3, height: 14, background: 'linear-gradient(180deg, #6366f1, #8b5cf6)', borderRadius: 2, display: 'inline-block' }} />
|
||||||
<img
|
生成图片
|
||||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.finalImageUrl}`}
|
</span>
|
||||||
alt=""
|
<div className="medio_box" style={{ aspectRatio: '1/1', background: 'linear-gradient(135deg, rgba(248,250,252,0.8) 0%, rgba(238,242,255,0.6) 100%)', borderRadius: 14, overflow: 'hidden', boxShadow: '0 4px 12px rgba(99, 102, 241, 0.06)', border: '1px solid rgba(99, 102, 241, 0.08)' }}>
|
||||||
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
<img
|
||||||
|
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.finalImageUrl}`}
|
||||||
|
alt=""
|
||||||
|
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{taskDetail?.finalVideoUrl && (
|
{taskDetail?.finalVideoUrl && (
|
||||||
<div style={{ marginTop: 16 }}>
|
<div style={{ marginTop: 24 }}>
|
||||||
<div>
|
<div>
|
||||||
<span style={{ fontSize: 14, fontWeight: 500, color: '#374151', marginBottom: 8, display: 'block' }}>生成视频</span>
|
<span style={{ fontSize: 14, fontWeight: 600, color: '#1e293b', marginBottom: 10, display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
<div className="medio_box" style={{ aspectRatio: '16/9', background: '#f1f5f9', borderRadius: 8, overflow: 'hidden' }}>
|
<span style={{ width: 3, height: 14, background: 'linear-gradient(180deg, #6366f1, #8b5cf6)', borderRadius: 2, display: 'inline-block' }} />
|
||||||
<video
|
生成视频
|
||||||
controls
|
</span>
|
||||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.finalVideoUrl}`}
|
<div className="medio_box" style={{ aspectRatio: '16/9', background: 'linear-gradient(135deg, rgba(248,250,252,0.8) 0%, rgba(238,242,255,0.6) 100%)', borderRadius: 14, overflow: 'hidden', boxShadow: '0 4px 12px rgba(99, 102, 241, 0.06)', border: '1px solid rgba(99, 102, 241, 0.08)' }}>
|
||||||
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
<video
|
||||||
|
controls
|
||||||
|
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.finalVideoUrl}`}
|
||||||
|
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -446,8 +511,28 @@ function InitialInfo() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ width: '28%', minWidth: '400px', background: '#fff', borderRadius: 12, overflowY: 'auto', maxHeight: 'calc(100vh - 90px)' }}>
|
</div>
|
||||||
<div style={{ padding: '14px' }}> 生成步骤</div>
|
<div
|
||||||
|
className="replication-form"
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
minWidth: '400px',
|
||||||
|
background: 'rgba(255,255,255,0.85)',
|
||||||
|
backdropFilter: 'blur(20px)',
|
||||||
|
borderRadius: 20,
|
||||||
|
overflow: 'hidden',
|
||||||
|
border: '1px solid rgba(99, 102, 241, 0.1)',
|
||||||
|
boxShadow: '0 8px 32px rgba(99, 102, 241, 0.08)',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
minHeight: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ padding: '16px 24px', borderBottom: '1px solid rgba(99, 102, 241, 0.08)', display: 'flex', alignItems: 'center', gap: 10, background: 'rgba(255,255,255,0.6)', backdropFilter: 'blur(10px)' }}>
|
||||||
|
<div style={{ width: 4, height: 18, background: 'linear-gradient(180deg, #6366f1 0%, #8b5cf6 100%)', borderRadius: 2 }} />
|
||||||
|
<span style={{ fontSize: 15, fontWeight: 700, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>生成步骤</span>
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: 1, overflowY: 'auto', padding: '8px 16px 16px' }}>
|
||||||
<Collapse
|
<Collapse
|
||||||
defaultActiveKey={['']}
|
defaultActiveKey={['']}
|
||||||
ghost
|
ghost
|
||||||
@@ -488,25 +573,25 @@ function InitialInfo() {
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
children: (
|
children: (
|
||||||
<div style={{ padding: '16px', background: '#f8fafc', borderRadius: 8, marginTop: 8 }}>
|
<div style={{ padding: '16px', background: 'linear-gradient(135deg, rgba(248,250,252,0.6) 0%, rgba(238,242,255,0.4) 100%)', borderRadius: 12, marginTop: 8, border: '1px solid rgba(99, 102, 241, 0.06)' }}>
|
||||||
{/* 步骤1: 原始素材 */}
|
{/* 步骤1: 原始素材 */}
|
||||||
{step.childId === 1 && (
|
{step.childId === 1 && (
|
||||||
<>
|
<>
|
||||||
<div style={{ display: 'flex', gap: 20, marginBottom: 20, alignItems: 'flex-start', flexWrap: 'wrap' }}>
|
<div style={{ display: 'flex', gap: 20, marginBottom: 20, alignItems: 'flex-start', flexWrap: 'wrap' }}>
|
||||||
<div style={{ flex: 1, minWidth: 200 }}>
|
<div style={{ flex: 1, minWidth: 200 }}>
|
||||||
<Text style={{ color: '#64748b', fontSize: 13, marginBottom: 10, display: 'block', fontWeight: 500 }}>原视频</Text>
|
<Text style={{ color: '#64748b', fontSize: 13, marginBottom: 10, display: 'block', fontWeight: 500 }}>原视频</Text>
|
||||||
<div style={{ height: 180, borderRadius: 10, background: '#f1f5f9', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
<div style={{ height: 180, borderRadius: 12, background: 'linear-gradient(135deg, rgba(248,250,252,0.8) 0%, rgba(238,242,255,0.6) 100%)', display: 'flex', alignItems: 'center', justifyContent: 'center', border: '1px solid rgba(99, 102, 241, 0.06)' }}>
|
||||||
{taskDetail?.material?.materialVideoUrl ? (
|
{taskDetail?.material?.materialVideoUrl ? (
|
||||||
<video
|
<video
|
||||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.material.materialVideoUrl}`}
|
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.material.materialVideoUrl}`}
|
||||||
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', borderRadius: 10 }}
|
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', borderRadius: 10 }}
|
||||||
controls
|
controls
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ textAlign: 'center', color: '#94a3b8' }}>
|
<div style={{ textAlign: 'center', color: '#94a3b8' }}>
|
||||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" style={{ marginBottom: 8 }}>
|
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" style={{ marginBottom: 8 }}>
|
||||||
<rect x="2" y="2" width="20" height="16" rx="2" ry="2"/>
|
<rect x="2" y="2" width="20" height="16" rx="2" ry="2" />
|
||||||
<path d="M8 12l4 2 4-2"/>
|
<path d="M8 12l4 2 4-2" />
|
||||||
</svg>
|
</svg>
|
||||||
<div style={{ fontSize: 13 }}>暂无视频</div>
|
<div style={{ fontSize: 13 }}>暂无视频</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -515,53 +600,51 @@ function InitialInfo() {
|
|||||||
</div>
|
</div>
|
||||||
<div style={{ flex: 1, minWidth: 200 }}>
|
<div style={{ flex: 1, minWidth: 200 }}>
|
||||||
<Text style={{ color: '#64748b', fontSize: 13, marginBottom: 10, display: 'block', fontWeight: 500 }}>产品图片</Text>
|
<Text style={{ color: '#64748b', fontSize: 13, marginBottom: 10, display: 'block', fontWeight: 500 }}>产品图片</Text>
|
||||||
<div style={{ height: 180, borderRadius: 10, background: '#f1f5f9', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
<div style={{ height: 180, borderRadius: 12, background: 'linear-gradient(135deg, rgba(248,250,252,0.8) 0%, rgba(238,242,255,0.6) 100%)', display: 'flex', alignItems: 'center', justifyContent: 'center', border: '1px solid rgba(99, 102, 241, 0.06)' }}>
|
||||||
{taskDetail?.material?.materialImageUrl ? (
|
{taskDetail?.material?.materialImageUrl ? (
|
||||||
<img
|
<img
|
||||||
src={taskDetail.material.materialImageUrl}
|
src={taskDetail.material.materialImageUrl}
|
||||||
alt="产品图片"
|
alt="产品图片"
|
||||||
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', borderRadius: 10 }}
|
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', borderRadius: 10 }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ textAlign: 'center', color: '#94a3b8' }}>
|
<div style={{ textAlign: 'center', color: '#94a3b8' }}>
|
||||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" style={{ marginBottom: 8 }}>
|
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" style={{ marginBottom: 8 }}>
|
||||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
|
||||||
<circle cx="8.5" cy="8.5" r="1.5" fill="currentColor"/>
|
<circle cx="8.5" cy="8.5" r="1.5" fill="currentColor" />
|
||||||
<path d="M21 15l-5-5L5 21"/>
|
<path d="M21 15l-5-5L5 21" />
|
||||||
</svg>
|
</svg>
|
||||||
<div style={{ fontSize: 13 }}>暂无图片</div>
|
<div style={{ fontSize: 13 }}>暂无图片</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={() => { message.info('正在生成图片提示词,请稍候...'); createone(step.id); }} type="primary" style={{ flex: 1, borderRadius: 8, background: '#6366f1', borderColor: '#6366f1', height: 36 }} disabled={step.status !== 'completed'}>
|
<Button onClick={() => { message.info('正在生成图片提示词,请稍候...'); createone(step.id); }} type="primary" style={{ flex: 1, borderRadius: 10, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', height: 36, fontWeight: 500, boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }} disabled={step.status !== 'completed'}>
|
||||||
下一步:生成图片提示词
|
下一步:生成图片提示词
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{/* 步骤2: 生成提示词 */}
|
{/* 步骤2: 生成提示词 */}
|
||||||
{step.childId === 2 && (
|
{step.childId === 2 && (
|
||||||
<>
|
<>
|
||||||
{/* <Text style={{ color: '#475569', fontSize: 13, lineHeight: 1.8, whiteSpace: 'pre-wrap' }}>
|
<div style={{ padding: '12px 14px', background: 'rgba(255,255,255,0.6)', borderRadius: 10, border: '1px solid rgba(99, 102, 241, 0.08)', marginBottom: 4 }}>
|
||||||
{taskDetail?.generationPrompt || '暂无提示词'}
|
<Text style={{ color: '#475569', fontSize: 13, lineHeight: 1.8, whiteSpace: 'pre-wrap' }}>
|
||||||
</Text> */}
|
{step?.output?.payload?.prompt || '暂无提示词'}
|
||||||
<Text style={{ color: '#475569', fontSize: 13, lineHeight: 1.8, whiteSpace: 'pre-wrap' }}>
|
</Text>
|
||||||
{/* {taskDetail?.generationPrompt || '暂无提示词'} */}
|
</div>
|
||||||
{step?.output?.payload?.prompt || '暂无提示词'}
|
|
||||||
</Text>
|
|
||||||
<Space style={{ marginTop: 16, gap: 12, width: '100%' }}>
|
<Space style={{ marginTop: 16, gap: 12, width: '100%' }}>
|
||||||
<Button
|
<Button
|
||||||
type="default"
|
type="default"
|
||||||
icon={<EditOutlined />}
|
icon={<EditOutlined />}
|
||||||
onClick={() => handleOpenModal(step?.output?.payload?.prompt, 'image', step.id)}
|
onClick={() => handleOpenModal(step?.output?.payload?.prompt, 'image', step.id)}
|
||||||
style={{ flex: 1, borderRadius: 8, borderColor: '#6366f1', color: '#6366f1', height: 36 }}
|
style={{ flex: 1, borderRadius: 10, borderColor: 'rgba(99, 102, 241, 0.3)', color: '#6366f1', height: 36, fontWeight: 500, background: 'rgba(99, 102, 241, 0.04)' }}
|
||||||
disabled={step.status !== 'completed'}
|
disabled={step.status !== 'completed'}
|
||||||
>
|
>
|
||||||
修改提示词
|
修改提示词
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => { message.info('正在生成图片,请稍候...'); createimage(step.id); }} type="primary" style={{ flex: 1, borderRadius: 8, background: '#6366f1', borderColor: '#6366f1', height: 36 }} disabled={step.status !== 'completed'}>
|
<Button onClick={() => { message.info('正在生成图片,请稍候...'); createimage(step.id); }} type="primary" style={{ flex: 1, borderRadius: 10, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', height: 36, fontWeight: 500, boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }} disabled={step.status !== 'completed'}>
|
||||||
下一步:生成图片
|
下一步:生成图片
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
@@ -570,19 +653,39 @@ function InitialInfo() {
|
|||||||
{/* 步骤3: 生成产品融合图 */}
|
{/* 步骤3: 生成产品融合图 */}
|
||||||
{step.childId === 3 && (
|
{step.childId === 3 && (
|
||||||
<>
|
<>
|
||||||
<div style={{ borderRadius: 8, overflow: 'hidden', marginBottom: 16, background: '#f1f5f9' }}>
|
<div style={{ borderRadius: 12, overflow: 'hidden', marginBottom: 16, background: 'linear-gradient(135deg, rgba(248,250,252,0.8) 0%, rgba(238,242,255,0.6) 100%)', border: '1px solid rgba(99, 102, 241, 0.08)', boxShadow: '0 2px 8px rgba(99, 102, 241, 0.04)', position: 'relative' }}>
|
||||||
{/* {step.output.result.result_image_url} */}
|
|
||||||
|
|
||||||
|
|
||||||
{taskDetail.finalImageUrl ? (
|
{taskDetail.finalImageUrl ? (
|
||||||
<img src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.finalImageUrl}`} alt="融合图" style={{ width: '100%', height: 180, objectFit: 'cover' }} />
|
<img src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.finalImageUrl}`} alt="融合图" style={{ width: '100%', height: 180, objectFit: 'cover' }} />
|
||||||
) : (
|
) : (
|
||||||
<div style={{ width: '100%', height: 180, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#94a3b8', fontSize: 14 }}>暂无融合图</div>
|
<div style={{ width: '100%', height: 180, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#94a3b8', fontSize: 14 }}>暂无融合图</div>
|
||||||
)}
|
)}
|
||||||
|
<Button
|
||||||
|
type="default"
|
||||||
|
icon={<EditOutlined />}
|
||||||
|
onClick={() => newcreateimage()}
|
||||||
|
disabled={step.status !== 'completed'}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 8,
|
||||||
|
left: 8,
|
||||||
|
borderRadius: 8,
|
||||||
|
borderColor: 'rgba(99, 102, 241, 0.3)',
|
||||||
|
color: '#6366f1',
|
||||||
|
height: 30,
|
||||||
|
fontWeight: 500,
|
||||||
|
background: 'rgba(255,255,255,0.85)',
|
||||||
|
backdropFilter: 'blur(4px)',
|
||||||
|
padding: '0 12px',
|
||||||
|
fontSize: 12,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
重新生成
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{/* 引擎选择器 */}
|
{/* 引擎选择器和视频参数设置 */}
|
||||||
<div style={{ marginBottom: 12 }}>
|
<p style={{ marginBottom: 6, fontSize: 14, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>视频参数选择:</p>
|
||||||
<div style={{ position: 'relative', display: 'inline-block' }}>
|
<div style={{ width: '100%', display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
|
||||||
|
<div style={{ flex: 1, position: 'relative', display: 'inline-block' }}>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowEngineModal(!showEngineModal);
|
setShowEngineModal(!showEngineModal);
|
||||||
@@ -590,12 +693,12 @@ function InitialInfo() {
|
|||||||
}}
|
}}
|
||||||
className="image-settings-trigger"
|
className="image-settings-trigger"
|
||||||
style={{
|
style={{
|
||||||
minWidth: 200,
|
width: '100%',
|
||||||
padding: '4px 12px',
|
padding: '4px 12px',
|
||||||
height: 34,
|
height: 34,
|
||||||
borderRadius: 8,
|
borderRadius: 10,
|
||||||
border: 'none',
|
border: '1px solid rgba(99, 102, 241, 0.15)',
|
||||||
backgroundColor: '#f1f5f9',
|
backgroundColor: 'rgba(255,255,255,0.7)',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -603,7 +706,7 @@ function InitialInfo() {
|
|||||||
transition: 'all 0.2s',
|
transition: 'all 0.2s',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SettingOutlined style={{ fontSize: 14, color: '#64748b' }} />
|
<SettingOutlined style={{ fontSize: 14, color: '#6366f1' }} />
|
||||||
<Text style={{
|
<Text style={{
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
@@ -619,7 +722,7 @@ function InitialInfo() {
|
|||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
bottom: 'calc(100% + 8px)',
|
bottom: 'calc(100% + 8px)',
|
||||||
left: -20,
|
left: -10,
|
||||||
width: 350,
|
width: 350,
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
borderRadius: 16,
|
borderRadius: 16,
|
||||||
@@ -709,11 +812,9 @@ function InitialInfo() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 视频参数设置 */}
|
{/* 视频参数设置 */}
|
||||||
<div style={{ marginBottom: 16 }}>
|
<div style={{ flex: 1, position: 'relative', display: 'inline-block' }}>
|
||||||
<div style={{ position: 'relative', display: 'inline-block' }}>
|
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowVideoSettingsModal(!showVideoSettingsModal);
|
setShowVideoSettingsModal(!showVideoSettingsModal);
|
||||||
@@ -721,12 +822,12 @@ function InitialInfo() {
|
|||||||
}}
|
}}
|
||||||
className="image-settings-trigger"
|
className="image-settings-trigger"
|
||||||
style={{
|
style={{
|
||||||
minWidth: 220,
|
width: '100%',
|
||||||
padding: '4px 12px',
|
padding: '4px 12px',
|
||||||
height: 34,
|
height: 34,
|
||||||
borderRadius: 8,
|
borderRadius: 10,
|
||||||
border: 'none',
|
border: '1px solid rgba(99, 102, 241, 0.15)',
|
||||||
backgroundColor: '#f1f5f9',
|
backgroundColor: 'rgba(255,255,255,0.7)',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -734,7 +835,7 @@ function InitialInfo() {
|
|||||||
transition: 'all 0.2s',
|
transition: 'all 0.2s',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LayoutOutlined style={{ fontSize: 14, color: '#64748b' }} />
|
<LayoutOutlined style={{ fontSize: 14, color: '#6366f1' }} />
|
||||||
<Text style={{
|
<Text style={{
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
@@ -750,7 +851,7 @@ function InitialInfo() {
|
|||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
bottom: 'calc(100% + 8px)',
|
bottom: 'calc(100% + 8px)',
|
||||||
left: -20,
|
left: -160,
|
||||||
width: 350,
|
width: 350,
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
borderRadius: 16,
|
borderRadius: 16,
|
||||||
@@ -942,41 +1043,35 @@ function InitialInfo() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Space style={{ width: '100%', gap: 12 }}>
|
<Button
|
||||||
<Button type="default" icon={<EditOutlined />} style={{ flex: 1, borderRadius: 8, borderColor: '#6366f1', color: '#6366f1', height: 36 }
|
type="primary"
|
||||||
} onClick={() => newcreateimage()} disabled={step.status !== 'completed'}>
|
style={{ width: '100%', borderRadius: 10, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', height: 36, fontWeight: 500, boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }}
|
||||||
重新生成
|
onClick={() => { message.info('正在生成视频提示词,请稍候...'); handleNextStep(step.id); }}
|
||||||
</Button>
|
disabled={step.status !== 'completed'}
|
||||||
<Button
|
>
|
||||||
type="primary"
|
下一步:生成视频提示词
|
||||||
style={{ flex: 1, borderRadius: 8, background: '#6366f1', borderColor: '#6366f1', height: 36 }}
|
</Button>
|
||||||
onClick={() => { message.info('正在生成视频提示词,请稍候...'); handleNextStep(step.id); }}
|
|
||||||
disabled={step.status !== 'completed'}
|
|
||||||
>
|
|
||||||
下一步:生成视频提示词
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{/* 步骤4: 生成视频提示词 */}
|
{/* 步骤4: 生成视频提示词 */}
|
||||||
{step.childId === 4 && (
|
{step.childId === 4 && (
|
||||||
<>
|
<>
|
||||||
<Text style={{ color: '#475569', fontSize: 13, lineHeight: 1.8, whiteSpace: 'pre-wrap' }}>
|
<div style={{ padding: '12px 14px', background: 'rgba(255,255,255,0.6)', borderRadius: 10, border: '1px solid rgba(99, 102, 241, 0.08)', marginBottom: 4 }}>
|
||||||
{/* {taskDetail?.videoPrompt || '暂无视频提示词'} */}
|
<Text style={{ color: '#475569', fontSize: 13, lineHeight: 1.8, whiteSpace: 'pre-wrap' }}>
|
||||||
{step?.output?.payload?.finalPrompt || '暂无提示词'}
|
{step?.output?.payload?.finalPrompt || '暂无提示词'}
|
||||||
|
</Text>
|
||||||
</Text>
|
</div>
|
||||||
<Space style={{ marginTop: 16, gap: 12, width: '100%' }}>
|
<Space style={{ marginTop: 16, gap: 12, width: '100%' }}>
|
||||||
<Button
|
<Button
|
||||||
type="default"
|
type="default"
|
||||||
icon={<EditOutlined />}
|
icon={<EditOutlined />}
|
||||||
onClick={() => handleOpenModal(step?.output?.payload?.promptSchema, 'video', step.id)}
|
onClick={() => handleOpenModal(step?.output?.payload?.promptSchema, 'video', step.id)}
|
||||||
style={{ flex: 1, borderRadius: 8, borderColor: '#6366f1', color: '#6366f1', height: 36 }}
|
style={{ flex: 1, borderRadius: 10, borderColor: 'rgba(99, 102, 241, 0.3)', color: '#6366f1', height: 36, fontWeight: 500, background: 'rgba(99, 102, 241, 0.04)' }}
|
||||||
disabled={step.status !== 'completed'}
|
disabled={step.status !== 'completed'}
|
||||||
>
|
>
|
||||||
修改提示词
|
修改提示词
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => { message.info('正在生成视频,请稍候...'); createvideo(step.id,step.engineId); }} type="primary" style={{ flex: 1, borderRadius: 8, background: '#6366f1', borderColor: '#6366f1', height: 36 }} disabled={step.status !== 'completed'}>
|
<Button onClick={() => { message.info('正在生成视频,请稍候...'); createvideo(step.id, step.engineId); }} type="primary" style={{ flex: 1, borderRadius: 10, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', height: 36, fontWeight: 500, boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }} disabled={step.status !== 'completed'}>
|
||||||
下一步:生成视频
|
下一步:生成视频
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
@@ -985,7 +1080,7 @@ function InitialInfo() {
|
|||||||
{/* 步骤5: 生成最终视频 */}
|
{/* 步骤5: 生成最终视频 */}
|
||||||
{step.childId === 5 && (
|
{step.childId === 5 && (
|
||||||
<>
|
<>
|
||||||
<div style={{ borderRadius: 8, overflow: 'hidden', marginBottom: 16, position: 'relative', background: '#f1f5f9' }}>
|
<div style={{ borderRadius: 12, overflow: 'hidden', marginBottom: 16, position: 'relative', background: 'linear-gradient(135deg, rgba(248,250,252,0.8) 0%, rgba(238,242,255,0.6) 100%)', border: '1px solid rgba(99, 102, 241, 0.08)', boxShadow: '0 2px 8px rgba(99, 102, 241, 0.04)' }}>
|
||||||
{step.status === 'completed' && taskDetail?.finalVideoUrl ? (
|
{step.status === 'completed' && taskDetail?.finalVideoUrl ? (
|
||||||
<video src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.finalVideoUrl}`} style={{ width: '100%', height: 180, objectFit: 'cover' }} controls />
|
<video src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail.finalVideoUrl}`} style={{ width: '100%', height: 180, objectFit: 'cover' }} controls />
|
||||||
) : step.status === 'processing' ? (
|
) : step.status === 'processing' ? (
|
||||||
@@ -1000,13 +1095,13 @@ function InitialInfo() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Space style={{ width: '100%', gap: 12 }}>
|
<Space style={{ width: '100%', gap: 12 }}>
|
||||||
<Button onClick={() => agincreatevideo()} type="default" icon={<EditOutlined />} style={{ flex: 1, borderRadius: 8, borderColor: '#6366f1', color: '#6366f1', height: 36 }} disabled={step.status !== 'completed'}>
|
<Button onClick={() => agincreatevideo()} type="default" icon={<EditOutlined />} style={{ flex: 1, borderRadius: 10, borderColor: 'rgba(99, 102, 241, 0.3)', color: '#6366f1', height: 36, fontWeight: 500, background: 'rgba(99, 102, 241, 0.04)' }} disabled={step.status !== 'completed'}>
|
||||||
重新生成
|
重新生成
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
icon={<DownloadOutlined />}
|
icon={<DownloadOutlined />}
|
||||||
style={{ flex: 1, borderRadius: 8, background: '#6366f1', borderColor: '#6366f1', height: 36 }}
|
style={{ flex: 1, borderRadius: 10, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', height: 36, fontWeight: 500, boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }}
|
||||||
disabled={step.status !== 'completed'}
|
disabled={step.status !== 'completed'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const videoUrl = `${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail?.finalVideoUrl}&download=1`;
|
const videoUrl = `${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${taskDetail?.finalVideoUrl}&download=1`;
|
||||||
@@ -1032,21 +1127,33 @@ function InitialInfo() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Modal
|
<Modal
|
||||||
title="修改提示词"
|
title={
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||||
|
<div style={{ width: 4, height: 18, background: 'linear-gradient(180deg, #6366f1 0%, #8b5cf6 100%)', borderRadius: 2 }} />
|
||||||
|
<span style={{ fontSize: 15, fontWeight: 700, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
|
||||||
|
{currentType === 'video' ? '修改视频提示词' : '修改提示词'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
open={modalVisible}
|
open={modalVisible}
|
||||||
onCancel={() => setModalVisible(false)}
|
onCancel={() => { if (!promptSaving) setModalVisible(false); }}
|
||||||
footer={[
|
footer={[
|
||||||
<Button key="cancel" onClick={() => setModalVisible(false)}>取消</Button>,
|
<Button key="cancel" onClick={() => setModalVisible(false)} disabled={promptSaving} style={{ borderRadius: 8 }}>取消</Button>,
|
||||||
<Button key="confirm" type="primary" onClick={handleConfirm} loading={promptSaving}>确认</Button>,
|
<Button key="confirm" type="primary" onClick={handleConfirm} loading={promptSaving} disabled={promptSaving} style={{ borderRadius: 8, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none' }}>确认</Button>,
|
||||||
]}
|
]}
|
||||||
width={800}
|
width={800}
|
||||||
|
style={{ borderRadius: 16 }}
|
||||||
|
styles={{
|
||||||
|
header: { background: 'rgba(255,255,255,0.6)', backdropFilter: 'blur(10px)', borderBottom: '1px solid rgba(99, 102, 241, 0.08)', padding: '16px 24px' },
|
||||||
|
body: { background: 'linear-gradient(135deg, #f8fafc 0%, #eef2ff 50%, #f0f9ff 100%)', padding: '20px 24px' },
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{currentType === 'image' ? (
|
{currentType === 'image' ? (
|
||||||
<TextArea
|
<TextArea
|
||||||
value={promptText}
|
value={promptText}
|
||||||
onChange={(e) => setPromptText(e.target.value)}
|
onChange={(e) => setPromptText(e.target.value)}
|
||||||
rows={8}
|
rows={8}
|
||||||
style={{ width: '100%', height: 300 }}
|
style={{ width: '100%', height: 300, borderRadius: 10, border: '1px solid rgba(99, 102, 241, 0.15)', background: 'rgba(255,255,255,0.8)' }}
|
||||||
placeholder="请输入提示词"
|
placeholder="请输入提示词"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@@ -1057,93 +1164,168 @@ function InitialInfo() {
|
|||||||
</Modal>
|
</Modal>
|
||||||
{/* 创作记录弹窗 */}
|
{/* 创作记录弹窗 */}
|
||||||
<Modal
|
<Modal
|
||||||
title="创作记录"
|
title={
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||||
|
<div style={{ width: 4, height: 20, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', borderRadius: 2 }} />
|
||||||
|
<span style={{ fontSize: 16, fontWeight: 600, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>创作记录</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
open={isModalOpen}
|
open={isModalOpen}
|
||||||
onCancel={() => setIsModalOpen(false)}
|
onCancel={() => setIsModalOpen(false)}
|
||||||
width={800}
|
width={800}
|
||||||
footer={null}
|
footer={null}
|
||||||
style={{ borderRadius: 12 }}
|
styles={{
|
||||||
|
body: { background: 'linear-gradient(180deg, #f8fafc 0%, #eef2ff 100%)' },
|
||||||
|
header: { background: '#fff', borderBottom: '1px solid rgba(99, 102, 241, 0.1)', padding: '20px 24px' },
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{/* 搜索区域 */}
|
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16, gap: 8 }}>
|
||||||
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16 }}>
|
|
||||||
<Input
|
<Input
|
||||||
placeholder="搜索产品名称"
|
placeholder="搜索产品名称"
|
||||||
style={{ width: 200, borderRadius: 6, marginRight: 8 }}
|
style={{
|
||||||
|
width: 200,
|
||||||
|
borderRadius: 10,
|
||||||
|
border: '1px solid rgba(99, 102, 241, 0.2)',
|
||||||
|
height: 40,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Button type="primary" style={{ borderRadius: 6 }}>
|
<Button
|
||||||
|
type="primary"
|
||||||
|
style={{
|
||||||
|
borderRadius: 10,
|
||||||
|
height: 40,
|
||||||
|
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||||
|
border: 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
搜索
|
搜索
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 表格 */}
|
<div
|
||||||
<Table
|
style={{
|
||||||
columns={[
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.9) 0%, rgba(255,255,255,0.7) 100%)',
|
||||||
{
|
backdropFilter: 'blur(20px)',
|
||||||
title: '产品名称',
|
borderRadius: 16,
|
||||||
dataIndex: 'targetProjectName',
|
overflow: 'hidden',
|
||||||
key: 'targetProjectName',
|
border: '1px solid rgba(99, 102, 241, 0.1)',
|
||||||
},
|
boxShadow: '0 8px 32px rgba(99, 102, 241, 0.08)',
|
||||||
{
|
|
||||||
title: '状态',
|
|
||||||
dataIndex: 'status',
|
|
||||||
key: 'status',
|
|
||||||
render: (text: string) => {
|
|
||||||
const statusMap: Record<string, string> = {
|
|
||||||
'pending': '子任务待处理',
|
|
||||||
'waiting_user': '等待用户确认或触发',
|
|
||||||
'processing': '子任务处理中',
|
|
||||||
'completed': '子任务完成',
|
|
||||||
'failed': '子任务失败',
|
|
||||||
'cancelled': '子任务取消',
|
|
||||||
};
|
|
||||||
return statusMap[text] || text || '-';
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '创建时间',
|
|
||||||
dataIndex: 'createdAt',
|
|
||||||
key: 'createdAt',
|
|
||||||
render: (text: string) => {
|
|
||||||
if (!text) return '-';
|
|
||||||
const date = new Date(text);
|
|
||||||
const year = date.getFullYear();
|
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
||||||
const day = String(date.getDate()).padStart(2, '0');
|
|
||||||
const hours = String(date.getHours()).padStart(2, '0');
|
|
||||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
||||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
||||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
key: 'action',
|
|
||||||
render: (_, record) => (
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
setIsModalOpen(false);
|
|
||||||
navigate(`/initial/${record.id}/initialinfo`);
|
|
||||||
}}
|
|
||||||
style={{ color: '#6366f1', textDecoration: 'none', fontSize: 12, border: 'none', background: 'none', cursor: 'pointer' }}
|
|
||||||
>
|
|
||||||
查看详情
|
|
||||||
</button>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
dataSource={tableData}
|
|
||||||
rowKey="id"
|
|
||||||
pagination={{
|
|
||||||
current: currentPage,
|
|
||||||
pageSize: pageSize,
|
|
||||||
total: total,
|
|
||||||
showSizeChanger: true,
|
|
||||||
showQuickJumper: true,
|
|
||||||
showTotal: (total) => `共 ${total} 条`,
|
|
||||||
onChange: handlePageChange,
|
|
||||||
}}
|
}}
|
||||||
style={{ fontSize: 13 }}
|
>
|
||||||
/>
|
<Table
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: '产品名称',
|
||||||
|
dataIndex: 'title',
|
||||||
|
key: 'title',
|
||||||
|
render: (text: string) => (
|
||||||
|
<span style={{ fontSize: 14, color: '#1e293b', fontWeight: 500 }}>{text}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'createdAt',
|
||||||
|
key: 'createdAt',
|
||||||
|
render: (text: string) => {
|
||||||
|
if (!text) return '-';
|
||||||
|
const date = new Date(text);
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
const hours = String(date.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||||
|
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||||
|
return <span style={{ fontSize: 13, color: '#64748b' }}>{`${year}-${month}-${day} ${hours}:${minutes}:${seconds}`}</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
align: 'center',
|
||||||
|
render: (record) => (
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
onClick={() => navigate(`/removelens/${record.id}/removeinfo`)}
|
||||||
|
style={{
|
||||||
|
color: '#6366f1',
|
||||||
|
fontSize: 13,
|
||||||
|
padding: '4px 12px',
|
||||||
|
borderRadius: 6,
|
||||||
|
background: 'rgba(99, 102, 241, 0.1)',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.background = 'rgba(99, 102, 241, 0.15)';
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.background = 'rgba(99, 102, 241, 0.1)';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
查看详情
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
dataSource={tableData}
|
||||||
|
rowKey="id"
|
||||||
|
pagination={{
|
||||||
|
current: currentPage,
|
||||||
|
pageSize: pageSize,
|
||||||
|
total: total,
|
||||||
|
showSizeChanger: true,
|
||||||
|
showQuickJumper: true,
|
||||||
|
showTotal: (total) => `共 ${total} 条`,
|
||||||
|
onChange: handlePageChange,
|
||||||
|
style: {
|
||||||
|
padding: '16px 24px',
|
||||||
|
borderTop: '1px solid rgba(99, 102, 241, 0.1)',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
style={{ fontSize: 13 }}
|
||||||
|
scroll={{ y: 350 }}
|
||||||
|
components={{
|
||||||
|
body: {
|
||||||
|
row: ({ className, style, ...rest }) => (
|
||||||
|
<tr
|
||||||
|
{...rest}
|
||||||
|
className={className}
|
||||||
|
style={{
|
||||||
|
...style,
|
||||||
|
transition: 'all 0.2s ease',
|
||||||
|
borderBottom: '1px solid rgba(99, 102, 241, 0.05)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
cell: ({ className, style, ...rest }) => (
|
||||||
|
<td
|
||||||
|
{...rest}
|
||||||
|
className={className}
|
||||||
|
style={{
|
||||||
|
...style,
|
||||||
|
padding: '16px 24px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
cell: ({ className, style, ...rest }) => (
|
||||||
|
<th
|
||||||
|
{...rest}
|
||||||
|
className={className}
|
||||||
|
style={{
|
||||||
|
...style,
|
||||||
|
background: 'rgba(99, 102, 241, 0.03)',
|
||||||
|
color: '#64748b',
|
||||||
|
fontWeight: 500,
|
||||||
|
fontSize: 13,
|
||||||
|
padding: '16px 24px',
|
||||||
|
borderBottom: 'none',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,26 +1,116 @@
|
|||||||
*{
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.material_box>div{
|
|
||||||
margin: 20px 10px;
|
.material_box {
|
||||||
|
padding: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.material_box>div {
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.medio_box {
|
.medio_box {
|
||||||
width: 280px;
|
width: 280px;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
}
|
}
|
||||||
.medio_box img ,.medio_box video {
|
|
||||||
|
.medio_box img,
|
||||||
|
.medio_box video {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 加载旋转动画 */
|
/* 加载旋转动画 */
|
||||||
@keyframes spinSlow {
|
@keyframes spinSlow {
|
||||||
from {
|
from {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 步骤状态动画 */
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 折叠面板样式优化 */
|
||||||
|
.ant-collapse-ghost > .ant-collapse-item {
|
||||||
|
border-bottom: 1px solid rgba(99, 102, 241, 0.06);
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-collapse-ghost > .ant-collapse-item:hover {
|
||||||
|
background: rgba(99, 102, 241, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-collapse-ghost > .ant-collapse-item > .ant-collapse-header {
|
||||||
|
padding: 14px 16px !important;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 步骤内容区滚动条 */
|
||||||
|
.replication-form-content::-webkit-scrollbar,
|
||||||
|
.replication-preview::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.replication-form-content::-webkit-scrollbar-thumb,
|
||||||
|
.replication-preview::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(99, 102, 241, 0.2);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.replication-form-content::-webkit-scrollbar-thumb:hover,
|
||||||
|
.replication-preview::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(99, 102, 241, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.replication-form-content::-webkit-scrollbar-track,
|
||||||
|
.replication-preview::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滑块样式 */
|
||||||
|
input[type="range"] {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"]::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #6366f1;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 2px solid #fff;
|
||||||
|
box-shadow: 0 2px 6px rgba(99, 102, 241, 0.4);
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"]::-webkit-slider-thumb:hover {
|
||||||
|
transform: scale(1.15);
|
||||||
|
box-shadow: 0 3px 10px rgba(99, 102, 241, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"]::-moz-range-thumb {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #6366f1;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 2px solid #fff;
|
||||||
|
box-shadow: 0 2px 6px rgba(99, 102, 241, 0.4);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user