From 809f257bc7fad8ab49f0d1181875f86ed5b75055 Mon Sep 17 00:00:00 2001 From: sion123 <450702724@qq.com> Date: Sat, 22 Nov 2025 16:32:07 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/material/MaterialGroup.vue | 38 ++- .../src/views/material/MaterialList.vue | 218 ++++++++++++++++-- 2 files changed, 224 insertions(+), 32 deletions(-) diff --git a/frontend/app/web-gold/src/views/material/MaterialGroup.vue b/frontend/app/web-gold/src/views/material/MaterialGroup.vue index a319a248cc..884e30db16 100644 --- a/frontend/app/web-gold/src/views/material/MaterialGroup.vue +++ b/frontend/app/web-gold/src/views/material/MaterialGroup.vue @@ -66,20 +66,21 @@ :title="groupModalTitle" @ok="handleSaveGroup" @cancel="handleCancelGroup" + @close="handleCancelGroup" > - - - + + + - + - - + + @@ -102,6 +103,7 @@ const groupList = ref([]) const groupModalVisible = ref(false) const groupModalTitle = ref('新建分组') const isEdit = ref(false) +const groupFormRef = ref(null) // 表单 const groupForm = reactive({ @@ -141,6 +143,10 @@ const handleCreateGroup = () => { groupForm.description = '' groupForm.sort = 0 groupModalVisible.value = true + // 清除表单验证状态 + setTimeout(() => { + groupFormRef.value?.clearValidate() + }, 0) } // 编辑分组 @@ -152,16 +158,18 @@ const handleEditGroup = (group) => { groupForm.description = group.description || '' groupForm.sort = group.sort || 0 groupModalVisible.value = true + // 清除表单验证状态 + setTimeout(() => { + groupFormRef.value?.clearValidate() + }, 0) } // 保存分组 const handleSaveGroup = async () => { - if (!groupForm.name.trim()) { - message.warning('请输入分组名称') - return - } - try { + // 验证表单 + const values = await groupFormRef.value.validateFields() + if (isEdit.value) { await MaterialGroupService.updateGroup(groupForm) message.success('更新成功') @@ -172,6 +180,10 @@ const handleSaveGroup = async () => { groupModalVisible.value = false loadGroupList() } catch (error) { + if (error?.errorFields) { + // 表单验证失败,不显示错误提示(Ant Design会自动显示) + return + } console.error('保存分组失败:', error) message.error(error.message || '保存失败,请重试') } @@ -184,6 +196,8 @@ const handleCancelGroup = () => { groupForm.name = '' groupForm.description = '' groupForm.sort = 0 + // 清除表单验证状态 + groupFormRef.value?.resetFields() } // 删除分组 diff --git a/frontend/app/web-gold/src/views/material/MaterialList.vue b/frontend/app/web-gold/src/views/material/MaterialList.vue index 21d9664116..167290cc5c 100644 --- a/frontend/app/web-gold/src/views/material/MaterialList.vue +++ b/frontend/app/web-gold/src/views/material/MaterialList.vue @@ -9,10 +9,17 @@ 上传素材 + + 批量分组 ({{ selectedFileIds.length }}) + 素材混剪 @@ -120,6 +127,10 @@
+ +
+ +
@@ -160,6 +171,35 @@ @confirm="handleConfirmUpload" @cancel="handleUploadCancel" /> + + + + + + + + {{ group.name }} + + + +
+

+ {{ groupingInfo }} +

+
+
+
+
-

选中素材:{{ selectedFiles.length }} 个

-

视频素材:{{ selectedVideoUrls.length }} 个

-

背景音乐:{{ selectedAudioUrls.length }} 个

+

视频素材:{{ allVideoUrls.length }} 个

+

背景音乐:{{ allAudioUrls.length }} 个

+

+ 系统将智能随机选择素材进行混剪 +

