@@ -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" | |||