@@ -48,4 +48,29 @@ export function useCodeUserApi() { | |||
url: `api/user/getUseCodeUser`, | |||
method: 'get' | |||
}) | |||
} | |||
/** | |||
* @description 修改用户名和头像 | |||
* @params | |||
* @returns | |||
*/ | |||
export function updUserInfoApi(data) { | |||
return request({ | |||
url: `api/user/updUserInfo`, | |||
method: 'post', | |||
data | |||
}) | |||
} | |||
/** | |||
* @description 获取用户信息 | |||
* @params | |||
* @returns | |||
*/ | |||
export function getUserInfoApi() { | |||
return request({ | |||
url: `api/user/getUserInfo`, | |||
method: 'get', | |||
}) | |||
} |
@@ -0,0 +1,51 @@ | |||
<template> | |||
<div class="loadingIcon"> | |||
<div> | |||
<el-icon class="is-loading"> | |||
<svg | |||
viewBox="0 0 1024 1024" | |||
xmlns="http://www.w3.org/2000/svg" | |||
data-v-ea893728="" | |||
> | |||
<path | |||
fill="currentColor" | |||
d="M512 64a32 32 0 0 1 32 32v192a32 32 0 0 1-64 0V96a32 32 0 0 1 32-32zm0 640a32 32 0 0 1 32 32v192a32 32 0 1 1-64 0V736a32 32 0 0 1 32-32zm448-192a32 32 0 0 1-32 32H736a32 32 0 1 1 0-64h192a32 32 0 0 1 32 32zm-640 0a32 32 0 0 1-32 32H96a32 32 0 0 1 0-64h192a32 32 0 0 1 32 32zM195.2 195.2a32 32 0 0 1 45.248 0L376.32 331.008a32 32 0 0 1-45.248 45.248L195.2 240.448a32 32 0 0 1 0-45.248zm452.544 452.544a32 32 0 0 1 45.248 0L828.8 783.552a32 32 0 0 1-45.248 45.248L647.744 692.992a32 32 0 0 1 0-45.248zM828.8 195.264a32 32 0 0 1 0 45.184L692.992 376.32a32 32 0 0 1-45.248-45.248l135.808-135.808a32 32 0 0 1 45.248 0zm-452.544 452.48a32 32 0 0 1 0 45.248L240.448 828.8a32 32 0 0 1-45.248-45.248l135.808-135.808a32 32 0 0 1 45.248 0z" | |||
></path> | |||
</svg> | |||
</el-icon> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { useI18n } from "vue-i18n"; | |||
const { locale } = useI18n(); | |||
const lanChange = ref(locale); | |||
const props = defineProps({ | |||
isLoading: { | |||
required: false, //是否必传 | |||
}, | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.loadingIcon { | |||
position: relative; | |||
width: 100%; | |||
height: 80px; | |||
line-height: 80px; | |||
text-align: center; | |||
transform: scale(2); | |||
> div { | |||
position: absolute; | |||
top: 0; | |||
left: 50%; | |||
transform: translateX(-50%); | |||
} | |||
.loadingText { | |||
position: relative; | |||
top: -2px; | |||
left: 4px; | |||
} | |||
} | |||
</style> |
@@ -4,30 +4,36 @@ export default { | |||
dashboard: "Dashboard", | |||
document: "Document", | |||
}, | |||
// 公用 | |||
public:{ | |||
cancel:'Cancel', | |||
confirm:'Confirm', | |||
username:'Username', | |||
avatar:'Avatar', | |||
}, | |||
// 登录页面国际化 | |||
login: { | |||
title: "Metavatar-PC", | |||
username: "Username", | |||
email: "E-mail", | |||
password: "Password", | |||
confirmPassword: "Confirm Password", | |||
confirmPassword: "Confirm password", | |||
login: "Sign in", | |||
register: "Sign up", | |||
loginBtn: "Sign in", | |||
registerBtn: "Sign up", | |||
verifyCode: "Verify Code", | |||
verifyCode: "Verify code", | |||
signIn: "Already got an account? Sign in here", | |||
signUp: "Don't have an account? Sign up here", | |||
forgetPwd: "I forgot my password", | |||
forgetPwdTitle:'forgot password', | |||
forgetPwdTitle:'Forgot password', | |||
SendEmail:'Send an email', | |||
resetPassword:'Reset password', | |||
setNewPwd:'Set new password', | |||
newPwd:'new password', | |||
oldPwd:'old password', | |||
confirmNewPwd:'confirm new password', | |||
confirm:'confirm', | |||
newPwd:'New password', | |||
oldPwd:'Old password', | |||
confirmNewPwd:'Confirm new password', | |||
confirm:'Confirm', | |||
gohome:'Back to home page' | |||
}, | |||
@@ -40,26 +46,29 @@ export default { | |||
// 创作视频 | |||
createVideo: { | |||
title: "CreateVideo", | |||
availableMould:"Available Moulds", | |||
title: "Create Video", | |||
availableMould:"Available moulds", | |||
personalWarehouse:'Personal warehouse', | |||
typeScript: "Type your Script", | |||
uploadAudio: "Upload Audio", | |||
typeScript: "Type your script", | |||
uploadAudio: "Upload audio", | |||
recordedAudio:'Recorded audio', | |||
startRecorded:'Start recording', | |||
stopRecorded:'Stop recording', | |||
aiScript: "Ai Script", | |||
aiScript: "Ai script", | |||
typeTitle: "Type a title", | |||
confirm: "Confirm", | |||
aiText: "Please input here", | |||
languages: "Languages", | |||
voices: "Voices", | |||
styles: "Styles", | |||
reselect: "reselect", | |||
reselect: "Reselect", | |||
sureUpload: "Confirm upload", | |||
generateVideo: "Generate Video", | |||
saveVideo:'save manuscript', | |||
generateVideo: "Generate video", | |||
saveVideo:'Save manuscript', | |||
select:'Please select', | |||
music:'Music', | |||
Aipicture:'Ai-generated pictures', | |||
AipictureTip:'Please customize your picture', | |||
}, | |||
// 我的视频 | |||
@@ -68,28 +77,29 @@ export default { | |||
search: "Search...", | |||
name: "Name", | |||
date: "Date", | |||
videoTitle: "Video Title:", | |||
videoTitle: "Video title:", | |||
download: "Download", | |||
delete: "Delete", | |||
more: "More", | |||
loading: "Loading , just a moment", | |||
noList: "Nothing here. How about making one ?", | |||
goCreate: "Go Create !", | |||
manuscript: "manuscript", | |||
goCreate: "Go create !", | |||
manuscript: "Manuscript", | |||
generating: "Generating", | |||
generationFailed: "Failed", | |||
generationSucceed: "Succeed", | |||
manuscriptText: "manuscript", | |||
generatingText: "The video is generating, please wait a moment", | |||
manuscriptText: "Manuscript", | |||
generatingText: "The video is generating, Please wait a moment", | |||
generationFailedText: "The video generate is failed, Reason:", | |||
edit:'edit' | |||
edit:'Edit' | |||
}, | |||
// 个人账户 | |||
account: { | |||
title: "My Account", | |||
reSetPwd: "Reset Password", | |||
until: "until", | |||
reSetPwd: "Reset password", | |||
editInfo:'Edit personal information', | |||
until: "Until", | |||
getMore: "UPGRADE", | |||
copyCode: "Copy invitation code", | |||
saveCode: "Save QR code", | |||
@@ -102,7 +112,7 @@ export default { | |||
failed: "Failed", | |||
msg1: "Obtain one video production time", | |||
inputCode:'Please enter the invitation code', | |||
submit:'submit', | |||
submit:'Submit', | |||
noListOrder: "没有数据哦,快去邀请更多人加入我们吧~", | |||
noListInvite: "No data, please invite more people to join us", | |||
}, | |||
@@ -128,7 +138,7 @@ export default { | |||
expired: | |||
"Your ticket is expired. You can click the button to resend new email then", | |||
sendSuccess: "The email has been resent, you can check it now", | |||
goLogin: "Login Now", | |||
goLogin: "Login now", | |||
reSend: "Resend", | |||
}, | |||
}; |
@@ -4,7 +4,13 @@ export default { | |||
dashboard: "首页", | |||
document: "项目文档", | |||
}, | |||
// 公用 | |||
public:{ | |||
cancel:'取消', | |||
confirm:'确定', | |||
username:'用户名', | |||
avatar:'头像', | |||
}, | |||
// 登录页面国际化 | |||
login: { | |||
title: "Metavatar-PC", | |||
@@ -60,6 +66,9 @@ export default { | |||
generateVideo: "生成视频", | |||
saveVideo:'保存草稿', | |||
select:'请选择', | |||
music:'音乐', | |||
Aipicture:'Ai生成图片', | |||
AipictureTip:'请自定义您的图片', | |||
}, | |||
// 我的视频 | |||
@@ -89,6 +98,7 @@ export default { | |||
account: { | |||
title: "个人账户", | |||
reSetPwd: "修改密码", | |||
editInfo:'编辑个人信息', | |||
until: "有效期至", | |||
getMore: "升级套餐", | |||
copyCode: "复制邀请码", | |||
@@ -16,7 +16,13 @@ | |||
<!--语言选择--> | |||
<lang-select class="navItem" style="color: #ffffff" /> | |||
<!-- <div class="avatar" v-if="AccessToken" @click="routerTo"></div> --> | |||
<div | |||
class="avatar" | |||
v-if="userInfoPinia.userInfo.avatarUrl" | |||
@click="routerTo" | |||
> | |||
<img :src="userInfoPinia.userInfo.avatarUrl" alt="" /> | |||
</div> | |||
<div class="navItem" v-if="AccessToken" @click="routerTo"> | |||
{{ $t("navbar.account") }} | |||
</div> | |||
@@ -43,7 +49,8 @@ import { useTagsViewStore } from "@/store/modules/tagsView"; | |||
import { useUserStore } from "@/store/modules/user"; | |||
import { useI18n } from "vue-i18n"; | |||
import LangSelect from "@/components/LangSelect/index.vue"; | |||
import { userInfoModules } from "@/store/modules/userInfo"; | |||
const userInfoPinia = userInfoModules(); //pinia | |||
const { locale } = useI18n(); | |||
// 当前语言状态与国际化组件 | |||
@@ -159,13 +166,23 @@ watch( | |||
box-shadow: 0 0 1px #0003; | |||
padding-right: 50px; | |||
.avatar { | |||
width: 30px; | |||
height: 30px; | |||
position: relative; | |||
width: 50px; | |||
height: 50px; | |||
margin-right: 10px; | |||
margin-left: 20px; | |||
background-color: #4769da; | |||
background-color: #fff; | |||
border-radius: 50%; | |||
cursor: pointer; | |||
overflow: hidden; | |||
img { | |||
position: absolute; | |||
top: 50%; | |||
left: 50%; | |||
transform: translate(-50%, -50%); | |||
max-width: 100%; | |||
max-height: 100%; | |||
} | |||
} | |||
.navItem { | |||
cursor: pointer; | |||
@@ -19,6 +19,7 @@ import "uno.css"; | |||
import "@/styles/my.scss"; // 引入自己定义的样式 | |||
// 引入组件 | |||
import myLoading from "@/components/myLoading.vue"; | |||
import myLoadingIcon from "@/components/myLoadingIcon.vue"; | |||
const app = createApp(App); | |||
@@ -33,4 +34,4 @@ app.use(ElementPlus); | |||
app.use(router).use(i18n).mount("#app"); | |||
// 注册全局组件 | |||
app.component('myLoading',myLoading) | |||
app.component('myLoading',myLoading).component('myLoadingIcon',myLoadingIcon) |
@@ -1,14 +1,19 @@ | |||
import { defineStore } from "pinia"; | |||
import { store } from "@/store"; | |||
export const userInfoPinia = defineStore("userInfo", () => { | |||
import { | |||
getUserInfoApi, | |||
} from "../../apis/myAccount"; | |||
export const userInfoModules = defineStore("userInfo", () => { | |||
// state | |||
const email = ref(""); | |||
const userInfo = ref(null) | |||
function setEmail(params) { | |||
email.value = params | |||
} | |||
return { email, setEmail }; | |||
async function getUserInfo() { | |||
const res = await getUserInfoApi() | |||
userInfo.value = res.data | |||
} | |||
return { userInfo, setEmail, getUserInfo }; | |||
}, | |||
{ | |||
persist: true, |
@@ -49,4 +49,29 @@ p { | |||
background: linear-gradient(225deg, | |||
rgb(81 217 202 / 100%), | |||
rgb(114 100 245 / 100%)); | |||
} | |||
} | |||
// 自定义按钮 | |||
.customBtn { | |||
padding: 10px 15px; | |||
text-align: center; | |||
background-color: #fff; | |||
color: #000; | |||
transition: all .3s; | |||
border-radius: 15px; | |||
cursor: pointer; | |||
&:hover { | |||
background-color: #000; | |||
color: #fff; | |||
} | |||
} | |||
// element公共样式 | |||
.el-dialog { | |||
border-radius: 30px !important; | |||
} | |||
// :deep(.el-input__wrapper.is-focus) { | |||
// box-shadow: 0 0 0 1px #000 !important; | |||
// } |
@@ -8,6 +8,7 @@ export {} | |||
declare module '@vue/runtime-core' { | |||
export interface GlobalComponents { | |||
Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default'] | |||
copy: typeof import('./../components/myLoading copy.vue')['default'] | |||
ElAlert: typeof import('element-plus/es')['ElAlert'] | |||
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb'] | |||
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem'] | |||
@@ -71,6 +72,7 @@ declare module '@vue/runtime-core' { | |||
LangSelect: typeof import('./../components/LangSelect/index.vue')['default'] | |||
MultiUpload: typeof import('./../components/Upload/MultiUpload.vue')['default'] | |||
MyLoading: typeof import('./../components/myLoading.vue')['default'] | |||
MyLoadingIcon: typeof import('./../components/myLoadingIcon.vue')['default'] | |||
Pagination: typeof import('./../components/Pagination/index.vue')['default'] | |||
RightPanel: typeof import('./../components/RightPanel/index.vue')['default'] | |||
RouterLink: typeof import('vue-router')['RouterLink'] | |||
@@ -124,9 +124,23 @@ | |||
<img :src="imgUrl" alt="" class="inmiddle" /> | |||
</div> | |||
</div> | |||
<!-- 选中需要上传的图片前后 --> | |||
<div class="left-top" v-show="showUploadBtn == 5"> | |||
<div class="editAiPicBox"> | |||
<div class="edit"> | |||
<div class="TagBox"></div> | |||
</div> | |||
<div class="AIpicBox"> | |||
<div class="picBox"> | |||
<img src="../../assets/img/trueImg.png" alt="" /> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- 底部 --> | |||
<div class="left-bottom"> | |||
<!-- tab栏 --> | |||
<div class="tab flex"> | |||
<div | |||
class="pointer" | |||
@@ -143,6 +157,14 @@ | |||
> | |||
{{ $t("createVideo.personalWarehouse") }} | |||
</div> | |||
<div | |||
v-if="AccessToken" | |||
class="pointer" | |||
:class="showFace == 2 ? 'tabAvtive' : ''" | |||
@click="chooseAiPic" | |||
> | |||
{{ $t("createVideo.Aipicture") }} | |||
</div> | |||
</div> | |||
<!-- 系统相册 --> | |||
<div | |||
@@ -202,6 +224,17 @@ | |||
</div> | |||
</div> | |||
</div> | |||
<!-- AI生成图片 --> | |||
<div | |||
class="Aipicture" | |||
v-if="showFace == 2" | |||
v-infinite-scroll="loadMoreAiFaceUser" | |||
:infinite-scroll-disabled="overLoadUser" | |||
> | |||
<div class="title"> | |||
{{ $t("createVideo.AipictureTip") }} | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- 右侧栏 --> | |||
@@ -213,7 +246,7 @@ | |||
<div class="text"> | |||
<!-- 文本域 --> | |||
<el-input | |||
maxlength="300" | |||
maxlength="3000" | |||
id="textInputId" | |||
v-model="textareaContent" | |||
:rows="15" | |||
@@ -313,7 +346,7 @@ | |||
<!-- ------------------------ 2 --> | |||
<el-tab-pane :label="$t('createVideo.uploadAudio')"> | |||
<div class="card2"> | |||
<!-- 上传视频 --> | |||
<!-- 上传音频 --> | |||
<p> | |||
<el-icon class="icon-VideoCamera"><i-ep-VideoCamera /></el-icon> | |||
{{ $t("createVideo.uploadAudio") }} | |||
@@ -352,7 +385,7 @@ | |||
</div> | |||
</div> | |||
<div class="AudioBox" v-if="audioUrl"> | |||
<audio controls :src="audioUrl"></audio> | |||
<audio controls :src="audioUrl" id="uploadAudio"></audio> | |||
</div> | |||
<!-- 保存视频 --> | |||
<el-button | |||
@@ -380,7 +413,72 @@ | |||
</div> | |||
</el-tab-pane> | |||
<!-- ----------------------- 3 --> | |||
<el-tab-pane v-if="false" :label="$t('createVideo.aiScript')"> | |||
<el-tab-pane :label="$t('createVideo.music')"> | |||
<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> | |||
<!-- ----------------------- 4 --> | |||
<el-tab-pane :label="$t('createVideo.aiScript')"> | |||
<div class="card3"> | |||
<div class="text"> | |||
<el-input | |||
@@ -448,7 +546,7 @@ | |||
</div> | |||
<!-- 试听的mp3 --> | |||
<div style="opacity: 0; width: 0"> | |||
<audio autoplay :src="previewAudioUrl"></audio> | |||
<audio autoplay :src="previewAudioUrl" id="audio"></audio> | |||
</div> | |||
</div> | |||
</template> | |||
@@ -663,6 +761,13 @@ async function changeFile() { | |||
} | |||
//#endregion --------------------------- | |||
//#region AI生成图片 | |||
function chooseAiPic() { | |||
showFace.value = 2; | |||
showUploadBtn.value = 5; | |||
} | |||
//#endregion -------------------------- | |||
//#region card1的逻辑 | |||
const textareaContent = ref(""); | |||
function insertIcon() { | |||
@@ -754,6 +859,7 @@ async function getPreviewAudio() { | |||
); | |||
} | |||
previewAudioUrl.value = res.data.data; | |||
document.getElementById("audio").play(); | |||
} catch (error) { | |||
ElMessage.error(error); | |||
} | |||
@@ -788,6 +894,7 @@ const stopRecording = async () => { | |||
audioUrl.value = URL.createObjectURL(recorder.getWAVBlob()); | |||
audioData.value = recorder.getWAVBlob(); | |||
isRecording.value = true; | |||
console.log(document.getElementById("uploadAudio").duration); | |||
} catch (error) { | |||
console.error(error); | |||
} | |||
@@ -831,14 +938,31 @@ async function uploadAudio(isSaveVideo) { | |||
const fileInput = ref(null); | |||
let intervalId = null; // 用于存储 setInterval 的返回值 | |||
const uploadAudioTime = ref(null); // 音频时长 | |||
const handleFileChange = async () => { | |||
const file = fileInput.value.files[0]; | |||
if (file) { | |||
const videoUrl = URL.createObjectURL(file); | |||
audioUrl.value = videoUrl; | |||
audioData.value = file; | |||
const audio2 = new Audio(); | |||
audio2.src = videoUrl; | |||
countAudioTime(audio2); | |||
} | |||
}; | |||
// 计算音频的时长 | |||
const countAudioTime = async (audio) => { | |||
while (isNaN(audio.duration) || audio.duration === Infinity) { | |||
// 延迟一会 不然网页都卡死 | |||
await new Promise((resolve) => setTimeout(resolve, 200)); | |||
// 设置随机播放时间,模拟调进度条 | |||
audio.currentTime = 10000000 * Math.random(); | |||
} | |||
// console.log("音频的总时长:", Math.floor(audio.duration)); | |||
uploadAudioTime.value = Math.floor(audio.duration); | |||
}; | |||
// 获取视频上传进度 | |||
const afterSaveAudioId = ref(null); | |||
async function uploadProgress(videoId, isSaveVideo) { | |||
@@ -1007,7 +1131,7 @@ onMounted(async () => { | |||
} | |||
.left { | |||
width: 70%; | |||
width: 68%; | |||
height: 80%; | |||
padding: 20px; | |||
:deep(.el-input__wrapper) { | |||
@@ -1160,6 +1284,54 @@ onMounted(async () => { | |||
border: 2px solid #000; | |||
border-radius: 50%; | |||
} | |||
.editAiPicBox { | |||
position: relative; | |||
display: flex; | |||
width: 100%; | |||
height: 100%; | |||
border: 1px solid #ccc; | |||
background-color: #f1f2f6; | |||
border-radius: 30px; | |||
overflow: hidden; | |||
.edit { | |||
width: 50%; | |||
height: 100%; | |||
background-color: #fff; | |||
.TagBox { | |||
width: 100%; | |||
height: 100%; | |||
border-radius: 0 0 30px 0; | |||
background-color: #f1f2f6; | |||
} | |||
} | |||
.AIpicBox { | |||
position: relative; | |||
width: 50%; | |||
height: 100%; | |||
border-radius: 30px 0 0 0; | |||
background-color: #fff; | |||
.picBox { | |||
position: relative; | |||
width: 100%; | |||
height: 100%; | |||
border-radius: 30px 0 0 0; | |||
background-color: #fff; | |||
img { | |||
position: absolute; | |||
top: 50%; | |||
left: 50%; | |||
transform: translate(-50%, -50%); | |||
max-width: 100%; | |||
max-height: 100%; | |||
border-radius: 30px; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
&-bottom { | |||
@@ -1197,7 +1369,7 @@ onMounted(async () => { | |||
overflow-y: scroll; | |||
.faceItem { | |||
position: relative; | |||
margin-right: 35px; | |||
margin-right: 30px; | |||
margin-bottom: 35px; | |||
width: 150px; | |||
height: 150px; | |||
@@ -1276,11 +1448,33 @@ onMounted(async () => { | |||
} | |||
} | |||
} | |||
.Aipicture { | |||
width: 100%; | |||
height: 180px; | |||
.title { | |||
font-size: 30px; | |||
text-align: center; | |||
line-height: 80px; | |||
background-image: -webkit-linear-gradient( | |||
left, | |||
#4b61dc, | |||
#15d1b8 25%, | |||
#4b61dc 50%, | |||
#15d1b8 75%, | |||
#4b61dc | |||
) !important; | |||
-webkit-text-fill-color: transparent !important; | |||
-webkit-background-clip: text !important; | |||
background-clip: text !important; | |||
-webkit-background-size: 200% 100% !important; | |||
background-size: 200% 100% !important; | |||
} | |||
} | |||
} | |||
} | |||
.right { | |||
width: 30%; | |||
width: 32%; | |||
height: 100%; | |||
padding: 30px 30px 20px 30px; | |||
background-color: #f1f2f6; | |||
@@ -1449,7 +1643,7 @@ onMounted(async () => { | |||
} | |||
:deep(.el-tabs__item) { | |||
width: 34%; | |||
width: 25%; | |||
} | |||
:deep(.el-tabs__item:hover) { | |||
@@ -502,7 +502,7 @@ import { | |||
import axios from "axios"; | |||
// 状态管理依赖 | |||
import { userInfoPinia } from "@/store/modules/userInfo"; | |||
import { userInfoModules } from "@/store/modules/userInfo"; | |||
// API依赖 | |||
import { | |||
@@ -515,7 +515,7 @@ import { useI18n } from "vue-i18n"; | |||
const { locale } = useI18n(); | |||
const currentLan = ref(locale); | |||
const userInfo = userInfoPinia(); | |||
const userInfoPinia = userInfoModules(); | |||
const route = useRoute(); | |||
const router = useRouter(); | |||
/** | |||
@@ -957,7 +957,7 @@ async function doLogin(data: any) { | |||
// 注册成功将账号密码保存到本地 | |||
saveInfo(data); | |||
isLogining.value = false; | |||
userInfo.setEmail(data.email); //保存邮箱到pinia | |||
userInfoPinia.getUserInfo(); //保存邮箱到pinia | |||
}) | |||
.catch((err) => { | |||
console.log(err, "err"); | |||
@@ -5,16 +5,31 @@ | |||
<!-- 头像 --> | |||
<div class="left-one border flex justify-between flex-wrap"> | |||
<div class="avatar"> | |||
<img :src="defaultAvater" alt="" /> | |||
<img | |||
:src=" | |||
userInfoPinia.userInfo.avatarUrl | |||
? userInfoPinia.userInfo.avatarUrl | |||
: defaultAvater | |||
" | |||
alt="" | |||
/> | |||
</div> | |||
<div class="info"> | |||
<div class="name">OLO</div> | |||
<span>{{ userInfo.email }}</span> | |||
<span | |||
class="password pointer" | |||
@click="router.push({ name: 'login', params: { type: 4 } })" | |||
>{{ $t("account.reSetPwd") }}</span | |||
> | |||
<div class="name" v-if="userInfoPinia.userInfo.name"> | |||
{{ userInfoPinia.userInfo.name }} | |||
</div> | |||
<span>{{ userInfoPinia.userInfo.email }}</span> | |||
<div> | |||
<span class="password pointer" @click="showEditInfoDialog = true">{{ | |||
$t("account.editInfo") | |||
}}</span> | |||
<span | |||
class="password pointer" | |||
style="margin-right: 10px" | |||
@click="router.push({ name: 'login', params: { type: 4 } })" | |||
>{{ $t("account.reSetPwd") }}</span | |||
> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- 剩余次数 --> | |||
@@ -134,6 +149,40 @@ | |||
</el-tab-pane> | |||
</el-tabs> | |||
</div> | |||
<!-- 修改个人信息弹窗 --> | |||
<el-dialog | |||
width="30%" | |||
class="editInfoDialog" | |||
v-model="showEditInfoDialog" | |||
:title="$t('account.editInfo')" | |||
destroy-on-close | |||
@close="closeInfoDialog" | |||
> | |||
<el-row> | |||
<el-col :span="4"> {{ $t("public.avatar") }}: </el-col> | |||
<el-col :span="20"> | |||
<div class="dialogAvatar"> | |||
<input accept="image/*" type="file" @change="chooseImg" /> | |||
<div class="add">+</div> | |||
<img v-if="dialogAvatarUrl" :src="dialogAvatarUrl" alt="" /> | |||
</div> | |||
</el-col> | |||
</el-row> | |||
<el-row> | |||
<el-col :span="4"> {{ $t("public.username") }}: </el-col> | |||
<el-col :span="20"> | |||
<el-input v-model="userNameinput" /> | |||
</el-col> | |||
</el-row> | |||
<el-row> | |||
<div class="customBtn" @click="showEditInfoDialog = false"> | |||
{{ $t("public.cancel") }} | |||
</div> | |||
<div class="customBtn" @click="submitUserInfo"> | |||
{{ $t("public.confirm") }} | |||
</div> | |||
</el-row> | |||
</el-dialog> | |||
</div> | |||
</template> | |||
@@ -141,21 +190,82 @@ | |||
//#region 导入文件 | |||
import { useI18n } from "vue-i18n"; | |||
import { useRouter } from "vue-router"; | |||
import { userInfoPinia } from "@/store/modules/userInfo"; | |||
import { userInfoModules } from "@/store/modules/userInfo"; | |||
import dayjs from "dayjs"; | |||
import imageCompression from "browser-image-compression"; //压缩图片插件 | |||
import { | |||
inviteCodeApi, | |||
useCodeApi, | |||
isUseApi, | |||
useCodeUserApi, | |||
updUserInfoApi, | |||
} from "../../apis/myAccount"; | |||
import { awsImgUpload } from "@/apis/createVideo"; | |||
import defaultAvater from "../../assets/img/left-top-logo.png"; | |||
//#endregion --------------------------- | |||
const { locale } = useI18n(); | |||
const lanChange = ref(locale); //国际化 | |||
const userInfo = userInfoPinia(); //pinia | |||
const userInfoPinia = userInfoModules(); //pinia | |||
//#region 编辑个人信息 | |||
const showEditInfoDialog = ref(false); //控制弹窗显隐 | |||
const userNameinput = ref(null); //用户名输入框绑定 | |||
const dialogAvatarUrl = ref(null); //图片回显地址 | |||
const dialogAvatarData = ref(null); //图片上传数据 | |||
const infoDialogLoad = ref(false); //load效果 | |||
// 选择图片 | |||
async function chooseImg(e) { | |||
if (e.target.files[0]) { | |||
dialogAvatarUrl.value = null; | |||
dialogAvatarData.value = null; | |||
// 压缩配置 | |||
const options = { | |||
maxSizeMB: 2, // 最大压缩大小为 1MB | |||
useWebWorker: true, // 使用 Web Worker 进行压缩,提高性能 | |||
}; | |||
const compressedFile = await imageCompression(e.target.files[0], options); | |||
dialogAvatarUrl.value = URL.createObjectURL(compressedFile); | |||
dialogAvatarData.value = compressedFile; | |||
} else { | |||
return; | |||
} | |||
} | |||
// 上传编辑信息 | |||
async function submitUserInfo() { | |||
let formData = new FormData(); | |||
const timestamp = Date.now(); | |||
formData.append("file", dialogAvatarData.value, timestamp + ".png"); | |||
const res1 = await awsImgUpload(formData); | |||
const data = { | |||
name: userNameinput.value, | |||
avatarUrl: res1.data.url, | |||
}; | |||
try { | |||
const res2 = await updUserInfoApi(data); | |||
ElMessage.success( | |||
lanChange.value == "zh-cn" ? `修改成功` : `Modified successfully` | |||
); | |||
userInfoPinia.getUserInfo(); | |||
showEditInfoDialog.value = false; | |||
} catch (error) { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" | |||
? `修改失败,请稍后重试` | |||
: `The modification failed. Please try again later` | |||
); | |||
} | |||
} | |||
// 关闭弹窗清空 | |||
function closeInfoDialog() { | |||
userNameinput.value = userInfoPinia.userInfo.name; | |||
dialogAvatarUrl.value = userInfoPinia.userInfo.avatarUrl; | |||
dialogAvatarData.value = null; | |||
} | |||
//#endregion --------------------------- | |||
//#region 二维码邀请码区域 | |||
const myInviteCode = ref(null); //我的邀请码 | |||
async function getMyInviteCode() { | |||
@@ -168,10 +278,18 @@ function copyInviteCode() { | |||
navigator.clipboard | |||
.writeText(inviteCode) | |||
.then(() => { | |||
ElMessage.success("邀请码已复制到剪贴板"); | |||
ElMessage.success( | |||
lanChange.value == "zh-cn" | |||
? "邀请码已复制到剪贴板" | |||
: `The invitation code has been copied to the clipboard` | |||
); | |||
}) | |||
.catch((error) => { | |||
ElMessage.error("无法复制邀请码:", error); | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" | |||
? "无法复制邀请码:" | |||
: `Unable to copy invitation code` | |||
); | |||
}); | |||
} | |||
// 下载二维码图片 | |||
@@ -228,6 +346,8 @@ onMounted(() => { | |||
getMyInviteCode(); // 查询用户的邀请码 | |||
getIsUseState(); //查询是否使用过邀请码 | |||
getInviteList(); | |||
userNameinput.value = userInfoPinia.userInfo.name; | |||
dialogAvatarUrl.value = userInfoPinia.userInfo.avatarUrl; | |||
}); | |||
//#endregion --------------------------- | |||
@@ -249,7 +369,7 @@ function routerTo(url) { | |||
} | |||
.left { | |||
width: 37%; | |||
width: 39%; | |||
height: 100%; | |||
padding: 30px; | |||
@@ -307,6 +427,7 @@ function routerTo(url) { | |||
&-two { | |||
position: relative; | |||
height: 80px; | |||
overflow: hidden; | |||
.number { | |||
font-size: 30px; | |||
@@ -322,12 +443,14 @@ function routerTo(url) { | |||
position: absolute; | |||
top: -1px; | |||
right: -1px; | |||
padding: 5px; | |||
width: 80px; | |||
height: 80px; | |||
line-height: 80px; | |||
line-height: 70px; | |||
text-align: center; | |||
background-color: #000; | |||
color: #fff; | |||
font-size: 14px; | |||
transition: all 0.3s; | |||
&:hover { | |||
background-color: #15d1b8; | |||
@@ -416,7 +539,7 @@ function routerTo(url) { | |||
.right { | |||
background-color: #f1f2f6; | |||
padding: 30px; | |||
width: 63%; | |||
width: 61%; | |||
height: 100%; | |||
// height: 95%; | |||
border-radius: 30px 0 0 0; | |||
@@ -567,6 +690,69 @@ function routerTo(url) { | |||
} | |||
} | |||
.editInfoDialog { | |||
.el-row { | |||
display: flex; | |||
justify-content: space-evenly; | |||
} | |||
.el-col { | |||
margin-bottom: 10px; | |||
text-align: right; | |||
line-height: 32px; | |||
padding-right: 20px; | |||
} | |||
.dialogAvatar { | |||
position: relative; | |||
width: 200px; | |||
height: 200px; | |||
background-color: rgba(204, 204, 204, 0.2); | |||
border-radius: 30px; | |||
overflow: hidden; | |||
&:hover { | |||
img { | |||
z-index: 0; | |||
} | |||
.add { | |||
background-color: rgba(0, 0, 0, 0.2); | |||
} | |||
} | |||
input { | |||
position: absolute; | |||
top: 0; | |||
left: 0; | |||
width: 100%; | |||
height: 100%; | |||
opacity: 0; | |||
z-index: 3; | |||
cursor: pointer; | |||
} | |||
.add { | |||
position: absolute; | |||
top: 0; | |||
left: 0; | |||
width: 100%; | |||
height: 100%; | |||
z-index: 1; | |||
text-align: center; | |||
line-height: 200px; | |||
font-size: 50px; | |||
font-weight: 700; | |||
color: #000; | |||
transition: all 0.3s; | |||
} | |||
img { | |||
position: absolute; | |||
top: 50%; | |||
left: 50%; | |||
transform: translate(-50%, -50%); | |||
padding: 10px; | |||
max-width: 100%; | |||
max-height: 100%; | |||
z-index: 2; | |||
transition: all 0.3s; | |||
} | |||
} | |||
} | |||
// 公共类名 | |||
.bigWord { | |||
font-size: 30px; | |||
@@ -38,7 +38,7 @@ | |||
<div class="workInfo flex"> | |||
<span>{{ item.title }}</span> | |||
<span>{{ item.createDate }}</span> | |||
<span>{{ dayjs(item.createDate).format("YYYY-MM-DD") }}</span> | |||
</div> | |||
<!-- 更多遮罩层 --> | |||
<div | |||
@@ -205,7 +205,7 @@ import { timestampToTime } from "@/utils/tools"; | |||
import { useI18n } from "vue-i18n"; | |||
import { getStatus } from "./videoStatus"; | |||
import { useRouter } from "vue-router"; | |||
import dayjs from "dayjs"; | |||
const router = useRouter(); | |||
// 当前语言状态与国际化组件 | |||
const { locale } = useI18n(); | |||
@@ -297,9 +297,9 @@ function getPhotoVideoList(pageNum, pageSize, title, searchFlag) { | |||
.then((res) => { | |||
console.log(res, "res"); | |||
res.data.list.forEach((item) => { | |||
item.createDate = timestampToTime(item.createDate) || ""; | |||
item.updateDate = timestampToTime(item.updateDate) || ""; | |||
item.createVideoDate = timestampToTime(item.createVideoDate) || ""; | |||
// item.createDate = timestampToTime(item.createDate) || ""; | |||
// item.updateDate = timestampToTime(item.updateDate) || ""; | |||
// item.createVideoDate = timestampToTime(item.createVideoDate) || ""; | |||
item.statusName = getStatus(item.videoStatus); | |||
}); | |||
videoList.value = res.data.list; | |||
@@ -514,6 +514,7 @@ onMounted(() => { | |||
white-space: nowrap; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
font-size: 13px; | |||
} | |||
.statusName { | |||
position: absolute; | |||
@@ -86,7 +86,8 @@ | |||
> | |||
<div class="orderCodeDialog"> | |||
<div class="codeImg"> | |||
<img src="../../assets/img/false1.png" alt="" /> | |||
<my-Loading-Icon v-if="!orderCodeUrl"></my-Loading-Icon> | |||
<img v-if="orderCodeUrl" src="../../assets/img/false1.png" alt="" /> | |||
</div> | |||
<div class="tips">扫描二维码进行付款</div> | |||
<div class="btnBox"> | |||
@@ -169,7 +170,6 @@ const showPayDialog = ref(false); //套餐信息dialog显示隐藏 | |||
const presentItme = reactive({}); //点击的套餐项 | |||
function clickPayDialog(item) { | |||
presentItme.value = item; | |||
console.log(presentItme); | |||
showPayDialog.value = true; | |||
} | |||
function showOrderCode(type) { | |||
@@ -179,6 +179,7 @@ function showOrderCode(type) { | |||
//#region 点击套餐内的支付 | |||
const orderCodeDialogShow = ref(false); | |||
const orderCodeUrl = ref(null); | |||
//#endregion ------------------------------------ | |||
//#region | |||
@@ -352,10 +353,18 @@ const orderCodeDialogShow = ref(false); | |||
width: 100%; | |||
.codeImg { | |||
display: flex; | |||
margin: auto; | |||
justify-content: space-evenly; | |||
align-items: center; | |||
width: 300px; | |||
height: 300px; | |||
border: 1px solid #ccc; | |||
border-radius: 15px; | |||
overflow: hidden; | |||
padding: 10px; | |||
img { | |||
width: 300px; | |||
height: 300px; | |||
width: 100%; | |||
height: 100%; | |||
} | |||
} | |||
.tips { | |||