抽离登录对话框

This commit is contained in:
05412 2024-08-05 11:50:56 +08:00
parent d50bd58504
commit 92abcad0db
7 changed files with 102 additions and 86 deletions

View File

@ -1,9 +1,86 @@
<script setup lang="ts"> <script setup lang="ts">
import {ref, watch} from "vue";
import {useLocale} from "vuetify";
import {login} from "../../script/functions.ts";
const {t} = useLocale()
const showLoginDialog = defineModel<boolean>()
const username = ref('')
const password = ref('')
const passwordVisibleState = ref(false)
const loading = ref(false)
const loginDisabled = ref(false)
const loginIcon = ref<any>(null)
const isLoginFailed = ref(false)
watch(() => showLoginDialog.value, (value, oldValue) => {
if(oldValue === false && value === true) {
isLoginFailed.value = false
}
})
async function tryLogin() {
loading.value = true
isLoginFailed.value = false
const isSuccess = await login(username.value, password.value)
if (isSuccess) {
loginIcon.value = "mdi-check"
loading.value = false
loginDisabled.value = true
setTimeout(() => {
showLoginDialog.value = false
loginDisabled.value = false
loginIcon.value = null
}, 1500)
} else {
isLoginFailed.value = true
loading.value = false
}
}
function resetLoginError() {
isLoginFailed.value = false
}
</script> </script>
<template> <template>
<v-dialog v-model="showLoginDialog" persistent width="450">
<v-card prepend-icon="mdi-login" :title="t('$vuetify.login.title')" class="px-10">
<v-text-field class="login-dialog-input" flat prepend-inner-icon="mdi-account-outline"
:label="t('$vuetify.login.username')"
density="compact" @input="resetLoginError"
hide-details single-line rounded="lg" variant="solo-filled" v-model="username"></v-text-field>
<v-text-field class="login-dialog-input" flat prepend-inner-icon="mdi-lock-outline"
:label="t('$vuetify.login.password')"
density="compact" hide-details single-line v-model="password" rounded="lg" variant="solo-filled"
:type="passwordVisibleState ? 'text' :'password'" title="password" @input="resetLoginError"
:append-inner-icon="passwordVisibleState ? 'mdi-eye-off' : 'mdi-eye'"
@click:append-inner="passwordVisibleState = !passwordVisibleState"></v-text-field>
<v-alert class="text-medium-emphasis text-caption mb-2" type="warning" closable v-model="isLoginFailed">
{{ t('$vuetify.login.failedNote') }}
</v-alert>
<v-card color="surface-variant mt-5" variant="tonal">
<v-card-text class="text-medium-emphasis text-caption">
{{ t('$vuetify.login.agreement') }}
<v-label class="text-caption"><a href="/input" target="_blank">{{ t('$vuetify.login.termsOfService') }}</a>
</v-label>
</v-card-text>
</v-card>
<v-checkbox class="login-dialog-input" :label="t('$vuetify.login.rememberMe')" hide-details
color="primary"></v-checkbox>
<v-card-actions>
<v-btn :text="t('$vuetify.login.button.cancel')" @click="showLoginDialog = false"></v-btn>
<v-btn :loading="loading" color="primary" :text="t('$vuetify.login.button.login')" @click="tryLogin"
:icon="loginIcon"
:disabled="loginDisabled">
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template> </template>
<style scoped> <style scoped>
.login-dialog-input {
margin: 10px 0;
}
</style> </style>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import {ref} from "vue"; import {ref, watch} from "vue";
import {useLocale} from "vuetify"; import {useLocale} from "vuetify";
import {register} from "../../script/functions.ts"; import {register} from "../../script/functions.ts";
@ -13,6 +13,11 @@ const registerFailedNote = ref('')
const loading = ref(false) const loading = ref(false)
const registerIcon = ref<any>(null) const registerIcon = ref<any>(null)
const disableRegister = ref(false) const disableRegister = ref(false)
watch(()=> showDialog.value, (value, oldValue) => {
if(oldValue === false && value === true) {
isRegisterFailed.value = false
}
})
async function tryRegister() { async function tryRegister() {
loading.value = true loading.value = true
@ -69,8 +74,8 @@ function resetRegisterFailed() {
</v-alert> </v-alert>
<v-card color="surface-variant mt-5" variant="tonal"> <v-card color="surface-variant mt-5" variant="tonal">
<v-card-text class="text-medium-emphasis text-caption"> <v-card-text class="text-medium-emphasis text-caption">
{{ t('$vuetify.login.agreement') }} {{ t('$vuetify.register.agreement') }}
<v-label class="text-caption"><a href="/input" target="_blank">{{ t('$vuetify.login.termsOfService') }}</a> <v-label class="text-caption"><a href="/input" target="_blank">{{ t('$vuetify.register.termsOfService') }}</a>
</v-label> </v-label>
</v-card-text> </v-card-text>
</v-card> </v-card>

View File

@ -1,46 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import {ref} from "vue"; import {ref} from "vue";
import {login, logout} from "../../script/functions"; import {logout} from "../../script/functions";
import {useUserinfoStore} from "../../stores/userinfo-store.ts"; import {useUserinfoStore} from "../../stores/userinfo-store.ts";
import {useLocale, useTheme} from "vuetify"; import {useLocale, useTheme} from "vuetify";
import RegisterDialog from "../items/RegisterDialog.vue"; import RegisterDialog from "../items/RegisterDialog.vue";
import LoginDialog from "../items/LoginDialog.vue";
const {t, current} = useLocale() const {t, current} = useLocale()
const theme = useTheme() const theme = useTheme()
const userinfoStore = useUserinfoStore() const userinfoStore = useUserinfoStore()
const showLoginDialog = ref(false) const showLoginDialog = ref(false)
const username = ref('')
const password = ref('')
const passwordVisibleState = ref(false)
const loading = ref(false)
const loginDisabled = ref(false)
const loginIcon = ref<any>(null)
const isLoginFailed = ref(false)
const showRegisterDialog = ref(false) const showRegisterDialog = ref(false)
async function tryLogin() {
loading.value = true
isLoginFailed.value = false
const isSuccess = await login(username.value, password.value)
if (isSuccess) {
loginIcon.value = "mdi-check"
loading.value = false
loginDisabled.value = true
setTimeout(() => {
showLoginDialog.value = false
loginDisabled.value = false
loginIcon.value = null
}, 1500)
} else {
isLoginFailed.value = true
loading.value = false
}
}
function resetLoginError() {
isLoginFailed.value = false
}
function toggleLocale() { function toggleLocale() {
switch (current.value) { switch (current.value) {
case 'en': case 'en':
@ -86,7 +57,7 @@ function toggleTheme() {
<v-menu open-on-hover activator="#avatar" open-delay="0" close-delay="500" location="bottom" width="150"> <v-menu open-on-hover activator="#avatar" open-delay="0" close-delay="500" location="bottom" width="150">
<v-list> <v-list>
<v-list-item value="1" @click="showLoginDialog = true; isLoginFailed = false" v-if="!userinfoStore.isLogin" <v-list-item value="1" @click="showLoginDialog = true" v-if="!userinfoStore.isLogin"
append-icon="mdi-login"> append-icon="mdi-login">
<v-list-item-title>{{ t('$vuetify.user.login') }}</v-list-item-title> <v-list-item-title>{{ t('$vuetify.user.login') }}</v-list-item-title>
</v-list-item> </v-list-item>
@ -112,40 +83,7 @@ function toggleTheme() {
</v-footer> </v-footer>
</v-app> </v-app>
</v-responsive> </v-responsive>
<v-dialog v-model="showLoginDialog" persistent width="450"> <LoginDialog v-model="showLoginDialog"></LoginDialog>
<v-card prepend-icon="mdi-login" :title="t('$vuetify.login.title')" class="px-10">
<v-text-field class="login-dialog-input" flat prepend-inner-icon="mdi-account-outline"
:label="t('$vuetify.login.username')"
density="compact" @input="resetLoginError"
hide-details single-line rounded="lg" variant="solo-filled" v-model="username"></v-text-field>
<v-text-field class="login-dialog-input" flat prepend-inner-icon="mdi-lock-outline"
:label="t('$vuetify.login.password')"
density="compact" hide-details single-line v-model="password" rounded="lg" variant="solo-filled"
:type="passwordVisibleState ? 'text' :'password'" title="password" @input="resetLoginError"
:append-inner-icon="passwordVisibleState ? 'mdi-eye-off' : 'mdi-eye'"
@click:append-inner="passwordVisibleState = !passwordVisibleState"></v-text-field>
<v-alert class="text-medium-emphasis text-caption mb-2" type="warning" closable v-model="isLoginFailed">
{{ t('$vuetify.login.failedNote') }}
</v-alert>
<v-card color="surface-variant mt-5" variant="tonal">
<v-card-text class="text-medium-emphasis text-caption">
{{ t('$vuetify.login.agreement') }}
<v-label class="text-caption"><a href="/input" target="_blank">{{ t('$vuetify.login.termsOfService') }}</a>
</v-label>
</v-card-text>
</v-card>
<v-checkbox class="login-dialog-input" :label="t('$vuetify.login.rememberMe')" hide-details
color="primary"></v-checkbox>
<v-card-actions>
<v-btn :text="t('$vuetify.login.button.cancel')" @click="showLoginDialog = false"></v-btn>
<v-btn :loading="loading" color="primary" :text="t('$vuetify.login.button.login')" @click="tryLogin"
:icon="loginIcon"
:disabled="loginDisabled">
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<RegisterDialog v-model:showDialog="showRegisterDialog"></RegisterDialog> <RegisterDialog v-model:showDialog="showRegisterDialog"></RegisterDialog>
</template> </template>
<style scoped> <style scoped>
@ -154,10 +92,6 @@ function toggleTheme() {
min-width: 200px; min-width: 200px;
} }
.login-dialog-input {
margin: 10px 0;
}
.home-menu-button { .home-menu-button {
width: 120px; width: 120px;
} }

View File

@ -1,9 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import RegisterDialog from "../items/RegisterDialog.vue"; import LoginDialog from "../items/LoginDialog.vue";
import {ref} from "vue";
const showDialog = ref(true)
</script> </script>
<template> <template>
<RegisterDialog></RegisterDialog> <LoginDialog v-model="showDialog"></LoginDialog>
</template> </template>
<style scoped> <style scoped>

View File

@ -30,19 +30,18 @@ const en: LocaleInterface = {
}, },
register: { register: {
title: "REGISTER", title: "REGISTER",
username: "USERNAME", username: "USERNAME, letters and numbers, 4-16 characters",
password: "PASSWORD", password: "PASSWORD, letters, numbers, underscores, 8-16 characters",
confirmPassword: "CONFIRM PASSWORD", confirmPassword: "CONFIRM PASSWORD",
usernameInvalidNote: "Invalid username format", usernameInvalidNote: "Invalid username format",
passwordInvalidNote: "Invalid password format", passwordInvalidNote: "Invalid password format",
passwordConfirmInvalidNote: "Password confirmation does not match the password", passwordConfirmInvalidNote: "Password confirmation does not match the password",
failedNote: "Register failed", failedNote: "Register failed",
rememberMe: "string", agreement: "PLEASE CONFIRM YOU HAVE READ AND AGREE TO THE",
agreement: "string", termsOfService: "TERMS OF SERVICE",
termsOfService: "string",
button: { button: {
register: "string", register: "REGISTER",
cancel: "string" cancel: "CANCEL"
} }
} }
} }

View File

@ -34,7 +34,6 @@ export default interface LocaleInterface {
passwordInvalidNote: string, passwordInvalidNote: string,
passwordConfirmInvalidNote: string, passwordConfirmInvalidNote: string,
failedNote: string, failedNote: string,
rememberMe: string,
agreement: string, agreement: string,
termsOfService: string, termsOfService: string,
button: { button: {

View File

@ -37,9 +37,8 @@ const zhHans: LocaleInterface = {
passwordInvalidNote: "密码不符合规则", passwordInvalidNote: "密码不符合规则",
passwordConfirmInvalidNote: "两次输入的密码不一致", passwordConfirmInvalidNote: "两次输入的密码不一致",
failedNote: "注册失败, 用户名重复或网络异常", failedNote: "注册失败, 用户名重复或网络异常",
rememberMe: "string", agreement: "我已阅读并同意",
agreement: "string", termsOfService: "服务条款",
termsOfService: "string",
button: { button: {
register: "注册", register: "注册",
cancel: "取消" cancel: "取消"