处理修复BUG
This commit is contained in:
@@ -23,7 +23,10 @@
|
||||
:file-name="currentWorkspace.primaryAsset.fileName"
|
||||
:preview-url="currentWorkspace.primaryAsset.previewUrl"
|
||||
:preview-kind="currentWorkspace.primaryAsset.previewKind"
|
||||
:source-url="currentWorkspace.primaryAsset.sourceUrl"
|
||||
allow-url
|
||||
@select="(file) => setAsset(activeTab, 'primaryAsset', file)"
|
||||
@url-change="(url) => setAssetUrl(activeTab, 'primaryAsset', url, currentTabCopy.primaryAssetAccept)"
|
||||
@clear="clearAsset(activeTab, 'primaryAsset')"
|
||||
/>
|
||||
|
||||
@@ -36,7 +39,10 @@
|
||||
:file-name="currentWorkspace.secondaryAsset.fileName"
|
||||
:preview-url="currentWorkspace.secondaryAsset.previewUrl"
|
||||
:preview-kind="currentWorkspace.secondaryAsset.previewKind"
|
||||
:source-url="currentWorkspace.secondaryAsset.sourceUrl"
|
||||
allow-url
|
||||
@select="(file) => setAsset(activeTab, 'secondaryAsset', file)"
|
||||
@url-change="(url) => setAssetUrl(activeTab, 'secondaryAsset', url, currentTabCopy.secondaryAssetAccept)"
|
||||
@clear="clearAsset(activeTab, 'secondaryAsset')"
|
||||
/>
|
||||
|
||||
@@ -51,9 +57,36 @@
|
||||
|
||||
<AiSectionCard
|
||||
step="02"
|
||||
title="标题标签关键词"
|
||||
description="类目和卖点一起配置,方便后续拆投放版本。"
|
||||
class="delivery-card delivery-card--step-02"
|
||||
>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">细分类目</span>
|
||||
<AiChoicePills
|
||||
v-model="currentWorkspace.categories"
|
||||
:options="categoryOptions"
|
||||
multiple
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">核心卖点</span>
|
||||
<el-input
|
||||
v-model="currentWorkspace.productFocus"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
resize="none"
|
||||
placeholder="例如:上脚显瘦、脚感轻、百搭、直播间主推款。"
|
||||
/>
|
||||
</div>
|
||||
</AiSectionCard>
|
||||
|
||||
<AiSectionCard
|
||||
step="03"
|
||||
title="音视频生成"
|
||||
description="先锁定投放规格,再决定整体风格、时长和画面清晰度。"
|
||||
class="delivery-card delivery-card--step-02"
|
||||
class="delivery-card delivery-card--step-03"
|
||||
>
|
||||
<div class="delivery-form-grid delivery-form-grid--two">
|
||||
<div class="delivery-field">
|
||||
@@ -88,10 +121,174 @@
|
||||
</AiSectionCard>
|
||||
|
||||
<AiSectionCard
|
||||
step="03"
|
||||
step="04"
|
||||
title="人脸和口播"
|
||||
description="控制性别、人脸图、语气和语音来源。"
|
||||
class="delivery-card delivery-card--step-04"
|
||||
>
|
||||
<div class="delivery-form-grid delivery-form-grid--two">
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">性别</span>
|
||||
<AiChoicePills v-model="currentWorkspace.gender" :options="genderOptions" />
|
||||
</div>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">语气</span>
|
||||
<AiChoicePills v-model="currentWorkspace.voiceTone" :options="voiceToneOptions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">语音来源</span>
|
||||
<AiChoicePills
|
||||
v-model="currentWorkspace.speechSource"
|
||||
:options="speechSourceOptions"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<AiAssetDropzone
|
||||
title="模特图 / 人脸图"
|
||||
description="上传清晰正脸图,适合用于数字人口播。"
|
||||
placeholder="上传模特图"
|
||||
helper="建议 1080px 以上,单人正面"
|
||||
accept="image/*"
|
||||
:file-name="currentWorkspace.avatarAsset.fileName"
|
||||
:preview-url="currentWorkspace.avatarAsset.previewUrl"
|
||||
:preview-kind="currentWorkspace.avatarAsset.previewKind"
|
||||
:source-url="currentWorkspace.avatarAsset.sourceUrl"
|
||||
allow-url
|
||||
@select="(file) => setAsset(activeTab, 'avatarAsset', file)"
|
||||
@url-change="(url) => setAssetUrl(activeTab, 'avatarAsset', url, 'image/*')"
|
||||
@clear="clearAsset(activeTab, 'avatarAsset')"
|
||||
/>
|
||||
</AiSectionCard>
|
||||
|
||||
<AiSectionCard
|
||||
step="05"
|
||||
title="话术和音乐"
|
||||
description="支持直接口播、原话术微调,或者转成纯音乐版本。"
|
||||
class="delivery-card delivery-card--step-05"
|
||||
>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">话术模式</span>
|
||||
<AiChoicePills v-model="currentWorkspace.scriptMode" :options="scriptModeOptions" />
|
||||
</div>
|
||||
|
||||
<div class="delivery-form-grid delivery-form-grid--two">
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">话术内容</span>
|
||||
<el-input
|
||||
v-model="currentWorkspace.scriptText"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
resize="none"
|
||||
placeholder="输入要口播的话术,或粘贴需要整体微调的原始话术。"
|
||||
/>
|
||||
</div>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">微调约束</span>
|
||||
<el-input
|
||||
v-model="currentWorkspace.scriptRules"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
resize="none"
|
||||
placeholder="例如:保留原来的成交语气,不改卖点顺序,强调防滑、透气、现货。"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AiAssetDropzone
|
||||
title="纯音乐素材"
|
||||
description="需要做纯音乐版本时可上传,也可以后续从原视频中抽取。"
|
||||
placeholder="上传背景音乐"
|
||||
helper="支持 MP3 / WAV"
|
||||
accept="audio/*"
|
||||
:file-name="currentWorkspace.musicAsset.fileName"
|
||||
:preview-url="currentWorkspace.musicAsset.previewUrl"
|
||||
:preview-kind="currentWorkspace.musicAsset.previewKind"
|
||||
:source-url="currentWorkspace.musicAsset.sourceUrl"
|
||||
allow-url
|
||||
@select="(file) => setAsset(activeTab, 'musicAsset', file)"
|
||||
@url-change="(url) => setAssetUrl(activeTab, 'musicAsset', url, 'audio/*')"
|
||||
@clear="clearAsset(activeTab, 'musicAsset')"
|
||||
/>
|
||||
</AiSectionCard>
|
||||
|
||||
<AiSectionCard
|
||||
step="06"
|
||||
title="场景和镜头"
|
||||
description="支持用提示词微调,也支持上传场景图直接替换原场景。"
|
||||
class="delivery-card delivery-card--step-06"
|
||||
>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">场景处理方式</span>
|
||||
<AiChoicePills v-model="currentWorkspace.sceneMode" :options="sceneModeOptions" />
|
||||
</div>
|
||||
|
||||
<div class="delivery-form-grid delivery-form-grid--two">
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">场景提示词</span>
|
||||
<el-input
|
||||
v-model="currentWorkspace.scenePrompt"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
resize="none"
|
||||
placeholder="例如:保留产品特写,换成室内直播间,镜头推进更快,背景偏暖光。"
|
||||
/>
|
||||
</div>
|
||||
<AiAssetDropzone
|
||||
title="场景图替换"
|
||||
description="有明确参考图时直接上传,优先保证场景结构和光线。"
|
||||
placeholder="上传场景参考图"
|
||||
helper="支持 JPG / PNG"
|
||||
accept="image/*"
|
||||
:file-name="currentWorkspace.sceneAsset.fileName"
|
||||
:preview-url="currentWorkspace.sceneAsset.previewUrl"
|
||||
:preview-kind="currentWorkspace.sceneAsset.previewKind"
|
||||
:source-url="currentWorkspace.sceneAsset.sourceUrl"
|
||||
allow-url
|
||||
@select="(file) => setAsset(activeTab, 'sceneAsset', file)"
|
||||
@url-change="(url) => setAssetUrl(activeTab, 'sceneAsset', url, 'image/*')"
|
||||
@clear="clearAsset(activeTab, 'sceneAsset')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">镜头节奏</span>
|
||||
<el-slider v-model="currentWorkspace.cameraRhythm" :min="1" :max="10" />
|
||||
</div>
|
||||
</AiSectionCard>
|
||||
|
||||
<AiSectionCard
|
||||
step="07"
|
||||
title="执行说明"
|
||||
description="补充收尾逻辑,例如保留人物动作,只替换场景和话术重点。"
|
||||
class="delivery-card delivery-card--step-07"
|
||||
>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">收口动作</span>
|
||||
<el-input
|
||||
v-model="currentWorkspace.cta"
|
||||
placeholder="例如:结尾引导领券下单,强调今晚库存。"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">执行说明</span>
|
||||
<el-input
|
||||
v-model="currentWorkspace.notes"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
resize="none"
|
||||
placeholder="补充执行说明,例如:第二版转成纯音乐短视频。"
|
||||
/>
|
||||
</div>
|
||||
</AiSectionCard>
|
||||
|
||||
<AiSectionCard
|
||||
step="08"
|
||||
title="任务和预览"
|
||||
description="先保存草稿,再开始组装带货视频。"
|
||||
class="delivery-card delivery-card--preview delivery-card--step-03"
|
||||
description="所有配置完成后,开始组装带货视频。"
|
||||
class="delivery-card delivery-card--preview delivery-card--step-08"
|
||||
>
|
||||
<div class="task-actions">
|
||||
<el-button class="delivery-primary-btn" type="primary" @click="startAssembly">
|
||||
@@ -149,197 +346,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</AiSectionCard>
|
||||
|
||||
<AiSectionCard
|
||||
step="04"
|
||||
title="场景和镜头"
|
||||
description="支持用提示词微调,也支持上传场景图直接替换原场景。"
|
||||
class="delivery-card delivery-card--step-04"
|
||||
>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">场景处理方式</span>
|
||||
<AiChoicePills v-model="currentWorkspace.sceneMode" :options="sceneModeOptions" />
|
||||
</div>
|
||||
|
||||
<div class="delivery-form-grid delivery-form-grid--two">
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">场景提示词</span>
|
||||
<el-input
|
||||
v-model="currentWorkspace.scenePrompt"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
resize="none"
|
||||
placeholder="例如:保留产品特写,换成室内直播间,镜头推进更快,背景偏暖光。"
|
||||
/>
|
||||
</div>
|
||||
<AiAssetDropzone
|
||||
title="场景图替换"
|
||||
description="有明确参考图时直接上传,优先保证场景结构和光线。"
|
||||
placeholder="上传场景参考图"
|
||||
helper="支持 JPG / PNG"
|
||||
accept="image/*"
|
||||
:file-name="currentWorkspace.sceneAsset.fileName"
|
||||
:preview-url="currentWorkspace.sceneAsset.previewUrl"
|
||||
:preview-kind="currentWorkspace.sceneAsset.previewKind"
|
||||
@select="(file) => setAsset(activeTab, 'sceneAsset', file)"
|
||||
@clear="clearAsset(activeTab, 'sceneAsset')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">镜头节奏</span>
|
||||
<el-slider v-model="currentWorkspace.cameraRhythm" :min="1" :max="10" />
|
||||
</div>
|
||||
</AiSectionCard>
|
||||
|
||||
<AiSectionCard
|
||||
step="05"
|
||||
title="人脸和口播"
|
||||
description="控制性别、人脸图、语气和语音来源。"
|
||||
class="delivery-card delivery-card--step-05"
|
||||
>
|
||||
<div class="delivery-form-grid delivery-form-grid--two">
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">性别</span>
|
||||
<AiChoicePills v-model="currentWorkspace.gender" :options="genderOptions" />
|
||||
</div>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">语气</span>
|
||||
<AiChoicePills v-model="currentWorkspace.voiceTone" :options="voiceToneOptions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">语音来源</span>
|
||||
<AiChoicePills
|
||||
v-model="currentWorkspace.speechSource"
|
||||
:options="speechSourceOptions"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<AiAssetDropzone
|
||||
title="模特图 / 人脸图"
|
||||
description="上传清晰正脸图,适合用于数字人口播。"
|
||||
placeholder="上传模特图"
|
||||
helper="建议 1080px 以上,单人正面"
|
||||
accept="image/*"
|
||||
:file-name="currentWorkspace.avatarAsset.fileName"
|
||||
:preview-url="currentWorkspace.avatarAsset.previewUrl"
|
||||
:preview-kind="currentWorkspace.avatarAsset.previewKind"
|
||||
@select="(file) => setAsset(activeTab, 'avatarAsset', file)"
|
||||
@clear="clearAsset(activeTab, 'avatarAsset')"
|
||||
/>
|
||||
</AiSectionCard>
|
||||
|
||||
<AiSectionCard
|
||||
step="06"
|
||||
title="话术和音乐"
|
||||
description="支持直接口播、原话术微调,或者转成纯音乐版本。"
|
||||
class="delivery-card delivery-card--step-06"
|
||||
>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">话术模式</span>
|
||||
<AiChoicePills v-model="currentWorkspace.scriptMode" :options="scriptModeOptions" />
|
||||
</div>
|
||||
|
||||
<div class="delivery-form-grid delivery-form-grid--two">
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">话术内容</span>
|
||||
<el-input
|
||||
v-model="currentWorkspace.scriptText"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
resize="none"
|
||||
placeholder="输入要口播的话术,或粘贴需要整体微调的原始话术。"
|
||||
/>
|
||||
</div>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">微调约束</span>
|
||||
<el-input
|
||||
v-model="currentWorkspace.scriptRules"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
resize="none"
|
||||
placeholder="例如:保留原来的成交语气,不改卖点顺序,强调防滑、透气、现货。"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AiAssetDropzone
|
||||
title="纯音乐素材"
|
||||
description="需要做纯音乐版本时可上传,也可以后续从原视频中抽取。"
|
||||
placeholder="上传背景音乐"
|
||||
helper="支持 MP3 / WAV"
|
||||
accept="audio/*"
|
||||
:file-name="currentWorkspace.musicAsset.fileName"
|
||||
:preview-url="currentWorkspace.musicAsset.previewUrl"
|
||||
:preview-kind="currentWorkspace.musicAsset.previewKind"
|
||||
@select="(file) => setAsset(activeTab, 'musicAsset', file)"
|
||||
@clear="clearAsset(activeTab, 'musicAsset')"
|
||||
/>
|
||||
</AiSectionCard>
|
||||
|
||||
<AiSectionCard
|
||||
step="07"
|
||||
title="标题标签关键词"
|
||||
description="类目、平台和卖点一起配置,方便后续拆投放版本。"
|
||||
class="delivery-card delivery-card--step-07"
|
||||
>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">细分类目</span>
|
||||
<AiChoicePills
|
||||
v-model="currentWorkspace.categories"
|
||||
:options="categoryOptions"
|
||||
multiple
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">投放平台</span>
|
||||
<AiChoicePills
|
||||
v-model="currentWorkspace.platforms"
|
||||
:options="platformOptions"
|
||||
multiple
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">核心卖点</span>
|
||||
<el-input
|
||||
v-model="currentWorkspace.productFocus"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
resize="none"
|
||||
placeholder="例如:上脚显瘦、脚感轻、百搭、直播间主推款。"
|
||||
/>
|
||||
</div>
|
||||
</AiSectionCard>
|
||||
|
||||
<AiSectionCard
|
||||
step="08"
|
||||
title="执行说明"
|
||||
description="补充收尾逻辑,例如保留人物动作,只替换场景和话术重点。"
|
||||
class="delivery-card delivery-card--step-08"
|
||||
>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">收口动作</span>
|
||||
<el-input
|
||||
v-model="currentWorkspace.cta"
|
||||
placeholder="例如:结尾引导领券下单,强调今晚库存。"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">执行说明</span>
|
||||
<el-input
|
||||
v-model="currentWorkspace.notes"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
resize="none"
|
||||
placeholder="补充执行说明,例如:第二版转成纯音乐短视频。"
|
||||
/>
|
||||
</div>
|
||||
</AiSectionCard>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
@@ -359,6 +365,7 @@ type AssetState = {
|
||||
fileName: string
|
||||
previewUrl: string
|
||||
previewKind: PreviewKind
|
||||
sourceUrl: string
|
||||
}
|
||||
|
||||
type AssetKey =
|
||||
@@ -390,7 +397,6 @@ type WorkspaceState = {
|
||||
scriptText: string
|
||||
scriptRules: string
|
||||
categories: string[]
|
||||
platforms: string[]
|
||||
productFocus: string
|
||||
cta: string
|
||||
notes: string
|
||||
@@ -425,7 +431,6 @@ const languageOptions = ['国语', '英语', '粤语', '西班牙语']
|
||||
const speechSourceOptions = ['文本直出', '从原视频提取语气', '从音频样本提取']
|
||||
const scriptModeOptions = ['直接口播', '原话术微调', '纯音乐版本']
|
||||
const categoryOptions = ['鞋子', '男装', '女装', '美妆', '家居', '零食', '数码']
|
||||
const platformOptions = ['抖音', '快手', '视频号', '小红书']
|
||||
|
||||
const tabCopies: Record<WorkspaceTab, TabCopy> = {
|
||||
remake: {
|
||||
@@ -465,6 +470,7 @@ function createAssetState(): AssetState {
|
||||
fileName: '',
|
||||
previewUrl: '',
|
||||
previewKind: 'file',
|
||||
sourceUrl: '',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,7 +497,6 @@ function createWorkspaceState(): WorkspaceState {
|
||||
scriptText: '',
|
||||
scriptRules: '',
|
||||
categories: ['鞋子'],
|
||||
platforms: ['抖音'],
|
||||
productFocus: '',
|
||||
cta: '',
|
||||
notes: '',
|
||||
@@ -521,6 +526,23 @@ function resolvePreviewKind(file: File): PreviewKind {
|
||||
return 'file'
|
||||
}
|
||||
|
||||
function resolvePreviewKindFromUrl(url: string, accept = ''): PreviewKind {
|
||||
const cleanUrl = url.split('?')[0]?.toLowerCase() || ''
|
||||
if (/\.(png|jpe?g|webp|gif|bmp|svg)$/.test(cleanUrl)) {
|
||||
return 'image'
|
||||
}
|
||||
if (/\.(mp4|mov|webm|m4v|ogg)$/.test(cleanUrl)) {
|
||||
return 'video'
|
||||
}
|
||||
if (accept.includes('image/*')) {
|
||||
return 'image'
|
||||
}
|
||||
if (accept.includes('video/*')) {
|
||||
return 'video'
|
||||
}
|
||||
return 'file'
|
||||
}
|
||||
|
||||
function releasePreviewUrl(url: string) {
|
||||
if (!url.startsWith('blob:')) {
|
||||
return
|
||||
@@ -536,6 +558,7 @@ function setAsset(tab: WorkspaceTab, assetKey: AssetKey, file: File) {
|
||||
}
|
||||
|
||||
target.fileName = file.name
|
||||
target.sourceUrl = ''
|
||||
target.previewKind = resolvePreviewKind(file)
|
||||
target.previewUrl = target.previewKind === 'file' ? '' : URL.createObjectURL(file)
|
||||
if (target.previewUrl) {
|
||||
@@ -543,6 +566,19 @@ function setAsset(tab: WorkspaceTab, assetKey: AssetKey, file: File) {
|
||||
}
|
||||
}
|
||||
|
||||
function setAssetUrl(tab: WorkspaceTab, assetKey: AssetKey, url: string, accept = '') {
|
||||
const target = workspaces[tab][assetKey]
|
||||
if (target.previewUrl) {
|
||||
releasePreviewUrl(target.previewUrl)
|
||||
}
|
||||
|
||||
const normalizedUrl = url.trim()
|
||||
target.sourceUrl = normalizedUrl
|
||||
target.fileName = normalizedUrl ? normalizedUrl : ''
|
||||
target.previewKind = normalizedUrl ? resolvePreviewKindFromUrl(normalizedUrl, accept) : 'file'
|
||||
target.previewUrl = target.previewKind === 'file' ? '' : normalizedUrl
|
||||
}
|
||||
|
||||
function clearAsset(tab: WorkspaceTab, assetKey: AssetKey) {
|
||||
const target = workspaces[tab][assetKey]
|
||||
if (target.previewUrl) {
|
||||
@@ -551,6 +587,7 @@ function clearAsset(tab: WorkspaceTab, assetKey: AssetKey) {
|
||||
target.fileName = ''
|
||||
target.previewUrl = ''
|
||||
target.previewKind = 'file'
|
||||
target.sourceUrl = ''
|
||||
}
|
||||
|
||||
function saveDraft() {
|
||||
|
||||
Reference in New Issue
Block a user