소스 검색

Merge remote-tracking branch 'origin/dev_YWQ' into dev_czc

dev_YWQ
congzc 1 년 전
부모
커밋
5d8c25b17f
11개의 변경된 파일342개의 추가작업 그리고 125개의 파일을 삭제
  1. +2
    -7
      package.json
  2. +19
    -19
      src/components/LangSelect/index.vue
  3. +13
    -2
      src/lang/package/en.ts
  4. +12
    -1
      src/lang/package/zh-cn.ts
  5. +17
    -6
      src/layout/components/Navbar.vue
  6. +57
    -57
      src/layout/components/TagsView/index.vue
  7. +4
    -0
      src/types/auto-imports.d.ts
  8. +2
    -1
      src/views/createVideo/index.vue
  9. +174
    -12
      src/views/login/index.vue
  10. +27
    -7
      src/views/myAccount/index.vue
  11. +15
    -13
      src/views/myStore/index.vue

+ 2
- 7
package.json 파일 보기

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


+ 19
- 19
src/components/LangSelect/index.vue 파일 보기

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


+ 13
- 2
src/lang/package/en.ts 파일 보기

@@ -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",
},
};

+ 12
- 1
src/lang/package/zh-cn.ts 파일 보기

@@ -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: "支付宝支付",
},
};

+ 17
- 6
src/layout/components/Navbar.vue 파일 보기

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


+ 57
- 57
src/layout/components/TagsView/index.vue 파일 보기

@@ -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%;


+ 4
- 0
src/types/auto-imports.d.ts 파일 보기

@@ -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']>


+ 2
- 1
src/views/createVideo/index.vue 파일 보기

@@ -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 },


+ 174
- 12
src/views/login/index.vue 파일 보기

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


+ 27
- 7
src/views/myAccount/index.vue 파일 보기

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

+ 15
- 13
src/views/myStore/index.vue 파일 보기

@@ -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 脚本世代",


불러오는 중...
취소
저장