|
- <template>
- <div class="page flex justify-around">
- <!-- 左侧栏 -->
- <div class="left">
- <!-- 顶部 -->
- <!-- 有图片时 -->
- <div class="left-top" v-show="!showUploadBtn">
- <el-row class="justify-between">
- <!-- title框 -->
- <div class="flex justify-between" style="width: 300px">
- <el-input
- v-model="title"
- :placeholder="$t('createVideo.typeTitle')"
- />
- <el-icon class="icon-edit"><i-ep-edit /></el-icon>
- </div>
- <!-- 上传按钮 -->
- <div class="btnBox" v-show="submitImgBtn">
- <button class="uploadBtn">
- {{ $t("createVideo.reselect") }}
- <input
- accept="image/*"
- type="file"
- @click="e.target.value = null"
- @change="lookImg"
- />
- </button>
- <button class="uploadBtn" @click="changeFile">
- {{ $t("createVideo.sureUpload") }}
- </button>
- </div>
- </el-row>
- <!-- 中间展示大图 -->
- <div class="bigImg inmiddle">
- <img :src="imgUrl" alt="" class="inmiddle" />
- </div>
- </div>
- <!-- 没有图片时 -->
- <div class="left-top" v-show="showUploadBtn">
- <!-- 中间展示大图 -->
- <div class="noImg">
- <my-loading
- style="margin-top: 150px"
- :isLoading="compressionImgLoad"
- ></my-loading>
- <div v-if="!compressionImgLoad" class="trueImg inmiddle">
- <div class="icon">✔</div>
- </div>
- <div v-if="!compressionImgLoad" class="upload inmiddle">
- +
- <input
- accept="image/*"
- type="file"
- @click="e.target.value = null"
- @change="lookImg"
- />
- </div>
- <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>
- </div>
- </div>
- </div>
-
- <!-- 底部 -->
- <div class="left-bottom">
- <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">
- <!-- 上传图片组件 -->
- <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, item.id)"
- v-for="item in AiFaceList"
- :key="item.id"
- >
- <!-- <div class="img"></div> -->
- <img :src="item.material" alt="" />
- <p>{{ item.title }}</p>
- </div>
- </div>
- </div>
- </div>
- <!-- 右侧栏 -->
- <div class="right">
- <el-tabs type="border-card" class="tabs">
- <!-- ----------------------------- 1 -->
- <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"
- v-model="textareaContent"
- :rows="15"
- type="textarea"
- :placeholder="$t('createVideo.aiText')"
- />
- <div @click="insertIcon">🕗</div>
- </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 soundOptions"
- :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 soundStyleOptions"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
- <el-button color="#000" type="primary">{{
- $t("createVideo.generateVideo")
- }}</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 type="file" ref="fileInput" @change="handleFileChange" />
- <video
- 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' : ''"
- >
- <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" class="pointer">
- 上传音频
- </button>
- </div>
- <el-button color="#000" type="primary">{{
- $t("createVideo.generateVideo")
- }}</el-button>
- </div>
- </el-tab-pane>
- <!-- ----------------------- 3 -->
- <el-tab-pane v-if="false" :label="$t('createVideo.aiScript')">
- <div class="card3">
- <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>
- </el-tabs>
- </div>
- </div>
- </template>
-
- <script setup>
- import { getCurrentInstance } from "vue";
- import {
- personPhotoList,
- awsImgUpload,
- videoUpload,
- saveImgApi,
- } 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) {
- 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;
- }
- const faceItemActive = ref(); //高亮处理
- // 点击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("");
-
- //#region 上传图片
- const imgUrl = ref(""); //图片回显路径
- const imgData = ref(""); // 上传图片文件
- const showUploadBtn = ref(false); //上传图片+号的显示隐藏
- const submitImgBtn = ref(false); //右上角俩按钮显示与隐藏
- // 上传图片
- 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);
- }
- }
- //#endregion ---------------------------
-
- //#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 = reactive([
- {
- value: "0",
- label: "中文",
- },
- {
- value: "1",
- label: "English",
- },
- ]);
- const soundOptions = reactive([
- {
- value: "0",
- label: "老人",
- },
- {
- value: "1",
- label: "小孩",
- },
- ]);
- const soundStyleOptions = reactive([
- {
- value: "0",
- label: "生气",
- },
- {
- value: "1",
- label: "抑郁",
- },
- ]);
- //#endregion
-
- //#region 录制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(true);
- const audioUrl = ref(null);
- const audioData = ref("");
-
- const startRecording = async () => {
- try {
- await recorder.start();
- console.log("Recording started");
- isRecording.value = false;
- } catch (error) {
- console.error(error);
- }
- };
-
- const stopRecording = async () => {
- try {
- const recording = await recorder.stop();
- audioUrl.value = URL.createObjectURL(recorder.getWAVBlob());
- audioData.value = recorder.getWAVBlob();
- isRecording.value = true;
- } catch (error) {
- console.error(error);
- }
- };
-
- const playRecording = () => {
- if (audioUrl.value) {
- const audio = new Audio(audioUrl.value);
- audio.play();
- }
- };
- 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);
- }
- }
- //#endregion ------------------------------------
-
- //#region 上传视频功能
- const fileInput = ref(null);
- const videoSrc = ref(null);
- const handleFileChange = () => {
- const file = fileInput.value.files[0];
- if (file) {
- const videoUrl = URL.createObjectURL(file);
- videoSrc.value = videoUrl;
- }
- };
- //#endregion --------------------------------------
-
- // $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: 1200px;
- height: calc(100vh - 80px);
- padding: 20px 0 0 40px;
- background-color: #fff;
- border-radius: 25px 0 0;
- }
-
- .left {
- width: 70%;
- height: 80%;
-
- :deep(.el-input__wrapper) {
- border: none;
- border-bottom: 2px solid #000;
- border-radius: 0;
- box-shadow: none;
- }
-
- .el-icon.icon-edit {
- width: 35px;
- height: 35px;
- font-size: 35px;
- color: #8c939d;
- text-align: center;
- }
-
- &-top {
- position: relative;
- width: 100%;
- // 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 {
- display: flex;
- align-items: center;
- justify-content: space-between;
- width: 80%;
- height: 88%;
- margin-top: 20px;
-
- img {
- max-width: 100%;
- max-height: 100%;
- }
- }
-
- .noImg {
- position: relative;
- width: 100%;
- height: 100%;
- border: 1px solid #000;
-
- .trueImg {
- top: 60px;
- width: 100px;
- height: 100px;
- 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;
- height: 100px;
- font-size: 50px;
- line-height: 100px;
- text-align: center;
- background-color: pink;
- border-radius: 50%;
- }
-
- .falseImg {
- top: 350px;
- // background-color: #795562;
- display: flex;
- justify-content: space-evenly;
- width: 500px;
- height: 100px;
-
- > div {
- position: relative;
- width: 100px;
- height: 100px;
- background-color: #ccc;
- border-radius: 50%;
- }
- }
- }
-
- // 公共类名
- input {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- opacity: 0;
- }
-
- .icon {
- position: absolute;
- right: 2px;
- width: 30px;
- height: 30px;
- font-size: 20px;
- font-weight: 700;
- line-height: 26px;
- text-align: center;
- border: 2px solid #000;
- border-radius: 50%;
- }
- }
-
- &-bottom {
- 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%;
- }
-
- .faceList {
- padding: 3px;
- display: flex;
- flex-wrap: wrap;
- justify-content: flex-start;
- width: 100%;
- height: 160px;
- overflow-y: scroll;
- .faceItem {
- position: relative;
- margin-right: 13px;
- margin-bottom: 10px;
- width: 150px;
- height: 150px;
- padding: 0 25px;
- line-height: 150px;
- text-align: center;
- border-radius: 50%;
- background-color: #fbf8f8;
- border: 2px solid #fff;
- box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 3px 3px;
- 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;
- border-radius: 50%;
- }
- }
- .faceItemActive {
- background: linear-gradient(
- 270deg,
- rgb(201, 113, 255) 24.97%,
- rgb(92, 121, 252) 100%
- );
- // border: 2px solid #fff;
- // box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 3px 3px;
- }
- }
- }
- }
-
- .right {
- width: 25%;
- height: 100%;
- background-color: green;
-
- :deep(.el-tabs--border-card) {
- font-size: 20px;
- border: none;
- }
-
- .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;
- }
-
- :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;
- width: 100%;
- border: #000 1px solid;
- border-radius: 10px;
-
- overflow: hidden;
- transition: all 0.3s;
- box-shadow: #00000026 2px 2px 2px 2px;
- &:hover {
- transform: translateY(-10px);
- }
- }
- .uploadAudio {
- padding: 20px;
- // margin-top: 30px;
- 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 {
- text-align: center;
-
- margin-bottom: 20px;
- height: 100px;
- font-size: 50px;
- font-weight: 700;
- line-height: 100px;
- input {
- position: absolute;
- width: 100%;
- height: 100%;
- opacity: 0;
- }
- video {
- width: 100%;
- height: 100%;
- }
- }
- > .el-button {
- width: 100%;
- height: 50px;
- margin-top: 10px;
- // background-color: #000;
- }
- }
- .card3 {
- .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;
- }
- }
-
- :deep(.el-tabs__item.is-active) {
- color: #909393;
- background-color: #000;
- }
-
- :deep(.el-tabs__nav) {
- width: 100%;
- }
-
- :deep(.el-tabs__item) {
- width: 34%;
- }
-
- :deep(.el-tabs__item:hover) {
- color: #909393;
- }
-
- :deep(.el-tabs__content) {
- height: calc(100% - 60px);
- padding: 0;
- }
- }
- }
- </style>
|