| @@ -20,6 +20,7 @@ | |||||
| "axios": "^1.3.4", | "axios": "^1.3.4", | ||||
| "echarts": "^5.2.2", | "echarts": "^5.2.2", | ||||
| "element-plus": "^2.3.1", | "element-plus": "^2.3.1", | ||||
| "js-audio-recorder": "^1.0.7", | |||||
| "nprogress": "^0.2.0", | "nprogress": "^0.2.0", | ||||
| "path-browserify": "^1.0.1", | "path-browserify": "^1.0.1", | ||||
| "path-to-regexp": "^6.2.0", | "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", | languages: "Languages", | ||||
| voices: "Voices", | voices: "Voices", | ||||
| styles: "Styles", | styles: "Styles", | ||||
| reload: "Reload", | |||||
| reselect: "reselect", | |||||
| sureUpload:'Confirm upload', | |||||
| generateVideo: "Generate Video", | generateVideo: "Generate Video", | ||||
| }, | }, | ||||
| @@ -32,7 +32,7 @@ export default { | |||||
| createVideo: { | createVideo: { | ||||
| title: "创作视频", | title: "创作视频", | ||||
| typeScript: "输入文案", | typeScript: "输入文案", | ||||
| uploadAudio: "上传视频", | |||||
| uploadAudio: "上传音频", | |||||
| aiScript: "AI生成文本", | aiScript: "AI生成文本", | ||||
| typeTitle: "输入标题", | typeTitle: "输入标题", | ||||
| confirm: "确定", | confirm: "确定", | ||||
| @@ -40,7 +40,8 @@ export default { | |||||
| languages: "选择语言", | languages: "选择语言", | ||||
| voices: "选择声音", | voices: "选择声音", | ||||
| styles: "选择风格", | styles: "选择风格", | ||||
| reload: "重新上传", | |||||
| reselect: "重新选择", | |||||
| sureUpload:'确认上传', | |||||
| generateVideo: "生成视频", | 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 | // https://cn.vuejs.org/guide/built-ins/transition.html#the-transition-component | ||||
| .left-top-box { | .left-top-box { | ||||
| height: 80px; | |||||
| background-color: #000; | background-color: #000; | ||||
| } | } | ||||
| @@ -76,7 +76,7 @@ function routerTo(url: RouteLocationRaw) { | |||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||
| .myRouter { | .myRouter { | ||||
| position: relative; | position: relative; | ||||
| height: calc(100% - 50px); | |||||
| height: calc(100% - 80px); | |||||
| padding: 200px 20px 0; | padding: 200px 20px 0; | ||||
| color: #fff; | color: #fff; | ||||
| @@ -18,7 +18,7 @@ | |||||
| <!--主页面--> | <!--主页面--> | ||||
| <app-main /> | <app-main /> | ||||
| <!-- 设置面板 -->- | |||||
| <!-- 设置面板 --> | |||||
| <RightPanel v-if="showSettings"> | <RightPanel v-if="showSettings"> | ||||
| <settings /> | <settings /> | ||||
| </RightPanel> | </RightPanel> | ||||
| @@ -21,6 +21,7 @@ declare module '@vue/runtime-core' { | |||||
| ElIcon: typeof import('element-plus/es')['ElIcon'] | ElIcon: typeof import('element-plus/es')['ElIcon'] | ||||
| ElInput: typeof import('element-plus/es')['ElInput'] | ElInput: typeof import('element-plus/es')['ElInput'] | ||||
| ElOption: typeof import('element-plus/es')['ElOption'] | ElOption: typeof import('element-plus/es')['ElOption'] | ||||
| ElProgress: typeof import('element-plus/es')['ElProgress'] | |||||
| ElRow: typeof import('element-plus/es')['ElRow'] | ElRow: typeof import('element-plus/es')['ElRow'] | ||||
| ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] | ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] | ||||
| ElSelect: typeof import('element-plus/es')['ElSelect'] | ElSelect: typeof import('element-plus/es')['ElSelect'] | ||||
| @@ -28,6 +29,7 @@ declare module '@vue/runtime-core' { | |||||
| ElTabPane: typeof import('element-plus/es')['ElTabPane'] | ElTabPane: typeof import('element-plus/es')['ElTabPane'] | ||||
| ElTabs: typeof import('element-plus/es')['ElTabs'] | ElTabs: typeof import('element-plus/es')['ElTabs'] | ||||
| ElTooltip: typeof import('element-plus/es')['ElTooltip'] | ElTooltip: typeof import('element-plus/es')['ElTooltip'] | ||||
| ElUpload: typeof import('element-plus/es')['ElUpload'] | |||||
| GithubCorner: typeof import('./../components/GithubCorner/index.vue')['default'] | GithubCorner: typeof import('./../components/GithubCorner/index.vue')['default'] | ||||
| Hamburger: typeof import('./../components/Hamburger/index.vue')['default'] | Hamburger: typeof import('./../components/Hamburger/index.vue')['default'] | ||||
| IconSelect: typeof import('./../components/IconSelect/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> | <el-icon class="icon-edit"><i-ep-edit /></el-icon> | ||||
| </div> | </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> | </el-row> | ||||
| <!-- 中间展示大图 --> | <!-- 中间展示大图 --> | ||||
| <div class="bigImg inmiddle"> | <div class="bigImg inmiddle"> | ||||
| @@ -34,7 +39,17 @@ | |||||
| </div> | </div> | ||||
| <div class="upload inmiddle"> | <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> | ||||
| <div class="falseImg inmiddle"> | <div class="falseImg inmiddle"> | ||||
| <div><div class="icon">×</div></div> | <div><div class="icon">×</div></div> | ||||
| @@ -51,10 +66,15 @@ | |||||
| <div style="margin-left: 20px">Generate By Ai</div> | <div style="margin-left: 20px">Generate By Ai</div> | ||||
| </div> | </div> | ||||
| <div class="faceList scrolly"> | <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> | </div> | ||||
| </div> | </div> | ||||
| @@ -64,19 +84,117 @@ | |||||
| <el-tabs type="border-card" class="tabs"> | <el-tabs type="border-card" class="tabs"> | ||||
| <!-- ----------------------------- 1 --> | <!-- ----------------------------- 1 --> | ||||
| <el-tab-pane :label="$t('createVideo.typeScript')"> | <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> | </el-tab-pane> | ||||
| <!-- ------------------------ 2 --> | <!-- ------------------------ 2 --> | ||||
| <el-tab-pane :label="$t('createVideo.uploadAudio')"> | <el-tab-pane :label="$t('createVideo.uploadAudio')"> | ||||
| <div class="card2"> | <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" /> | <input type="file" ref="fileInput" @change="handleFileChange" /> | ||||
| <video ref="videoPlayer" controls></video> | |||||
| <video | |||||
| v-if="videoSrc" | |||||
| :src="videoSrc" | |||||
| ref="videoPlayer" | |||||
| controls | |||||
| ></video> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </el-tab-pane> | </el-tab-pane> | ||||
| <!-- ----------------------- 3 --> | <!-- ----------------------- 3 --> | ||||
| <el-tab-pane :label="$t('createVideo.aiScript')"> | |||||
| <el-tab-pane v-if="false" :label="$t('createVideo.aiScript')"> | |||||
| <div class="card3"> | <div class="card3"> | ||||
| <div class="text"> | <div class="text"> | ||||
| <el-input | <el-input | ||||
| @@ -147,27 +265,115 @@ | |||||
| <script setup> | <script setup> | ||||
| import { ref } from "vue"; | import { ref } from "vue"; | ||||
| import { getCurrentInstance } 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 title = ref(""); | ||||
| // 上传图片 | // 上传图片 | ||||
| let imgUrl = ref(""); | let imgUrl = ref(""); | ||||
| function changeFile(e) { | |||||
| const imgData = ref(""); | |||||
| // 预览图片 | |||||
| function lookImg(e) { | |||||
| imgUrl.value = URL.createObjectURL(e.target.files[0]); | 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的 | // card3的 | ||||
| let language = reactive(""); | |||||
| let language = ref(""); | |||||
| let languageOptions = reactive([ | let languageOptions = reactive([ | ||||
| { | { | ||||
| @@ -179,6 +385,16 @@ let languageOptions = reactive([ | |||||
| label: "Option2", | label: "Option2", | ||||
| }, | }, | ||||
| ]); | ]); | ||||
| // $t在script中的使用 | |||||
| const { | |||||
| appContext: { | |||||
| config: { globalProperties }, | |||||
| }, | |||||
| } = getCurrentInstance(); | |||||
| const string = "createVideo.typeScript"; | |||||
| const t = globalProperties.$t(string); | |||||
| </script> | </script> | ||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||
| @@ -216,6 +432,14 @@ let languageOptions = reactive([ | |||||
| .uploadBtn { | .uploadBtn { | ||||
| position: relative; | position: relative; | ||||
| padding: 5px 10px; | |||||
| margin-left: 10px; | |||||
| border-radius: 5px; | |||||
| transition: all 0.3s; | |||||
| &:hover { | |||||
| background-color: #000; | |||||
| color: #fff; | |||||
| } | |||||
| } | } | ||||
| .bigImg { | .bigImg { | ||||
| @@ -245,7 +469,14 @@ let languageOptions = reactive([ | |||||
| background-color: green; | background-color: green; | ||||
| border-radius: 50%; | border-radius: 50%; | ||||
| } | } | ||||
| .el-upload { | |||||
| position: absolute; | |||||
| top: 0; | |||||
| left: 0; | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| opacity: 0; | |||||
| } | |||||
| .upload { | .upload { | ||||
| top: 200px; | top: 200px; | ||||
| width: 100px; | width: 100px; | ||||
| @@ -333,11 +564,13 @@ let languageOptions = reactive([ | |||||
| p { | p { | ||||
| height: 30px; | height: 30px; | ||||
| } | } | ||||
| img { | |||||
| width: 100%; | |||||
| } | |||||
| .img { | .img { | ||||
| width: 100px; | width: 100px; | ||||
| height: 100px; | height: 100px; | ||||
| background-color: green; | |||||
| // background-color: green; | |||||
| border-radius: 50%; | border-radius: 50%; | ||||
| } | } | ||||
| } | } | ||||
| @@ -357,21 +590,90 @@ let languageOptions = reactive([ | |||||
| .tabs { | .tabs { | ||||
| height: 100%; | 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 { | .card2 { | ||||
| padding: 10px; | padding: 10px; | ||||
| .uploadVideo { | |||||
| > div { | |||||
| position: relative; | position: relative; | ||||
| margin-top: 10px; | margin-top: 10px; | ||||
| width: 100%; | width: 100%; | ||||
| height: 100px; | |||||
| border: #000 1px solid; | |||||
| border-radius: 10px; | 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 { | input { | ||||
| position: absolute; | position: absolute; | ||||
| width: 100%; | width: 100%; | ||||
| height: 100%; | height: 100%; | ||||
| opacity: 0; | opacity: 0; | ||||
| } | } | ||||
| video { | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| .card3 { | .card3 { | ||||
| @@ -96,5 +96,8 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => { | |||||
| symbolId: "icon-[dir]-[name]", | 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" | resolved "https://registry.npmmirror.com/jiti/-/jiti-1.18.2.tgz" | ||||
| integrity sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg== | 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: | js-base64@^2.1.9: | ||||
| version "2.6.4" | version "2.6.4" | ||||
| resolved "https://registry.npmmirror.com/js-base64/-/js-base64-2.6.4.tgz" | resolved "https://registry.npmmirror.com/js-base64/-/js-base64-2.6.4.tgz" | ||||