FastMovieAI AI短剧创作平台 二开基线
- 源码: xhadmincn/FastMovieAI (Apache 2.0) - 二开: Docker部署, Swoole改Select, route.php修复, 补update/VERSION - vendor/示例资源已gitignore
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user