feat: login i18n

This commit is contained in:
KUN1007 2023-09-24 01:05:48 +08:00
parent 506788fc76
commit e11054ed0d
9 changed files with 225 additions and 135 deletions

View file

@ -6,6 +6,10 @@ import { useKUNGalgameUserStore } from '@/store/modules/kungalgamer'
import { useKUNGalgameMessageStore } from '@/store/modules/message' import { useKUNGalgameMessageStore } from '@/store/modules/message'
const info = useKUNGalgameMessageStore() const info = useKUNGalgameMessageStore()
// i18n
import { useI18n } from 'vue-i18n'
const { tm } = useI18n()
// //
const props = defineProps<{ const props = defineProps<{
email: string email: string
@ -37,16 +41,14 @@ const sendCode = () => {
// //
useKUNGalgameUserStore().sendCode(props.email) useKUNGalgameUserStore().sendCode(props.email)
info.info( info.info(tm('AlertInfo.code.code'))
'验证码已发送,您可以在邮箱中查看,注意查看垃圾邮件,如果未收到邮件,您可以在 30 秒后尝试重新发送邮箱验证码'
)
} }
} }
</script> </script>
<template> <template>
<button @click="sendCode" :disabled="isSending"> <button @click="sendCode" :disabled="isSending">
{{ isSending ? countdown : '发送验证码' }} {{ isSending ? countdown : $tm('login.register.send') }}
</button> </button>
</template> </template>

View file

@ -168,6 +168,7 @@ export default {
publish: 'Confirm Publish', publish: 'Confirm Publish',
draft: 'Save Draft', draft: 'Save Draft',
}, },
login: {
login: { login: {
loginTitle: 'Login', loginTitle: 'Login',
forget: 'Forgot your password? Click here to send reset email', forget: 'Forgot your password? Click here to send reset email',
@ -175,6 +176,30 @@ export default {
loginPassword: 'Password', loginPassword: 'Password',
capture: 'Click to proceed with the human verification process', capture: 'Click to proceed with the human verification process',
}, },
overlay: {
title: 'Welcome to KUN Visual Novel Registration',
world: `The World's`,
moe: 'Cutest',
forum: 'Galgame Forum',
login: 'Login',
home: 'Welcome Back',
kun: 'KUN Visual Novel Gives You',
hug: 'The Warmest Hug',
register: 'Register',
},
register: {
title: 'Register',
name: 'username',
email: 'email',
password: 'password',
code: 'Email verification code',
send: 'Send code',
click: 'Clicking Register means you have agreed to our',
agreement: 'User Agreement',
and: 'and',
privacy: 'Privacy Policy',
},
},
// 非页面组件这里统一用大驼峰 // 非页面组件这里统一用大驼峰
ComponentAlert: { ComponentAlert: {
confirm: 'OK', confirm: 'OK',
@ -189,8 +214,6 @@ export default {
}, },
login: { login: {
success: 'Login Successfully! Welcome to KUN Visual Novel ~ ', success: 'Login Successfully! Welcome to KUN Visual Novel ~ ',
emptyUsername: 'Username cannot be empty',
emptyPassword: 'Password cannot be empty',
invalidUsername: invalidUsername:
'Invalid username. Username should be 1 to 17 characters long and can include: Chinese characters, English letters, numbers, underscore, and tilde (~)', 'Invalid username. Username should be 1 to 17 characters long and can include: Chinese characters, English letters, numbers, underscore, and tilde (~)',
invalidPassword: invalidPassword:
@ -206,5 +229,8 @@ export default {
hint4: `Za~~~ko~♡ zako~♡ Just you wait, you'll regret it in the end`, hint4: `Za~~~ko~♡ zako~♡ Just you wait, you'll regret it in the end`,
answer: 'Answer', answer: 'Answer',
}, },
code: {
code: `The verification code has been sent. You can check it in your email. Please check your spam folder. If you haven't received the email, you can try to resend the email verification code after 30 seconds.`,
},
}, },
} }

View file

