Переглянути джерело

解决路由跳转黑屏

dev
congzc 2 роки тому
джерело
коміт
d4f01cb0c6
2 змінених файлів з 443 додано та 131 видалено
  1. +439
    -121
      src/views/createVideo/index copy.vue
  2. +4
    -10
      src/views/createVideo/index.vue

+ 439
- 121
src/views/createVideo/index copy.vue Переглянути файл

@@ -4,7 +4,7 @@
<div class="left">
<!-- 顶部 -->
<!-- 有图片时 -->
<div class="left-top" v-show="imgUrl">
<div class="left-top" v-show="!showUploadBtn">
<el-row class="justify-between">
<!-- title框 -->
<div class="flex justify-between" style="width: 300px">
@@ -15,10 +15,15 @@
<el-icon class="icon-edit"><i-ep-edit /></el-icon>
</div>
<!-- 上传按钮 -->
<div class="btnBox">
<div class="btnBox" v-show="submitImgBtn">
<button class="uploadBtn">
{{ $t("createVideo.reselect") }}
<input type="file" @change="lookImg" />
<input
accept="image/*"
type="file"
@click="e.target.value = null"
@change="lookImg"
/>
</button>
<button class="uploadBtn" @click="changeFile">
{{ $t("createVideo.sureUpload") }}
@@ -31,27 +36,26 @@
</div>
</div>
<!-- 没有图片时 -->
<div class="left-top" v-show="!imgUrl">
<div class="left-top" v-show="showUploadBtn">
<!-- 中间展示大图 -->
<div class="noImg">
<div class="trueImg inmiddle">
<my-loading
style="margin-top: 150px"
:isLoading="compressionImgLoad"
></my-loading>
<div v-if="!compressionImgLoad" class="trueImg inmiddle">
<div class="icon">✔</div>
</div>
<div class="upload inmiddle">
<div v-if="!compressionImgLoad" class="upload inmiddle">
+
<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"
/> -->
<input
accept="image/*"
type="file"
@click="e.target.value = null"
@change="lookImg"
/>
</div>
<div class="falseImg inmiddle">
<div v-if="!compressionImgLoad" class="falseImg inmiddle">
<div><div class="icon">×</div></div>
<div><div class="icon">×</div></div>
<div><div class="icon">×</div></div>
@@ -79,23 +83,26 @@
</div>
<div class="faceList">
<!-- 上传图片组件 -->
<div v-if="showFace == 1" class="faceItem">
<span class="uploadImg"
>+
<input class="pointer" type="file" @change="lookImg" />
</span>
<div
v-if="showFace == 1"
class="faceItem"
@click="showUploadBtn = true"
>
<span class="uploadImg">+ </span>
</div>
<my-loading :isLoading="feceItemLoading"></my-loading>
<!-- face列表 -->
<div
:class="faceItemActive == item.id ? 'faceItemActive' : ''"
class="faceItem pointer"
@click="setImgUrl(item.material)"
@click="setImgUrl(item.material, item.id)"
v-for="item in AiFaceList"
:key="item.id"
>
<!-- <div class="img"></div> -->
<img :src="item.material" alt="" />
<p>{{ item.title }}</p>
<div class="imgBox">
<img :src="item.material" alt="" />
</div>
s
</div>
</div>
</div>
@@ -114,18 +121,26 @@
<el-button type="primary">
{{ $t("createVideo.confirm") }}
</el-button>
<!-- 文本域 -->
<el-input
v-model="title"
id="textInputId"
v-model="textareaContent"
:rows="15"
type="textarea"
:placeholder="$t('createVideo.aiText')"
/>
<el-icon :size="20">
<Edit />
</el-icon>
<span
class="pointer"
@click="getPreviewAudio"
style="margin-right: 10px; font-size: 30px"
>🕬</span
>
<span class="pointer" @click="insertIcon">🕗</span>
</div>
<p>{{ $t("createVideo.languages") }}</p>
<!-- 语言 -->
<el-select
@change="chooseType"
v-model="language"
placeholder="Select"
style="width: 100%"
@@ -133,45 +148,90 @@
<el-option
v-for="item in languageOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
:label="item.name"
:value="item.id"
>
<img
:src="item.img"
alt=""
style="
width: 20px;
margin-right: 10px;
vertical-align: text-top;
"
/>
<span style="line-height: 20px">{{ item.name }}</span>
</el-option>
</el-select>
<p>{{ $t("createVideo.voices") }}</p>
<!-- 声音 -->
<el-select
v-model="language"
placeholder="Select"
@change="chooseStyle"
:disabled="language ? false : true"
v-model="sound"
:placeholder="language ? 'Select' : 'Please select language'"
style="width: 100%"
>
<el-option
v-for="item in languageOptions"
v-for="item in soundOptions"
:key="item.value"
:label="item.label"
:value="item.value"
:label="item.displayName"
:value="item.id"
/>
</el-select>
<p>{{ $t("createVideo.styles") }}</p>
<!-- 风格 -->
<el-select
v-model="language"
placeholder="Select"
:disabled="soundStyleOptions ? false : true"
v-model="soundStyle"
:placeholder="soundStyleOptions ? 'Select' : '暂无数据'"
style="width: 100%"
>
<el-option
v-for="item in languageOptions"
:key="item.value"
:label="item.label"
:value="item.value"
v-for="item in soundStyleOptions"
:key="item"
:label="item"
:value="item"
/>
</el-select>
<el-button type="primary">{{
$t("createVideo.generateVideo")
}}</el-button>
<el-button @click="saveOrUpdate(1)" color="#000" type="primary">
<my-loading :isLoading="createLoading"></my-loading>
<span v-if="!createLoading">{{
$t("createVideo.generateVideo")
}}</span></el-button
>
</div>
</el-tab-pane>
<!-- ------------------------ 2 -->
<el-tab-pane :label="$t('createVideo.uploadAudio')">
<div class="card2">
<!-- 上传视频 -->
<p>
<el-icon class="icon-VideoCamera"><i-ep-VideoCamera /></el-icon>
上传音频
</p>
<div class="uploadVideo">
+
<input
accept="audio/*"
type="file"
ref="fileInput"
@click="e.target.value = null"
@change="handleFileChange"
/>
<!-- <video
style="position: absolute; top: 0"
v-if="videoSrc"
:src="videoSrc"
ref="videoPlayer"
controls
></video> -->
</div>
<!-- 录制MP3上传语音 -->
<p>
<el-icon class="icon-Mic"><i-ep-Mic /></el-icon>
录制音频
</p>
<div
class="uploadAudio"
:class="audioUrl ? 'uploadAudioAvtion' : ''"
@@ -194,24 +254,16 @@
</div>
<!-- <button @click="playRecording">播放录音</button> -->
<audio v-if="audioUrl" controls :src="audioUrl"></audio>
<button v-if="audioUrl" @click="uploadAudio" class="pointer">
<!-- <button v-if="audioUrl" @click="uploadAudio" class="pointer">
上传音频
</button>
</div>
<!-- 上传视频 -->
<div
class="uploadVideo"
style="text-align: center; line-height: 70px"
>
上传视频
<input type="file" ref="fileInput" @change="handleFileChange" />
<video
v-if="videoSrc"
:src="videoSrc"
ref="videoPlayer"
controls
></video>
</button> -->
</div>
<el-button @click="saveOrUpdate(2)" color="#000" type="primary">
<my-loading :isLoading="createLoading"></my-loading>
<span v-if="!createLoading">{{
$t("createVideo.generateVideo")
}}</span>
</el-button>
</div>
</el-tab-pane>
<!-- ----------------------- 3 -->
@@ -281,25 +333,38 @@
</el-tab-pane>
</el-tabs>
</div>
<!-- 试听的mp3 -->
<div style="opacity: 0; width: 0">
<audio autoplay :src="previewAudioUrl"></audio>
</div>
</div>
</template>

