|
|
@@ -15,10 +15,15 @@ |
|
|
|
<el-icon class="icon-edit"><i-ep-edit /></el-icon> |
|
|
|
</div> |
|
|
|
<!-- 上传按钮 --> |
|
|
|
<button class="uploadBtn"> |
|
|
|
{{ $t("createVideo.reload") }} |
|
|
|
<input type="file" @change="changeFile" /> |
|
|
|
</button> |
|
|
|
<div class="btnBox"> |
|
|
|
<button class="uploadBtn"> |
|
|
|
{{ $t("createVideo.reselect") }} |
|
|
|
<input type="file" @change="lookImg" /> |
|
|
|
</button> |
|
|
|
<button class="uploadBtn" @click="changeFile"> |
|
|
|
{{ $t("createVideo.sureUpload") }} |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</el-row> |
|
|
|
<!-- 中间展示大图 --> |
|
|
|
<div class="bigImg inmiddle"> |
|
|
@@ -34,7 +39,17 @@ |
|
|
|
</div> |
|
|
|
<div class="upload inmiddle"> |
|
|
|
+ |
|
|
|
<input type="file" @change="changeFile" /> |
|
|
|
<input type="file" @change="lookImg" /> |
|
|
|
<!-- <el-upload |
|
|
|
class="el-upload" |
|
|
|
v-model="fileList" |
|
|
|
multiple |
|
|
|
:on-preview="handlePreview" |
|
|
|
:on-remove="handleRemove" |
|
|
|
:before-remove="beforeRemove" |
|
|
|
:limit="3" |
|
|
|
:on-exceed="handleExceed" |
|
|
|
/> --> |
|
|
|
</div> |
|
|
|
<div class="falseImg inmiddle"> |
|
|
|
<div><div class="icon">×</div></div> |
|
|
@@ -46,15 +61,41 @@ |
|
|
|
|
|
|
|
<!-- 底部 --> |
|
|
|
<div class="left-bottom"> |
|
|
|
<div class="ai"> |
|
|
|
<div class="aiAvatar"></div> |
|
|
|
<div style="margin-left: 20px">Generate By Ai</div> |
|
|
|
<div class="tab flex"> |
|
|
|
<div |
|
|
|
class="pointer" |
|
|
|
:class="showFace == 0 ? 'tabAvtive' : ''" |
|
|
|
@click="ChangeShowFaceList(0)" |
|
|
|
> |
|
|
|
{{ $t("createVideo.availableMould") }} |
|
|
|
</div> |
|
|
|
<div |
|
|
|
class="pointer" |
|
|
|
:class="showFace == 1 ? 'tabAvtive' : ''" |
|
|
|
@click="ChangeShowFaceList(1)" |
|
|
|
> |
|
|
|
{{ $t("createVideo.personalWarehouse") }} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="faceList scrolly"> |
|
|
|
<div class="faceItem" v-for="item in 7" :key="item"> |
|
|
|
<div class="img"></div> |
|
|
|
<!-- <img src="" alt="" /> --> |
|
|
|
<p>{{ item }}name</p> |
|
|
|
<div class="faceList"> |
|
|
|
<!-- 上传图片组件 --> |
|
|
|
<div v-if="showFace == 1" class="faceItem"> |
|
|
|
<span class="uploadImg" |
|
|
|
>+ |
|
|
|
<input class="pointer" type="file" @change="lookImg" /> |
|
|
|
</span> |
|
|
|
</div> |
|
|
|
<my-loading :isLoading="feceItemLoading"></my-loading> |
|
|
|
<!-- face列表 --> |
|
|
|
<div |
|
|
|
class="faceItem pointer" |
|
|
|
@click="setImgUrl(item.material)" |
|
|
|
v-for="item in AiFaceList" |
|
|
|
:key="item.id" |
|
|
|
> |
|
|
|
<!-- <div class="img"></div> --> |
|
|
|
<img :src="item.material" alt="" /> |
|
|
|
<p>{{ item.title }}</p> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
@@ -64,21 +105,105 @@ |
|
|
|
<el-tabs type="border-card" class="tabs"> |
|
|
|
<!-- ----------------------------- 1 --> |
|
|
|
<el-tab-pane :label="$t('createVideo.typeScript')"> |
|
|
|
<div class="card1">111111111111111111</div> |
|
|
|
<div class="card1"> |
|
|
|
<div class="text"> |
|
|
|
<el-input |
|
|
|
v-model="title" |
|
|
|
:placeholder="$t('createVideo.typeTitle')" |
|
|
|
/> |
|
|
|
<el-button type="primary"> |
|
|
|
{{ $t("createVideo.confirm") }} |
|
|
|
</el-button> |
|
|
|
<el-input |
|
|
|
v-model="title" |
|
|
|
:rows="15" |
|
|
|
type="textarea" |
|
|
|
:placeholder="$t('createVideo.aiText')" |
|
|
|
/> |
|
|
|
<el-icon :size="20"> |
|
|
|
<Edit /> |
|
|
|
</el-icon> |
|
|
|
</div> |
|
|
|
<p>{{ $t("createVideo.languages") }}</p> |
|
|
|
<el-select |
|
|
|
v-model="language" |
|
|
|
placeholder="Select" |
|
|
|
style="width: 100%" |
|
|
|
> |
|
|
|
<el-option |
|
|
|
v-for="item in languageOptions" |
|
|
|
:key="item.value" |
|
|
|
:label="item.label" |
|
|
|
:value="item.value" |
|
|
|
/> |
|
|
|
</el-select> |
|
|
|
<p>{{ $t("createVideo.voices") }}</p> |
|
|
|
<el-select |
|
|
|
v-model="language" |
|
|
|
placeholder="Select" |
|
|
|
style="width: 100%" |
|
|
|
> |
|
|
|
<el-option |
|
|
|
v-for="item in languageOptions" |
|
|
|
:key="item.value" |
|
|
|
:label="item.label" |
|
|
|
:value="item.value" |
|
|
|
/> |
|
|
|
</el-select> |
|
|
|
<p>{{ $t("createVideo.styles") }}</p> |
|
|
|
<el-select |
|
|
|
v-model="language" |
|
|
|
placeholder="Select" |
|
|
|
style="width: 100%" |
|
|
|
> |
|
|
|
<el-option |
|
|
|
v-for="item in languageOptions" |
|
|
|
:key="item.value" |
|
|
|
:label="item.label" |
|
|
|
:value="item.value" |
|
|
|
/> |
|
|
|
</el-select> |
|
|
|
<el-button type="primary">{{ |
|
|
|
$t("createVideo.generateVideo") |
|
|
|
}}</el-button> |
|
|
|
</div> |
|
|
|
</el-tab-pane> |
|
|
|
<!-- ------------------------ 2 --> |
|
|
|
<el-tab-pane :label="$t('createVideo.uploadAudio')"> |
|
|
|
<div class="card2"> |
|
|
|
<!-- 录制MP3上传语音 --> |
|
|
|
<div class="uploadAudio"> |
|
|
|
<button @click="startRecording">开始录音</button> |
|
|
|
<button @click="stopRecording">停止录音</button> |
|
|
|
<button @click="playRecording">播放录音</button> |
|
|
|
<div |
|
|
|
class="uploadAudio" |
|
|
|
:class="audioUrl ? 'uploadAudioAvtion' : ''" |
|
|
|
> |
|
|
|
<div class="btnBox"> |
|
|
|
<button |
|
|
|
class="pointer" |
|
|
|
:disabled="!isRecording" |
|
|
|
@click="startRecording" |
|
|
|
> |
|
|
|
开始录音 |
|
|
|
</button> |
|
|
|
<button |
|
|
|
class="pointer" |
|
|
|
@click="stopRecording" |
|
|
|
:disabled="isRecording" |
|
|
|
> |
|
|
|
停止录音 |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
<!-- <button @click="playRecording">播放录音</button> --> |
|
|
|
<audio v-if="audioUrl" controls :src="audioUrl"></audio> |
|
|
|
<button v-if="audioUrl" @click="uploadAudio">上传音频</button> |
|
|
|
<button v-if="audioUrl" @click="uploadAudio" class="pointer"> |
|
|
|
上传音频 |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
<!-- 上传视频 --> |
|
|
|
<div class="uploadVideo"> |
|
|
|
<div |
|
|
|
class="uploadVideo" |
|
|
|
style="text-align: center; line-height: 70px" |
|
|
|
> |
|
|
|
上传视频 |
|
|
|
<input type="file" ref="fileInput" @change="handleFileChange" /> |
|
|
|
<video |
|
|
|
v-if="videoSrc" |
|
|
@@ -90,7 +215,7 @@ |
|
|
|
</div> |
|
|
|
</el-tab-pane> |
|
|
|
<!-- ----------------------- 3 --> |
|
|
|
<el-tab-pane :label="$t('createVideo.aiScript')"> |
|
|
|
<el-tab-pane v-if="false" :label="$t('createVideo.aiScript')"> |
|
|
|
<div class="card3"> |
|
|
|
<div class="text"> |
|
|
|
<el-input |
|
|
@@ -158,60 +283,102 @@ |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<script setup> |
|
|
|
import { ref } from "vue"; |
|
|
|
import { getCurrentInstance } from "vue"; |
|
|
|
import Recorder from "js-audio-recorder"; |
|
|
|
import { |
|
|
|
personPhotoList, |
|
|
|
awsImgUpload, |
|
|
|
videoUpload, |
|
|
|
saveImgApi, |
|
|
|
} from "@/apis/createVideo"; |
|
|
|
|
|
|
|
// 获取AI头像 |
|
|
|
const AiFaceList = ref([]); |
|
|
|
const feceItemLoading = ref(false); |
|
|
|
function getAiFaceList(pageNum, pageSize) { |
|
|
|
feceItemLoading.value = true; |
|
|
|
personPhotoList(pageNum, pageSize) |
|
|
|
.then((res) => { |
|
|
|
AiFaceList.value = res.data.list; |
|
|
|
feceItemLoading.value = false; |
|
|
|
}) |
|
|
|
.catch((err) => { |
|
|
|
feceItemLoading.value = false; |
|
|
|
console.log(err, "err"); |
|
|
|
}); |
|
|
|
} |
|
|
|
// 选择显示的face列表 |
|
|
|
const showFace = ref("0"); |
|
|
|
function ChangeShowFaceList(index) { |
|
|
|
showFace.value = index; |
|
|
|
} |
|
|
|
// 标题 |
|
|
|
let title = ref(""); |
|
|
|
const title = ref(""); |
|
|
|
|
|
|
|
// 上传图片 |
|
|
|
let imgUrl = ref(""); |
|
|
|
function changeFile(e) { |
|
|
|
const imgUrl = ref(""); |
|
|
|
const imgData = ref(""); |
|
|
|
const showUploadBtn = ref(false); |
|
|
|
// 预览图片 |
|
|
|
function lookImg(e) { |
|
|
|
imgUrl.value = URL.createObjectURL(e.target.files[0]); |
|
|
|
imgData.value = e.target.files[0]; |
|
|
|
} |
|
|
|
// 上传图片 |
|
|
|
async function changeFile() { |
|
|
|
imgUrl.value = URL.createObjectURL(imgData.value); |
|
|
|
let formData = new FormData(); |
|
|
|
const timestamp = Date.now(); |
|
|
|
formData.append("file", imgData.value, timestamp + ".png"); |
|
|
|
try { |
|
|
|
const res = await awsImgUpload(formData); |
|
|
|
if (res.code == 200) { |
|
|
|
ElMessage.success("上传成功!"); |
|
|
|
} else { |
|
|
|
ElMessage.error("上传失败!"); |
|
|
|
} |
|
|
|
const res2 = await saveImgApi({ material: res.data.url }); |
|
|
|
} catch (error) { |
|
|
|
ElMessage.error(error); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// $t在script中的使用 |
|
|
|
|
|
|
|
const { |
|
|
|
appContext: { |
|
|
|
config: { globalProperties }, |
|
|
|
}, |
|
|
|
} = getCurrentInstance(); |
|
|
|
// 点击aifece |
|
|
|
function setImgUrl(url) { |
|
|
|
imgUrl.value = url; |
|
|
|
} |
|
|
|
|
|
|
|
const string = "createVideo.typeScript"; |
|
|
|
const t = globalProperties.$t(string); |
|
|
|
// 录制MP3上传语音 |
|
|
|
|
|
|
|
const parameter = { |
|
|
|
sampleBits: 16, // 采样位数,支持 8 或 16,默认是16 |
|
|
|
sampleRate: 8000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000 |
|
|
|
numChannels: 1, // 声道,支持 1 或 2, 默认是1 |
|
|
|
}; |
|
|
|
const recorder = new Recorder(parameter); |
|
|
|
const isRecording = ref(false); |
|
|
|
const isRecording = ref(true); |
|
|
|
const audioUrl = ref(null); |
|
|
|
const audioData = ref(""); |
|
|
|
|
|
|
|
const startRecording = async () => { |
|
|
|
if (!isRecording.value) { |
|
|
|
try { |
|
|
|
await recorder.start(); |
|
|
|
console.log("Recording started"); |
|
|
|
isRecording.value = true; |
|
|
|
} catch (error) { |
|
|
|
console.error(error); |
|
|
|
} |
|
|
|
try { |
|
|
|
await recorder.start(); |
|
|
|
console.log("Recording started"); |
|
|
|
isRecording.value = false; |
|
|
|
} catch (error) { |
|
|
|
console.error(error); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const stopRecording = async () => { |
|
|
|
if (isRecording.value) { |
|
|
|
try { |
|
|
|
const recording = await recorder.stop(); |
|
|
|
audioUrl.value = URL.createObjectURL(recorder.getWAVBlob()); |
|
|
|
isRecording.value = false; |
|
|
|
} catch (error) { |
|
|
|
console.error(error); |
|
|
|
} |
|
|
|
try { |
|
|
|
const recording = await recorder.stop(); |
|
|
|
audioUrl.value = URL.createObjectURL(recorder.getWAVBlob()); |
|
|
|
audioData.value = recorder.getWAVBlob(); |
|
|
|
isRecording.value = true; |
|
|
|
} catch (error) { |
|
|
|
console.error(error); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
@@ -221,7 +388,17 @@ const playRecording = () => { |
|
|
|
audio.play(); |
|
|
|
} |
|
|
|
}; |
|
|
|
const uploadAudio = () => {}; |
|
|
|
async function uploadAudio(params) { |
|
|
|
let formData = new FormData(); |
|
|
|
const timestamp = Date.now(); |
|
|
|
formData.append("file", audioData.value, timestamp + ".mp3"); |
|
|
|
try { |
|
|
|
const res = await videoUpload(formData); |
|
|
|
ElMessage.success("上传成功!"); |
|
|
|
} catch (error) { |
|
|
|
ElMessage.error(error); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 上传视频功能 |
|
|
|
const fileInput = ref(null); |
|
|
@@ -247,12 +424,26 @@ let languageOptions = reactive([ |
|
|
|
label: "Option2", |
|
|
|
}, |
|
|
|
]); |
|
|
|
|
|
|
|
// $t在script中的使用 |
|
|
|
const { |
|
|
|
appContext: { |
|
|
|
config: { globalProperties }, |
|
|
|
}, |
|
|
|
} = getCurrentInstance(); |
|
|
|
|
|
|
|
const string = "createVideo.typeScript"; |
|
|
|
const t = globalProperties.$t(string); |
|
|
|
|
|
|
|
onMounted(() => { |
|
|
|
getAiFaceList(1, 100); |
|
|
|
}); |
|
|
|
</script> |
|
|
|
|
|
|
|
<style lang="scss" scoped> |
|
|
|
.page { |
|
|
|
// height: calc(100vh - 84px); |
|
|
|
height: 887px; |
|
|
|
// height: 1200px; |
|
|
|
height: calc(100vh - 80px); |
|
|
|
padding: 20px 0 0 40px; |
|
|
|
background-color: #fff; |
|
|
|
border-radius: 25px 0 0; |
|
|
@@ -260,7 +451,7 @@ let languageOptions = reactive([ |
|
|
|
|
|
|
|
.left { |
|
|
|
width: 70%; |
|
|
|
height: 100%; |
|
|
|
height: 80%; |
|
|
|
|
|
|
|
:deep(.el-input__wrapper) { |
|
|
|
border: none; |
|
|
@@ -280,10 +471,19 @@ let languageOptions = reactive([ |
|
|
|
&-top { |
|
|
|
position: relative; |
|
|
|
width: 100%; |
|
|
|
height: 65%; |
|
|
|
// height: 65%; |
|
|
|
height: 600px; |
|
|
|
|
|
|
|
.uploadBtn { |
|
|
|
position: relative; |
|
|
|
padding: 5px 10px; |
|
|
|
margin-left: 10px; |
|
|
|
border-radius: 5px; |
|
|
|
transition: all 0.3s; |
|
|
|
&:hover { |
|
|
|
background-color: #000; |
|
|
|
color: #fff; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.bigImg { |
|
|
@@ -313,7 +513,14 @@ let languageOptions = reactive([ |
|
|
|
background-color: green; |
|
|
|
border-radius: 50%; |
|
|
|
} |
|
|
|
|
|
|
|
.el-upload { |
|
|
|
position: absolute; |
|
|
|
top: 0; |
|
|
|
left: 0; |
|
|
|
width: 100%; |
|
|
|
height: 100%; |
|
|
|
opacity: 0; |
|
|
|
} |
|
|
|
.upload { |
|
|
|
top: 200px; |
|
|
|
width: 100px; |
|
|
@@ -371,17 +578,25 @@ let languageOptions = reactive([ |
|
|
|
width: 100%; |
|
|
|
height: 32%; |
|
|
|
margin-top: 10px; |
|
|
|
|
|
|
|
.tab { |
|
|
|
padding: 10px; |
|
|
|
> div { |
|
|
|
margin-right: 20px; |
|
|
|
padding: 5px 10px; |
|
|
|
transition: all 0.3s; |
|
|
|
border-radius: 5px; |
|
|
|
&:hover { |
|
|
|
background-color: #000; |
|
|
|
color: #fff; |
|
|
|
} |
|
|
|
} |
|
|
|
.tabAvtive { |
|
|
|
background-color: #000; |
|
|
|
color: #fff; |
|
|
|
} |
|
|
|
} |
|
|
|
.ai { |
|
|
|
width: 100%; |
|
|
|
|
|
|
|
.aiAvatar { |
|
|
|
width: 100px; |
|
|
|
height: 100px; |
|
|
|
margin-left: 25px; |
|
|
|
background-color: pink; |
|
|
|
border-radius: 50%; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.faceList { |
|
|
@@ -389,23 +604,43 @@ let languageOptions = reactive([ |
|
|
|
flex-wrap: wrap; |
|
|
|
justify-content: flex-start; |
|
|
|
width: 100%; |
|
|
|
height: 130px; |
|
|
|
|
|
|
|
height: 160px; |
|
|
|
overflow-y: scroll; |
|
|
|
.faceItem { |
|
|
|
position: relative; |
|
|
|
margin-right: 10px; |
|
|
|
margin-bottom: 10px; |
|
|
|
width: 150px; |
|
|
|
height: 150px; |
|
|
|
padding: 0 25px; |
|
|
|
line-height: 30px; |
|
|
|
line-height: 150px; |
|
|
|
text-align: center; |
|
|
|
|
|
|
|
border-radius: 50%; |
|
|
|
border: 2px solid #000; |
|
|
|
overflow: hidden; |
|
|
|
input { |
|
|
|
position: absolute; |
|
|
|
top: 0; |
|
|
|
left: 0; |
|
|
|
width: 100%; |
|
|
|
height: 100%; |
|
|
|
opacity: 0; |
|
|
|
} |
|
|
|
.uploadImg { |
|
|
|
line-height: 145px; |
|
|
|
font-size: 50px; |
|
|
|
font-weight: 700; |
|
|
|
text-align: center; |
|
|
|
} |
|
|
|
p { |
|
|
|
height: 30px; |
|
|
|
} |
|
|
|
|
|
|
|
img { |
|
|
|
width: 100%; |
|
|
|
} |
|
|
|
.img { |
|
|
|
width: 100px; |
|
|
|
height: 100px; |
|
|
|
background-color: green; |
|
|
|
border-radius: 50%; |
|
|
|
} |
|
|
|
} |
|
|
@@ -425,26 +660,80 @@ let languageOptions = reactive([ |
|
|
|
|
|
|
|
.tabs { |
|
|
|
height: 100%; |
|
|
|
.card1 { |
|
|
|
.text { |
|
|
|
padding: 10px; |
|
|
|
border: 1px solid #000; |
|
|
|
|
|
|
|
.el-button { |
|
|
|
float: right; |
|
|
|
margin-top: 10px; |
|
|
|
} |
|
|
|
|
|
|
|
:deep(.el-textarea__inner) { |
|
|
|
resize: none; |
|
|
|
box-shadow: none; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
p { |
|
|
|
margin: 10px 0; |
|
|
|
} |
|
|
|
|
|
|
|
> .el-button { |
|
|
|
width: 100%; |
|
|
|
height: 50px; |
|
|
|
margin-top: 10px; |
|
|
|
} |
|
|
|
} |
|
|
|
.card2 { |
|
|
|
padding: 10px; |
|
|
|
> div { |
|
|
|
position: relative; |
|
|
|
margin-top: 10px; |
|
|
|
width: 100%; |
|
|
|
height: 100px; |
|
|
|
border: #000 1px solid; |
|
|
|
border-radius: 10px; |
|
|
|
background-color: red; |
|
|
|
|
|
|
|
overflow: hidden; |
|
|
|
transition: all 0.3s; |
|
|
|
box-shadow: #00000026 2px 2px 2px 2px; |
|
|
|
&:hover { |
|
|
|
transform: translateY(-10px); |
|
|
|
box-shadow: #00000026 2px 2px 2px 2px; |
|
|
|
} |
|
|
|
} |
|
|
|
.uploadAudio { |
|
|
|
padding: 20px; |
|
|
|
margin-bottom: 20px; |
|
|
|
height: 70px; |
|
|
|
.btnBox { |
|
|
|
display: flex; |
|
|
|
justify-content: space-between; |
|
|
|
margin-bottom: 10px; |
|
|
|
} |
|
|
|
audio { |
|
|
|
height: 50px; |
|
|
|
width: 100%; |
|
|
|
} |
|
|
|
button { |
|
|
|
border-radius: 10px; |
|
|
|
height: 30px; |
|
|
|
line-height: 22px; |
|
|
|
padding: 5px 10px; |
|
|
|
background-color: #fff; |
|
|
|
transition: all 0.3s; |
|
|
|
&:hover { |
|
|
|
background-color: #000; |
|
|
|
|
|
|
|
color: #fff; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
.uploadAudioAvtion { |
|
|
|
height: 170px; |
|
|
|
} |
|
|
|
.uploadVideo { |
|
|
|
height: 100px; |
|
|
|
input { |
|
|
|
position: absolute; |
|
|
|
width: 100%; |
|
|
|