From b96e4ec4bfe3f860ee95f2999d11fec03e46998f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E4=BD=B3=E8=89=BA?= <13935794190@163.com> Date: Thu, 2 Jul 2026 14:13:40 +0800 Subject: [PATCH] =?UTF-8?q?=E7=B4=A0=E6=9D=90=E4=BA=91=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- video-gen-app/src/api/client.ts | 2 +- video-gen-app/src/api/index.ts | 8 +- video-gen-app/src/pages/GenerateConver.tsx | 6 +- video-gen-app/src/pages/GeneratedRecord.tsx | 953 +++++++++++------- video-gen-app/src/pages/HomePage.tsx | 8 +- video-gen-app/src/pages/InitialInfo.tsx | 2 +- .../src/pages/InitialReplication.tsx | 10 +- video-gen-app/src/pages/ProjectsPage.tsx | 6 +- video-gen-app/src/pages/RemoveLens.tsx | 8 +- 9 files changed, 595 insertions(+), 408 deletions(-) diff --git a/video-gen-app/src/api/client.ts b/video-gen-app/src/api/client.ts index 8762d20e..2badb7c5 100644 --- a/video-gen-app/src/api/client.ts +++ b/video-gen-app/src/api/client.ts @@ -130,5 +130,5 @@ export const api = { post: (path: string, body?: unknown, auth = true, skipAuthRedirect = false) => apiRequest(path, { method: 'POST', body, auth, skipAuthRedirect }), put: (path: string, body?: unknown, auth = true) => apiRequest(path, { method: 'PUT', body, auth }), - delete: (path: string, auth = true) => apiRequest(path, { method: 'DELETE', auth }), + delete: (path: string, body?: unknown, auth = true) => apiRequest(path, { method: 'DELETE', body, auth }), }; diff --git a/video-gen-app/src/api/index.ts b/video-gen-app/src/api/index.ts index 08667a1a..aeb2528f 100644 --- a/video-gen-app/src/api/index.ts +++ b/video-gen-app/src/api/index.ts @@ -720,4 +720,10 @@ export async function getHomeCaseHeader(): Promise { // 首页素材按钮资源 export async function getHomeCaseButton(id: string,limit:number=5): Promise { return api.get(`/home-materials?category_id=${id}&limit_per_category=${limit}&include_empty_categories=false&response_mode=grouped&page=1&page_size=20`); -} \ No newline at end of file +} + + + +export async function deleteResourcesMaterial(params:any): Promise { + return api.delete(`/generation-ai/history/batch`, params); +} diff --git a/video-gen-app/src/pages/GenerateConver.tsx b/video-gen-app/src/pages/GenerateConver.tsx index ea7a4ce3..de941db4 100644 --- a/video-gen-app/src/pages/GenerateConver.tsx +++ b/video-gen-app/src/pages/GenerateConver.tsx @@ -1165,8 +1165,8 @@ const AIChatPage: React.FC = () => { position: 'relative', overflow: 'hidden', flexWrap: 'wrap', gap: 12, }}>
-
-
+ {/*
*/} +

{ 输入想法、剧本或上传参考,智能生成视频/图片

-
+ {/*
*/}
diff --git a/video-gen-app/src/pages/GeneratedRecord.tsx b/video-gen-app/src/pages/GeneratedRecord.tsx index 546a9c3a..167d6602 100644 --- a/video-gen-app/src/pages/GeneratedRecord.tsx +++ b/video-gen-app/src/pages/GeneratedRecord.tsx @@ -14,9 +14,9 @@ import { ReloadOutlined, StarOutlined, PlayCircleOutlined, - + DeleteOutlined, } 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 { Text } = Typography; @@ -200,8 +200,8 @@ const GeneratedRecord: React.FC = () => { } // 检查下载数量限制 - if (selectedItems.size > 10) { - message.warning('最多只能单次下载10个文件'); + if (selectedItems.size > 30) { + message.warning('最多只能单次下载30个文件'); return; } @@ -245,12 +245,12 @@ const GeneratedRecord: React.FC = () => { selectedContent.forEach((item, index) => { setTimeout(() => { const url = `${baseUrl}${item.videoUrl || item.imageUrl}&download=1`; - const link = document.createElement('a'); - link.href = url; - link.download = ''; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); + const link = document.createElement('a'); + link.href = url; + link.download = ''; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); }, index * 500); }); return; @@ -265,6 +265,109 @@ const GeneratedRecord: React.FC = () => { message.destroy('downloadProgress'); 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 = ( +
+
以下内容所连带的图片和视频都将被删除:
+
    + {selectedData.map((item: any, index: number) => ( +
  • + {item.shotReplicateProjectTitle}— —{item.shotSegmentLabel} +
  • + ))} +
+
确定删除吗?
+
+ ); + } else if (filterType === 'hot_opening_replicate') { + confirmContent = ( +
+
爆款开头复刻中以下内容所连带的图片和视频都将被删除:
+
    + {selectedData.map((item: any, index: number) => ( +
  • + {item.moduleProjectTitle} +
  • + ))} +
+
确定删除吗?
+
+ ); + } 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 () => { setPreTestTemplatesLoading(true); @@ -565,11 +668,10 @@ const GeneratedRecord: React.FC = () => { message.error(error.message || '文件名更新失败'); } }; - // 日期选择器变化处理函数 const handleDateChange = (dateString: string) => { setSelectedDate(dateString); }; - useEffect(() => { + const loadRecordList = () => { setLoading(true); let historySource = ''; if (filterType === 'project') { @@ -615,7 +717,6 @@ const GeneratedRecord: React.FC = () => { data.forEach(group => { group.page = 1; }); - // 如果是第一页,替换数据;否则追加数据 if (Pagebreak.page === 1) { setRecordList(data); } else { @@ -630,6 +731,9 @@ const GeneratedRecord: React.FC = () => { setLoading(false); }); } + }; + useEffect(() => { + loadRecordList(); }, [filterType, filterMedia, Pagebreak.page, selectedDate]); // 加载更多 const handleLoadMore = () => { @@ -763,7 +867,7 @@ const GeneratedRecord: React.FC = () => { }} icon={} > - 爆款开头复刻 + 爆款复刻 +
{/* 分组内加载更多 */} {group.total && group.total > group.items.length && ( @@ -1335,7 +1445,7 @@ const GeneratedRecord: React.FC = () => { onCancel={handleClosePreview} width={1200} footer={ -
+
+
} - style={{ borderRadius: 20 }} + style={{ borderRadius: 16, overflow: 'hidden' }} styles={{ - body: { height: 460, 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' }, + body: { height: 500, display: 'flex', flexDirection: 'column', padding: 0 }, + header: { background: '#fff', borderBottom: '1px solid #f1f5f9', padding: '16px 24px' }, + mask: { backgroundColor: 'rgba(0, 0, 0, 0.5)' }, }} > - {/* 内容区域 */}
- {/* 媒体预览 */} -
- {/* 检查媒体是否过期 */} - {isMediaExpired(previewItem.videoUrl || previewItem.imageUrl) ? ( -
-
⚠️
-

图片/视频资源已过期,请刷新重新加载~

-
- ) : filterMedia === 'video' ? ( -
+
- + padding: 20, + }}> + {isMediaExpired(previewItem.videoUrl || previewItem.imageUrl) ? ( +
+
⚠️
+

资源已过期

+

请刷新页面重新加载

+
+ ) : filterMedia === 'video' ? ( +
+ +
+
+
+ 文件信息 +
-
- {/* ID */} - {/*
- ID - - {previewItem.id || '-'} - -
*/} - - {/* 类型 */} -
- 类型 - - {filterMedia === 'video' ? '视频' : '图片'} +
+
+ 类型 + + {filterMedia === 'video' ? '视频' : '图片'} + +
+ {filterType === 'hot_opening_replicate' && ( +
+ 类属 + + 爆款复刻——{previewItem.moduleProjectTitle}
-
- 请求 - - {previewItem.originalPrompt} + )} + {filterType === 'shot_replicate' && ( +
+ 类属 + + {previewItem.shotReplicateProjectTitle}— —{previewItem.shotSegmentLabel}
+ )} - {/* 分辨率 */} - {filterMedia === 'image' && ( - <> -
- 比例 - - {previewItem.imageProportion} - -
-
- 分辨率 - - {previewItem.imageSize} - -
-
- 尺寸 - - {previewItem.imagePx} - -
- - )} - {filterMedia === 'video' && ( - <> -
- 比例 - - {previewItem.aspectRatio} - -
-
- 分辨率 - - {previewItem.resolution} - -
- {/*
- 尺寸 - - {previewItem.imagePx} - -
*/} - - )} +
+ 请求 + + {previewItem.originalPrompt} + +
- {/* 时长(视频) */} - {filterMedia === 'video' && ( -
- 时长 - + {filterMedia === 'image' && ( + <> +
+ 比例 + + {previewItem.imageProportion} + +
+
+ 分辨率 + + {previewItem.imageSize} + +
+
+ 尺寸 + + {previewItem.imagePx} + +
+ + )} + + {filterMedia === 'video' && ( + <> +
+ 比例 + + {previewItem.aspectRatio} + +
+
+ 分辨率 + + {previewItem.resolution} + +
+
+ 时长 + {`${previewItem.duration}秒` || '-'}
- )} - - {/* 文件大小 */} - {/*
- 文件大小 - - {previewItem.size ? formatFileSize(previewItem.size) : '-'} - -
*/} - - {/* 创建时间 */} -
- 创建时间 - - {formatDateTime(previewItem.createdAt || previewItem.generatedDate)} - -
- - {/* 生成引擎 */} -
- 生成引擎 - - {previewItem.engine || previewItem.engineName || '-'} - -
-
- - {/* 分隔线 */} -
- - - {previewItem.mediaReferences && previewItem.mediaReferences.length > 0 && ( - <> - - 依靠附件 - - {previewItem.mediaReferences.map((item, index) => ( -
{ - // 暂停视频 - if (videoRef.current) { - videoRef.current.pause(); - } - const url = `${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${item.url}`; - window.open(url, '_blank'); - }} - style={{ - display: 'flex', - alignItems: 'center', - gap: 8, - padding: 8, - borderRadius: 6, - cursor: 'pointer', - backgroundColor: '#f1f5f9', - marginBottom: 4, - transition: 'background-color 0.2s', - }} - onMouseEnter={(e) => { - (e.currentTarget as HTMLElement).style.backgroundColor = '#e2e8f0'; - }} - onMouseLeave={(e) => { - (e.currentTarget as HTMLElement).style.backgroundColor = '#f1f5f9'; - }} - > - {item.type === 'image' ? ( - - ) : ( - - )} - - {item.name || `媒体${index + 1}`} - -
- ))} )} + +
+ 创建时间 + + {formatDateTime(previewItem.createdAt || previewItem.generatedDate)} + +
+ +
+ 生成引擎 + + {previewItem.engine || previewItem.engineName || '-'} + +
+
+ +
+ + {previewItem.mediaReferences && previewItem.mediaReferences.length > 0 && !['hot_opening_replicate', 'shot_replicate'].includes(filterType) && ( + <> +
+
+ + 依靠附件 + +
+ {previewItem.mediaReferences.map((item, index) => ( +
{ + if (videoRef.current) { + videoRef.current.pause(); + } + const url = `${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${item.url}`; + window.open(url, '_blank'); + }} + style={{ + display: 'flex', + alignItems: 'center', + gap: 10, + padding: '10px 12px', + borderRadius: 8, + cursor: 'pointer', + backgroundColor: '#f8fafc', + marginBottom: 8, + transition: 'all 0.25s ease', + border: '1px solid transparent', + }} + onMouseEnter={(e) => { + const el = e.currentTarget as HTMLElement; + el.style.backgroundColor = '#e0f2fe'; + el.style.borderColor = '#bae6fd'; + }} + onMouseLeave={(e) => { + const el = e.currentTarget as HTMLElement; + el.style.backgroundColor = '#f8fafc'; + el.style.borderColor = 'transparent'; + }} + > + {item.type === 'image' ? ( + + ) : ( + + )} + + {item.name || `媒体${index + 1}`} + +
+ ))} + + )}
@@ -1744,7 +1925,7 @@ const GeneratedRecord: React.FC = () => { })()}
- +
是否开启前测: @@ -1924,7 +2105,7 @@ const GeneratedRecord: React.FC = () => { onClick: () => { const id = String(record.id); 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); const newOauthSelectOpens = [...oauthSelectOpens]; newOauthSelectOpens[index] = false; @@ -2019,14 +2200,14 @@ const GeneratedRecord: React.FC = () => { .filter(item => item.advertiserId) .map(item => String(item.advertiserId))} onChange={(targetKeys) => { - const newSelected = historyOAuthList.filter(item => + const newSelected = historyOAuthList.filter(item => item.advertiserId && targetKeys.includes(String(item.advertiserId)) ); setSelectedHistoryAccounts(newSelected); }} titles={['可选账户列表', '已选账户列表']} showSearch - filterOption={(inputValue, item) => + filterOption={(inputValue, item) => String(item.advertiserId).toLowerCase().includes(inputValue.toLowerCase()) || (item.advertiserName && item.advertiserName.toLowerCase().includes(inputValue.toLowerCase())) } diff --git a/video-gen-app/src/pages/HomePage.tsx b/video-gen-app/src/pages/HomePage.tsx index 642504bf..66df45cd 100644 --- a/video-gen-app/src/pages/HomePage.tsx +++ b/video-gen-app/src/pages/HomePage.tsx @@ -98,7 +98,7 @@ const HomePage: React.FC = () => { const aiEntries = [ { icon: , - title: '爆款开头复刻', + title: '爆款复刻', description: '上传参考视频与产品图片,一键复刻爆款视频开头', action: '立即创作', path: '/initial', @@ -106,7 +106,7 @@ const HomePage: React.FC = () => { { icon: , title: '拆镜复刻', - description: '一键拆解画面分镜,助力仿拍或创作', + description: '精细化镜头复刻工具,拆分参考视频单镜头独立复刻,提升素材原创度,规避素材同质化', action: '开始混剪', path: '/removelens', }, @@ -143,7 +143,7 @@ const HomePage: React.FC = () => { return (
-

首页工作台

+

首页工作台

{ background: 'linear-gradient(180deg, #6366f1, #a855f7)', }} />

- 我的项目 + 我的项目(操作步骤)
diff --git a/video-gen-app/src/pages/InitialInfo.tsx b/video-gen-app/src/pages/InitialInfo.tsx index c4f2e53f..8c0135ce 100644 --- a/video-gen-app/src/pages/InitialInfo.tsx +++ b/video-gen-app/src/pages/InitialInfo.tsx @@ -473,7 +473,7 @@ function InitialInfo() {

- 爆款开头复刻 - 任务详情 + 爆款复刻 - 任务详情

diff --git a/video-gen-app/src/pages/InitialReplication.tsx b/video-gen-app/src/pages/InitialReplication.tsx index 8c29e1c8..cff58540 100644 --- a/video-gen-app/src/pages/InitialReplication.tsx +++ b/video-gen-app/src/pages/InitialReplication.tsx @@ -450,7 +450,7 @@ const GenerateConver: React.FC = () => { position: 'relative', overflow: 'hidden', flexWrap: 'wrap', gap: 12, }}>
-
+ {/*
*/}

{ WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text', - textAlign: 'center', + // textAlign: 'center', }}> - 爆款开头复刻 + 爆款复刻

上传参考视频与产品图片,一键复刻爆款视频开头

-
+ {/*
*/}
)} diff --git a/video-gen-app/src/pages/ProjectsPage.tsx b/video-gen-app/src/pages/ProjectsPage.tsx index 3ed92569..e8cece2c 100644 --- a/video-gen-app/src/pages/ProjectsPage.tsx +++ b/video-gen-app/src/pages/ProjectsPage.tsx @@ -116,7 +116,7 @@ const ProjectsPage: React.FC = () => { position: 'relative', overflow: 'hidden', flexWrap: 'wrap', gap: 12, }}>
-
+ {/*
*/}

{ WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text', - textAlign: 'center', + // textAlign: 'center', }}> 我的项目

@@ -134,7 +134,7 @@ const ProjectsPage: React.FC = () => { 共 {projects.length} 个项目 · 按行业分类管理

-
+ {/*
*/}