@ -167,6 +167,7 @@ export default {
publish: '确认发布', publish: '确认发布',
draft: '保存草稿', draft: '保存草稿',
}, },
login: {
login: { login: {
loginTitle: '登录', loginTitle: '登录',
forget: '忘记密码? 点击发送重置邮件', forget: '忘记密码? 点击发送重置邮件',
@ -174,6 +175,30 @@ export default {
loginPassword: '密码', loginPassword: '密码',
capture: '点击进行人机身份验证', capture: '点击进行人机身份验证',
}, },
overlay: {
title: '欢迎注册 KUNgalgame',
world: `世界上最`,
moe: '萌',
forum: '的 Galgame 论坛',
login: '登录',
home: '欢迎回家',
kun: 'KUNgalgame 给你',
hug: '最温暖的拥抱',
register: '注册',
},
register: {
title: '注册',
name: '用户名',
email: '邮箱',
password: '密码',
code: '邮箱验证码',
send: '发送验证码',
click: '点击注册表示您已经同意我们的',
agreement: '用户协议',
and: '和',
privacy: '隐私政策',
},
},
// 非页面组件这里统一用大驼峰 // 非页面组件这里统一用大驼峰
ComponentAlert: { ComponentAlert: {
confirm: '确定', confirm: '确定',
@ -188,8 +213,6 @@ export default {
}, },
login: { login: {
login: '登陆成功!欢迎来到 鲲 Galgame ~ ', login: '登陆成功!欢迎来到 鲲 Galgame ~ ',
emptyUsername: '用户名不可为空',
emptyPassword: '密码不可为空',
invalidUsername: invalidUsername:
'非法的用户名,用户名为 1 到 17 位,可以包含:中文、英文、数字、下划线、波浪线', '非法的用户名,用户名为 1 到 17 位,可以包含:中文、英文、数字、下划线、波浪线',
invalidPassword: invalidPassword:
@ -205,5 +228,8 @@ export default {
hint4: '杂~~~鱼~♡杂鱼~♡你就看吧,最后害的还是你自己', hint4: '杂~~~鱼~♡杂鱼~♡你就看吧,最后害的还是你自己',
answer: '答案', answer: '答案',
}, },
code: {
code: '验证码已发送,您可以在邮箱中查看,注意查看垃圾邮件,如果未收到邮件,您可以在 30 秒后尝试重新发送邮箱验证码',
},
}, },
} }

View file

