| @@ -12,6 +12,18 @@ export function personPhotoList(pageNum, pageSize, sendType) { | |||
| }) | |||
| } | |||
| /** | |||
| * @description 获取人脸列表 | |||
| * @params pageNum, pageSize | |||
| * @returns data | |||
| */ | |||
| export function personPhotoUserListList(pageNum, pageSize, sendType) { | |||
| return request({ | |||
| url: `api/personPhoto/userList?pageNum=${pageNum}&pageSize=${pageSize}&sendType=${sendType}`, | |||
| method: 'get' | |||
| }) | |||
| } | |||
| /** | |||
| * @description 上传图片 | |||
| * @params data | |||
| @@ -37,6 +49,18 @@ export function saveImgApi(data) { | |||
| }) | |||
| } | |||
| /** | |||
| * @description 删除图片 | |||
| * @params id | |||
| * @returns data | |||
| */ | |||
| export function delImgApi(id) { | |||
| return request({ | |||
| url: `api/personPhoto/del?id=${id}`, | |||
| method: 'get', | |||
| }) | |||
| } | |||
| /** | |||
| * @description 上传视频 | |||
| * @params data | |||
| @@ -105,10 +129,17 @@ export function chooseTypeApi(id) { | |||
| * @params id | |||
| * @returns data | |||
| */ | |||
| export function previewAudioApi(genTxt, voiceId, voiceStyle) { | |||
| // export function previewAudioApi(genTxt, voiceId, voiceStyle) { | |||
| // return request({ | |||
| // url: `api/voiceMould/preview?genTxt=${genTxt}&voiceId=${voiceId}&voiceStyle=${voiceStyle}`, | |||
| // method: 'GET', | |||
| // }) | |||
| // } | |||
| export function previewAudioApi(gen_txt, voice_id, voice_style) { | |||
| return request({ | |||
| url: `api/voiceMould/preview?genTxt=${genTxt}&voiceId=${voiceId}&voiceStyle=${voiceStyle}`, | |||
| method: 'GET', | |||
| url: `api/voiceMould/preview`, | |||
| method: 'POST', | |||
| data: { gen_txt, voice_id, voice_style } | |||
| }) | |||
| } | |||
| @@ -81,6 +81,7 @@ export default { | |||
| draftText: "Draft", | |||
| generatingText: "The video is generating, please wait a moment", | |||
| generationFailedText: "The video generate is failed, Reason:", | |||
| edit:'edit' | |||
| }, | |||
| // 个人账户 | |||
| @@ -81,6 +81,7 @@ export default { | |||
| draftText: "草稿", | |||
| generatingText: "视频正在生成中,请稍等哦", | |||
| generationFailedText: "视频生成失败。原因:", | |||
| edit:'编辑' | |||
| }, | |||
| // 个人账户 | |||
| @@ -23,7 +23,7 @@ const tagsViewStore = useTagsViewStore(); | |||
| /* 50= navbar 50 */ | |||
| min-height: calc(100vh - 80px); | |||
| overflow: hidden; | |||
| // overflow: hidden; | |||
| // background-color: var(--el-bg-color-page); | |||
| background-color: #000; | |||
| } | |||
| @@ -5,7 +5,6 @@ declare global { | |||
| const ElForm: typeof import('element-plus/es')['ElForm'] | |||
| const ElMessage: typeof import('element-plus/es')['ElMessage'] | |||
| const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] | |||
| const ElTree: typeof import('element-plus/es')['ElTree'] | |||
| const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] | |||
| const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] | |||
| const computed: typeof import('vue')['computed'] | |||
| @@ -275,7 +274,6 @@ declare module 'vue' { | |||
| readonly ElForm: UnwrapRef<typeof import('element-plus/es')['ElForm']> | |||
| readonly ElMessage: UnwrapRef<typeof import('element-plus/es')['ElMessage']> | |||
| readonly ElMessageBox: UnwrapRef<typeof import('element-plus/es')['ElMessageBox']> | |||
| readonly ElTree: UnwrapRef<typeof import('element-plus/es')['ElTree']> | |||
| readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']> | |||
| readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']> | |||
| readonly computed: UnwrapRef<typeof import('vue')['computed']> | |||
| @@ -49,11 +49,13 @@ declare module '@vue/runtime-core' { | |||
| IEpCaretBottom: typeof import('~icons/ep/caret-bottom')['default'] | |||
| IEpCaretTop: typeof import('~icons/ep/caret-top')['default'] | |||
| IEpClose: typeof import('~icons/ep/close')['default'] | |||
| IEpCloseBold: typeof import('~icons/ep/close-bold')['default'] | |||
| IEpCollection: typeof import('~icons/ep/collection')['default'] | |||
| IEpDelete: typeof import('~icons/ep/delete')['default'] | |||
| IEpDownload: typeof import('~icons/ep/download')['default'] | |||
| IEpEdit: typeof import('~icons/ep/edit')['default'] | |||
| IEpGoods: typeof import('~icons/ep/goods')['default'] | |||
| IEpLoading: typeof import('~icons/ep/loading')['default'] | |||
| IEpMenu: typeof import('~icons/ep/menu')['default'] | |||
| IEpMic: typeof import('~icons/ep/mic')['default'] | |||
| IEpPlus: typeof import('~icons/ep/plus')['default'] | |||
| @@ -7,7 +7,11 @@ | |||
| <div class="left-top" v-show="!showUploadBtn"> | |||
| <el-row class="justify-between"> | |||
| <!-- title框 --> | |||
| <div class="flex justify-between" style="width: 300px"> | |||
| <div | |||
| v-show="!submitImgBtn" | |||
| class="flex justify-between" | |||
| style="width: 300px" | |||
| > | |||
| <el-input | |||
| v-model="title" | |||
| :placeholder="$t('createVideo.typeTitle')" | |||
| @@ -18,12 +22,7 @@ | |||
| <div class="btnBox" v-show="submitImgBtn"> | |||
| <button class="uploadBtn"> | |||
| {{ $t("createVideo.reselect") }} | |||
| <input | |||
| accept="image/*" | |||
| type="file" | |||
| @click="e.target.value = null" | |||
| @change="lookImg" | |||
| /> | |||
| <input accept="image/*" type="file" @change="lookImg" /> | |||
| </button> | |||
| <button class="uploadBtn" @click="changeFile"> | |||
| {{ $t("createVideo.sureUpload") }} | |||
| @@ -44,21 +43,34 @@ | |||
| :isLoading="compressionImgLoad" | |||
| ></my-loading> | |||
| <div v-if="!compressionImgLoad" class="trueImg inmiddle"> | |||
| <div class="imgBox"> | |||
| <img src="../../assets/img/trueImg.png" alt="" /> | |||
| </div> | |||
| <div class="icon">✔</div> | |||
| </div> | |||
| <div v-if="!compressionImgLoad" class="upload inmiddle"> | |||
| + | |||
| <input | |||
| accept="image/*" | |||
| type="file" | |||
| @click="e.target.value = null" | |||
| @change="lookImg" | |||
| /> | |||
| <input accept="image/*" type="file" @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 class="imgBox"> | |||
| <img src="../../assets/img/false1.png" alt="" /> | |||
| </div> | |||
| <div class="icon">×</div> | |||
| </div> | |||
| <div> | |||
| <div class="imgBox"> | |||
| <img src="../../assets/img/false2.png" alt="" /> | |||
| </div> | |||
| <div class="icon">×</div> | |||
| </div> | |||
| <div> | |||
| <div class="imgBox"> | |||
| <img src="../../assets/img/false3.png" alt="" /> | |||
| </div> | |||
| <div class="icon">×</div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -83,11 +95,7 @@ | |||
| </div> | |||
| <div class="faceList"> | |||
| <!-- 上传图片组件 --> | |||
| <div | |||
| v-if="showFace == 1" | |||
| class="faceItem" | |||
| @click="showUploadBtn = true" | |||
| > | |||
| <div v-if="showFace == 1" class="faceItem" @click="clickAddBtn"> | |||
| <span class="uploadImg">+ </span> | |||
| </div> | |||
| <my-loading :isLoading="feceItemLoading"></my-loading> | |||
| @@ -114,13 +122,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" | |||
| @@ -142,7 +143,7 @@ | |||
| <el-select | |||
| @change="chooseType" | |||
| v-model="language" | |||
| placeholder="Select" | |||
| :placeholder="$t('createVideo.select')" | |||
| style="width: 100%" | |||
| > | |||
| <el-option | |||
| @@ -169,7 +170,7 @@ | |||
| @change="chooseStyle" | |||
| :disabled="language ? false : true" | |||
| v-model="sound" | |||
| :placeholder="language ? 'Select' : 'Please select language'" | |||
| :placeholder="$t('createVideo.select')" | |||
| style="width: 100%" | |||
| > | |||
| <el-option | |||
| @@ -184,7 +185,7 @@ | |||
| <el-select | |||
| :disabled="soundStyleOptions ? false : true" | |||
| v-model="soundStyle" | |||
| :placeholder="soundStyleOptions ? 'Select' : '暂无数据'" | |||
| :placeholder="$t('createVideo.select')" | |||
| style="width: 100%" | |||
| > | |||
| <el-option | |||
| @@ -194,12 +195,25 @@ | |||
| :value="item" | |||
| /> | |||
| </el-select> | |||
| <!-- 生成视频 --> | |||
| <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 | |||
| > | |||
| <!-- 保存视频 --> | |||
| <el-button | |||
| @click="saveOrUpdate(1, 'saveVideo')" | |||
| color="#000" | |||
| type="primary" | |||
| style="margin-left: 0" | |||
| > | |||
| <my-loading :isLoading="createLoading"></my-loading> | |||
| <span v-if="!createLoading">{{ | |||
| $t("createVideo.saveVideo") | |||
| }}</span></el-button | |||
| > | |||
| </div> | |||
| </el-tab-pane> | |||
| <!-- ------------------------ 2 --> | |||
| @@ -208,7 +222,7 @@ | |||
| <!-- 上传视频 --> | |||
| <p> | |||
| <el-icon class="icon-VideoCamera"><i-ep-VideoCamera /></el-icon> | |||
| 上传音频 | |||
| {{ $t("createVideo.uploadAudio") }} | |||
| </p> | |||
| <div class="uploadVideo"> | |||
| + | |||
| @@ -230,7 +244,7 @@ | |||
| <!-- 录制MP3上传语音 --> | |||
| <p> | |||
| <el-icon class="icon-Mic"><i-ep-Mic /></el-icon> | |||
| 录制音频 | |||
| {{ $t("createVideo.recordedAudio") }} | |||
| </p> | |||
| <div | |||
| class="uploadAudio" | |||
| @@ -242,14 +256,14 @@ | |||
| :disabled="!isRecording" | |||
| @click="startRecording" | |||
| > | |||
| 开始录音 | |||
| {{ $t("createVideo.startRecorded") }} | |||
| </button> | |||
| <button | |||
| class="pointer" | |||
| @click="stopRecording" | |||
| :disabled="isRecording" | |||
| > | |||
| 停止录音 | |||
| {{ $t("createVideo.stopRecorded") }} | |||
| </button> | |||
| </div> | |||
| <!-- <button @click="playRecording">播放录音</button> --> | |||
| @@ -258,12 +272,25 @@ | |||
| 上传音频 | |||
| </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> | |||
| <!-- 保存视频 --> | |||
| <el-button | |||
| @click="saveOrUpdate(2, 'saveVideo')" | |||
| color="#000" | |||
| type="primary" | |||
| style="margin-left: 0" | |||
| > | |||
| <my-loading :isLoading="createLoading"></my-loading> | |||
| <span v-if="!createLoading">{{ | |||
| $t("createVideo.saveVideo") | |||
| }}</span></el-button | |||
| > | |||
| </div> | |||
| </el-tab-pane> | |||
| <!-- ----------------------- 3 --> | |||
| @@ -355,9 +382,13 @@ import { | |||
| createVideoApi, | |||
| previewAudioApi, | |||
| } from "@/apis/createVideo"; | |||
| import { useI18n } from "vue-i18n"; | |||
| import Recorder from "js-audio-recorder"; //mp3插件 | |||
| import imageCompression from "browser-image-compression"; //压缩图片插件 | |||
| const { locale } = useI18n(); | |||
| const lanChange = ref(locale); | |||
| //#region 头像区的逻辑 | |||
| // 获取AI头像 | |||
| const AiFaceList = ref([]); | |||
| @@ -399,18 +430,27 @@ function setImgUrl(url, id) { | |||
| //#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; | |||
| if (e.target.files[0]) { | |||
| faceItemActive.value = null; | |||
| imgUrl.value = null; | |||
| imgData.value = null; | |||
| showUploadBtn.value = true; | |||
| submitImgBtn.value = false; | |||
| 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; | |||
| } else { | |||
| return; | |||
| } | |||
| } | |||
| //#endregion ------------------ | |||
| @@ -422,6 +462,11 @@ const imgUrl = ref(""); //图片回显路径 | |||
| const imgData = ref(""); // 上传图片文件 | |||
| const showUploadBtn = ref(false); //上传图片+号的显示隐藏 | |||
| const submitImgBtn = ref(false); //右上角俩按钮显示与隐藏 | |||
| // 点击AI头像区的+号 | |||
| function clickAddBtn() { | |||
| faceItemActive.value = null; | |||
| showUploadBtn.value = true; | |||
| } | |||
| // 上传图片 | |||
| async function changeFile() { | |||
| imgUrl.value = URL.createObjectURL(imgData.value); | |||
| @@ -429,13 +474,21 @@ async function changeFile() { | |||
| const timestamp = Date.now(); | |||
| formData.append("file", imgData.value, timestamp + ".png"); | |||
| try { | |||
| // 上传图片 | |||
| const res = await awsImgUpload(formData); | |||
| if (res.code == 200) { | |||
| ElMessage.success("上传成功!"); | |||
| // 保存图片 | |||
| const res2 = await saveImgApi({ material: res.data.url }); | |||
| // 重新获取列表 | |||
| getAiFaceList(1, 100, 1); | |||
| if (res2.code == 200) { | |||
| ElMessage.success( | |||
| lanChange.value == "zh-cn" ? `上传成功` : `Upload successfully` | |||
| ); | |||
| } else { | |||
| ElMessage.error("上传失败!"); | |||
| ElMessage.error( | |||
| lanChange.value == "zh-cn" ? `上传失败` : `Upload failure` | |||
| ); | |||
| } | |||
| const res2 = await saveImgApi({ material: res.data.url }); | |||
| } catch (error) { | |||
| ElMessage.error(error); | |||
| } | |||
| @@ -503,11 +556,19 @@ async function chooseStyle() { | |||
| const previewAudioUrl = ref(null); | |||
| async function getPreviewAudio() { | |||
| if (!textareaContent.value) { | |||
| ElMessage.error("您还没有输入文案"); | |||
| ElMessage.error( | |||
| lanChange.value == "zh-cn" | |||
| ? `您还没有输入文案` | |||
| : `You have not entered the copy yet` | |||
| ); | |||
| return; | |||
| } | |||
| if (!sound.value) { | |||
| ElMessage.error("您还没有选择声音"); | |||
| ElMessage.error( | |||
| lanChange.value == "zh-cn" | |||
| ? `您还没有选择声音` | |||
| : `You have not selected the sound` | |||
| ); | |||
| return; | |||
| } | |||
| @@ -565,7 +626,7 @@ const playRecording = () => { | |||
| } | |||
| }; | |||
| // 上传音频 | |||
| async function uploadAudio() { | |||
| async function uploadAudio(isSaveVideo) { | |||
| createLoading.value = true; | |||
| let formData = new FormData(); | |||
| const timestamp = Date.now(); | |||
| @@ -576,12 +637,14 @@ async function uploadAudio() { | |||
| if (res) { | |||
| // ElMessage.success("上传成功!"); | |||
| } else { | |||
| ElMessage.error("音频上传失败!"); | |||
| ElMessage.error( | |||
| lanChange.value == "zh-cn" ? `上传音频失败!` : `Failed to upload audio!` | |||
| ); | |||
| return; | |||
| } | |||
| // 查询进度 | |||
| intervalId = setInterval(() => { | |||
| uploadProgress(res.data.videoId); | |||
| uploadProgress(res.data.videoId, isSaveVideo); | |||
| }, 1000); | |||
| // ElMessage.success("上传成功!"); | |||
| } catch (error) { | |||
| @@ -604,7 +667,7 @@ const handleFileChange = async () => { | |||
| }; | |||
| // 获取视频上传进度 | |||
| const afterSaveAudioId = ref(null); | |||
| async function uploadProgress(videoId) { | |||
| async function uploadProgress(videoId, isSaveVideo) { | |||
| const res = await uploadProgressApi(videoId); | |||
| if (res.data == "complete") { | |||
| clearInterval(intervalId); | |||
| @@ -625,9 +688,20 @@ async function uploadProgress(videoId) { | |||
| }; | |||
| try { | |||
| const res3 = await saveOrUpdateApi(saveOrUpdateData); // 保存数据 | |||
| if (isSaveVideo == "saveVideo") { | |||
| ElMessage.success( | |||
| lanChange.value == "zh-cn" ? `已保存为草稿` : `Saved as draft` | |||
| ); | |||
| createLoading.value = false; | |||
| return; | |||
| } | |||
| const res4 = await createVideoApi({ id: res3.data.id }); // 生成视频 | |||
| if (res4.message == "success") { | |||
| ElMessage.success("视频正在生成,请耐心等待"); | |||
| ElMessage.success( | |||
| lanChange.value == "zh-cn" | |||
| ? `视频正在生成中,请耐心等待` | |||
| : `The video is being generated, please wait patiently` | |||
| ); | |||
| } | |||
| createLoading.value = false; | |||
| } catch (error) { | |||
| @@ -644,7 +718,7 @@ const tabFrom = ref(1); | |||
| const createLoading = ref(false); | |||
| // 上传音频后,得到videoId | |||
| // 保存数据 | |||
| async function saveOrUpdate(index) { | |||
| async function saveOrUpdate(index, isSaveVideo) { | |||
| if (createLoading.value == true) { | |||
| return; | |||
| } | |||
| @@ -661,9 +735,20 @@ async function saveOrUpdate(index) { | |||
| createLoading.value = true; | |||
| try { | |||
| const res2 = await saveOrUpdateApi(saveOrUpdateData); // 保存数据 | |||
| if (isSaveVideo == "saveVideo") { | |||
| ElMessage.success( | |||
| lanChange.value == "zh-cn" ? `已保存为草稿` : `Saved as draft` | |||
| ); | |||
| createLoading.value = false; | |||
| return; | |||
| } | |||
| const res3 = await createVideoApi({ id: res2.data.id }); // 生成视频 | |||
| if (res3.message == "success") { | |||
| ElMessage.success("视频正在生成,请耐心等待"); | |||
| ElMessage.success( | |||
| lanChange.value == "zh-cn" | |||
| ? `视频正在生成中,请耐心等待` | |||
| : `The video is being generated, please wait patiently` | |||
| ); | |||
| } | |||
| createLoading.value = false; | |||
| } catch (error) { | |||
| @@ -673,7 +758,7 @@ async function saveOrUpdate(index) { | |||
| } | |||
| if (index == 2) { | |||
| try { | |||
| const res1 = await uploadAudio(); // 上传音频 | |||
| const res1 = await uploadAudio(isSaveVideo); // 上传音频 | |||
| } catch (error) { | |||
| ElMessage.error(error); | |||
| createLoading.value = false; | |||
| @@ -768,13 +853,34 @@ onMounted(() => { | |||
| width: 100%; | |||
| height: 100%; | |||
| border: 1px solid #000; | |||
| .imgBox { | |||
| position: relative; | |||
| width: 100%; | |||
| height: 100%; | |||
| border-radius: 50%; | |||
| overflow: hidden; | |||
| border: 1px solid #ccc; | |||
| img { | |||
| position: absolute; | |||
| top: 50%; | |||
| left: 50%; | |||
| transform: translate(-50%, -50%); | |||
| max-width: 120%; | |||
| } | |||
| } | |||
| .trueImg { | |||
| position: relative; | |||
| top: 60px; | |||
| width: 100px; | |||
| height: 100px; | |||
| background-color: green; | |||
| border-radius: 50%; | |||
| .icon { | |||
| position: absolute; | |||
| top: 0%; | |||
| right: -5%; | |||
| border: 2px solid #00ff95; | |||
| color: #00ff95; | |||
| } | |||
| } | |||
| .el-upload { | |||
| position: absolute; | |||
| @@ -791,7 +897,7 @@ onMounted(() => { | |||
| font-size: 50px; | |||
| line-height: 100px; | |||
| text-align: center; | |||
| background-color: pink; | |||
| background-color: #ccc; | |||
| border-radius: 50%; | |||
| } | |||
| @@ -800,7 +906,7 @@ onMounted(() => { | |||
| // background-color: #795562; | |||
| display: flex; | |||
| justify-content: space-evenly; | |||
| width: 500px; | |||
| width: 700px; | |||
| height: 100px; | |||
| > div { | |||
| @@ -809,6 +915,13 @@ onMounted(() => { | |||
| height: 100px; | |||
| background-color: #ccc; | |||
| border-radius: 50%; | |||
| .icon { | |||
| position: absolute; | |||
| top: 0%; | |||
| right: -5%; | |||
| border: 2px solid red; | |||
| color: red; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -821,6 +934,7 @@ onMounted(() => { | |||
| width: 100%; | |||
| height: 100%; | |||
| opacity: 0; | |||
| cursor: pointer; | |||
| } | |||
| .icon { | |||
| @@ -3,8 +3,8 @@ | |||
| <!-- 左侧栏 --> | |||
| <div class="left"> | |||
| <!-- 顶部 --> | |||
| <!-- 有图片时 --> | |||
| <div class="left-top" v-show="!showUploadBtn"> | |||
| <!-- 选中AiFace --> | |||
| <div class="left-top" v-show="showUploadBtn == 1"> | |||
| <el-row class="justify-between"> | |||
| <!-- title框 --> | |||
| <div | |||
| @@ -18,24 +18,14 @@ | |||
| /> | |||
| <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" @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="left-top" v-show="showUploadBtn == 2"> | |||
| <!-- 中间展示大图 --> | |||
| <div class="noImg"> | |||
| <my-loading | |||
| @@ -43,6 +33,9 @@ | |||
| :isLoading="compressionImgLoad" | |||
| ></my-loading> | |||
| <div v-if="!compressionImgLoad" class="trueImg inmiddle"> | |||
| <div class="imgBox"> | |||
| <img src="../../assets/img/trueImg.png" alt="" /> | |||
| </div> | |||
| <div class="icon">✔</div> | |||
| </div> | |||
| <div v-if="!compressionImgLoad" class="upload inmiddle"> | |||
| @@ -50,10 +43,84 @@ | |||
| <input accept="image/*" type="file" @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 class="imgBox"> | |||
| <img src="../../assets/img/false1.png" alt="" /> | |||
| </div> | |||
| <div class="icon">×</div> | |||
| </div> | |||
| <div> | |||
| <div class="imgBox"> | |||
| <img src="../../assets/img/false2.png" alt="" /> | |||
| </div> | |||
| <div class="icon">×</div> | |||
| </div> | |||
| <div> | |||
| <div class="imgBox"> | |||
| <img src="../../assets/img/false3.png" alt="" /> | |||
| </div> | |||
| <div class="icon">×</div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- loading --> | |||
| <div class="left-top" v-show="showUploadBtn == 3"> | |||
| <!-- 中间展示大图 --> | |||
| <div class="noImg"> | |||
| <my-loading | |||
| style="margin-top: 150px" | |||
| :isLoading="compressionImgLoad" | |||
| ></my-loading> | |||
| <div v-if="!compressionImgLoad" class="trueImg inmiddle"> | |||
| <div class="imgBox"> | |||
| <img src="../../assets/img/trueImg.png" alt="" /> | |||
| </div> | |||
| <div class="icon">✔</div> | |||
| </div> | |||
| <div v-if="!compressionImgLoad" class="upload inmiddle"> | |||
| + | |||
| <input accept="image/*" type="file" @change="lookImg" /> | |||
| </div> | |||
| <div v-if="!compressionImgLoad" class="falseImg inmiddle"> | |||
| <div> | |||
| <div class="imgBox"> | |||
| <img src="../../assets/img/false1.png" alt="" /> | |||
| </div> | |||
| <div class="icon">×</div> | |||
| </div> | |||
| <div> | |||
| <div class="imgBox"> | |||
| <img src="../../assets/img/false2.png" alt="" /> | |||
| </div> | |||
| <div class="icon">×</div> | |||
| </div> | |||
| <div> | |||
| <div class="imgBox"> | |||
| <img src="../../assets/img/false3.png" alt="" /> | |||
| </div> | |||
| <div class="icon">×</div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- 选中需要上传的图片前后 --> | |||
| <div class="left-top" v-show="showUploadBtn == 4"> | |||
| <el-row class="justify-between"> | |||
| <!-- 上传按钮 --> | |||
| <div class="btnBox" v-show="submitImgBtn"> | |||
| <button class="uploadBtn"> | |||
| {{ $t("createVideo.reselect") }} | |||
| <input accept="image/*" type="file" @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> | |||
| @@ -75,7 +142,8 @@ | |||
| {{ $t("createVideo.personalWarehouse") }} | |||
| </div> | |||
| </div> | |||
| <div class="faceList"> | |||
| <!-- 系统相册 --> | |||
| <div class="faceList" v-if="showFace == 0"> | |||
| <!-- 上传图片组件 --> | |||
| <div v-if="showFace == 1" class="faceItem" @click="clickAddBtn"> | |||
| <span class="uploadImg">+ </span> | |||
| @@ -95,6 +163,33 @@ | |||
| s | |||
| </div> | |||
| </div> | |||
| <!-- 个人相册 --> | |||
| <div class="faceList" v-if="showFace == 1"> | |||
| <!-- 上传图片组件 --> | |||
| <div v-if="showFace == 1" class="faceItem" @click="clickAddBtn"> | |||
| <span class="uploadImg">+ </span> | |||
| </div> | |||
| <my-loading :isLoading="feceItemLoading2"></my-loading> | |||
| <!-- face列表 --> | |||
| <div | |||
| :class="faceItemActive == item.id ? 'faceItemActive' : ''" | |||
| class="faceItem pointer" | |||
| @click="setImgUrl(item.material, item.id)" | |||
| v-for="item in userAiFaceList" | |||
| :key="item.id" | |||
| > | |||
| <div class="imgBox"> | |||
| <img :src="item.material" alt="" /> | |||
| </div> | |||
| <div | |||
| v-if="faceItemActive == item.id" | |||
| @click="deleteFaceImg(item.id)" | |||
| class="deleteIcon" | |||
| > | |||
| ✖︎ | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- 右侧栏 --> | |||
| @@ -353,7 +448,9 @@ | |||
| import { getCurrentInstance } from "vue"; | |||
| import { | |||
| personPhotoList, | |||
| personPhotoUserListList, | |||
| awsImgUpload, | |||
| delImgApi, | |||
| videoUpload, | |||
| saveImgApi, | |||
| uploadProgressApi, | |||
| @@ -374,7 +471,9 @@ const lanChange = ref(locale); | |||
| //#region 头像区的逻辑 | |||
| // 获取AI头像 | |||
| const AiFaceList = ref([]); | |||
| const userAiFaceList = ref([]); | |||
| const feceItemLoading = ref(false); | |||
| const feceItemLoading2 = ref(false); | |||
| function getAiFaceList(pageNum, pageSize, sendType) { | |||
| feceItemLoading.value = true; | |||
| personPhotoList(pageNum, pageSize, sendType) | |||
| @@ -387,6 +486,18 @@ function getAiFaceList(pageNum, pageSize, sendType) { | |||
| console.log(err, "err"); | |||
| }); | |||
| } | |||
| function getAiFaceUserList(pageNum, pageSize, sendType) { | |||
| feceItemLoading2.value = true; | |||
| personPhotoUserListList(pageNum, pageSize, sendType) | |||
| .then((res) => { | |||
| userAiFaceList.value = res.data.list; | |||
| feceItemLoading2.value = false; | |||
| }) | |||
| .catch((err) => { | |||
| feceItemLoading2.value = false; | |||
| console.log(err, "err"); | |||
| }); | |||
| } | |||
| // 选择显示的face列表 | |||
| const showFace = ref("0"); | |||
| function ChangeShowFaceList(index) { | |||
| @@ -395,7 +506,7 @@ function ChangeShowFaceList(index) { | |||
| getAiFaceList(1, 100, 1); | |||
| } | |||
| if (index == 1) { | |||
| getAiFaceList(1, 100, 3); | |||
| getAiFaceUserList(1, 100, 3); | |||
| } | |||
| showFace.value = index; | |||
| } | |||
| @@ -403,10 +514,32 @@ const faceItemActive = ref(); //高亮处理和当前选择图片的ID | |||
| // 点击aifece | |||
| function setImgUrl(url, id) { | |||
| faceItemActive.value = id; | |||
| showUploadBtn.value = false; | |||
| showUploadBtn.value = 1; | |||
| submitImgBtn.value = false; | |||
| imgUrl.value = url; | |||
| } | |||
| async function deleteFaceImg(id) { | |||
| const msg = | |||
| lanChange.value == "zh-cn" | |||
| ? `确定要删除这张图片吗? ` | |||
| : `Are you sure to delete this image ? `; | |||
| const notice = lanChange.value == "zh-cn" ? "提示" : "Notice"; | |||
| const confirm = lanChange.value == "zh-cn" ? "确定" : "Confirm"; | |||
| const cancel = lanChange.value == "zh-cn" ? "取消" : "Cancel"; | |||
| ElMessageBox.confirm(msg, notice, { | |||
| confirmButtonText: confirm, | |||
| cancelButtonText: cancel, | |||
| }).then(async () => { | |||
| try { | |||
| const res = await delImgApi(id); | |||
| imgUrl.value = null; | |||
| faceItemActive.value = null; | |||
| getAiFaceUserList(1, 100, 3); | |||
| } catch (error) { | |||
| ElMessage.error(errpr); | |||
| } | |||
| }); | |||
| } | |||
| //#endregion ------------------------------ | |||
| //#region 预览图片 | |||
| @@ -416,7 +549,7 @@ async function lookImg(e) { | |||
| faceItemActive.value = null; | |||
| imgUrl.value = null; | |||
| imgData.value = null; | |||
| showUploadBtn.value = true; | |||
| showUploadBtn.value = 3; | |||
| submitImgBtn.value = false; | |||
| compressionImgLoad.value = true; | |||
| // 压缩配置 | |||
| @@ -427,7 +560,7 @@ async function lookImg(e) { | |||
| const compressedFile = await imageCompression(e.target.files[0], options); | |||
| imgUrl.value = URL.createObjectURL(compressedFile); | |||
| imgData.value = compressedFile; | |||
| showUploadBtn.value = false; | |||
| showUploadBtn.value = 4; | |||
| submitImgBtn.value = true; | |||
| compressionImgLoad.value = false; | |||
| } else { | |||
| @@ -442,15 +575,17 @@ const title = ref(""); | |||
| //#region 上传图片 | |||
| const imgUrl = ref(""); //图片回显路径 | |||
| const imgData = ref(""); // 上传图片文件 | |||
| const showUploadBtn = ref(false); //上传图片+号的显示隐藏 | |||
| const showUploadBtn = ref(1); //上传图片+号的显示隐藏 | |||
| const submitImgBtn = ref(false); //右上角俩按钮显示与隐藏 | |||
| // 点击AI头像区的+号 | |||
| function clickAddBtn() { | |||
| faceItemActive.value = null; | |||
| showUploadBtn.value = true; | |||
| showUploadBtn.value = 2; | |||
| } | |||
| // 上传图片 | |||
| async function changeFile() { | |||
| compressionImgLoad.value = true; | |||
| showUploadBtn.value = 3; | |||
| imgUrl.value = URL.createObjectURL(imgData.value); | |||
| let formData = new FormData(); | |||
| const timestamp = Date.now(); | |||
| @@ -461,7 +596,9 @@ async function changeFile() { | |||
| // 保存图片 | |||
| const res2 = await saveImgApi({ material: res.data.url }); | |||
| // 重新获取列表 | |||
| getAiFaceList(1, 100, 1); | |||
| compressionImgLoad.value = false; | |||
| showUploadBtn.value = 4; | |||
| getAiFaceUserList(1, 100, 3); | |||
| if (res2.code == 200) { | |||
| ElMessage.success( | |||
| lanChange.value == "zh-cn" ? `上传成功` : `Upload successfully` | |||
| @@ -661,6 +798,7 @@ async function uploadProgress(videoId, isSaveVideo) { | |||
| } | |||
| // 定义参数 | |||
| const saveOrUpdateData = { | |||
| title: title.value, | |||
| personPhotoId: faceItemActive.value, //任务照片id, | |||
| voiceFrom: tabFrom.value, //:声音来源 1:来自选择模板,2:来自用户上传 | |||
| paperwork: textareaContent.value, //文案 | |||
| @@ -706,6 +844,7 @@ async function saveOrUpdate(index, isSaveVideo) { | |||
| } | |||
| tabFrom.value = index; | |||
| const saveOrUpdateData = { | |||
| title: title.value, | |||
| personPhotoId: faceItemActive.value, //任务照片id, | |||
| voiceFrom: tabFrom.value, //:声音来源 1:来自选择模板,2:来自用户上传 | |||
| paperwork: textareaContent.value, //文案 | |||
| @@ -765,6 +904,7 @@ const t = globalProperties.$t(string); | |||
| onMounted(() => { | |||
| getAiFaceList(1, 100, 1); | |||
| getAiFaceUserList(1, 100, 3); | |||
| voiceTotal(); | |||
| chooseType(); | |||
| }); | |||
| @@ -835,13 +975,34 @@ onMounted(() => { | |||
| width: 100%; | |||
| height: 100%; | |||
| border: 1px solid #000; | |||
| .imgBox { | |||
| position: relative; | |||
| width: 100%; | |||
| height: 100%; | |||
| border-radius: 50%; | |||
| overflow: hidden; | |||
| border: 1px solid #ccc; | |||
| img { | |||
| position: absolute; | |||
| top: 50%; | |||
| left: 50%; | |||
| transform: translate(-50%, -50%); | |||
| max-width: 120%; | |||
| } | |||
| } | |||
| .trueImg { | |||
| position: relative; | |||
| top: 60px; | |||
| width: 100px; | |||
| height: 100px; | |||
| background-color: green; | |||
| border-radius: 50%; | |||
| .icon { | |||
| position: absolute; | |||
| top: 0%; | |||
| right: -5%; | |||
| border: 2px solid #00ff95; | |||
| color: #00ff95; | |||
| } | |||
| } | |||
| .el-upload { | |||
| position: absolute; | |||
| @@ -858,7 +1019,7 @@ onMounted(() => { | |||
| font-size: 50px; | |||
| line-height: 100px; | |||
| text-align: center; | |||
| background-color: pink; | |||
| background-color: #ccc; | |||
| border-radius: 50%; | |||
| } | |||
| @@ -867,7 +1028,7 @@ onMounted(() => { | |||
| // background-color: #795562; | |||
| display: flex; | |||
| justify-content: space-evenly; | |||
| width: 500px; | |||
| width: 700px; | |||
| height: 100px; | |||
| > div { | |||
| @@ -876,6 +1037,13 @@ onMounted(() => { | |||
| height: 100px; | |||
| background-color: #ccc; | |||
| border-radius: 50%; | |||
| .icon { | |||
| position: absolute; | |||
| top: 0%; | |||
| right: -5%; | |||
| border: 2px solid red; | |||
| color: red; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -888,6 +1056,7 @@ onMounted(() => { | |||
| width: 100%; | |||
| height: 100%; | |||
| opacity: 0; | |||
| cursor: pointer; | |||
| } | |||
| .icon { | |||
| @@ -973,6 +1142,20 @@ onMounted(() => { | |||
| width: 100%; | |||
| } | |||
| } | |||
| .deleteIcon { | |||
| position: absolute; | |||
| top: 0; | |||
| right: 15px; | |||
| z-index: 999; | |||
| width: 25px; | |||
| height: 25px; | |||
| border: 2px solid #6d76ef; | |||
| border-radius: 50%; | |||
| color: #6d76ef; | |||
| font-size: 20px; | |||
| line-height: 23px; | |||
| cursor: pointer; | |||
| } | |||
| .uploadImg { | |||
| line-height: 145px; | |||
| font-size: 50px; | |||
| @@ -68,12 +68,17 @@ | |||
| /></el-icon> | |||
| <div class="showBtn" @click.stop="mouseenter(item)">...</div> | |||
| <div class="btns" v-show="showPopup"> | |||
| <div class="btns-item" @click.stop="handleDownload(item.id)"> | |||
| <!-- 下载按钮 --> | |||
| <div | |||
| class="btns-item" | |||
| @click.stop="handleDownload(item.videoPlayUrl)" | |||
| > | |||
| <div class="btns-item-icon"> | |||
| <el-icon class="icon-delete"><i-ep-download /></el-icon> | |||
| </div> | |||
| <div class="text">{{ $t("myCreating.download") }}</div> | |||
| </div> | |||
| <!-- 删除按钮 --> | |||
| <div | |||
| class="btns-item" | |||
| @click.stop="handelDelete(item.id, item.title)" | |||
| @@ -134,16 +139,19 @@ | |||
| }}</span> | |||
| </div> | |||
| </div> | |||
| <!-- 有视频路径的页脚 --> | |||
| <template #footer v-if="presentItem.videoPlayUrl"> | |||
| <span class="dialog-footer"> | |||
| <!-- dialog下载按钮 --> | |||
| <button | |||
| class="elBtn" | |||
| type="primary" | |||
| @click.stop="handleDownload(presentItem.id)" | |||
| @click.stop="handleDownload(presentItem.videoPlayUrl)" | |||
| > | |||
| <el-icon class="icon-delete"><i-ep-download /></el-icon | |||
| >{{ $t("myCreating.download") }} | |||
| </button> | |||
| <!-- dialog删除按钮 --> | |||
| <button | |||
| class="elBtn" | |||
| @click.stop="handelDelete(presentItem.id, presentItem.title)" | |||
| @@ -153,6 +161,15 @@ | |||
| </button> | |||
| </span> | |||
| </template> | |||
| <!-- 无视频路径的页脚 --> | |||
| <template #footer v-if="!presentItem.videoPlayUrl"> | |||
| <span class="dialog-footer"> | |||
| <!-- dialog编辑按钮 --> | |||
| <button class="elBtn" type="primary" @click.stop=""> | |||
| {{ $t("myCreating.edit") }} | |||
| </button> | |||
| </span> | |||
| </template> | |||
| </el-dialog> | |||
| <div v-if="!isLoading" class="pagination"> | |||
| @@ -171,6 +188,7 @@ | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script setup> | |||
| import { userPhotoVideoList, delUserPhotoVideoByID } from "@/apis/myCreating"; | |||
| import { timestampToTime } from "@/utils/tools"; | |||
| @@ -221,8 +239,20 @@ function mouseenter(item) { | |||
| presentItem.value = item; | |||
| showPopup.value = !showPopup.value; | |||
| } | |||
| function handleDownload() {} | |||
| // 下载 | |||
| function handleDownload(itemVideoPlayUrl) { | |||
| if (itemVideoPlayUrl) { | |||
| const link = document.createElement("a"); | |||
| link.href = itemVideoPlayUrl; | |||
| document.body.appendChild(link); | |||
| link.click(); | |||
| document.body.removeChild(link); | |||
| } else { | |||
| ElMessage.error( | |||
| lanChange.value == "zh-cn" ? `无法下载` : `Unable to download` | |||
| ); | |||
| } | |||
| } | |||
| function goCreate() { | |||
| router.push("/createVideo"); | |||
| @@ -300,6 +330,7 @@ function delPhotoVideo(videoID) { | |||
| console.log(res, "res"); | |||
| const msg = lanChange.value == "zh-cn" ? `删除成功!` : `Success !`; | |||
| ElMessage.success(msg); | |||
| showDialog.value = false; | |||
| getPhotoVideoList(pageNum.value, pageSize.value, title.value); | |||
| }) | |||
| .catch((err) => { | |||
| @@ -316,8 +347,8 @@ onMounted(() => { | |||
| <style lang="scss" scoped> | |||
| .page { | |||
| height: 1200px; | |||
| padding: 50px 130px; | |||
| min-height: calc(100vh - 80px); | |||
| padding: 20px 130px; | |||
| background-color: #fff; | |||
| border-radius: 25px 0 0; | |||
| .top { | |||
| @@ -338,8 +369,9 @@ onMounted(() => { | |||
| flex-wrap: wrap; | |||
| justify-content: flex-start; | |||
| width: 100%; | |||
| height: 200px; | |||
| // height: 200px; | |||
| margin-top: 20px; | |||
| padding-bottom: 50px; | |||
| cursor: pointer; | |||
| .workItem { | |||
| position: relative; | |||
| @@ -356,6 +388,7 @@ onMounted(() => { | |||
| height: 170px; | |||
| cursor: pointer; | |||
| padding: 10px; | |||
| overflow: hidden; | |||
| .coverImg { | |||
| max-width: 90%; | |||
| max-height: 170px; | |||
| @@ -541,7 +574,7 @@ onMounted(() => { | |||
| } | |||
| .pagination { | |||
| position: fixed; | |||
| bottom: 50px; | |||
| bottom: 20px; | |||
| left: 50%; | |||
| transform: translateX(-50%); | |||
| background-color: #ffffff; | |||
| @@ -583,7 +616,8 @@ onMounted(() => { | |||
| padding: 10px 40px 30px 40px; | |||
| video { | |||
| width: 100%; | |||
| max-width: 100%; | |||
| max-height: 600px; | |||
| margin: auto; | |||
| } | |||
| .insteadVideo { | |||