| @@ -20,6 +20,7 @@ | |||
| "axios": "^1.3.4", | |||
| "echarts": "^5.2.2", | |||
| "element-plus": "^2.3.1", | |||
| "js-audio-recorder": "^1.0.7", | |||
| "nprogress": "^0.2.0", | |||
| "path-browserify": "^1.0.1", | |||
| "path-to-regexp": "^6.2.0", | |||
| @@ -0,0 +1,22 @@ | |||
| import request from "@/utils/request.js"; | |||
| /** | |||
| * @description 上传图片 | |||
| * @params ticket | |||
| * @returns data | |||
| */ | |||
| export function awsImgUpload(formData) { | |||
| return request({ | |||
| url: `/api/upload/awsImgUpload`, | |||
| method: 'post', | |||
| data: formData | |||
| }) | |||
| } | |||
| export function awsMp3Upload(formData) { | |||
| return request({ | |||
| url: `/api/upload/awsImgUpload`, | |||
| method: 'post', | |||
| data: formData | |||
| }) | |||
| } | |||
| @@ -40,7 +40,8 @@ export default { | |||
| languages: "Languages", | |||
| voices: "Voices", | |||
| styles: "Styles", | |||
| reload: "Reload", | |||
| reselect: "reselect", | |||
| sureUpload:'Confirm upload', | |||
| generateVideo: "Generate Video", | |||
| }, | |||
| @@ -32,7 +32,7 @@ export default { | |||
| createVideo: { | |||
| title: "创作视频", | |||
| typeScript: "输入文案", | |||
| uploadAudio: "上传视频", | |||
| uploadAudio: "上传音频", | |||
| aiScript: "AI生成文本", | |||
| typeTitle: "输入标题", | |||
| confirm: "确定", | |||
| @@ -40,7 +40,8 @@ export default { | |||
| languages: "选择语言", | |||
| voices: "选择声音", | |||
| styles: "选择风格", | |||
| reload: "重新上传", | |||
| reselect: "重新选择", | |||
| sureUpload:'确认上传', | |||
| generateVideo: "生成视频", | |||
| }, | |||
| @@ -55,6 +55,7 @@ const logo = ref(new URL(`../../../assets/logo.png`, import.meta.url).href); | |||
| // https://cn.vuejs.org/guide/built-ins/transition.html#the-transition-component | |||
| .left-top-box { | |||
| height: 80px; | |||
| background-color: #000; | |||
| } | |||
| @@ -76,7 +76,7 @@ function routerTo(url: RouteLocationRaw) { | |||
| <style lang="scss" scoped> | |||
| .myRouter { | |||
| position: relative; | |||
| height: calc(100% - 50px); | |||
| height: calc(100% - 80px); | |||
| padding: 200px 20px 0; | |||
| color: #fff; | |||
| @@ -18,7 +18,7 @@ | |||
| <!--主页面--> | |||
| <app-main /> | |||
| <!-- 设置面板 -->- | |||
| <!-- 设置面板 --> | |||
| <RightPanel v-if="showSettings"> | |||
| <settings /> | |||
| </RightPanel> | |||
| @@ -21,6 +21,7 @@ declare module '@vue/runtime-core' { | |||
| ElIcon: typeof import('element-plus/es')['ElIcon'] | |||
| ElInput: typeof import('element-plus/es')['ElInput'] | |||
| ElOption: typeof import('element-plus/es')['ElOption'] | |||
| ElProgress: typeof import('element-plus/es')['ElProgress'] | |||
| ElRow: typeof import('element-plus/es')['ElRow'] | |||
| ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] | |||
| ElSelect: typeof import('element-plus/es')['ElSelect'] | |||
| @@ -28,6 +29,7 @@ declare module '@vue/runtime-core' { | |||
| ElTabPane: typeof import('element-plus/es')['ElTabPane'] | |||
| ElTabs: typeof import('element-plus/es')['ElTabs'] | |||
| ElTooltip: typeof import('element-plus/es')['ElTooltip'] | |||
| ElUpload: typeof import('element-plus/es')['ElUpload'] | |||
| GithubCorner: typeof import('./../components/GithubCorner/index.vue')['default'] | |||
| Hamburger: typeof import('./../components/Hamburger/index.vue')['default'] | |||
| IconSelect: typeof import('./../components/IconSelect/index.vue')['default'] | |||
| @@ -0,0 +1,510 @@ | |||
| <template> | |||
| <div class="page flex justify-around"> | |||
| <!-- 左侧栏 --> | |||
| <div class="left"> | |||
| <!-- 顶部 --> | |||
| <!-- 有图片时 --> | |||
| <div class="left-top" v-show="imgUrl"> | |||
| <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> | |||
| <!-- 上传按钮 --> | |||
| <button class="uploadBtn"> | |||
| {{ $t("createVideo.reload") }} | |||
| <input type="file" @change="changeFile" /> | |||
| </button> | |||
| </el-row> | |||
| <!-- 中间展示大图 --> | |||
| <div class="bigImg inmiddle"> | |||
| <img :src="imgUrl" alt="" class="inmiddle" /> | |||
| </div> | |||
| </div> | |||
| <!-- 没有图片时 --> | |||
| <div class="left-top" v-show="!imgUrl"> | |||
| <!-- 中间展示大图 --> | |||
| <div class="noImg"> | |||
| <div class="trueImg inmiddle"> | |||
| <div class="icon">✔</div> | |||
| </div> | |||
| <div class="upload inmiddle"> | |||
| + | |||
| <input type="file" @change="changeFile" /> | |||
| </div> | |||
| <div 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="ai"> | |||
| <div class="aiAvatar"></div> | |||
| <div style="margin-left: 20px">Generate By Ai</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> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- 右侧栏 --> | |||
| <div class="right"> | |||
| <el-tabs type="border-card" class="tabs"> | |||
| <!-- ----------------------------- 1 --> | |||
| <el-tab-pane :label="$t('createVideo.typeScript')"> | |||
| <div class="card1">111111111111111111</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> | |||
| <audio v-if="audioUrl" controls :src="audioUrl"></audio> | |||
| <button v-if="audioUrl" @click="uploadAudio">上传音频</button> | |||
| </div> | |||
| <!-- 上传视频 --> | |||
| <div class="uploadVideo"> | |||
| <input type="file" ref="fileInput" @change="handleFileChange" /> | |||
| <video | |||
| v-if="videoSrc" | |||
| :src="videoSrc" | |||
| ref="videoPlayer" | |||
| controls | |||
| ></video> | |||
| </div> | |||
| </div> | |||
| </el-tab-pane> | |||
| <!-- ----------------------- 3 --> | |||
| <el-tab-pane :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 { ref } from "vue"; | |||
| import { getCurrentInstance } from "vue"; | |||
| import Recorder from "js-audio-recorder"; | |||
| // 标题 | |||
| let title = ref(""); | |||
| // 上传图片 | |||
| let imgUrl = ref(""); | |||
| function changeFile(e) { | |||
| imgUrl.value = URL.createObjectURL(e.target.files[0]); | |||
| } | |||
| // $t在script中的使用 | |||
| const { | |||
| appContext: { | |||
| config: { globalProperties }, | |||
| }, | |||
| } = getCurrentInstance(); | |||
| 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 audioUrl = ref(null); | |||
| const startRecording = async () => { | |||
| if (!isRecording.value) { | |||
| try { | |||
| await recorder.start(); | |||
| console.log("Recording started"); | |||
| isRecording.value = true; | |||
| } 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); | |||
| } | |||
| } | |||
| }; | |||
| const playRecording = () => { | |||
| if (audioUrl.value) { | |||
| const audio = new Audio(audioUrl.value); | |||
| audio.play(); | |||
| } | |||
| }; | |||
| const uploadAudio = () => {}; | |||
| // 上传视频功能 | |||
| 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; | |||
| } | |||
| }; | |||
| // card3的 | |||
| let language = ref(""); | |||
| let languageOptions = reactive([ | |||
| { | |||
| value: "Option1", | |||
| label: "Option1", | |||
| }, | |||
| { | |||
| value: "Option2", | |||
| label: "Option2", | |||
| }, | |||
| ]); | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .page { | |||
| // height: calc(100vh - 84px); | |||
| height: 887px; | |||
| padding: 20px 0 0 40px; | |||
| background-color: #fff; | |||
| border-radius: 25px 0 0; | |||
| } | |||
| .left { | |||
| width: 70%; | |||
| height: 100%; | |||
| :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%; | |||
| .uploadBtn { | |||
| position: relative; | |||
| } | |||
| .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%; | |||
| } | |||
| .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; | |||
| .ai { | |||
| width: 100%; | |||
| .aiAvatar { | |||
| width: 100px; | |||
| height: 100px; | |||
| margin-left: 25px; | |||
| background-color: pink; | |||
| border-radius: 50%; | |||
| } | |||
| } | |||
| .faceList { | |||
| display: flex; | |||
| flex-wrap: wrap; | |||
| justify-content: flex-start; | |||
| width: 100%; | |||
| height: 130px; | |||
| .faceItem { | |||
| width: 150px; | |||
| height: 150px; | |||
| padding: 0 25px; | |||
| line-height: 30px; | |||
| text-align: center; | |||
| p { | |||
| height: 30px; | |||
| } | |||
| .img { | |||
| width: 100px; | |||
| height: 100px; | |||
| background-color: green; | |||
| border-radius: 50%; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .right { | |||
| width: 25%; | |||
| height: 100%; | |||
| background-color: green; | |||
| :deep(.el-tabs--border-card) { | |||
| font-size: 20px; | |||
| border: none; | |||
| } | |||
| .tabs { | |||
| height: 100%; | |||
| .card2 { | |||
| padding: 10px; | |||
| > div { | |||
| position: relative; | |||
| margin-top: 10px; | |||
| width: 100%; | |||
| height: 100px; | |||
| border-radius: 10px; | |||
| background-color: red; | |||
| overflow: hidden; | |||
| transition: all 0.3s; | |||
| &:hover { | |||
| transform: translateY(-10px); | |||
| box-shadow: #00000026 2px 2px 2px 2px; | |||
| } | |||
| } | |||
| .uploadAudio { | |||
| margin-bottom: 20px; | |||
| } | |||
| .uploadVideo { | |||
| input { | |||
| position: absolute; | |||
| width: 100%; | |||
| height: 100%; | |||
| opacity: 0; | |||
| } | |||
| video { | |||
| width: 100%; | |||
| height: 100%; | |||
| } | |||
| } | |||
| } | |||
| .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> | |||
| @@ -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> | |||
| @@ -51,10 +66,15 @@ | |||
| <div style="margin-left: 20px">Generate By Ai</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="faceItem pointer" | |||
| @click="setImgUrl(item.material)" | |||
| v-for="item in AiFace" | |||
| :key="item.id" | |||
| > | |||
| <!-- <div class="img"></div> --> | |||
| <img :src="item.material" alt="" /> | |||
| <p>{{ item.title }}</p> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -64,19 +84,117 @@ | |||
| <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"> | |||
| <div class="uploadVideo"> | |||
| <!-- 录制MP3上传语音 --> | |||
| <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> | |||
| <!-- 上传视频 --> | |||
| <div | |||
| class="uploadVideo" | |||
| style="text-align: center; line-height: 70px" | |||
| > | |||
| 上传视频 | |||
| <input type="file" ref="fileInput" @change="handleFileChange" /> | |||
| <video ref="videoPlayer" controls></video> | |||
| <video | |||
| v-if="videoSrc" | |||
| :src="videoSrc" | |||
| ref="videoPlayer" | |||
| controls | |||
| ></video> | |||
| </div> | |||
| </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 | |||
| @@ -147,27 +265,115 @@ | |||
| <script setup> | |||
| import { ref } from "vue"; | |||
| import { getCurrentInstance } from "vue"; | |||
| import Recorder from "js-audio-recorder"; | |||
| import axios from "axios"; | |||
| import { awsImgUpload, awsMp3Upload } from "@/apis/createVideo"; | |||
| import { ElMessage } from "element-plus"; | |||
| // 获取AI头像 | |||
| const AiFace = ref([]); | |||
| function getAiFace() { | |||
| axios({ | |||
| url: "https://photo.metavatar.cc/C//api/personPhoto/list?pageNum=1&pageSize=1", | |||
| method: "get", | |||
| }).then((res) => { | |||
| AiFace.value = res.data.data.list; | |||
| console.log(res.data.data.list); | |||
| }); | |||
| } | |||
| getAiFace(); | |||
| // 标题 | |||
| let title = ref(""); | |||
| // 上传图片 | |||
| let imgUrl = ref(""); | |||
| function changeFile(e) { | |||
| const imgData = ref(""); | |||
| // 预览图片 | |||
| 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); | |||
| ElMessage.success("上传成功!"); | |||
| } catch (error) { | |||
| ElMessage.error(error); | |||
| } | |||
| } | |||
| // $t在script中的使用 | |||
| // 点击aifece | |||
| function setImgUrl(url) { | |||
| imgUrl.value = url; | |||
| } | |||
| const { | |||
| appContext: { | |||
| config: { globalProperties }, | |||
| }, | |||
| } = getCurrentInstance(); | |||
| // 录制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 string = "createVideo.typeScript"; | |||
| const t = globalProperties.$t(string); | |||
| 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 awsMp3Upload(formData); | |||
| ElMessage.success("上传成功!"); | |||
| } catch (error) { | |||
| ElMessage.error(error); | |||
| } | |||
| } | |||
| // 上传视频功能 | |||
| 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; | |||
| } | |||
| }; | |||
| // card3的 | |||
| let language = reactive(""); | |||
| let language = ref(""); | |||
| let languageOptions = reactive([ | |||
| { | |||
| @@ -179,6 +385,16 @@ let languageOptions = reactive([ | |||
| label: "Option2", | |||
| }, | |||
| ]); | |||
| // $t在script中的使用 | |||
| const { | |||
| appContext: { | |||
| config: { globalProperties }, | |||
| }, | |||
| } = getCurrentInstance(); | |||
| const string = "createVideo.typeScript"; | |||
| const t = globalProperties.$t(string); | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| @@ -216,6 +432,14 @@ let languageOptions = reactive([ | |||
| .uploadBtn { | |||
| position: relative; | |||
| padding: 5px 10px; | |||
| margin-left: 10px; | |||
| border-radius: 5px; | |||
| transition: all 0.3s; | |||
| &:hover { | |||
| background-color: #000; | |||
| color: #fff; | |||
| } | |||
| } | |||
| .bigImg { | |||
| @@ -245,7 +469,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; | |||
| @@ -333,11 +564,13 @@ let languageOptions = reactive([ | |||
| p { | |||
| height: 30px; | |||
| } | |||
| img { | |||
| width: 100%; | |||
| } | |||
| .img { | |||
| width: 100px; | |||
| height: 100px; | |||
| background-color: green; | |||
| // background-color: green; | |||
| border-radius: 50%; | |||
| } | |||
| } | |||
| @@ -357,21 +590,90 @@ 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; | |||
| .uploadVideo { | |||
| > 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); | |||
| } | |||
| } | |||
| .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%; | |||
| height: 100%; | |||
| opacity: 0; | |||
| } | |||
| video { | |||
| width: 100%; | |||
| height: 100%; | |||
| } | |||
| } | |||
| } | |||
| .card3 { | |||
| @@ -96,5 +96,8 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => { | |||
| symbolId: "icon-[dir]-[name]", | |||
| }), | |||
| ], | |||
| define: { | |||
| global:{} | |||
| } | |||
| }; | |||
| }); | |||
| @@ -3722,6 +3722,11 @@ jiti@^1.16.0, jiti@^1.18.2: | |||
| resolved "https://registry.npmmirror.com/jiti/-/jiti-1.18.2.tgz" | |||
| integrity sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg== | |||
| js-audio-recorder@^1.0.7: | |||
| version "1.0.7" | |||
| resolved "https://registry.yarnpkg.com/js-audio-recorder/-/js-audio-recorder-1.0.7.tgz#01501cb9eb143d57994b42bd5ae97974fda18908" | |||
| integrity sha512-JiDODCElVHGrFyjGYwYyNi7zCbKk9va9C77w+zCPMmi4C6ix7zsX2h3ddHugmo4dOTOTCym9++b/wVW9nC0IaA== | |||
| js-base64@^2.1.9: | |||
| version "2.6.4" | |||
| resolved "https://registry.npmmirror.com/js-base64/-/js-base64-2.6.4.tgz" | |||