@ -36,6 +36,7 @@
width: 100%; width: 100%;
/* 头部的颜色 */ /* 头部的颜色 */
background-color: var(--kungalgame-trans-blue-0); background-color: var(--kungalgame-trans-blue-0);
border-radius: 7px 7px 0 0;
align-items: center; align-items: center;
span:nth-child(1) { span:nth-child(1) {
font-family: serif; font-family: serif;

View file

@ -70,6 +70,7 @@ const navBar: nav[] = [
height: 100%; height: 100%;
width: 120px; width: 120px;
background-color: var(--kungalgame-trans-pink-0); background-color: var(--kungalgame-trans-pink-0);
border-radius: 0 0 0 7px;
border-right: 1px solid var(--kungalgame-blue-4); border-right: 1px solid var(--kungalgame-blue-4);
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;

View file

@ -38,19 +38,28 @@ const handleClickRegister = () => {
<div class="overlay"> <div class="overlay">
<div class="panel left"> <div class="panel left">
<h2> <h2>
欢迎注册 KUNgalgame <br /><br /> 世界上最<span></span> {{ $tm('login.overlay.title') }}<br /><br />
galgame 论坛 {{ $tm('login.overlay.world') }}
<span>{{ $tm('login.overlay.moe') }}</span>
{{ $tm('login.overlay.forum') }}
</h2> </h2>
<br /> <br />
<button class="btn" @click="handleClickSignIn">登陆</button> <button class="btn" @click="handleClickSignIn">
{{ $tm('login.overlay.login') }}
</button>
</div> </div>
<div class="panel right"> <div class="panel right">
<h2> <h2>
欢迎回家 <br /> {{ $tm('login.overlay.home') }}
<br /> <br />
KUNgalgame 给你最温暖的拥抱 <br />
{{ $tm('login.overlay.kun') }}
<br />
{{ $tm('login.overlay.hug') }}
</h2> </h2>
<button class="btn" @click="handleClickRegister">注册</button> <button class="btn" @click="handleClickRegister">
{{ $tm('login.overlay.register') }}
</button>
</div> </div>
</div> </div>
</div> </div>
@ -98,44 +107,28 @@ const handleClickRegister = () => {
width: 100%; width: 100%;
} }
.container.active .login { .container.active {
.login {
transform: translateX(100%); transform: translateX(100%);
} }
.register {
.container.active .register {
animation: show 0.6s; animation: show 0.6s;
opacity: 1; opacity: 1;
transform: translateX(100%); transform: translateX(100%);
z-index: 5; z-index: 5;
}
@keyframes show {
0%,
49.99% {
opacity: 0;
z-index: 1;
} }
.container-overlay {
50%,
100% {
opacity: 1;
z-index: 5;
}
}
.container.active .container-overlay {
transform: translateX(-100%); transform: translateX(-100%);
} }
.overlay {
.container.active .overlay {
transform: translateX(50%); transform: translateX(50%);
} }
.left {
.container.active .left {
transform: translateX(0); transform: translateX(0);
} }
.right {
.container.active .right {
transform: translateX(20%); transform: translateX(20%);
}
} }
.container-overlay { .container-overlay {
@ -155,28 +148,29 @@ const handleClickRegister = () => {
transform: translateX(0); transform: translateX(0);
transition: transform 0.6s ease-in-out; transition: transform 0.6s ease-in-out;
width: 200%; width: 200%;
h2 {
margin-top: 150px;
font-size: medium;
color: var(--kungalgame-font-color-2);
}
h2 span {
color: var(--kungalgame-red-4);
padding: 0 5px;
font-size: 20px;
}
.left {
transform: translateX(-20%);
}
.right {
right: 0;
transform: translateX(0);
}
} }
.btn {
position: absolute;
bottom: 7%;
border-radius: 50px;
background-color: var(--kungalgame-trans-white-5);
border: 1px solid var(--kungalgame-blue-4);
color: var(--kungalgame-blue-4);
cursor: pointer;
font-size: 0.9em;
letter-spacing: 0.1rem;
padding: 0.6rem 4rem;
text-transform: uppercase;
transition: transform 80ms ease-in;
overflow: hidden;
white-space: nowrap;
}
.btn:hover {
background-color: var(--kungalgame-blue-4);
color: var(--kungalgame-white);
}
/* 交互页面的盒子 */ /* 交互页面的盒子 */
.panel { .panel {
align-items: center; align-items: center;
@ -192,45 +186,47 @@ const handleClickRegister = () => {
width: 50%; width: 50%;
background-color: var(--kungalgame-trans-white-5); background-color: var(--kungalgame-trans-white-5);
} }
.btn { .btn {
width: 150px;
position: absolute;
border-radius: 50px;
cursor: pointer;
font-size: 15px;
letter-spacing: 2px;
padding: 7px 0px;
text-transform: uppercase;
transition: all 0.2s;
overflow: hidden;
white-space: nowrap;
/* 红色按钮距离底部的距离 */ /* 红色按钮距离底部的距离 */
position: absolute; position: absolute;
bottom: 7%; bottom: 10%;
background-color: var(--kungalgame-red-0); background-color: var(--kungalgame-red-0);
border: 1px solid var(--kungalgame-red-4); border: 1px solid var(--kungalgame-red-4);
color: var(--kungalgame-red-4); color: var(--kungalgame-red-4);
}
.btn:hover { &:hover {
background-color: var(--kungalgame-red-4); background-color: var(--kungalgame-red-4);
color: var(--kungalgame-white); color: var(--kungalgame-white);
} }
h2 { &:active {
margin-top: 150px;
font-size: medium;
color: var(--kungalgame-font-color-2);
}
h2 span {
color: var(--kungalgame-red-4);
padding: 0 5px;
font-size: 20px;
}
.left {
transform: translateX(-20%);
}
.right {
right: 0;
transform: translateX(0);
}
.btn:active {
transform: scale(0.95); transform: scale(0.95);
}
} }
.btn:focus { @keyframes show {
outline: none; 0%,
49.99% {
opacity: 0;
z-index: 1;
}
50%,
100% {
opacity: 1;
z-index: 5;
}
} }
</style> </style>

View file

@ -4,6 +4,8 @@ import { useKUNGalgameUserStore } from '@/store/modules/kungalgamer'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
// 使 // 使
import { useKUNGalgameMessageStore } from '@/store/modules/message' import { useKUNGalgameMessageStore } from '@/store/modules/message'
//
import message from '@/components/alert/Message'
// //
const Capture = defineAsyncComponent( const Capture = defineAsyncComponent(
@ -38,7 +40,6 @@ const handleVerify = (result: boolean) => {
if (result) { if (result) {
// //
isShowValidate.value = false isShowValidate.value = false
info.info(tm('AlertInfo.capture.success'))
isCaptureComplete.value = true isCaptureComplete.value = true
} }
} }
@ -47,11 +48,11 @@ const isEmptyInput = (): boolean => {
// //
if (!loginForm.name.trim()) { if (!loginForm.name.trim()) {
// //
info.info(tm('AlertInfo.login.emptyUsername')) message('Username cannot be empty', '用户名不可为空', 'warn')
return false return false
} else if (!loginForm.password.trim()) { } else if (!loginForm.password.trim()) {
// //
info.info(tm('AlertInfo.login.emptyPassword')) message('Password cannot be empty', '密码不可为空', 'warn')
return false return false
} else { } else {
return true return true
@ -86,7 +87,11 @@ const handleLogin = () => {
} }
// //
if (!isCaptureComplete.value) { if (!isCaptureComplete.value) {
info.info(tm('AlertInfo.capture.click')) message(
'Please click above to complete the human verification',
'请点击上方完成人机身份验证',
'warn'
)
return return
} }
// //
@ -110,29 +115,29 @@ const handleLogin = () => {
:isShowValidate="isShowValidate" :isShowValidate="isShowValidate"
/> />
<div class="form"> <div class="form">
<h2 class="title">{{ $tm('login.loginTitle') }}</h2> <h2 class="title">{{ $tm('login.login.loginTitle') }}</h2>
<input <input
v-model="loginForm.name" v-model="loginForm.name"
type="text" type="text"
:placeholder="($tm('login.loginUsername') as string)" :placeholder="($tm('login.login.loginUsername') as string)"
class="input" class="input"
/> />
<input <input
v-model="loginForm.password" v-model="loginForm.password"
type="password" type="password"
:placeholder="($tm('login.loginPassword') as string)" :placeholder="($tm('login.login.loginPassword') as string)"
class="input" class="input"
/> />
<!-- 忘记密码 --> <!-- 忘记密码 -->
<span class="forget">{{ $tm('login.forget') }}</span> <span class="forget">{{ $tm('login.login.forget') }}</span>
<span @click="isShowValidate = true" class="capture">{{ <span @click="isShowValidate = true" class="capture">{{
$tm('login.capture') $tm('login.login.capture')
}}</span> }}</span>
<!-- 点击登录 --> <!-- 点击登录 -->
<button @click="handleLogin" class="btn" type="submit"> <button @click="handleLogin" class="btn" type="submit">
{{ $tm('login.loginTitle') }} {{ $tm('login.login.loginTitle') }}
</button> </button>
</div> </div>
</div> </div>
@ -199,6 +204,7 @@ const handleLogin = () => {
} }
.btn { .btn {
width: 150px;
position: absolute; position: absolute;
bottom: 10%; bottom: 10%;
border-radius: 50px; border-radius: 50px;
@ -208,7 +214,7 @@ const handleLogin = () => {
cursor: pointer; cursor: pointer;
font-size: 15px; font-size: 15px;
letter-spacing: 2px; letter-spacing: 2px;
padding: 7px 50px; padding: 7px 0px;
text-transform: uppercase; text-transform: uppercase;
transition: all 0.2s; transition: all 0.2s;
overflow: hidden; overflow: hidden;

View file

@ -1,9 +1,8 @@
<!--
TODO: 注册表单未完成注册逻辑未完成发送验证码的逻辑未完成i18n 未完成
-->
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive } from 'vue' import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
// 使
import { useKUNGalgameMessageStore } from '@/store/modules/message'
// //
import message from '@/components/alert/Message' import message from '@/components/alert/Message'
import { useKUNGalgameUserStore } from '@/store/modules/kungalgamer' import { useKUNGalgameUserStore } from '@/store/modules/kungalgamer'
@ -16,6 +15,11 @@ import {
import Code from '@/components/verification-code/Code.vue' import Code from '@/components/verification-code/Code.vue'
import Capture from '@/components/capture/Capture.vue' import Capture from '@/components/capture/Capture.vue'
// i18n
import { useI18n } from 'vue-i18n'
const { tm } = useI18n()
const info = useKUNGalgameMessageStore()
const router = useRouter() const router = useRouter()
const isShowValidate = ref(false) const isShowValidate = ref(false)
const isCaptureComplete = ref(false) const isCaptureComplete = ref(false)
@ -83,6 +87,7 @@ const isValidInput = (): boolean => {
const handleSendCode = () => { const handleSendCode = () => {
// //
if (!isValidInput()) { if (!isValidInput()) {
message('Form cannot be empty', '表单不可为空', 'warn')
return return
} }
@ -99,10 +104,20 @@ const handleSendCode = () => {
const handleRegister = () => { const handleRegister = () => {
if (!isSendCode.value) { if (!isSendCode.value) {
message(
'Need to send an email verification code',
'需要发送邮箱验证码',
'warn'
)
return return
} }
if (!registerForm.code.trim()) { if (!registerForm.code.trim()) {
message(
'Email verification code cannot be empty',
'邮箱验证码不可为空',
'warn'
)
return return
} }
@ -124,6 +139,7 @@ const handleRegister = () => {
if (res.code === 200) { if (res.code === 200) {
router.push('/') router.push('/')
message('Register successfully!', '注册成功!', 'success') message('Register successfully!', '注册成功!', 'success')
info.info(tm('AlertInfo.login.success'))
} else { } else {
} }
}) })
@ -146,13 +162,13 @@ const handleRegister = () => {
<!-- 注册表单 --> <!-- 注册表单 -->
<div class="form"> <div class="form">
<!-- 标题 --> <!-- 标题 -->
<h2 class="title">注册</h2> <h2 class="title">{{ $tm('login.register.title') }}</h2>
<div class="container" v-for="item in registerFormItem" :key="item.index"> <div class="container" v-for="item in registerFormItem" :key="item.index">
<input <input
v-model="registerForm[item.value]" v-model="registerForm[item.value]"
:type="item.type" :type="item.type"
:placeholder="item.placeholder" :placeholder="`${$tm(`login.register.${item.placeholder}`)}`"
:class="item.class" :class="item.class"
/> />
</div> </div>
@ -165,14 +181,24 @@ const handleRegister = () => {
/> />
<!-- 注册按钮 --> <!-- 注册按钮 -->
<button @click="handleRegister" class="btn" type="submit">注册</button> <button @click="handleRegister" class="btn" type="submit">
{{ $tm('login.register.title') }}
</button>
<!-- 用户协议提示等 --> <!-- 用户协议提示等 -->
<span class="user-agreement"> <span class="user-agreement">
点击注册表示您已经同意我们的 {{ $tm('login.register.click') }}
<router-link to="/licence"><span>用户协议</span></router-link>
<!-- 用户协议和隐私政策 -->
<router-link to="/privacy"><span>隐私政策</span></router-link> <div class="licence">
<router-link to="/licence">
<span>{{ $tm('login.register.agreement') }}</span>
</router-link>
{{ $tm('login.register.and') }}
<router-link to="/privacy">
<span>{{ $tm('login.register.privacy') }}</span>
</router-link>
</div>
</span> </span>
</div> </div>
</div> </div>
@ -246,9 +272,15 @@ const handleRegister = () => {
color: var(--kungalgame-red-4); color: var(--kungalgame-red-4);
font-style: oblique; font-style: oblique;
} }
.licence {
display: flex;
justify-content: center;
}
} }
.btn { .btn {
width: 150px;
position: absolute; position: absolute;
bottom: 10%; bottom: 10%;
border-radius: 50px; border-radius: 50px;
@ -258,7 +290,7 @@ const handleRegister = () => {
cursor: pointer; cursor: pointer;
font-size: 15px; font-size: 15px;
letter-spacing: 2px; letter-spacing: 2px;
padding: 7px 50px; padding: 7px 0;
text-transform: uppercase; text-transform: uppercase;
transition: all 0.2s; transition: all 0.2s;
margin-top: 30px; margin-top: 30px;

View file

@ -11,28 +11,28 @@ export const registerFormItem: RegisterFormItem[] = [
index: 1, index: 1,
value: 'name', value: 'name',
type: 'text', type: 'text',
placeholder: '用户名', placeholder: 'name',
class: 'input', class: 'input',
}, },
{ {
index: 2, index: 2,
value: 'email', value: 'email',
type: 'email', type: 'email',
placeholder: '邮箱', placeholder: 'email',
class: 'input', class: 'input',
}, },
{ {
index: 3, index: 3,
value: 'password', value: 'password',
type: 'text', type: 'text',
placeholder: '密码', placeholder: 'password',
class: 'input', class: 'input',
}, },
{ {
index: 4, index: 4,
value: 'code', value: 'code',
type: 'text', type: 'text',
placeholder: '邮箱验证码', placeholder: 'code',
class: 'input', class: 'input',
}, },
] ]