Files
fastmovieai/fastmovie-vue/src/components/xl-dialogue/xl-dialogue-create.vue
T
李建琦 eae151fd57 FastMovieAI AI短剧创作平台 二开基线
- 源码: xhadmincn/FastMovieAI (Apache 2.0)
- 二开: Docker部署, Swoole改Select, route.php修复, 补update/VERSION
- vendor/示例资源已gitignore
2026-08-26 18:37:36 +08:00

200 lines
8.4 KiB
Vue

<script setup lang="ts">
import { ResponseCode } from '@/common/const';
import { $http } from '@/common/http';
import { useRefs, useWebConfigStore } from '@/stores';
import { ElMessage } from 'element-plus';
const webConfigStore = useWebConfigStore();
const { WEBCONFIG } = useRefs(webConfigStore);
const dialogueCreateDialogVisible = ref(false);
const storyboard = ref<any>();
const dialogueForm = ref<any>({
id: '',
drama_id: '',
storyboard_id: '',
actor_id: '',
prosody_speed: 1,
prosody_volume: 50,
emotion: '',
start_time: 0,
end_time: 1000,
content: '',
actor: {
name: "选择演员",
headimg: "",
}
});
const dialogueFormRules = ref<any>({
actor_id: [{ required: true, message: '请选择演员', trigger: 'change' }],
content: [{ required: true, message: '请输入对话内容', trigger: 'blur' }],
prosody_speed: [{ required: true, message: '请选择语速', trigger: 'change' }],
prosody_volume: [{ required: true, message: '请选择音量', trigger: 'change' }],
emotion: [{ required: true, message: '请选择情感', trigger: 'change' }],
start_time: [{ required: true, message: '请选择字幕&语音开始时间', trigger: 'change' }],
end_time: [{ required: true, message: '请选择字幕结束时间', trigger: 'change' }],
});
const emit = defineEmits(['success']);
const dialogueFormRef = ref<any>();
const openDialogueCreateDialog = (item: any, form?: any) => {
if (form) {
dialogueForm.value = Object.assign(dialogueForm.value, form);
}
storyboard.value = item;
dialogueForm.value.storyboard_id = item.id;
dialogueForm.value.drama_id = item.drama_id;
nextTick(() => {
dialogueCreateDialogVisible.value = true;
});
}
const closeDialogueCreateDialog = () => {
dialogueFormRef.value?.resetFields();
dialogueForm.value = {
id: '',
drama_id: '',
storyboard_id: '',
actor_id: '',
prosody_speed: 1,
prosody_volume: 50,
emotion: '',
start_time: 0,
end_time: 1000,
content: '',
actor: {
name: "选择演员",
headimg: "",
}
};
dialogueCreateDialogVisible.value = false;
}
const submitDialogueCreateLoading = ref(false);
const submitDialogueCreateDialog = () => {
dialogueFormRef.value?.validate().then((valid: boolean) => {
if (valid) {
if (submitDialogueCreateLoading.value) return;
submitDialogueCreateLoading.value = true;
$http.post('/app/shortplay/api/StoryboardDialogue/save', dialogueForm.value).then((res: any) => {
if (res.code === ResponseCode.SUCCESS) {
ElMessage.success(res.msg);
closeDialogueCreateDialog();
emit('success', storyboard.value, res.data);
} else {
ElMessage.error(res.msg);
}
}).catch(() => {
ElMessage.error('创建失败');
}).finally(() => {
submitDialogueCreateLoading.value = false;
});
}
});
}
const actorPopoverRef = ref();
const actorButtonRef = ref();
const handleActorSelect = (actor: any) => {
dialogueForm.value.actor_id = actor.id;
dialogueForm.value.actor = actor;
actorPopoverRef.value?.hide();
}
const speedFormatTooltip = (value: number) => {
if (value === 1) {
return '正常';
}
return `${value}x倍速`;
}
const volumeFormatTooltip = (value: number) => {
if (value === 0) {
return '静音';
}
if (value === 100) {
return '最大';
}
if (value === 50) {
return '正常';
}
if (value < 50) {
return `-${value}%`;
}
return `+${value}%`;
}
const handleBeforeClose = () => {
if (submitDialogueCreateLoading.value) return;
closeDialogueCreateDialog();
}
defineExpose({
open: openDialogueCreateDialog,
close: closeDialogueCreateDialog
})
</script>
<template>
<div v-if="dialogueCreateDialogVisible">
<el-dialog v-model="dialogueCreateDialogVisible" class="generate-scene-dialog" draggable append-to-body
:close-on-press-escape="false" :close-on-click-modal="false" :before-close="handleBeforeClose"
width="min(100%,800px)">
<template #header>
<span class="font-weight-600" v-if="!dialogueForm.id">新增对话</span>
<span class="font-weight-600" v-else>编辑对话</span>
</template>
<el-form label-position="top" :model="dialogueForm" :rules="dialogueFormRules" ref="dialogueFormRef"
size="large">
<div class="flex grid-gap-4">
<el-form-item label="演员" prop="actor" class="w-30">
<div class="flex flex-y-center grid-gap-2 rounded-4 p-4 bg-overlay w-100 pointer bg-hover-bg"
ref="actorButtonRef">
<el-avatar :src="dialogueForm.actor.headimg" :alt="dialogueForm.actor.name" shape="square"
class="icon-model"></el-avatar>
<span class="h10">{{ dialogueForm.actor.name }}</span>
</div>
</el-form-item>
<el-form-item label="对话内容" prop="content" class="flex-1">
<el-input type="textarea" v-model="dialogueForm.content" placeholder="请输入对话内容"
:autosize="{ minRows: 3, maxRows: 10 }" />
</el-form-item>
</div>
<div class="flex grid-gap-4">
<el-form-item label="语速" prop="prosody_speed" class="flex-1">
<div class="w-100 px-6">
<el-slider v-model="dialogueForm.prosody_speed" :step="0.1" show-stops :min="0.5" :max="2"
:format-tooltip="speedFormatTooltip" />
</div>
</el-form-item>
<el-form-item label="音量" prop="prosody_volume" class="flex-1">
<div class="w-100 px-6 pb-7">
<el-slider v-model="dialogueForm.prosody_volume" :step="1" :min="0" :max="100"
:format-tooltip="volumeFormatTooltip" :marks="{ 0: '静音', 100: '最大', 50: '正常' }" />
</div>
</el-form-item>
</div>
<el-form-item label="情感" prop="emotion">
<el-radio-group v-model="dialogueForm.emotion">
<el-radio v-for="item in WEBCONFIG?.enum?.voice_emotion" :key="item.value" :value="item.value"
border>{{
item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<div class="flex grid-gap-4">
<el-form-item label="字幕&语音开始时间(毫秒)" prop="start_time">
<el-input-number v-model="dialogueForm.start_time" :min="0" :max="1000000" :step="100" />
</el-form-item>
<el-form-item label="字幕结束时间(毫秒)" prop="end_time">
<el-input-number v-model="dialogueForm.end_time" :min="0" :max="1000000" :step="100" />
</el-form-item>
</div>
</el-form>
<template #footer>
<div class="flex flex-center grid-gap-2 w-100">
<el-button type="info" @click="closeDialogueCreateDialog"
:disabled="submitDialogueCreateLoading">取消</el-button>
<div class="flex-1"></div>
<el-button type="success" @click="submitDialogueCreateDialog"
:loading="submitDialogueCreateLoading">提交</el-button>
</div>
</template>
</el-dialog>
<el-popover ref="actorPopoverRef" :virtual-ref="actorButtonRef" virtual-triggering placement="bottom-start"
width="min(100vw,880px)" trigger="click">
<xl-actor @select="handleActorSelect"
:types="[{ label: '本集', value: 'episode' }, { label: '本剧', value: 'drama' }]"
:query="{ drama_id: storyboard?.drama_id, episode_id: storyboard?.episode_id }" />
</el-popover>
</div>
</template>
<style lang="scss" scoped></style>