| @@ -5,13 +5,8 @@ | |||
| "type": "module", | |||
| "scripts": { | |||
| "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": { | |||
| "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> | |||
| <el-dropdown class="outSide" trigger="click" @command="handleLanguageChange"> | |||
| <div> | |||
| @@ -38,6 +19,25 @@ function handleLanguageChange(lang: string) { | |||
| </el-dropdown> | |||
| </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> | |||
| .outSide { | |||
| cursor: pointer; | |||
| @@ -7,11 +7,13 @@ export default { | |||
| // 登录页面国际化 | |||
| login: { | |||
| title: "Metavatar-PC", | |||
| title: "Metavatar-PC Sign in", | |||
| username: "Username", | |||
| password: "Password", | |||
| login: "Login", | |||
| login: "Sign in", | |||
| verifyCode: "Verify Code", | |||
| signUp: "Don't have an account? Sign up here", | |||
| forgetPwd: "I forgot my password", | |||
| }, | |||
| // 导航栏国际化 | |||
| @@ -51,6 +53,7 @@ export default { | |||
| account: { | |||
| title: "My Account", | |||
| reSetPwd: "Reset Password", | |||
| until: "until", | |||
| getMore: "UPGRADE", | |||
| copyCode: "Copy invitation code", | |||
| saveCode: "Save QR code", | |||
| @@ -67,5 +70,13 @@ export default { | |||
| // 商店 | |||
| 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: { | |||
| title: "Metavatar-PC", | |||
| title: "Metavatar-PC 登录", | |||
| username: "用户名", | |||
| password: "密码", | |||
| login: "登 录", | |||
| verifyCode: "验证码", | |||
| signUp: "没有账号?现在加入!", | |||
| forgetPwd: "忘记密码", | |||
| }, | |||
| // 导航栏国际化 | |||
| @@ -51,6 +53,7 @@ export default { | |||
| account: { | |||
| title: "个人账户", | |||
| reSetPwd: "修改密码", | |||
| until: "有效期至", | |||
| getMore: "升级套餐", | |||
| copyCode: "复制邀请码", | |||
| saveCode: "保存二维码", | |||
| @@ -67,5 +70,13 @@ export default { | |||
| // 商店 | |||
| shop: { | |||
| 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 { useTagsViewStore } from "@/store/modules/tagsView"; | |||
| import { useUserStore } from "@/store/modules/user"; | |||
| import { useI18n } from "vue-i18n"; | |||
| import LangSelect from "@/components/LangSelect/index.vue"; | |||
| const { locale } = useI18n(); | |||
| // 当前语言状态与国际化组件 | |||
| const lanChange = ref(locale); | |||
| const appStore = useAppStore(); | |||
| const tagsViewStore = useTagsViewStore(); | |||
| const userStore = useUserStore(); | |||
| @@ -106,10 +112,16 @@ const { isFullscreen, toggle } = useFullscreen(); | |||
| * 注销 | |||
| */ | |||
| 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(() => { | |||
| userStore | |||
| .logout() | |||
| @@ -186,10 +198,9 @@ watch( | |||
| height: 80px; | |||
| padding: 0 20px; | |||
| color: #fff; | |||
| background-color: #fff; | |||
| background-color: #000; | |||
| box-shadow: 0 0 1px #0003; | |||
| padding-right: 50px; | |||
| .avatar { | |||
| width: 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"> | |||
| import { | |||
| getCurrentInstance, | |||
| @@ -241,63 +298,6 @@ onMounted(() => { | |||
| }); | |||
| </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> | |||
| .tags-container { | |||
| width: 100%; | |||
| @@ -2,8 +2,10 @@ | |||
| export {} | |||
| declare global { | |||
| const EffectScope: typeof import('vue')['EffectScope'] | |||
| const ElForm: typeof import('element-plus/es')['ElForm'] | |||
| const ElMessage: typeof import('element-plus/es')['ElMessage'] | |||
| const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] | |||
| const ElTree: typeof import('element-plus/es')['ElTree'] | |||
| const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] | |||
| const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] | |||
| const computed: typeof import('vue')['computed'] | |||
| @@ -270,8 +272,10 @@ import { UnwrapRef } from 'vue' | |||
| declare module 'vue' { | |||
| interface ComponentCustomProperties { | |||
| 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 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 autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']> | |||
| readonly computed: UnwrapRef<typeof import('vue')['computed']> | |||
| @@ -141,6 +141,7 @@ | |||
| </template> | |||
| <script setup> | |||
| import { ref } from "vue"; | |||
| import { getCurrentInstance } from "vue"; | |||
| // 标题 | |||
| let title = ref(""); | |||
| // 上传图片 | |||
| @@ -150,7 +151,7 @@ function changeFile(e) { | |||
| } | |||
| // $t在script中的使用 | |||
| import { getCurrentInstance } from "vue"; | |||
| const { | |||
| appContext: { | |||
| config: { globalProperties }, | |||
| @@ -9,10 +9,9 @@ | |||
| ></video> | |||
| </div> | |||
| <el-form | |||
| v-if="true" | |||
| v-if="isLogin" | |||
| ref="loginFormRef" | |||
| :model="loginData" | |||
| :rules="loginRules" | |||
| class="login-form" | |||
| > | |||
| <div class="flex text-white items-center py-4"> | |||
| @@ -84,15 +83,108 @@ | |||
| size="default" | |||
| :loading="loading" | |||
| type="primary" | |||
| class="w-full" | |||
| class="loginBtn" | |||
| @click.prevent="handleLogin" | |||
| >{{ $t("login.login") }} | |||
| </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> | |||
| </el-form> | |||
| </div> | |||
| @@ -111,6 +203,11 @@ import { LocationQuery, LocationQueryValue, useRoute } from "vue-router"; | |||
| import { getCaptchaApi } from "@/api/auth"; | |||
| import { LoginData } from "@/api/auth/types"; | |||
| import { useI18n } from "vue-i18n"; | |||
| const { locale } = useI18n(); | |||
| const currentLan = ref(locale); | |||
| const userStore = useUserStore(); | |||
| const route = useRoute(); | |||
| @@ -118,19 +215,27 @@ const route = useRoute(); | |||
| * 按钮loading | |||
| */ | |||
| const loading = ref(false); | |||
| /** | |||
| * 是否大写锁定 | |||
| */ | |||
| const isCapslock = ref(false); | |||
| /** | |||
| * 密码是否可见 | |||
| */ | |||
| const passwordVisible = ref(false); | |||
| /** | |||
| * 验证码图片Base64字符串 | |||
| */ | |||
| const captchaBase64 = ref(); | |||
| /** | |||
| * 登录/注册 | |||
| */ | |||
| const isLogin = ref(true); | |||
| /** | |||
| * 登录表单引用 | |||
| */ | |||
| @@ -147,6 +252,14 @@ const loginRules = { | |||
| 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%; | |||
| padding: 20px; | |||
| overflow: hidden; | |||
| backdrop-filter: blur(5px); | |||
| 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; | |||
| transform: translateY(-50%); | |||
| border: 1px solid #cacaca; | |||
| .captcha { | |||
| 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 { | |||
| position: absolute; | |||
| top: 0; | |||
| @@ -268,13 +431,12 @@ onMounted(() => { | |||
| .el-form-item { | |||
| background: rgb(0 0 0 / 10%); | |||
| border: 1px solid rgb(255 255 255 / 10%); | |||
| border: 1px solid #ffffff2d; | |||
| border-radius: 5px; | |||
| } | |||
| .el-input { | |||
| background: transparent; | |||
| // 子组件 scoped 无效,使用 :deep | |||
| :deep(.el-input__wrapper) { | |||
| padding: 0; | |||
| @@ -17,8 +17,13 @@ | |||
| </div> | |||
| <!-- 剩余次数 --> | |||
| <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')"> | |||
| {{ $t("account.getMore") }} | |||
| </div> | |||
| @@ -27,12 +32,19 @@ | |||
| <div class="left-three border flex justify-between"> | |||
| <div class="codeImg"></div> | |||
| <div class="other"> | |||
| <div> | |||
| Invite friends to get | |||
| <div v-if="currentLan == 'en'"> | |||
| <span>Invite friends to get</span> | |||
| <span class="bigWord">free</span> | |||
| video | |||
| <span class="bigWord">Generation</span> | |||
| <span>video</span> | |||
| <span class="bigWord">Generation!</span> | |||
| </div> | |||
| <div v-if="currentLan == 'zh-cn'"> | |||
| <span>邀请更多好友</span> | |||
| <span class="bigWord">免费</span> | |||
| <span>生成视频!</span> | |||
| </div> | |||
| <div class="btns"> | |||
| <span class="myBtn">{{ $t("account.copyCode") }}</span> | |||
| <span class="myBtn">{{ $t("account.saveCode") }}</span> | |||
| @@ -80,13 +92,20 @@ | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script setup> | |||
| import { useI18n } from "vue-i18n"; | |||
| import { useRouter } from "vue-router"; | |||
| const { locale } = useI18n(); | |||
| const currentLan = ref(locale); | |||
| const router = useRouter(); | |||
| function routerTo(url) { | |||
| router.push(url); | |||
| } | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .page { | |||
| height: 887px; | |||
| @@ -128,7 +147,7 @@ function routerTo(url) { | |||
| .password { | |||
| display: inline-block; | |||
| float: right; | |||
| // float: right; | |||
| padding: 2px 8px; | |||
| border: 1px solid #000; | |||
| border-radius: 5px; | |||
| @@ -299,5 +318,6 @@ function routerTo(url) { | |||
| .bigWord { | |||
| font-size: 30px; | |||
| font-weight: 700; | |||
| margin: 0 10px; | |||
| } | |||
| </style> | |||
| @@ -9,7 +9,7 @@ | |||
| <div class="content"> | |||
| <div class="title"> | |||
| <el-icon><i-ep-edit /></el-icon> | |||
| {{ item.title }} | |||
| {{ $t(item.title) }} | |||
| </div> | |||
| <div class="money"> | |||
| <span class="price">{{ item.price }}元</span>/{{ | |||
| @@ -21,7 +21,7 @@ | |||
| >分钟/月 | |||
| </div> | |||
| <div class="button" :style="`background-color:${item.color} ;`"> | |||
| 选择方案 | |||
| {{ $t("shop.selectPlan") }} | |||
| </div> | |||
| </div> | |||
| <!-- 商品权益 --> | |||
| @@ -43,7 +43,7 @@ | |||
| <el-dialog v-model="showPayDialog" width="60%" center> | |||
| <div class="payDialog"> | |||
| <div class="left"> | |||
| <div class="title">{{ presentItme.value.title }}</div> | |||
| <div class="title">{{ $t(presentItme.value.title) }}</div> | |||
| <div class="price"> | |||
| <span>{{ presentItme.value.price }}</span> | |||
| 元/{{ presentItme.value.priceTime }} | |||
| @@ -57,8 +57,10 @@ | |||
| </div> | |||
| </div> | |||
| <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> | |||
| </div> | |||
| @@ -70,13 +72,13 @@ const title = ref(""); | |||
| const list = reactive([ | |||
| { | |||
| color: "#f3af00", | |||
| title: "基础款", | |||
| title: "shop.Lite", | |||
| icon: "", | |||
| price: "0", | |||
| priceTime: "14天", | |||
| time: "5", | |||
| authority: [ | |||
| "D-ID 水印", | |||
| "Metavatar 水印", | |||
| "高级和标准演示器", | |||
| "15 个 AI 演示提示", | |||
| "AI 脚本世代", | |||
| @@ -84,13 +86,13 @@ const list = reactive([ | |||
| }, | |||
| { | |||
| color: "#c971ff", | |||
| title: "升级款", | |||
| title: "shop.Pro", | |||
| icon: "", | |||
| price: "10", | |||
| priceTime: "30天", | |||
| time: "10", | |||
| authority: [ | |||
| "D-ID 水印", | |||
| "Metavatar 水印", | |||
| "高级和标准演示器", | |||
| "15 个 AI 演示提示", | |||
| "AI 脚本世代", | |||
| @@ -99,13 +101,13 @@ const list = reactive([ | |||
| }, | |||
| { | |||
| color: "#7b61ff", | |||
| title: "豪华款", | |||
| title: "shop.Adv", | |||
| icon: "", | |||
| price: "20", | |||
| priceTime: "30天", | |||
| time: "15", | |||
| authority: [ | |||
| "D-ID 水印", | |||
| "Metavatar 水印", | |||
| "高级和标准演示器", | |||
| "15 个 AI 演示提示", | |||
| "AI 脚本世代", | |||
| @@ -115,13 +117,13 @@ const list = reactive([ | |||
| }, | |||
| { | |||
| color: "#000", | |||
| title: "探索款", | |||
| title: "shop.Ent", | |||
| icon: "", | |||
| price: "50", | |||
| priceTime: "30天", | |||
| time: "40", | |||
| authority: [ | |||
| "D-ID 水印", | |||
| "Metavatar 水印", | |||
| "高级和标准演示器", | |||
| "15 个 AI 演示提示", | |||
| "AI 脚本世代", | |||