<script setup>
import { getCurrentInstance } from "vue";
import Recorder from "js-audio-recorder";
import {
personPhotoList,
awsImgUpload,
videoUpload,
saveImgApi,
uploadProgressApi,
saveAudioApi,
voiceTotalApi,
chooseTypeApi,
saveOrUpdateApi,
createVideoApi,
previewAudioApi,
} from "@/apis/createVideo";
import Recorder from "js-audio-recorder"; //mp3插件
import imageCompression from "browser-image-compression"; //压缩图片插件

//#region 头像区的逻辑
// 获取AI头像
const AiFaceList = ref([]);
const feceItemLoading = ref(false);
function getAiFaceList(pageNum, pageSize) {
function getAiFaceList(pageNum, pageSize, sendType) {
feceItemLoading.value = true;
personPhotoList(pageNum, pageSize)
personPhotoList(pageNum, pageSize, sendType)
.then((res) => {
AiFaceList.value = res.data.list;
feceItemLoading.value = false;
@@ -312,20 +377,51 @@ function getAiFaceList(pageNum, pageSize) {
// 选择显示的face列表
const showFace = ref("0");
function ChangeShowFaceList(index) {
AiFaceList.value = null;
if (index == 0) {
getAiFaceList(1, 100, 1);
}
if (index == 1) {
getAiFaceList(1, 100, 3);
}
showFace.value = index;
}
const faceItemActive = ref(); //高亮处理和当前选择图片的ID
// 点击aifece
function setImgUrl(url, id) {
faceItemActive.value = id;
showUploadBtn.value = false;
submitImgBtn.value = false;
imgUrl.value = url;
}
//#endregion ------------------------------

//#region 预览图片
const compressionImgLoad = ref(false);
async function lookImg(e) {
// 压缩配置
compressionImgLoad.value = true;
const options = {
maxSizeMB: 2, // 最大压缩大小为 1MB
useWebWorker: true, // 使用 Web Worker 进行压缩,提高性能
};
const compressedFile = await imageCompression(e.target.files[0], options);
imgUrl.value = URL.createObjectURL(compressedFile);
imgData.value = compressedFile;
showUploadBtn.value = false;
submitImgBtn.value = true;
compressionImgLoad.value = false;
}
//#endregion ------------------

// 标题
const title = ref("");

// 上传图片
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];
}
//#region 上传图片
const imgUrl = ref(""); //图片回显路径
const imgData = ref(""); // 上传图片文件
const showUploadBtn = ref(false); //上传图片+号的显示隐藏
const submitImgBtn = ref(false); //右上角俩按钮显示与隐藏
// 上传图片
async function changeFile() {
imgUrl.value = URL.createObjectURL(imgData.value);
@@ -344,13 +440,92 @@ async function changeFile() {
ElMessage.error(error);
}
}
//#endregion ---------------------------

// 点击aifece
function setImgUrl(url) {
imgUrl.value = url;
//#region card1的逻辑
const textareaContent = ref("");
function insertIcon() {
const textarea = document.getElementById("textInputId");
const icon = "🕗"; // 这里使用了一个星星作为示例图标,你可以替换为你自己的图标

// 获取光标位置
const startPos = textarea.selectionStart;
const endPos = textarea.selectionEnd;

console.log(startPos);
console.log(endPos);
// 在光标位置插入图标
textareaContent.value =
textareaContent.value.substring(0, startPos) +
icon +
textareaContent.value.substring(endPos, textareaContent.value.length);

// 设置光标位置为插入图标后的位置
const newCursorPos = startPos + icon.length;
textarea.setSelectionRange(newCursorPos, newCursorPos);
textarea.focus();
}
const language = ref(""); //选择的语言
const sound = ref(""); // 选择的声音
const soundStyle = ref(""); //选择的风格
// 语言下拉框内容
const languageOptions = ref([
{
id: "0",
name: "中文",
},
{
id: "1",
name: "English",
},
]);
async function voiceTotal() {
const res = await voiceTotalApi();
languageOptions.value = res.data;
}
// 声音下拉框内容
const soundOptions = ref([]);
async function chooseType() {
const res = await chooseTypeApi(language.value);
soundOptions.value = res.data;
sound.value = null;
}
// 风格下拉框内容
const soundStyleOptions = ref(null);
async function chooseStyle() {
const item = soundOptions.value.find((element) => element.id == sound.value);

soundStyle.value = null;
soundStyleOptions.value = item.style;
}

// 录制MP3上传语音
// 试听
const previewAudioUrl = ref(null);
async function getPreviewAudio() {
if (!textareaContent.value) {
ElMessage.error("您还没有输入文案");
return;
}
if (!sound.value) {
ElMessage.error("您还没有选择声音");
return;
}

try {
const res = await previewAudioApi(
textareaContent.value,
sound.value,
soundStyle.value
);
previewAudioUrl.value = res.data.data;
console.log(previewAudioUrl.value);
} catch (error) {
ElMessage.error(error);
}
}
//#endregion

//#region 录制MP3上传语音
const parameter = {
sampleBits: 16, // 采样位数,支持 8 或 16,默认是16
sampleRate: 8000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000
@@ -358,8 +533,9 @@ const parameter = {
};
const recorder = new Recorder(parameter);
const isRecording = ref(true);
const audioUrl = ref(null);
const audioUrl = ref(null); // audio控件的url
const audioData = ref("");
const videoId = ref(null); //上传mp3的videoId,创建视频会用到

const startRecording = async () => {
try {
@@ -388,42 +564,127 @@ const playRecording = () => {
audio.play();
}
};
async function uploadAudio(params) {
// 上传音频
async function uploadAudio() {
createLoading.value = true;
let formData = new FormData();
const timestamp = Date.now();
formData.append("file", audioData.value, timestamp + ".mp3");
try {
const res = await videoUpload(formData);
ElMessage.success("上传成功!");
videoId.value = res.data.videoId;
if (res) {
// ElMessage.success("上传成功!");
} else {
ElMessage.error("音频上传失败!");
return;
}
// 查询进度
intervalId = setInterval(() => {
uploadProgress(res.data.videoId);
}, 1000);
// ElMessage.success("上传成功!");
} catch (error) {
ElMessage.error(error);
}
}
//#endregion ------------------------------------

// 上传视频功能
//#region 上传音频功能
const fileInput = ref(null);
const videoSrc = ref(null);
const handleFileChange = () => {

let intervalId = null; // 用于存储 setInterval 的返回值
const handleFileChange = async () => {
const file = fileInput.value.files[0];
if (file) {
const videoUrl = URL.createObjectURL(file);
videoSrc.value = videoUrl;
audioUrl.value = videoUrl;
audioData.value = file;
}
};
// 获取视频上传进度
const afterSaveAudioId = ref(null);
async function uploadProgress(videoId) {
const res = await uploadProgressApi(videoId);
if (res.data == "complete") {
clearInterval(intervalId);
try {
const res2 = await saveAudioApi({ videoId: videoId });
afterSaveAudioId.value = res2.data.id; // 得到id
} catch (error) {
ElMessage.error(error);
}
// 定义参数
const saveOrUpdateData = {
personPhotoId: faceItemActive.value, //任务照片id,
voiceFrom: tabFrom.value, //:声音来源 1:来自选择模板,2:来自用户上传
paperwork: textareaContent.value, //文案
speakTypeStr: soundStyle.value, //声音风格
voiceMouldId: sound.value, //选择声音id voiceFrom=1 时必传
voiceMaterialId: afterSaveAudioId.value, //上传音频且保存音频后得到的id voiceFrom=2 时必传
};
try {
const res3 = await saveOrUpdateApi(saveOrUpdateData); // 保存数据
const res4 = await createVideoApi({ id: res3.data.id }); // 生成视频
if (res4.message == "success") {
ElMessage.success("视频正在生成,请耐心等待");
}
createLoading.value = false;
} catch (error) {
ElMessage.error(error);
createLoading.value = false;
}
}
}
//#endregion --------------------------------------

// card3的
let language = ref("");
//#region 生成视频
// tab栏切换
const tabFrom = ref(1);
const createLoading = ref(false);
// 上传音频后,得到videoId
// 保存数据
async function saveOrUpdate(index) {
if (createLoading.value == true) {
return;
}
tabFrom.value = index;
const saveOrUpdateData = {
personPhotoId: faceItemActive.value, //任务照片id,
voiceFrom: tabFrom.value, //:声音来源 1:来自选择模板,2:来自用户上传
paperwork: textareaContent.value, //文案
speakTypeStr: soundStyle.value, //声音风格
voiceMouldId: sound.value, //选择声音id voiceFrom=1 时必传
voiceMaterialId: afterSaveAudioId.value, //上传音频且保存音频后得到的id voiceFrom=2 时必传
};
if (index == 1) {
createLoading.value = true;
try {
const res2 = await saveOrUpdateApi(saveOrUpdateData); // 保存数据
const res3 = await createVideoApi({ id: res2.data.id }); // 生成视频
if (res3.message == "success") {
ElMessage.success("视频正在生成,请耐心等待");
}
createLoading.value = false;
} catch (error) {
ElMessage.error(error);
createLoading.value = false;
}
}
if (index == 2) {
try {
const res1 = await uploadAudio(); // 上传音频
} catch (error) {
ElMessage.error(error);
createLoading.value = false;
}
}
}
//#endregion ----------------------------

let languageOptions = reactive([
{
value: "Option1",
label: "Option1",
},
{
value: "Option2",
label: "Option2",
},
]);
//#region

//#endregion ----------------------------

// $t在script中的使用
const {
@@ -436,7 +697,9 @@ const string = "createVideo.typeScript";
const t = globalProperties.$t(string);

onMounted(() => {
getAiFaceList(1, 100);
getAiFaceList(1, 100, 1);
voiceTotal();
chooseType();
});
</script>

@@ -472,7 +735,7 @@ onMounted(() => {
position: relative;
width: 100%;
// height: 65%;
height: 600px;
height: 580px;

.uploadBtn {
position: relative;
@@ -600,31 +863,48 @@ onMounted(() => {
}

.faceList {
padding: 8px;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
width: 100%;
height: 160px;
height: 180px;
overflow-y: scroll;
.faceItem {
position: relative;
margin-right: 10px;
margin-bottom: 10px;
margin-right: 35px;
margin-bottom: 35px;
width: 150px;
height: 150px;
padding: 0 25px;
// padding: 0 25px;
line-height: 150px;
text-align: center;
border-radius: 50%;
border: 2px solid #000;
overflow: hidden;
input {
background-color: #fbf8f8;
// border: 2px solid #fff;
box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 5px 5px;
// overflow: hidden;
transition: all 0.3s;
.imgBox {
cursor: pointer;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
right: 0;
bottom: 0;
z-index: 3;
width: 150px;
height: 150px;
border-radius: 50%;
background-color: #fbf8f8;
overflow: hidden;
img {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
}
}
.uploadImg {
line-height: 145px;
@@ -632,16 +912,28 @@ onMounted(() => {
font-weight: 700;
text-align: center;
}
p {
height: 30px;
}
img {
width: 100%;
}
.img {
width: 100px;
height: 100px;
}
.faceItemActive {
position: relative;
transition: all 0.3s;
&::before {
content: "";
position: absolute;
// top: -3px;
// right: -7px;
// left: -3px;
// bottom: -7px;
top: -3px;
right: -3px;
left: -3px;
bottom: -3px;
z-index: 2;
border-radius: 50%;
background: linear-gradient(
225deg,
rgb(81 217 202 / 100%),
rgb(114 100 245 / 100%)
);
}
}
}
@@ -664,7 +956,6 @@ onMounted(() => {
.text {
padding: 10px;
border: 1px solid #000;

.el-button {
float: right;
margin-top: 10px;
@@ -685,9 +976,18 @@ onMounted(() => {
height: 50px;
margin-top: 10px;
}

:deep(.el-input .is-focus) {
// box-shadow: none !important;
// box-shadow: 0 0 0 1px #000 !important;
}
}
.card2 {
padding: 10px;
.el-icon {
// color: red;
vertical-align: text-top;
}
> div {
position: relative;
margin-top: 10px;
@@ -704,6 +1004,7 @@ onMounted(() => {
}
.uploadAudio {
padding: 20px;
// margin-top: 30px;
margin-bottom: 20px;
height: 70px;
.btnBox {
@@ -733,18 +1034,35 @@ onMounted(() => {
height: 170px;
}
.uploadVideo {
text-align: center;
margin-bottom: 20px;
height: 100px;
font-size: 50px;
font-weight: 700;
line-height: 100px;
input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
}
video {
position: absolute;
top: 0;
left: 0;
z-index: -1;
width: 100%;
height: 100%;
}
}
> .el-button {
width: 100%;
height: 50px;
margin-top: 10px;
// background-color: #000;
}
}
.card3 {
.text {


+ 4
- 10
src/views/createVideo/index.vue Переглянути файл

@@ -114,13 +114,6 @@
<el-tab-pane :label="$t('createVideo.typeScript')">
<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
id="textInputId"
@@ -333,10 +326,11 @@
</el-tab-pane>
</el-tabs>
</div>
<!-- 试听的mp3 -->
<div style="opacity: 0; width: 0">
<audio autoplay :src="previewAudioUrl"></audio>
</div>
</div>

<!-- 试听的mp3 -->
<audio autoplay :src="previewAudioUrl"></audio>
</template>

<script setup>


Завантаження…
Відмінити
Зберегти