@@ -5,13 +5,8 @@ | |||||
"type": "module", | "type": "module", | ||||
"scripts": { | "scripts": { | ||||
"dev": "vite serve --mode development", | "dev": "vite serve --mode development", | ||||
"build:prod": "vite build --mode production &&vue-tsc --noEmit", | |||||
"prepare": "husky install", | |||||
"lint:eslint": "eslint --fix --ext .ts,.js,.vue ./src ", | |||||
"lint:prettier": "prettier --write \"**/*.{js,cjs,ts,json,tsx,css,less,scss,vue,html,md}\"", | |||||
"lint:stylelint": "stylelint \"**/*.{css,scss,vue}\" --fix", | |||||
"lint:lint-staged": "lint-staged", | |||||
"commit": "git-cz" | |||||
"build:dev": "vite build --mode development &&vue-tsc --noEmit", | |||||
"build:prod": "vite build --mode production &&vue-tsc --noEmit" | |||||
}, | }, | ||||
"config": { | "config": { | ||||
"commitizen": { | "commitizen": { | ||||
@@ -1,22 +1,3 @@ | |||||
<script setup lang="ts"> | |||||
import { useI18n } from "vue-i18n"; | |||||
import SvgIcon from "@/components/SvgIcon/index.vue"; | |||||
import { useAppStore } from "@/store/modules/app"; | |||||
const appStore = useAppStore(); | |||||
const { locale } = useI18n(); | |||||
function handleLanguageChange(lang: string) { | |||||
locale.value = lang; | |||||
appStore.changeLanguage(lang); | |||||
if (lang == "en") { | |||||
ElMessage.success("Switch Language Successful!"); | |||||
} else { | |||||
ElMessage.success("切换语言成功!"); | |||||
} | |||||
} | |||||
</script> | |||||
<template> | <template> | ||||
<el-dropdown class="outSide" trigger="click" @command="handleLanguageChange"> | <el-dropdown class="outSide" trigger="click" @command="handleLanguageChange"> | ||||
<div> | <div> | ||||
@@ -38,6 +19,25 @@ function handleLanguageChange(lang: string) { | |||||
</el-dropdown> | </el-dropdown> | ||||
</template> | </template> | ||||
<script setup lang="ts"> | |||||
import { useI18n } from "vue-i18n"; | |||||
import SvgIcon from "@/components/SvgIcon/index.vue"; | |||||
import { useAppStore } from "@/store/modules/app"; | |||||
const appStore = useAppStore(); | |||||
const { locale } = useI18n(); | |||||
function handleLanguageChange(lang: string) { | |||||
locale.value = lang; | |||||
appStore.changeLanguage(lang); | |||||
if (lang == "en") { | |||||
ElMessage.success("Switch Language Successful!"); | |||||
} else { | |||||
ElMessage.success("切换语言成功!"); | |||||
} | |||||
} | |||||
</script> | |||||
<style lang="scss" scoped> | <style lang="scss" scoped> | ||||
.outSide { | .outSide { | ||||
cursor: pointer; | cursor: pointer; | ||||
@@ -7,11 +7,13 @@ export default { | |||||
// 登录页面国际化 | // 登录页面国际化 | ||||
login: { | login: { | ||||
title: "Metavatar-PC", | |||||
title: "Metavatar-PC Sign in", | |||||
username: "Username", | username: "Username", | ||||
password: "Password", | password: "Password", | ||||
login: "Login", | |||||
login: "Sign in", | |||||
verifyCode: "Verify Code", | verifyCode: "Verify Code", | ||||
signUp: "Don't have an account? Sign up here", | |||||
forgetPwd: "I forgot my password", | |||||
}, | }, | ||||
// 导航栏国际化 | // 导航栏国际化 | ||||
@@ -51,6 +53,7 @@ export default { | |||||
account: { | account: { | ||||
title: "My Account", | title: "My Account", | ||||
reSetPwd: "Reset Password", | reSetPwd: "Reset Password", | ||||
until: "until", | |||||
getMore: "UPGRADE", | getMore: "UPGRADE", | ||||
copyCode: "Copy invitation code", | copyCode: "Copy invitation code", | ||||
saveCode: "Save QR code", | saveCode: "Save QR code", | ||||
@@ -67,5 +70,13 @@ export default { | |||||
// 商店 | // 商店 | ||||
shop: { | shop: { | ||||
title: "Shop", | title: "Shop", | ||||
Trial: "Trial", | |||||
Lite: "Lite", | |||||
Pro: "Pro", | |||||
Adv: "Advanced", | |||||
Ent: "Enterprise", | |||||
selectPlan: "SELECT PLAN", | |||||
WeChatPay: "WeChatPay", | |||||
Alipay: "Alipay", | |||||
}, | }, | ||||
}; | }; |
@@ -7,11 +7,13 @@ export default { | |||||
// 登录页面国际化 | // 登录页面国际化 | ||||
login: { | login: { | ||||
title: "Metavatar-PC", | |||||
title: "Metavatar-PC 登录", | |||||
username: "用户名", | username: "用户名", | ||||
password: "密码", | password: "密码", | ||||
login: "登 录", | login: "登 录", | ||||
verifyCode: "验证码", | verifyCode: "验证码", | ||||
signUp: "没有账号?现在加入!", | |||||
forgetPwd: "忘记密码", | |||||
}, | }, | ||||
// 导航栏国际化 | // 导航栏国际化 | ||||
@@ -51,6 +53,7 @@ export default { | |||||
account: { | account: { | ||||
title: "个人账户", | title: "个人账户", | ||||
reSetPwd: "修改密码", | reSetPwd: "修改密码", | ||||
until: "有效期至", | |||||
getMore: "升级套餐", | getMore: "升级套餐", | ||||
copyCode: "复制邀请码", | copyCode: "复制邀请码", | ||||
saveCode: "保存二维码", | saveCode: "保存二维码", | ||||
@@ -67,5 +70,13 @@ export default { | |||||
// 商店 | // 商店 | ||||
shop: { | shop: { | ||||
title: "商店", | title: "商店", | ||||
Trial: "尝鲜版", | |||||
Lite: "基础版", | |||||
Pro: "专业版", | |||||
Adv: "加强版", | |||||
Ent: "企业版", | |||||
selectPlan: "方案选择", | |||||
WeChatPay: "微信支付", | |||||
Alipay: "支付宝支付", | |||||
}, | }, | ||||
}; | }; |
@@ -79,8 +79,14 @@ import { RouteLocationRaw, useRoute, useRouter } from "vue-router"; | |||||
import { useAppStore } from "@/store/modules/app"; | import { useAppStore } from "@/store/modules/app"; | ||||
import { useTagsViewStore } from "@/store/modules/tagsView"; | import { useTagsViewStore } from "@/store/modules/tagsView"; | ||||
import { useUserStore } from "@/store/modules/user"; | import { useUserStore } from "@/store/modules/user"; | ||||
import { useI18n } from "vue-i18n"; | |||||
import LangSelect from "@/components/LangSelect/index.vue"; | import LangSelect from "@/components/LangSelect/index.vue"; | ||||
const { locale } = useI18n(); | |||||
// 当前语言状态与国际化组件 | |||||
const lanChange = ref(locale); | |||||
const appStore = useAppStore(); | const appStore = useAppStore(); | ||||
const tagsViewStore = useTagsViewStore(); | const tagsViewStore = useTagsViewStore(); | ||||
const userStore = useUserStore(); | const userStore = useUserStore(); | ||||
@@ -106,10 +112,16 @@ const { isFullscreen, toggle } = useFullscreen(); | |||||
* 注销 | * 注销 | ||||
*/ | */ | ||||
function logout() { | function logout() { | ||||
ElMessageBox.confirm("确定注销并退出系统吗?", "提示", { | |||||
confirmButtonText: "确定", | |||||
cancelButtonText: "取消", | |||||
type: "warning", | |||||
const msg = | |||||
(lanChange.value as string) == "zh-cn" | |||||
? "确定退出登录吗?" | |||||
: "Are you sure to log out?"; | |||||
const notice = (lanChange.value as string) == "zh-cn" ? "提示" : "Notice"; | |||||
const confirm = (lanChange.value as string) == "zh-cn" ? "确定" : "Confirm"; | |||||
const cancel = (lanChange.value as string) == "zh-cn" ? "取消" : "Cancel"; | |||||
ElMessageBox.confirm(msg, notice, { | |||||
confirmButtonText: confirm, | |||||
cancelButtonText: cancel, | |||||
}).then(() => { | }).then(() => { | ||||
userStore | userStore | ||||
.logout() | .logout() | ||||
@@ -186,10 +198,9 @@ watch( | |||||
height: 80px; | height: 80px; | ||||
padding: 0 20px; | padding: 0 20px; | ||||
color: #fff; | color: #fff; | ||||
background-color: #fff; | |||||
background-color: #000; | background-color: #000; | ||||
box-shadow: 0 0 1px #0003; | box-shadow: 0 0 1px #0003; | ||||
padding-right: 50px; | |||||
.avatar { | .avatar { | ||||
width: 30px; | width: 30px; | ||||
height: 30px; | height: 30px; | ||||
@@ -1,3 +1,60 @@ | |||||
<template> | |||||
<div v-if="false" class="tags-container"> | |||||
<scroll-pane ref="scrollPaneRef" @scroll="handleScroll"> | |||||
<router-link | |||||
:class="'tags-item ' + (isActive(tag) ? 'active' : '')" | |||||
v-for="tag in visitedViews" | |||||
:key="tag.path" | |||||
:data-path="tag.path" | |||||
:to="{ path: tag.path, query: tag.query }" | |||||
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''" | |||||
@contextmenu.prevent="openTagMenu(tag, $event)" | |||||
> | |||||
{{ translateRouteTitleI18n(tag.meta?.title) }} | |||||
<span | |||||
v-if="!isAffix(tag)" | |||||
class="tags-item-close" | |||||
@click.prevent.stop="closeSelectedTag(tag)" | |||||
> | |||||
<i-ep-close class="text-[10px]" /> | |||||
</span> | |||||
</router-link> | |||||
</scroll-pane> | |||||
<!-- tag标签操作菜单 --> | |||||
<ul | |||||
v-show="tagMenuVisible" | |||||
class="tag-menu" | |||||
:style="{ left: left + 'px', top: top + 'px' }" | |||||
> | |||||
<li @click="refreshSelectedTag(selectedTag)"> | |||||
<svg-icon icon-class="refresh" /> | |||||
刷新 | |||||
</li> | |||||
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"> | |||||
<svg-icon icon-class="close" /> | |||||
关闭 | |||||
</li> | |||||
<li @click="closeOtherTags"> | |||||
<svg-icon icon-class="close_other" /> | |||||
关闭其它 | |||||
</li> | |||||
<li v-if="!isFirstView()" @click="closeLeftTags"> | |||||
<svg-icon icon-class="close_left" /> | |||||
关闭左侧 | |||||
</li> | |||||
<li v-if="!isLastView()" @click="closeRightTags"> | |||||
<svg-icon icon-class="close_right" /> | |||||
关闭右侧 | |||||
</li> | |||||
<li @click="closeAllTags(selectedTag)"> | |||||
<svg-icon icon-class="close_all" /> | |||||
关闭所有 | |||||
</li> | |||||
</ul> | |||||
</div> | |||||
</template> | |||||
<script setup lang="ts"> | <script setup lang="ts"> | ||||
import { | import { | ||||
getCurrentInstance, | getCurrentInstance, | ||||
@@ -241,63 +298,6 @@ onMounted(() => { | |||||
}); | }); | ||||
</script> | </script> | ||||
<template> | |||||
<div v-if="false" class="tags-container"> | |||||
<scroll-pane ref="scrollPaneRef" @scroll="handleScroll"> | |||||
<router-link | |||||
:class="'tags-item ' + (isActive(tag) ? 'active' : '')" | |||||
v-for="tag in visitedViews" | |||||
:key="tag.path" | |||||
:data-path="tag.path" | |||||
:to="{ path: tag.path, query: tag.query }" | |||||
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''" | |||||
@contextmenu.prevent="openTagMenu(tag, $event)" | |||||
> | |||||
{{ translateRouteTitleI18n(tag.meta?.title) }} | |||||
<span | |||||
v-if="!isAffix(tag)" | |||||
class="tags-item-close" | |||||
@click.prevent.stop="closeSelectedTag(tag)" | |||||
> | |||||
<i-ep-close class="text-[10px]" /> | |||||
</span> | |||||
</router-link> | |||||
</scroll-pane> | |||||
<!-- tag标签操作菜单 --> | |||||
<ul | |||||
v-show="tagMenuVisible" | |||||
class="tag-menu" | |||||
:style="{ left: left + 'px', top: top + 'px' }" | |||||
> | |||||
<li @click="refreshSelectedTag(selectedTag)"> | |||||
<svg-icon icon-class="refresh" /> | |||||
刷新 | |||||
</li> | |||||
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"> | |||||
<svg-icon icon-class="close" /> | |||||
关闭 | |||||
</li> | |||||
<li @click="closeOtherTags"> | |||||
<svg-icon icon-class="close_other" /> | |||||
关闭其它 | |||||
</li> | |||||
<li v-if="!isFirstView()" @click="closeLeftTags"> | |||||
<svg-icon icon-class="close_left" /> | |||||
关闭左侧 | |||||
</li> | |||||
<li v-if="!isLastView()" @click="closeRightTags"> | |||||
<svg-icon icon-class="close_right" /> | |||||
关闭右侧 | |||||
</li> | |||||
<li @click="closeAllTags(selectedTag)"> | |||||
<svg-icon icon-class="close_all" /> | |||||
关闭所有 | |||||
</li> | |||||
</ul> | |||||
</div> | |||||
</template> | |||||
<style lang="scss" scoped> | <style lang="scss" scoped> | ||||
.tags-container { | .tags-container { | ||||
width: 100%; | width: 100%; | ||||
@@ -2,8 +2,10 @@ | |||||
export {} | export {} | ||||
declare global { | declare global { | ||||
const EffectScope: typeof import('vue')['EffectScope'] | const EffectScope: typeof import('vue')['EffectScope'] | ||||
const ElForm: typeof import('element-plus/es')['ElForm'] | |||||
const ElMessage: typeof import('element-plus/es')['ElMessage'] | const ElMessage: typeof import('element-plus/es')['ElMessage'] | ||||
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] | const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] | ||||
const ElTree: typeof import('element-plus/es')['ElTree'] | |||||
const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] | const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] | ||||
const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] | const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] | ||||
const computed: typeof import('vue')['computed'] | const computed: typeof import('vue')['computed'] | ||||
@@ -270,8 +272,10 @@ import { UnwrapRef } from 'vue' | |||||
declare module 'vue' { | declare module 'vue' { | ||||
interface ComponentCustomProperties { | interface ComponentCustomProperties { | ||||
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']> | readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']> | ||||
readonly ElForm: UnwrapRef<typeof import('element-plus/es')['ElForm']> | |||||
readonly ElMessage: UnwrapRef<typeof import('element-plus/es')['ElMessage']> | readonly ElMessage: UnwrapRef<typeof import('element-plus/es')['ElMessage']> | ||||
readonly ElMessageBox: UnwrapRef<typeof import('element-plus/es')['ElMessageBox']> | readonly ElMessageBox: UnwrapRef<typeof import('element-plus/es')['ElMessageBox']> | ||||
readonly ElTree: UnwrapRef<typeof import('element-plus/es')['ElTree']> | |||||
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']> | readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']> | ||||
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']> | readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']> | ||||
readonly computed: UnwrapRef<typeof import('vue')['computed']> | readonly computed: UnwrapRef<typeof import('vue')['computed']> | ||||
@@ -141,6 +141,7 @@ | |||||
</template> | </template> | ||||
<script setup> | <script setup> | ||||
import { ref } from "vue"; | import { ref } from "vue"; | ||||
import { getCurrentInstance } from "vue"; | |||||
// 标题 | // 标题 | ||||
let title = ref(""); | let title = ref(""); | ||||
// 上传图片 | // 上传图片 | ||||
@@ -150,7 +151,7 @@ function changeFile(e) { | |||||
} | } | ||||
// $t在script中的使用 | // $t在script中的使用 | ||||
import { getCurrentInstance } from "vue"; | |||||
const { | const { | ||||
appContext: { | appContext: { | ||||
config: { globalProperties }, | config: { globalProperties }, | ||||
@@ -9,10 +9,9 @@ | |||||
></video> | ></video> | ||||
</div> | </div> | ||||
<el-form | <el-form | ||||
v-if="true" | |||||
v-if="isLogin" | |||||
ref="loginFormRef" | ref="loginFormRef" | ||||
:model="loginData" | :model="loginData" | ||||
:rules="loginRules" | |||||
class="login-form" | class="login-form" | ||||
> | > | ||||
<div class="flex text-white items-center py-4"> | <div class="flex text-white items-center py-4"> | ||||
@@ -84,15 +83,108 @@ | |||||
size="default" | size="default" | ||||
:loading="loading" | :loading="loading" | ||||
type="primary" | type="primary" | ||||
class="w-full" | |||||
class="loginBtn" | |||||
@click.prevent="handleLogin" | @click.prevent="handleLogin" | ||||
>{{ $t("login.login") }} | >{{ $t("login.login") }} | ||||
</el-button> | </el-button> | ||||
<!-- 账号密码提示 --> | |||||
<div class="mt-4 text-white text-sm"> | |||||
<span>{{ $t("login.username") }}: admin</span> | |||||
<span class="ml-4"> {{ $t("login.password") }}: 123456</span> | |||||
<div :class="currentLan == 'zh-cn' ? 'bottomBtnCN' : 'bottomBtnEN'"> | |||||
<div style="margin-bottom: 5px"> | |||||
<span class="join">{{ $t("login.signUp") }}</span> | |||||
</div> | |||||
<div> | |||||
<span class="join">{{ $t("login.forgetPwd") }}</span> | |||||
</div> | |||||
</div> | |||||
</el-form> | |||||
<el-form | |||||
v-if="!isLogin" | |||||
ref="loginFormRef" | |||||
:model="loginData" | |||||
class="login-form" | |||||
> | |||||
<div class="flex text-white items-center py-4"> | |||||
<span class="text-2xl flex-1 text-center">{{ $t("login.title") }}</span> | |||||
<lang-select style="color: #fff" /> | |||||
</div> | |||||
<el-form-item prop="username"> | |||||
<div class="p-2 text-white"> | |||||
<svg-icon icon-class="user" /> | |||||
</div> | |||||
<el-input | |||||
class="flex-1" | |||||
ref="username" | |||||
size="large" | |||||
v-model="loginData.username" | |||||
:placeholder="$t('login.username')" | |||||
name="username" | |||||
/> | |||||
</el-form-item> | |||||
<el-tooltip | |||||
:disabled="isCapslock === false" | |||||
content="Caps lock is On" | |||||
placement="right" | |||||
> | |||||
<el-form-item prop="password"> | |||||
<span class="p-2 text-white"> | |||||
<svg-icon icon-class="password" /> | |||||
</span> | |||||
<el-input | |||||
class="flex-1" | |||||
v-model="loginData.password" | |||||
placeholder="密码" | |||||
:type="passwordVisible === false ? 'password' : 'input'" | |||||
size="large" | |||||
name="password" | |||||
@keyup="checkCapslock" | |||||
@keyup.enter="handleLogin" | |||||
/> | |||||
<span class="mr-2" @click="passwordVisible = !passwordVisible"> | |||||
<svg-icon | |||||
:icon-class="passwordVisible === false ? 'eye' : 'eye-open'" | |||||
class="text-white cursor-pointer" | |||||
/> | |||||
</span> | |||||
</el-form-item> | |||||
</el-tooltip> | |||||
<!-- 验证码 --> | |||||
<el-form-item prop="verifyCode"> | |||||
<span class="p-2 text-white"> | |||||
<svg-icon icon-class="verify_code" /> | |||||
</span> | |||||
<el-input | |||||
v-model="loginData.verifyCode" | |||||
auto-complete="off" | |||||
:placeholder="$t('login.verifyCode')" | |||||
class="w-[60%]" | |||||
@keyup.enter="handleLogin" | |||||
/> | |||||
<div class="captcha"> | |||||
<img :src="captchaBase64" @click="getCaptcha" /> | |||||
</div> | |||||
</el-form-item> | |||||
<el-button | |||||
size="default" | |||||
:loading="loading" | |||||
type="primary" | |||||
class="loginBtn" | |||||
@click.prevent="handleLogin" | |||||
>{{ $t("login.login") }} | |||||
</el-button> | |||||
<div :class="currentLan == 'zh-cn' ? 'bottomBtnCN' : 'bottomBtnEN'"> | |||||
<div style="margin-bottom: 5px" @click="loginFlag"> | |||||
<span class="join">{{ $t("login.signUp") }}</span> | |||||
</div> | |||||
<div> | |||||
<span class="join">{{ $t("login.forgetPwd") }}</span> | |||||
</div> | |||||
</div> | </div> | ||||
</el-form> | </el-form> | ||||
</div> | </div> | ||||
@@ -111,6 +203,11 @@ import { LocationQuery, LocationQueryValue, useRoute } from "vue-router"; | |||||
import { getCaptchaApi } from "@/api/auth"; | import { getCaptchaApi } from "@/api/auth"; | ||||
import { LoginData } from "@/api/auth/types"; | import { LoginData } from "@/api/auth/types"; | ||||
import { useI18n } from "vue-i18n"; | |||||
const { locale } = useI18n(); | |||||
const currentLan = ref(locale); | |||||
const userStore = useUserStore(); | const userStore = useUserStore(); | ||||
const route = useRoute(); | const route = useRoute(); | ||||
@@ -118,19 +215,27 @@ const route = useRoute(); | |||||
* 按钮loading | * 按钮loading | ||||
*/ | */ | ||||
const loading = ref(false); | const loading = ref(false); | ||||
/** | /** | ||||
* 是否大写锁定 | * 是否大写锁定 | ||||
*/ | */ | ||||
const isCapslock = ref(false); | const isCapslock = ref(false); | ||||
/** | /** | ||||
* 密码是否可见 | * 密码是否可见 | ||||
*/ | */ | ||||
const passwordVisible = ref(false); | const passwordVisible = ref(false); | ||||
/** | /** | ||||
* 验证码图片Base64字符串 | * 验证码图片Base64字符串 | ||||
*/ | */ | ||||
const captchaBase64 = ref(); | const captchaBase64 = ref(); | ||||
/** | |||||
* 登录/注册 | |||||
*/ | |||||
const isLogin = ref(true); | |||||
/** | /** | ||||
* 登录表单引用 | * 登录表单引用 | ||||
*/ | */ | ||||
@@ -147,6 +252,14 @@ const loginRules = { | |||||
verifyCode: [{ required: true, trigger: "blur" }], | verifyCode: [{ required: true, trigger: "blur" }], | ||||
}; | }; | ||||
/** | |||||
* @description: 切换登录/注册状态 | |||||
*/ | |||||
function loginFlag() { | |||||
isLogin.value = !isLogin; | |||||
console.log(isLogin.value, "isLogin.value"); | |||||
} | |||||
/** | /** | ||||
* 密码校验器 | * 密码校验器 | ||||
*/ | */ | ||||
@@ -235,13 +348,15 @@ onMounted(() => { | |||||
max-width: 100%; | max-width: 100%; | ||||
padding: 20px; | padding: 20px; | ||||
overflow: hidden; | overflow: hidden; | ||||
backdrop-filter: blur(5px); | |||||
background: linear-gradient( | background: linear-gradient( | ||||
225deg, | |||||
rgb(81 217 202 / 100%), | |||||
rgb(114 100 245 / 100%) | |||||
123.64deg, | |||||
rgba(188, 193, 196, 0.1) -22.71%, | |||||
rgba(255, 255, 255, 0.039) 70.04% | |||||
); | ); | ||||
border-radius: 20px; | border-radius: 20px; | ||||
transform: translateY(-50%); | transform: translateY(-50%); | ||||
border: 1px solid #cacaca; | |||||
.captcha { | .captcha { | ||||
position: absolute; | position: absolute; | ||||
@@ -257,6 +372,54 @@ onMounted(() => { | |||||
} | } | ||||
} | } | ||||
.loginBtn { | |||||
width: 100%; | |||||
height: 40px; | |||||
background: linear-gradient(270deg, #4b61dc, #15d1b8); | |||||
transition: all 0.3s; | |||||
&:hover { | |||||
transform: scale(1.02); | |||||
} | |||||
} | |||||
.bottomBtnCN { | |||||
display: flex; | |||||
justify-content: space-between; | |||||
align-items: center; | |||||
padding: 0 10px; | |||||
color: #fff; | |||||
text-align: center; | |||||
margin-top: 20px; | |||||
.join { | |||||
cursor: pointer; | |||||
border-bottom: 2px solid #ffffff00; | |||||
padding: 0 3px; | |||||
padding-bottom: 2px; | |||||
transition: all 0.3s; | |||||
&:hover { | |||||
border-bottom: 2px solid #ffffff; | |||||
} | |||||
} | |||||
} | |||||
.bottomBtnEN { | |||||
padding: 0 10px; | |||||
color: #fff; | |||||
text-align: center; | |||||
margin-top: 20px; | |||||
.join { | |||||
cursor: pointer; | |||||
border-bottom: 2px solid #ffffff00; | |||||
padding: 0 3px; | |||||
padding-bottom: 2px; | |||||
transition: all 0.3s; | |||||
&:hover { | |||||
border-bottom: 2px solid #ffffff; | |||||
} | |||||
} | |||||
} | |||||
.videoBox { | .videoBox { | ||||
position: absolute; | position: absolute; | ||||
top: 0; | top: 0; | ||||
@@ -268,13 +431,12 @@ onMounted(() => { | |||||
.el-form-item { | .el-form-item { | ||||
background: rgb(0 0 0 / 10%); | background: rgb(0 0 0 / 10%); | ||||
border: 1px solid rgb(255 255 255 / 10%); | |||||
border: 1px solid #ffffff2d; | |||||
border-radius: 5px; | border-radius: 5px; | ||||
} | } | ||||
.el-input { | .el-input { | ||||
background: transparent; | background: transparent; | ||||
// 子组件 scoped 无效,使用 :deep | // 子组件 scoped 无效,使用 :deep | ||||
:deep(.el-input__wrapper) { | :deep(.el-input__wrapper) { | ||||
padding: 0; | padding: 0; | ||||
@@ -17,8 +17,13 @@ | |||||
</div> | </div> | ||||
<!-- 剩余次数 --> | <!-- 剩余次数 --> | ||||
<div class="left-two border"> | <div class="left-two border"> | ||||
<div class="number">3 Videos Generation Left</div> | |||||
<div class="time">Until 2023/12/31</div> | |||||
<div v-if="currentLan == 'en'" class="number"> | |||||
3 Videos Generation Left | |||||
</div> | |||||
<div v-if="currentLan == 'zh-cn'" class="number"> | |||||
剩余生成视频次数:3条 | |||||
</div> | |||||
<div class="time">{{ $t("account.until") }} 2023/12/31</div> | |||||
<div class="getMore pointer" @click="routerTo('/myStore')"> | <div class="getMore pointer" @click="routerTo('/myStore')"> | ||||
{{ $t("account.getMore") }} | {{ $t("account.getMore") }} | ||||
</div> | </div> | ||||
@@ -27,12 +32,19 @@ | |||||
<div class="left-three border flex justify-between"> | <div class="left-three border flex justify-between"> | ||||
<div class="codeImg"></div> | <div class="codeImg"></div> | ||||
<div class="other"> | <div class="other"> | ||||
<div> | |||||
Invite friends to get | |||||
<div v-if="currentLan == 'en'"> | |||||
<span>Invite friends to get</span> | |||||
<span class="bigWord">free</span> | <span class="bigWord">free</span> | ||||
video | |||||
<span class="bigWord">Generation</span> | |||||
<span>video</span> | |||||
<span class="bigWord">Generation!</span> | |||||
</div> | </div> | ||||
<div v-if="currentLan == 'zh-cn'"> | |||||
<span>邀请更多好友</span> | |||||
<span class="bigWord">免费</span> | |||||
<span>生成视频!</span> | |||||
</div> | |||||
<div class="btns"> | <div class="btns"> | ||||
<span class="myBtn">{{ $t("account.copyCode") }}</span> | <span class="myBtn">{{ $t("account.copyCode") }}</span> | ||||
<span class="myBtn">{{ $t("account.saveCode") }}</span> | <span class="myBtn">{{ $t("account.saveCode") }}</span> | ||||
@@ -80,13 +92,20 @@ | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</template> | </template> | ||||
<script setup> | <script setup> | ||||
import { useI18n } from "vue-i18n"; | |||||
import { useRouter } from "vue-router"; | import { useRouter } from "vue-router"; | ||||
const { locale } = useI18n(); | |||||
const currentLan = ref(locale); | |||||
const router = useRouter(); | const router = useRouter(); | ||||
function routerTo(url) { | function routerTo(url) { | ||||
router.push(url); | router.push(url); | ||||
} | } | ||||
</script> | </script> | ||||
<style lang="scss" scoped> | <style lang="scss" scoped> | ||||
.page { | .page { | ||||
height: 887px; | height: 887px; | ||||
@@ -128,7 +147,7 @@ function routerTo(url) { | |||||
.password { | .password { | ||||
display: inline-block; | display: inline-block; | ||||
float: right; | |||||
// float: right; | |||||
padding: 2px 8px; | padding: 2px 8px; | ||||
border: 1px solid #000; | border: 1px solid #000; | ||||
border-radius: 5px; | border-radius: 5px; | ||||
@@ -299,5 +318,6 @@ function routerTo(url) { | |||||
.bigWord { | .bigWord { | ||||
font-size: 30px; | font-size: 30px; | ||||
font-weight: 700; | font-weight: 700; | ||||
margin: 0 10px; | |||||
} | } | ||||
</style> | </style> |
@@ -9,7 +9,7 @@ | |||||
<div class="content"> | <div class="content"> | ||||
<div class="title"> | <div class="title"> | ||||
<el-icon><i-ep-edit /></el-icon> | <el-icon><i-ep-edit /></el-icon> | ||||
{{ item.title }} | |||||
{{ $t(item.title) }} | |||||
</div> | </div> | ||||
<div class="money"> | <div class="money"> | ||||
<span class="price">{{ item.price }}元</span>/{{ | <span class="price">{{ item.price }}元</span>/{{ | ||||
@@ -21,7 +21,7 @@ | |||||
>分钟/月 | >分钟/月 | ||||
</div> | </div> | ||||
<div class="button" :style="`background-color:${item.color} ;`"> | <div class="button" :style="`background-color:${item.color} ;`"> | ||||
选择方案 | |||||
{{ $t("shop.selectPlan") }} | |||||
</div> | </div> | ||||
</div> | </div> | ||||
<!-- 商品权益 --> | <!-- 商品权益 --> | ||||
@@ -43,7 +43,7 @@ | |||||
<el-dialog v-model="showPayDialog" width="60%" center> | <el-dialog v-model="showPayDialog" width="60%" center> | ||||
<div class="payDialog"> | <div class="payDialog"> | ||||
<div class="left"> | <div class="left"> | ||||
<div class="title">{{ presentItme.value.title }}</div> | |||||
<div class="title">{{ $t(presentItme.value.title) }}</div> | |||||
<div class="price"> | <div class="price"> | ||||
<span>{{ presentItme.value.price }}</span> | <span>{{ presentItme.value.price }}</span> | ||||
元/{{ presentItme.value.priceTime }} | 元/{{ presentItme.value.priceTime }} | ||||
@@ -57,8 +57,10 @@ | |||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="right"> | <div class="right"> | ||||
<div style="background-color: #07c160">微信支付</div> | |||||
<div style="background-color: #1378fe">支付宝支付</div> | |||||
<div style="background-color: #07c160"> | |||||
{{ $t("shop.WeChatPay") }} | |||||
</div> | |||||
<div style="background-color: #1378fe">{{ $t("shop.Alipay") }}</div> | |||||
<div style="background-color: #0070ba">Paypal</div> | <div style="background-color: #0070ba">Paypal</div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
@@ -70,13 +72,13 @@ const title = ref(""); | |||||
const list = reactive([ | const list = reactive([ | ||||
{ | { | ||||
color: "#f3af00", | color: "#f3af00", | ||||
title: "基础款", | |||||
title: "shop.Lite", | |||||
icon: "", | icon: "", | ||||
price: "0", | price: "0", | ||||
priceTime: "14天", | priceTime: "14天", | ||||
time: "5", | time: "5", | ||||
authority: [ | authority: [ | ||||
"D-ID 水印", | |||||
"Metavatar 水印", | |||||
"高级和标准演示器", | "高级和标准演示器", | ||||
"15 个 AI 演示提示", | "15 个 AI 演示提示", | ||||
"AI 脚本世代", | "AI 脚本世代", | ||||
@@ -84,13 +86,13 @@ const list = reactive([ | |||||
}, | }, | ||||
{ | { | ||||
color: "#c971ff", | color: "#c971ff", | ||||
title: "升级款", | |||||
title: "shop.Pro", | |||||
icon: "", | icon: "", | ||||
price: "10", | price: "10", | ||||
priceTime: "30天", | priceTime: "30天", | ||||
time: "10", | time: "10", | ||||
authority: [ | authority: [ | ||||
"D-ID 水印", | |||||
"Metavatar 水印", | |||||
"高级和标准演示器", | "高级和标准演示器", | ||||
"15 个 AI 演示提示", | "15 个 AI 演示提示", | ||||
"AI 脚本世代", | "AI 脚本世代", | ||||
@@ -99,13 +101,13 @@ const list = reactive([ | |||||
}, | }, | ||||
{ | { | ||||
color: "#7b61ff", | color: "#7b61ff", | ||||
title: "豪华款", | |||||
title: "shop.Adv", | |||||
icon: "", | icon: "", | ||||
price: "20", | price: "20", | ||||
priceTime: "30天", | priceTime: "30天", | ||||
time: "15", | time: "15", | ||||
authority: [ | authority: [ | ||||
"D-ID 水印", | |||||
"Metavatar 水印", | |||||
"高级和标准演示器", | "高级和标准演示器", | ||||
"15 个 AI 演示提示", | "15 个 AI 演示提示", | ||||
"AI 脚本世代", | "AI 脚本世代", | ||||
@@ -115,13 +117,13 @@ const list = reactive([ | |||||
}, | }, | ||||
{ | { | ||||
color: "#000", | color: "#000", | ||||
title: "探索款", | |||||
title: "shop.Ent", | |||||
icon: "", | icon: "", | ||||
price: "50", | price: "50", | ||||
priceTime: "30天", | priceTime: "30天", | ||||
time: "40", | time: "40", | ||||
authority: [ | authority: [ | ||||
"D-ID 水印", | |||||
"Metavatar 水印", | |||||
"高级和标准演示器", | "高级和标准演示器", | ||||
"15 个 AI 演示提示", | "15 个 AI 演示提示", | ||||
"AI 脚本世代", | "AI 脚本世代", | ||||