@@ -206,9 +248,10 @@ import { UploadOutlined, SearchOutlined, FileOutlined, - DeleteOutlined + DeleteOutlined, + TagsOutlined } from '@ant-design/icons-vue' -import { MaterialService } from '@/api/material' +import { MaterialService, MaterialGroupService } from '@/api/material' import { MixService } from '@/api/mix' import MaterialUploadModal from '@/components/material/MaterialUploadModal.vue' import { formatFileSize, formatDate } from '@/utils/file' @@ -222,6 +265,12 @@ const uploading = ref(false) const mixModalVisible = ref(false) const mixing = ref(false) +// 分组相关 +const groupModalVisible = ref(false) +const groupList = ref([]) +const selectedGroupId = ref(null) +const groupingFileId = ref(null) // 当前正在分组的单个文件ID + // 筛选条件 const filters = reactive({ fileCategory: 'video', // 默认分类为视频 @@ -485,6 +534,28 @@ const selectedAudioUrls = computed(() => selectedFiles.value.map((file) => (isAudioFile(file) ? file?.fileUrl || file?.previewUrl : null)).filter(Boolean) ) +// 获取所有视频素材 +const allVideoUrls = computed(() => + fileList.value.map((file) => (isVideoFile(file) ? file?.fileUrl || file?.previewUrl : null)).filter(Boolean) +) + +// 获取所有音频素材 +const allAudioUrls = computed(() => + fileList.value.map((file) => (isAudioFile(file) ? file?.fileUrl || file?.previewUrl : null)).filter(Boolean) +) + +// 分组提示信息 +const groupingInfo = computed(() => { + if (groupingFileId.value) { + // 单个文件分组 + const file = fileList.value.find(f => f.id === groupingFileId.value) + return file ? `将为文件 "${file.fileName}" 添加到选中的分组中` : '' + } else { + // 批量分组 + return `将为 ${selectedFileIds.value.length} 个文件添加到选中的分组中` + } +}) + const mixForm = reactive({ title: '', text: '', @@ -498,16 +569,13 @@ const resetMixForm = () => { } const handleOpenMixModal = () => { - if (selectedFileIds.value.length === 0) { - message.warning('请先选择至少一个素材') + // 检查是否有可用的视频和音频素材 + if (allVideoUrls.value.length === 0) { + message.warning('暂无可用的视频素材') return } - if (selectedVideoUrls.value.length === 0) { - message.warning('请至少选择一个视频素材') - return - } - if (selectedAudioUrls.value.length === 0) { - message.warning('请至少选择一个背景音乐素材') + if (allAudioUrls.value.length === 0) { + message.warning('暂无可用的音频素材') return } mixModalVisible.value = true @@ -529,21 +597,24 @@ const handleMixConfirm = async () => { return } const produceCount = Math.max(1, Math.min(10, Number(mixForm.produceCount) || 1)) - if (selectedVideoUrls.value.length === 0) { - message.warning('请至少选择一个视频素材') + + // 再次检查素材可用性 + if (allVideoUrls.value.length === 0) { + message.warning('暂无可用的视频素材') return } - if (selectedAudioUrls.value.length === 0) { - message.warning('请至少选择一个背景音乐素材') + if (allAudioUrls.value.length === 0) { + message.warning('暂无可用的音频素材') return } + mixing.value = true try { const { data } = await MixService.batchProduceAlignment({ title, text, - videoUrls: selectedVideoUrls.value, - bgMusicUrls: selectedAudioUrls.value, + videoUrls: allVideoUrls.value, + bgMusicUrls: allAudioUrls.value, produceCount }) const jobIds = Array.isArray(data) ? data : [] @@ -565,7 +636,87 @@ const handleMixConfirm = async () => { // 初始化 onMounted(() => { loadFileList() + loadGroupList() }) + +// 加载分组列表 +const loadGroupList = async () => { + try { + const res = await MaterialGroupService.getGroupList() + if (res.code === 0) { + groupList.value = res.data || [] + } + } catch (error) { + console.error('加载分组列表失败:', error) + } +} + +// 打开批量分组模态框 +const handleOpenGroupModal = () => { + if (selectedFileIds.value.length === 0) { + message.warning('请先选择要分组的文件') + return + } + groupingFileId.value = null // 清空单个文件分组标记 + groupModalVisible.value = true +} + +// 单个文件分组 +const handleSingleGroup = (file) => { + groupingFileId.value = file.id // 标记是单个文件分组 + groupModalVisible.value = true +} + +// 执行批量分组 +const handleBatchGroup = async () => { + if (!selectedGroupId.value) { + message.warning('请选择分组') + return + } + + try { + let fileIds + let successMessage + + if (groupingFileId.value) { + // 单个文件分组 + fileIds = [groupingFileId.value] + successMessage = '文件分组成功' + } else { + // 批量分组 + fileIds = selectedFileIds.value + successMessage = '批量分组成功' + } + + await MaterialGroupService.addFilesToGroups({ + fileIds: fileIds, + groupIds: [selectedGroupId.value] + }) + message.success(successMessage) + + // 重置状态 + groupModalVisible.value = false + selectedGroupId.value = null + groupingFileId.value = null + + // 如果是批量分组,清除选中状态 + if (!groupingFileId.value) { + selectedFileIds.value = [] + } + + loadGroupList() // 刷新分组列表以更新文件计数 + } catch (error) { + console.error('分组失败:', error) + message.error(error.message || '分组失败,请重试') + } +} + +// 取消分组操作 +const handleGroupCancel = () => { + groupModalVisible.value = false + selectedGroupId.value = null + groupingFileId.value = null +}