素材云批量删除

This commit is contained in:
sjy
2026-07-02 14:13:40 +08:00
parent 7a2d127c92
commit b96e4ec4bf
9 changed files with 595 additions and 408 deletions
+1 -1
View File
@@ -130,5 +130,5 @@ export const api = {
post: <T>(path: string, body?: unknown, auth = true, skipAuthRedirect = false) => post: <T>(path: string, body?: unknown, auth = true, skipAuthRedirect = false) =>
apiRequest<T>(path, { method: 'POST', body, auth, skipAuthRedirect }), apiRequest<T>(path, { method: 'POST', body, auth, skipAuthRedirect }),
put: <T>(path: string, body?: unknown, auth = true) => apiRequest<T>(path, { method: 'PUT', body, auth }), put: <T>(path: string, body?: unknown, auth = true) => apiRequest<T>(path, { method: 'PUT', body, auth }),
delete: <T>(path: string, auth = true) => apiRequest<T>(path, { method: 'DELETE', auth }), delete: <T>(path: string, body?: unknown, auth = true) => apiRequest<T>(path, { method: 'DELETE', body, auth }),
}; };
+6
View File
@@ -721,3 +721,9 @@ export async function getHomeCaseHeader(): Promise<any> {
export async function getHomeCaseButton(id: string,limit:number=5): Promise<any> { export async function getHomeCaseButton(id: string,limit:number=5): Promise<any> {
return api.get(`/home-materials?category_id=${id}&limit_per_category=${limit}&include_empty_categories=false&response_mode=grouped&page=1&page_size=20`); return api.get(`/home-materials?category_id=${id}&limit_per_category=${limit}&include_empty_categories=false&response_mode=grouped&page=1&page_size=20`);
} }
export async function deleteResourcesMaterial(params:any): Promise<any> {
return api.delete(`/generation-ai/history/batch`, params);
}
+3 -3
View File
@@ -1165,8 +1165,8 @@ const AIChatPage: React.FC = () => {
position: 'relative', overflow: 'hidden', flexWrap: 'wrap', gap: 12, position: 'relative', overflow: 'hidden', flexWrap: 'wrap', gap: 12,
}}> }}>
<div style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 16 }}> <div style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 16 }}>
<div style={{ width: '15%', height: 2, background: 'linear-gradient(90deg, transparent, #6366f1, #8b5cf6, transparent)', borderRadius: 1 }} /> {/* <div style={{ width: '15%', height: 2, background: 'linear-gradient(90deg, transparent, #6366f1, #8b5cf6, transparent)', borderRadius: 1 }} /> */}
<div style={{ flex: 1, minWidth: 0, textAlign: 'center' }}> <div style={{ flex: 1, minWidth: 0, }}>
<h2 style={{ <h2 style={{
margin: 0, margin: 0,
fontSize: 18, fontSize: 18,
@@ -1182,7 +1182,7 @@ const AIChatPage: React.FC = () => {
/ /
</p> </p>
</div> </div>
<div style={{ width: '15%', height: 2, background: 'linear-gradient(90deg, transparent, #8b5cf6, #6366f1, transparent)', borderRadius: 1 }} /> {/* <div style={{ width: '15%', height: 2, background: 'linear-gradient(90deg, transparent, #8b5cf6, #6366f1, transparent)', borderRadius: 1 }} /> */}
</div> </div>
</div> </div>
+310 -129
View File
@@ -14,9 +14,9 @@ import {
ReloadOutlined, ReloadOutlined,
StarOutlined, StarOutlined,
PlayCircleOutlined, PlayCircleOutlined,
DeleteOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { gethistory, gethistoryItems, getOAuthList, asyncBatchUploadMaterial, updateFilename, getUploadHistory, getAllOAuthAccountList, getOpenTypeAll, getPreTestList, getDefaultPreTest } from '../api'; import { gethistory, gethistoryItems, getOAuthList, asyncBatchUploadMaterial, updateFilename, getUploadHistory, getAllOAuthAccountList, getOpenTypeAll, getPreTestList, getDefaultPreTest, deleteHistory, deleteResourcesMaterial } from '../api';
const { Search } = Input; const { Search } = Input;
const { Text } = Typography; const { Text } = Typography;
@@ -200,8 +200,8 @@ const GeneratedRecord: React.FC = () => {
} }
// 检查下载数量限制 // 检查下载数量限制
if (selectedItems.size > 10) { if (selectedItems.size > 30) {
message.warning('最多只能单次下载10个文件'); message.warning('最多只能单次下载30个文件');
return; return;
} }
@@ -265,6 +265,109 @@ const GeneratedRecord: React.FC = () => {
message.destroy('downloadProgress'); message.destroy('downloadProgress');
message.success(`下载完成,共 ${successCount} 个文件`); message.success(`下载完成,共 ${successCount} 个文件`);
setIsSelectionMode(false);
setSelectedItems(new Set());
};
const handleDeleteSelected = async () => {
if (selectedItems.size === 0) {
message.warning('请先选择要删除的媒体');
return;
}
const selectedData = recordlist.flatMap((group: any) =>
group.items.filter((item: any) => selectedItems.has(getItemResourceId(item)))
);
let confirmContent: React.ReactNode = '';
if (filterType === 'shot_replicate') {
confirmContent = (
<div>
<div style={{ marginBottom: 8 }}></div>
<ul style={{ margin: 0, paddingLeft: 20 }}>
{selectedData.map((item: any, index: number) => (
<li key={index} style={{ marginBottom: 4, color: '#334155' }}>
{item.shotReplicateProjectTitle} {item.shotSegmentLabel}
</li>
))}
</ul>
<div style={{ marginTop: 12 }}></div>
</div>
);
} else if (filterType === 'hot_opening_replicate') {
confirmContent = (
<div>
<div style={{ marginBottom: 8 }}></div>
<ul style={{ margin: 0, paddingLeft: 20 }}>
{selectedData.map((item: any, index: number) => (
<li key={index} style={{ marginBottom: 4, color: '#334155' }}>
{item.moduleProjectTitle}
</li>
))}
</ul>
<div style={{ marginTop: 12 }}></div>
</div>
);
} else {
confirmContent = `确定删除选中的 ${selectedItems.size} 条记录吗?`;
}
Modal.confirm({
title: '确认删除',
content: confirmContent,
okText: '确定删除',
cancelText: '取消',
okButtonProps: { danger: true },
onOk: async () => {
let historySource = '';
if (filterType === 'project') {
historySource = 'generation_record';
} else if (filterType === 'creation') {
historySource = 'chat_task';
} else if (filterType === 'hot_opening_replicate') {
historySource = 'hot_opening_replicate';
} else if (filterType === 'shot_replicate') {
historySource = 'shot_replicate';
}
const selectedContent: any[] = [];
recordlist.forEach((group: any) => {
group.items.forEach((item: any) => {
if (selectedItems.has(getItemResourceId(item))) {
selectedContent.push(item);
}
});
});
let idArr: any[] = [];
if (historySource === 'generation_record') {
idArr = selectedContent.map((item: any) => item.id);
} else if (historySource === 'chat_task') {
idArr = selectedContent.map((item: any) => item.id);
} else if (historySource === 'hot_opening_replicate') {
idArr = selectedContent.map((item: any) => item.moduleProjectId);
} else if (historySource === 'shot_replicate') {
idArr = selectedContent.map((item: any) => item.shotSegmentId);
}
const params = {
history_source: historySource,
ids: idArr,
};
try {
await deleteResourcesMaterial(params);
message.success('删除成功');
setIsSelectionMode(false);
setSelectedItems(new Set());
setPagebreak(prev => ({ ...prev, page: 1 }));
loadRecordList();
} catch (error) {
message.error('删除失败,请重试');
}
},
});
}; };
const loadPreTestTemplates = async () => { const loadPreTestTemplates = async () => {
setPreTestTemplatesLoading(true); setPreTestTemplatesLoading(true);
@@ -565,11 +668,10 @@ const GeneratedRecord: React.FC = () => {
message.error(error.message || '文件名更新失败'); message.error(error.message || '文件名更新失败');
} }
}; };
// 日期选择器变化处理函数
const handleDateChange = (dateString: string) => { const handleDateChange = (dateString: string) => {
setSelectedDate(dateString); setSelectedDate(dateString);
}; };
useEffect(() => { const loadRecordList = () => {
setLoading(true); setLoading(true);
let historySource = ''; let historySource = '';
if (filterType === 'project') { if (filterType === 'project') {
@@ -615,7 +717,6 @@ const GeneratedRecord: React.FC = () => {
data.forEach(group => { data.forEach(group => {
group.page = 1; group.page = 1;
}); });
// 如果是第一页,替换数据;否则追加数据
if (Pagebreak.page === 1) { if (Pagebreak.page === 1) {
setRecordList(data); setRecordList(data);
} else { } else {
@@ -630,6 +731,9 @@ const GeneratedRecord: React.FC = () => {
setLoading(false); setLoading(false);
}); });
} }
};
useEffect(() => {
loadRecordList();
}, [filterType, filterMedia, Pagebreak.page, selectedDate]); }, [filterType, filterMedia, Pagebreak.page, selectedDate]);
// 加载更多 // 加载更多
const handleLoadMore = () => { const handleLoadMore = () => {
@@ -763,7 +867,7 @@ const GeneratedRecord: React.FC = () => {
}} }}
icon={<StarOutlined />} icon={<StarOutlined />}
> >
</Button> </Button>
<Button <Button
type={filterType === 'shot_replicate' ? 'primary' : 'default'} type={filterType === 'shot_replicate' ? 'primary' : 'default'}
@@ -818,6 +922,12 @@ const GeneratedRecord: React.FC = () => {
> >
</Button> </Button>
<Button
onClick={() => handleDeleteSelected()}
style={{ borderRadius: 8, background: 'linear-gradient(135deg, #bf0b0a 0%, #ff8165 100%)', border: 'none', boxShadow: '0 4px 16px rgba(191, 11, 10, 0.35)', fontWeight: 600, padding: '8px 24px', color: '#fff' }}
>
({selectedItems.size})
</Button>
<Button <Button
onClick={() => handleDownloadSelected()} onClick={() => handleDownloadSelected()}
style={{ style={{
@@ -1335,7 +1445,7 @@ const GeneratedRecord: React.FC = () => {
onCancel={handleClosePreview} onCancel={handleClosePreview}
width={1200} width={1200}
footer={ footer={
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 12, padding: '16px 24px', background: 'rgba(255,255,255,0.6)', borderTop: '1px solid rgba(99, 102, 241, 0.08)' }}> <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 12, padding: '16px 24px', background: '#fff', borderTop: '1px solid #f1f5f9' }}>
<Button <Button
type="primary" type="primary"
icon={<DownloadOutlined />} icon={<DownloadOutlined />}
@@ -1351,53 +1461,131 @@ const GeneratedRecord: React.FC = () => {
link.click(); link.click();
document.body.removeChild(link); document.body.removeChild(link);
}} }}
style={{ borderRadius: 8, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }} style={{ borderRadius: 8, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', boxShadow: '0 4px 16px rgba(99, 102, 241, 0.35)', fontWeight: 600, padding: '8px 24px' }}
disabled={isMediaExpired(previewItem.videoUrl || previewItem.imageUrl)} disabled={isMediaExpired(previewItem.videoUrl || previewItem.imageUrl)}
> >
{isMediaExpired(previewItem.videoUrl || previewItem.imageUrl) ? '资源已过期' : '下载'} {isMediaExpired(previewItem.videoUrl || previewItem.imageUrl) ? '资源已过期' : '下载'}
</Button> </Button>
<Button
type="primary"
icon={<DeleteOutlined />}
onClick={() => {
let deleteId = '';
let confirmContent: React.ReactNode = '';
if (filterType === 'shot_replicate') {
deleteId = previewItem.shotSegmentId;
confirmContent = (
<div>
<div style={{ marginBottom: 8 }}></div>
<ul style={{ margin: 0, paddingLeft: 20 }}>
<li style={{ marginBottom: 4, color: '#334155' }}>
{previewItem.shotReplicateProjectTitle} {previewItem.shotSegmentLabel}
</li>
</ul>
<div style={{ marginTop: 12 }}></div>
</div>
);
} else if (filterType === 'hot_opening_replicate') {
deleteId = previewItem.moduleProjectId;
confirmContent = (
<div>
<div style={{ marginBottom: 8 }}></div>
<ul style={{ margin: 0, paddingLeft: 20 }}>
<li style={{ marginBottom: 4, color: '#334155' }}>
{previewItem.moduleProjectTitle}
</li>
</ul>
<div style={{ marginTop: 12 }}></div>
</div>
);
} else {
deleteId = previewItem.id;
confirmContent = '确定删除该记录吗?';
}
if (!deleteId) {
message.warning('无法获取删除ID');
return;
}
Modal.confirm({
title: '确认删除',
content: confirmContent,
okText: '确定删除',
cancelText: '取消',
okButtonProps: { danger: true },
onOk: async () => {
let historySource = '';
if (filterType === 'project') {
historySource = 'generation_record';
} else if (filterType === 'creation') {
historySource = 'chat_task';
} else if (filterType === 'hot_opening_replicate') {
historySource = 'hot_opening_replicate';
} else if (filterType === 'shot_replicate') {
historySource = 'shot_replicate';
}
let params = {
history_source: historySource,
ids: [deleteId],
}
try {
await deleteResourcesMaterial(params); // 调用删除接口
message.success('删除成功');
handleClosePreview();
setPagebreak(prev => ({ ...prev, page: 1 }));
loadRecordList();
} catch (error) {
message.error('删除失败,请重试');
}
},
});
}}
style={{ borderRadius: 8, background: 'linear-gradient(135deg, #bf0b0a 0%, #ff8165 100%)', border: 'none', boxShadow: '0 4px 16px rgba(191, 11, 10, 0.35)', fontWeight: 600, padding: '8px 24px' }}
disabled={isMediaExpired(previewItem.videoUrl || previewItem.imageUrl)}
>
</Button>
<Button <Button
onClick={handleSinglePushToMedia} onClick={handleSinglePushToMedia}
style={{ borderRadius: 8 }} style={{ borderRadius: 8, background: '#f8f9fc', border: '1px solid #e2e8f0', color: '#64748b', fontWeight: 600, padding: '8px 24px' }}
disabled={isMediaExpired(previewItem.videoUrl || previewItem.imageUrl)} disabled={isMediaExpired(previewItem.videoUrl || previewItem.imageUrl)}
> >
</Button> </Button>
</div> </div>
} }
style={{ borderRadius: 20 }} style={{ borderRadius: 16, overflow: 'hidden' }}
styles={{ styles={{
body: { height: 460, display: 'flex', flexDirection: 'column', padding: 0 }, body: { height: 500, display: 'flex', flexDirection: 'column', padding: 0 },
header: { background: 'rgba(255,255,255,0.6)', backdropFilter: 'blur(10px)', borderBottom: '1px solid rgba(99, 102, 241, 0.08)', padding: '16px 24px' }, header: { background: '#fff', borderBottom: '1px solid #f1f5f9', padding: '16px 24px' },
mask: { backgroundColor: 'rgba(0, 0, 0, 0.5)' },
}} }}
> >
{/* 内容区域 */}
<div style={{ <div style={{
flex: 1, width: '100%',
display: 'flex', display: 'flex',
flexWrap: 'wrap', height: '100%',
height: '460px', background: '#f8fafc',
gap: 20, }}>
<div style={{
width: '70%',
height: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
padding: 20, padding: 20,
overflow: 'auto',
justifyContent: 'center',
alignItems: 'center',
}}> }}>
{/* 媒体预览 */}
<div style={{
flex: 1,
minWidth: '280px',
maxWidth: '800px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
minHeight: '200px',
}}>
{/* 检查媒体是否过期 */}
{isMediaExpired(previewItem.videoUrl || previewItem.imageUrl) ? ( {isMediaExpired(previewItem.videoUrl || previewItem.imageUrl) ? (
<div style={{ textAlign: 'center', padding: '40px' }}> <div style={{ textAlign: 'center', padding: '40px', backgroundColor: '#fff', borderRadius: 12 }}>
<div style={{ fontSize: 48, marginBottom: 16 }}></div> <div style={{ fontSize: 48, marginBottom: 16 }}></div>
<p style={{ fontSize: 16, color: '#ff4d4f', marginBottom: 16 }}>/</p> <p style={{ fontSize: 15, color: '#ef4444', fontWeight: 500, marginBottom: 8 }}></p>
<p style={{ fontSize: 13, color: '#94a3b8' }}></p>
</div> </div>
) : filterMedia === 'video' ? ( ) : filterMedia === 'video' ? (
<video <video
@@ -1407,8 +1595,8 @@ const GeneratedRecord: React.FC = () => {
autoPlay autoPlay
style={{ style={{
maxWidth: '100%', maxWidth: '100%',
maxHeight: '55vh', maxHeight: '100%',
borderRadius: 8, borderRadius: 12,
objectFit: 'contain', objectFit: 'contain',
}} }}
/> />
@@ -1418,148 +1606,136 @@ const GeneratedRecord: React.FC = () => {
alt="预览" alt="预览"
style={{ style={{
maxWidth: '100%', maxWidth: '100%',
maxHeight: '55vh', maxHeight: '100%',
objectFit: 'contain', objectFit: 'contain',
borderRadius: 8 borderRadius: 12,
}} }}
/> />
)} )}
</div> </div>
{/* 参数信息 */}
<div style={{ <div style={{
width: '100%', width: '30%',
minWidth: '280px', height: '100%',
maxWidth: '320px',
background: '#f8fafc',
borderRadius: 12,
padding: 20,
maxHeight: '55vh',
overflowY: 'auto', overflowY: 'auto',
overflowX: 'hidden', scrollbarWidth: 'none',
backgroundColor: '#fff',
padding: 24,
borderLeft: '1px solid #f1f5f9',
}}> }}>
<Typography.Text strong style={{ fontSize: 14, color: '#475569', display: 'block', marginBottom: 16 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 20 }}>
<div style={{ width: 2, height: 18, background: 'linear-gradient(180deg, #6366f1 0%, #8b5cf6 100%)', borderRadius: 1 }} />
<Typography.Text strong style={{ fontSize: 15, color: '#1e293b', fontWeight: 600 }}>
</Typography.Text> </Typography.Text>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}> <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
{/* ID */} <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
{/* <div style={{ display: 'flex', justifyContent: 'space-between' }}> <span style={{ color: '#94a3b8', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: '#94a3b8', fontSize: 13 }}>ID</span> <span style={{ color: '#334155', fontSize: 13, fontWeight: 600 }}>
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
{previewItem.id || '-'}
</span>
</div> */}
{/* 类型 */}
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span style={{ color: '#94a3b8', fontSize: 13 }}></span>
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
{filterMedia === 'video' ? '视频' : '图片'} {filterMedia === 'video' ? '视频' : '图片'}
</span> </span>
</div> </div>
<div style={{ display: 'flex', gap: 12 }}> {filterType === 'hot_opening_replicate' && (
<span style={{ color: '#94a3b8', fontSize: 13, flexShrink: 0, width: 40 }}></span> <div style={{ display: 'flex', justifyContent: 'space-between', gap: 4 }}>
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500, flex: 1, wordBreak: 'break-all' }}> <span style={{ color: '#94a3b8', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500, wordBreak: 'break-all', lineHeight: '1.5' }}>
{previewItem.moduleProjectTitle}
</span>
</div>
)}
{filterType === 'shot_replicate' && (
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 4 }}>
<span style={{ color: '#94a3b8', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500, wordBreak: 'break-all', lineHeight: '1.5' }}>
{previewItem.shotReplicateProjectTitle} {previewItem.shotSegmentLabel}
</span>
</div>
)}
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: '#94a3b8', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500, wordBreak: 'break-all', lineHeight: '1.5' }}>
{previewItem.originalPrompt} {previewItem.originalPrompt}
</span> </span>
</div> </div>
{/* 分辨率 */}
{filterMedia === 'image' && ( {filterMedia === 'image' && (
<> <>
<div style={{ display: 'flex', justifyContent: 'space-between' }}> <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ color: '#94a3b8', fontSize: 13 }}></span> <span style={{ color: '#94a3b8', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}> <span style={{ color: '#334155', fontSize: 13, fontWeight: 600 }}>
{previewItem.imageProportion} {previewItem.imageProportion}
</span> </span>
</div> </div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}> <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ color: '#94a3b8', fontSize: 13 }}></span> <span style={{ color: '#94a3b8', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}> <span style={{ color: '#334155', fontSize: 13, fontWeight: 600 }}>
{previewItem.imageSize} {previewItem.imageSize}
</span> </span>
</div> </div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}> <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ color: '#94a3b8', fontSize: 13 }}></span> <span style={{ color: '#94a3b8', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}> <span style={{ color: '#334155', fontSize: 13, fontWeight: 600 }}>
{previewItem.imagePx} {previewItem.imagePx}
</span> </span>
</div> </div>
</> </>
)} )}
{filterMedia === 'video' && ( {filterMedia === 'video' && (
<> <>
<div style={{ display: 'flex', justifyContent: 'space-between' }}> <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ color: '#94a3b8', fontSize: 13 }}></span> <span style={{ color: '#94a3b8', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}> <span style={{ color: '#334155', fontSize: 13, fontWeight: 600 }}>
{previewItem.aspectRatio} {previewItem.aspectRatio}
</span> </span>
</div> </div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}> <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ color: '#94a3b8', fontSize: 13 }}></span> <span style={{ color: '#94a3b8', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}> <span style={{ color: '#334155', fontSize: 13, fontWeight: 600 }}>
{previewItem.resolution} {previewItem.resolution}
</span> </span>
</div> </div>
{/* <div style={{ display: 'flex', justifyContent: 'space-between' }}> <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ color: '#94a3b8', fontSize: 13 }}>尺寸</span> <span style={{ color: '#94a3b8', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}> <span style={{ color: '#334155', fontSize: 13, fontWeight: 600 }}>
{previewItem.imagePx}
</span>
</div> */}
</>
)}
{/* 时长(视频) */}
{filterMedia === 'video' && (
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span style={{ color: '#94a3b8', fontSize: 13 }}></span>
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
{`${previewItem.duration}` || '-'} {`${previewItem.duration}` || '-'}
</span> </span>
</div> </div>
</>
)} )}
{/* 文件大小 */} <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
{/* <div style={{ display: 'flex', justifyContent: 'space-between' }}> <span style={{ color: '#94a3b8', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: '#94a3b8', fontSize: 13 }}>文件大小</span> <span style={{ color: '#334155', fontSize: 13, fontWeight: 600 }}>
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
{previewItem.size ? formatFileSize(previewItem.size) : '-'}
</span>
</div> */}
{/* 创建时间 */}
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span style={{ color: '#94a3b8', fontSize: 13 }}></span>
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
{formatDateTime(previewItem.createdAt || previewItem.generatedDate)} {formatDateTime(previewItem.createdAt || previewItem.generatedDate)}
</span> </span>
</div> </div>
{/* 生成引擎 */} <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}> <span style={{ color: '#94a3b8', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: '#94a3b8', fontSize: 13 }}></span> <span style={{ color: '#334155', fontSize: 13, fontWeight: 600 }}>
<span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
{previewItem.engine || previewItem.engineName || '-'} {previewItem.engine || previewItem.engineName || '-'}
</span> </span>
</div> </div>
</div> </div>
{/* 分隔线 */} <div style={{ borderTop: '1px solid #f1f5f9', margin: '20px 0' }} />
<div style={{ borderTop: '1px dashed #e2e8f0', margin: '16px 0' }} />
{previewItem.mediaReferences && previewItem.mediaReferences.length > 0 && !['hot_opening_replicate', 'shot_replicate'].includes(filterType) && (
{previewItem.mediaReferences && previewItem.mediaReferences.length > 0 && (
<> <>
<Typography.Text strong style={{ fontSize: 14, color: '#475569', display: 'block', marginBottom: 12 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 16 }}>
<div style={{ width: 2, height: 18, background: 'linear-gradient(180deg, #3b82f6 0%, #60a5fa 100%)', borderRadius: 1 }} />
<Typography.Text strong style={{ fontSize: 15, color: '#1e293b', fontWeight: 600 }}>
</Typography.Text> </Typography.Text>
</div>
{previewItem.mediaReferences.map((item, index) => ( {previewItem.mediaReferences.map((item, index) => (
<div <div
key={item.url} key={item.url}
onClick={() => { onClick={() => {
// 暂停视频
if (videoRef.current) { if (videoRef.current) {
videoRef.current.pause(); videoRef.current.pause();
} }
@@ -1569,27 +1745,32 @@ const GeneratedRecord: React.FC = () => {
style={{ style={{
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: 8, gap: 10,
padding: 8, padding: '10px 12px',
borderRadius: 6, borderRadius: 8,
cursor: 'pointer', cursor: 'pointer',
backgroundColor: '#f1f5f9', backgroundColor: '#f8fafc',
marginBottom: 4, marginBottom: 8,
transition: 'background-color 0.2s', transition: 'all 0.25s ease',
border: '1px solid transparent',
}} }}
onMouseEnter={(e) => { onMouseEnter={(e) => {
(e.currentTarget as HTMLElement).style.backgroundColor = '#e2e8f0'; const el = e.currentTarget as HTMLElement;
el.style.backgroundColor = '#e0f2fe';
el.style.borderColor = '#bae6fd';
}} }}
onMouseLeave={(e) => { onMouseLeave={(e) => {
(e.currentTarget as HTMLElement).style.backgroundColor = '#f1f5f9'; const el = e.currentTarget as HTMLElement;
el.style.backgroundColor = '#f8fafc';
el.style.borderColor = 'transparent';
}} }}
> >
{item.type === 'image' ? ( {item.type === 'image' ? (
<PictureOutlined style={{ color: '#3b82f6', fontSize: 14 }} /> <PictureOutlined style={{ color: '#3b82f6', fontSize: 16 }} />
) : ( ) : (
<VideoCameraOutlined style={{ color: '#f59e0b', fontSize: 14 }} /> <VideoCameraOutlined style={{ color: '#f59e0b', fontSize: 16 }} />
)} )}
<span style={{ color: '#334155', fontSize: 13 }}> <span style={{ color: '#334155', fontSize: 13, fontWeight: 500 }}>
{item.name || `媒体${index + 1}`} {item.name || `媒体${index + 1}`}
</span> </span>
</div> </div>
@@ -1924,7 +2105,7 @@ const GeneratedRecord: React.FC = () => {
onClick: () => { onClick: () => {
const id = String(record.id); const id = String(record.id);
const newOauthItems = [...selectedOauthItems]; const newOauthItems = [...selectedOauthItems];
newOauthItems[index] = { value: id, label: String(record.accountId)+'-'+(record.accountName || '-') }; newOauthItems[index] = { value: id, label: String(record.accountId) + '-' + (record.accountName || '-') };
setSelectedOauthItems(newOauthItems); setSelectedOauthItems(newOauthItems);
const newOauthSelectOpens = [...oauthSelectOpens]; const newOauthSelectOpens = [...oauthSelectOpens];
newOauthSelectOpens[index] = false; newOauthSelectOpens[index] = false;
+4 -4
View File
@@ -98,7 +98,7 @@ const HomePage: React.FC = () => {
const aiEntries = [ const aiEntries = [
{ {
icon: <FileTextOutlined style={{ fontSize: 24, color: '#6366f1' }} />, icon: <FileTextOutlined style={{ fontSize: 24, color: '#6366f1' }} />,
title: '爆款开头复刻', title: '爆款复刻',
description: '上传参考视频与产品图片,一键复刻爆款视频开头', description: '上传参考视频与产品图片,一键复刻爆款视频开头',
action: '立即创作', action: '立即创作',
path: '/initial', path: '/initial',
@@ -106,7 +106,7 @@ const HomePage: React.FC = () => {
{ {
icon: <ScissorOutlined style={{ fontSize: 24, color: '#f97316' }} />, icon: <ScissorOutlined style={{ fontSize: 24, color: '#f97316' }} />,
title: '拆镜复刻', title: '拆镜复刻',
description: '一键拆解画面分镜,助力仿拍或创作', description: '精细化镜头复刻工具,拆分参考视频单镜头独立复刻,提升素材原创度,规避素材同质化',
action: '开始混剪', action: '开始混剪',
path: '/removelens', path: '/removelens',
}, },
@@ -143,7 +143,7 @@ const HomePage: React.FC = () => {
return ( return (
<div className="content_box"> <div className="content_box">
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 }}>
<h3></h3> <h3> </h3>
<p <p
style={{ style={{
cursor: 'pointer', cursor: 'pointer',
@@ -190,7 +190,7 @@ const HomePage: React.FC = () => {
background: 'linear-gradient(180deg, #6366f1, #a855f7)', background: 'linear-gradient(180deg, #6366f1, #a855f7)',
}} /> }} />
<div style={{ fontSize: 17, fontWeight: 700, color: '#1f2937', letterSpacing: 0.3 }}> <div style={{ fontSize: 17, fontWeight: 700, color: '#1f2937', letterSpacing: 0.3 }}>
<span style={{ color: '#a8a8a8ff', fontSize: 12 }}>()</span>
</div> </div>
</div> </div>
</div> </div>
+1 -1
View File
@@ -473,7 +473,7 @@ function InitialInfo() {
<div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #6366f1, #8b5cf6, transparent)', borderRadius: 1 }} /> <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 style={{ margin: 0, fontSize: 16, fontWeight: 700, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
- -
</h3> </h3>
<div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #8b5cf6, #6366f1, transparent)', borderRadius: 1 }} /> <div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #8b5cf6, #6366f1, transparent)', borderRadius: 1 }} />
@@ -450,7 +450,7 @@ const GenerateConver: React.FC = () => {
position: 'relative', overflow: 'hidden', flexWrap: 'wrap', gap: 12, position: 'relative', overflow: 'hidden', flexWrap: 'wrap', gap: 12,
}}> }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
<div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #6366f1, #8b5cf6, transparent)', borderRadius: 1 }} /> {/* <div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #6366f1, #8b5cf6, transparent)', borderRadius: 1 }} /> */}
<div> <div>
<h2 style={{ <h2 style={{
margin: 0, margin: 0,
@@ -460,15 +460,15 @@ const GenerateConver: React.FC = () => {
WebkitBackgroundClip: 'text', WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent', WebkitTextFillColor: 'transparent',
backgroundClip: 'text', backgroundClip: 'text',
textAlign: 'center', // textAlign: 'center',
}}> }}>
</h2> </h2>
<p style={{ fontSize: 13, color: '#64748b', margin: '4px 0 0 0' }}> <p style={{ fontSize: 13, color: '#64748b', margin: '4px 0 0 0' }}>
</p> </p>
</div> </div>
<div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #8b5cf6, #6366f1, transparent)', borderRadius: 1 }} /> {/* <div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #8b5cf6, #6366f1, transparent)', borderRadius: 1 }} /> */}
</div> </div>
<Button <Button
type="primary" type="primary"
@@ -796,7 +796,7 @@ const GenerateConver: React.FC = () => {
</h3> </h3>
<p style={{ margin: 0, fontSize: 13, color: '#64748b', textAlign: 'center', padding: '0 40px', lineHeight: 1.6 }}> <p style={{ margin: 0, fontSize: 13, color: '#64748b', textAlign: 'center', padding: '0 40px', lineHeight: 1.6 }}>
</p> </p>
</div> </div>
)} )}
+3 -3
View File
@@ -116,7 +116,7 @@ const ProjectsPage: React.FC = () => {
position: 'relative', overflow: 'hidden', flexWrap: 'wrap', gap: 12, position: 'relative', overflow: 'hidden', flexWrap: 'wrap', gap: 12,
}}> }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
<div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #6366f1, #8b5cf6, transparent)', borderRadius: 1 }} /> {/* <div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #6366f1, #8b5cf6, transparent)', borderRadius: 1 }} /> */}
<div> <div>
<h2 style={{ <h2 style={{
margin: 0, margin: 0,
@@ -126,7 +126,7 @@ const ProjectsPage: React.FC = () => {
WebkitBackgroundClip: 'text', WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent', WebkitTextFillColor: 'transparent',
backgroundClip: 'text', backgroundClip: 'text',
textAlign: 'center', // textAlign: 'center',
}}> }}>
</h2> </h2>
@@ -134,7 +134,7 @@ const ProjectsPage: React.FC = () => {
{projects.length} · {projects.length} ·
</p> </p>
</div> </div>
<div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #8b5cf6, #6366f1, transparent)', borderRadius: 1 }} /> {/* <div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #8b5cf6, #6366f1, transparent)', borderRadius: 1 }} /> */}
</div> </div>
<div style={{ display: 'flex', gap: 12 }}> <div style={{ display: 'flex', gap: 12 }}>
<Button icon={<FileTextOutlined />} onClick={() => navigate('/records')} size="large" style={{ <Button icon={<FileTextOutlined />} onClick={() => navigate('/records')} size="large" style={{
+4 -4
View File
@@ -177,7 +177,7 @@ export default function VideoFrameExtractor() {
position: 'relative', overflow: 'hidden', flexWrap: 'wrap', gap: 12, position: 'relative', overflow: 'hidden', flexWrap: 'wrap', gap: 12,
}}> }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
<div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #6366f1, #8b5cf6, transparent)', borderRadius: 1 }} /> {/* <div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #6366f1, #8b5cf6, transparent)', borderRadius: 1 }} /> */}
<div> <div>
<h2 style={{ <h2 style={{
margin: 0, margin: 0,
@@ -187,15 +187,15 @@ export default function VideoFrameExtractor() {
WebkitBackgroundClip: 'text', WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent', WebkitTextFillColor: 'transparent',
backgroundClip: 'text', backgroundClip: 'text',
textAlign: 'center', // textAlign: 'center',
}}> }}>
AI视频拆镜工作台 AI视频拆镜工作台
</h2> </h2>
<p style={{ fontSize: 13, color: '#64748b', margin: '4px 0 0 0' }}> <p style={{ fontSize: 13, color: '#64748b', margin: '4px 0 0 0' }}>
仿
</p> </p>
</div> </div>
<div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #8b5cf6, #6366f1, transparent)', borderRadius: 1 }} /> {/* <div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, transparent, #8b5cf6, #6366f1, transparent)', borderRadius: 1 }} /> */}
</div> </div>
<Button <Button