send-stream
This commit is contained in:
@@ -4,14 +4,14 @@
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<h2 class="text-xl font-bold">生成数字人</h2>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
||||
<section class="bg-white p-4 rounded shadow lg:col-span-1">
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-3">
|
||||
<section class="p-4 bg-white rounded shadow lg:col-span-1">
|
||||
<div class="space-y-3">
|
||||
<div class="text-gray-600 text-sm">形象、背景、脚本、分辨率、字幕等配置。</div>
|
||||
<button class="px-4 py-2 bg-purple-600 text-white rounded">生成视频</button>
|
||||
<div class="text-sm text-gray-600">形象、背景、脚本、分辨率、字幕等配置。</div>
|
||||
<button class="px-4 py-2 text-white bg-purple-600 rounded">生成视频</button>
|
||||
</div>
|
||||
</section>
|
||||
<section class="bg-white p-4 rounded shadow lg:col-span-2">
|
||||
<section class="p-4 bg-white rounded shadow lg:col-span-2">
|
||||
<div class="text-gray-500">视频预览、任务队列、渲染进度</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,14 @@
|
||||
</template>
|
||||
上传素材
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
ghost
|
||||
:disabled="selectedFileIds.length === 0"
|
||||
@click="handleOpenMixModal"
|
||||
>
|
||||
素材混剪
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="selectedFileIds.length > 0"
|
||||
type="primary"
|
||||
@@ -142,11 +150,47 @@
|
||||
@confirm="handleConfirmUpload"
|
||||
@cancel="handleUploadCancel"
|
||||
/>
|
||||
<a-modal
|
||||
v-model:open="mixModalVisible"
|
||||
title="素材混剪"
|
||||
centered
|
||||
:confirm-loading="mixing"
|
||||
ok-text="提交混剪"
|
||||
cancel-text="取消"
|
||||
@ok="handleMixConfirm"
|
||||
@cancel="handleMixCancel"
|
||||
>
|
||||
<div class="mix-modal__summary">
|
||||
<p>选中素材:{{ selectedFiles.length }} 个</p>
|
||||
<p>视频素材:{{ selectedVideoUrls.length }} 个</p>
|
||||
<p>背景音乐:{{ selectedAudioUrls.length }} 个</p>
|
||||
</div>
|
||||
<a-form layout="vertical">
|
||||
<a-form-item label="视频标题" required>
|
||||
<a-input v-model:value="mixForm.title" placeholder="请输入生成视频标题" />
|
||||
</a-form-item>
|
||||
<a-form-item label="文案内容" required>
|
||||
<a-textarea
|
||||
v-model:value="mixForm.text"
|
||||
placeholder="请输入文案(每句话换行以便自动拆分)"
|
||||
:rows="4"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="生成成片数量" required>
|
||||
<a-input-number
|
||||
v-model:value="mixForm.produceCount"
|
||||
:min="1"
|
||||
:max="10"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { message, Modal } from 'ant-design-vue'
|
||||
import {
|
||||
UploadOutlined,
|
||||
@@ -154,6 +198,7 @@ import {
|
||||
FileOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { MaterialService } from '@/api/material'
|
||||
import { MixService } from '@/api/mix'
|
||||
import MaterialUploadModal from '@/components/material/MaterialUploadModal.vue'
|
||||
import { formatFileSize, formatDate } from '@/utils/file'
|
||||
|
||||
@@ -163,6 +208,8 @@ const fileList = ref([])
|
||||
const selectedFileIds = ref([])
|
||||
const uploadModalVisible = ref(false)
|
||||
const uploading = ref(false)
|
||||
const mixModalVisible = ref(false)
|
||||
const mixing = ref(false)
|
||||
|
||||
// 筛选条件
|
||||
const filters = reactive({
|
||||
@@ -370,6 +417,111 @@ const handleImageError = (e) => {
|
||||
img.style.display = 'none'
|
||||
}
|
||||
|
||||
const selectedFiles = computed(() =>
|
||||
fileList.value.filter((file) => selectedFileIds.value.includes(file.id))
|
||||
)
|
||||
|
||||
const isVideoFile = (file) => {
|
||||
if (!file) return false
|
||||
if (file.isVideo) return true
|
||||
if (file.fileCategory === 'video') return true
|
||||
if (typeof file.fileType === 'string' && file.fileType.startsWith('video')) return true
|
||||
return false
|
||||
}
|
||||
|
||||
const isAudioFile = (file) => {
|
||||
if (!file) return false
|
||||
if (file.fileCategory === 'audio') return true
|
||||
if (file.fileType === 'audio') return true
|
||||
if (typeof file.fileType === 'string' && file.fileType.startsWith('audio')) return true
|
||||
return false
|
||||
}
|
||||
|
||||
const selectedVideoUrls = computed(() =>
|
||||
selectedFiles.value.map((file) => (isVideoFile(file) ? file?.fileUrl || file?.previewUrl : null)).filter(Boolean)
|
||||
)
|
||||
|
||||
const selectedAudioUrls = computed(() =>
|
||||
selectedFiles.value.map((file) => (isAudioFile(file) ? file?.fileUrl || file?.previewUrl : null)).filter(Boolean)
|
||||
)
|
||||
|
||||
const mixForm = reactive({
|
||||
title: '',
|
||||
text: '',
|
||||
produceCount: 1
|
||||
})
|
||||
|
||||
const resetMixForm = () => {
|
||||
mixForm.title = ''
|
||||
mixForm.text = ''
|
||||
mixForm.produceCount = 1
|
||||
}
|
||||
|
||||
const handleOpenMixModal = () => {
|
||||
if (selectedFileIds.value.length === 0) {
|
||||
message.warning('请先选择至少一个素材')
|
||||
return
|
||||
}
|
||||
if (selectedVideoUrls.value.length === 0) {
|
||||
message.warning('请至少选择一个视频素材')
|
||||
return
|
||||
}
|
||||
if (selectedAudioUrls.value.length === 0) {
|
||||
message.warning('请至少选择一个背景音乐素材')
|
||||
return
|
||||
}
|
||||
mixModalVisible.value = true
|
||||
}
|
||||
|
||||
const handleMixCancel = () => {
|
||||
mixModalVisible.value = false
|
||||
}
|
||||
|
||||
const handleMixConfirm = async () => {
|
||||
const title = mixForm.title.trim()
|
||||
const text = mixForm.text.trim()
|
||||
if (!title) {
|
||||
message.warning('请输入视频标题')
|
||||
return
|
||||
}
|
||||
if (!text) {
|
||||
message.warning('请输入文案内容')
|
||||
return
|
||||
}
|
||||
const produceCount = Math.max(1, Math.min(10, Number(mixForm.produceCount) || 1))
|
||||
if (selectedVideoUrls.value.length === 0) {
|
||||
message.warning('请至少选择一个视频素材')
|
||||
return
|
||||
}
|
||||
if (selectedAudioUrls.value.length === 0) {
|
||||
message.warning('请至少选择一个背景音乐素材')
|
||||
return
|
||||
}
|
||||
mixing.value = true
|
||||
try {
|
||||
const { data } = await MixService.batchProduceAlignment({
|
||||
title,
|
||||
text,
|
||||
videoUrls: selectedVideoUrls.value,
|
||||
bgMusicUrls: selectedAudioUrls.value,
|
||||
produceCount
|
||||
})
|
||||
const jobIds = Array.isArray(data) ? data : []
|
||||
message.success(
|
||||
jobIds.length > 0
|
||||
? `混剪任务提交成功,JobId:${jobIds.join(', ')}`
|
||||
: '混剪任务提交成功'
|
||||
)
|
||||
mixModalVisible.value = false
|
||||
resetMixForm()
|
||||
} catch (error) {
|
||||
console.error('混剪失败:', error)
|
||||
message.error(error?.message || '混剪任务提交失败,请重试')
|
||||
} finally {
|
||||
mixing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
loadFileList()
|
||||
@@ -524,5 +676,20 @@ onMounted(() => {
|
||||
color: var(--color-text-3);
|
||||
}
|
||||
|
||||
.mix-modal__summary {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px;
|
||||
background: var(--color-bg-2);
|
||||
border: 1px dashed var(--color-border);
|
||||
border-radius: var(--radius-card);
|
||||
font-size: 13px;
|
||||
color: var(--color-text-2);
|
||||
}
|
||||
|
||||
.mix-modal__summary p {
|
||||
margin: 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<h2 class="text-xl font-bold">素材混剪</h2>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
||||
<section class="bg-white p-4 rounded shadow lg:col-span-1">
|
||||
<div class="text-gray-600 text-sm">文案拆解与镜头建议。</div>
|
||||
</section>
|
||||
<section class="bg-white p-4 rounded shadow lg:col-span-2">
|
||||
<div class="text-gray-500">素材匹配与时间线,导出到剪映。</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user