feat(login): 全面增强登录组件用户体验
✨ 新增功能: - 支持回车键登录,在任意输入框按回车即可触发登录 - 登录失败时自动刷新验证码并清空验证码输入框 - 重定向访问时显示优雅的loading状态和身份过期提示 - 智能检测URL重定向参数,提供个性化用户引导 🎨 界面优化: - 新增专业的loading界面,包含旋转器和动态点阵动画 - 优化过渡动画效果,提供平滑的视觉体验 - 使用项目主题色,确保视觉统一性 - 支持暗色模式适配 🔧 技术改进: - 增强错误处理机制,防止重复提交 - 优化验证码刷新逻辑,提升用户操作便利性 - 改进loading时序控制,确保用户看到重要提示信息
This commit is contained in:
parent
92c886346c
commit
d415592762
@ -1,5 +1,27 @@
|
||||
<template>
|
||||
<div class="space-y-6">
|
||||
<!-- 重定向Loading状态 -->
|
||||
<div v-if="isRedirectLoading" class="text-center space-y-6 py-8">
|
||||
<div class="flex justify-center">
|
||||
<n-spin size="large" stroke="var(--primary-color)" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
正在检查登录状态...
|
||||
</h1>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
请稍候,系统正在为您准备登录界面
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex justify-center space-x-1">
|
||||
<div class="loading-dot" style="animation-delay: 0s;" />
|
||||
<div class="loading-dot" style="animation-delay: 0.1s;" />
|
||||
<div class="loading-dot" style="animation-delay: 0.2s;" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 正常登录状态 -->
|
||||
<div v-else>
|
||||
<!-- 头部问候语 -->
|
||||
<div class="text-center">
|
||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">
|
||||
@ -11,7 +33,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 登录表单 -->
|
||||
<n-form ref="formRef" :rules="rules" :model="formValue" :show-label="false" size="large" class="space-y-4">
|
||||
<n-form ref="formRef" :rules="rules" :model="formValue" :show-label="false" size="large" class="space-y-4" @keyup.enter="handleLogin">
|
||||
<!-- 账号输入 -->
|
||||
<n-form-item path="account">
|
||||
<n-input
|
||||
@ -19,6 +41,7 @@
|
||||
clearable
|
||||
:placeholder="$t('login.accountPlaceholder')"
|
||||
class="login-input"
|
||||
@keyup.enter="handleLogin"
|
||||
/>
|
||||
</n-form-item>
|
||||
|
||||
@ -31,6 +54,7 @@
|
||||
clearable
|
||||
show-password-on="click"
|
||||
class="login-input"
|
||||
@keyup.enter="handleLogin"
|
||||
>
|
||||
<template #password-invisible-icon>
|
||||
<icon-park-outline-preview-close-one />
|
||||
@ -50,6 +74,7 @@
|
||||
clearable
|
||||
class="flex-1 login-input"
|
||||
maxlength="5"
|
||||
@keyup.enter="handleLogin"
|
||||
/>
|
||||
<div
|
||||
class="w-32 h-12 cursor-pointer rounded-lg border-2 border-gray-200 flex items-center justify-center overflow-hidden bg-white dark:bg-gray-700 dark:border-gray-600 transition-colors duration-200 captcha-button"
|
||||
@ -99,6 +124,7 @@
|
||||
<!-- </div> -->
|
||||
</n-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -106,6 +132,7 @@ import type { FormInst } from 'naive-ui'
|
||||
import { useAppStore, useAuthStore } from '@/store'
|
||||
import { fetchCaptchaPng } from '@/service/api/auth'
|
||||
import { local } from '@/utils'
|
||||
import { coiMsgWarning } from '@/utils/coi'
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
@ -148,6 +175,7 @@ const formValue = ref({
|
||||
})
|
||||
const isRemember = ref(false)
|
||||
const isLoading = ref(false)
|
||||
const isRedirectLoading = ref(false)
|
||||
|
||||
// 验证码相关
|
||||
const captchaImage = ref('')
|
||||
@ -169,6 +197,10 @@ async function getCaptcha() {
|
||||
|
||||
const formRef = ref<FormInst | null>(null)
|
||||
function handleLogin() {
|
||||
// 防止重复提交
|
||||
if (isLoading.value)
|
||||
return
|
||||
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (errors)
|
||||
return
|
||||
@ -180,14 +212,38 @@ function handleLogin() {
|
||||
local.set('loginAccount', { account, pwd })
|
||||
else local.remove('loginAccount')
|
||||
|
||||
try {
|
||||
await authStore.login(account, pwd, captchaKey.value, securityCode, isRemember.value)
|
||||
}
|
||||
catch (error) {
|
||||
// 登录失败时刷新验证码并清空验证码输入框
|
||||
await getCaptcha()
|
||||
formValue.value.securityCode = ''
|
||||
console.warn('[Login Failed]:', error)
|
||||
}
|
||||
finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
checkUserAccount()
|
||||
getCaptcha()
|
||||
|
||||
// 检查是否有重定向参数,显示身份过期提示和loading
|
||||
const route = useRoute()
|
||||
if (route.query.redirect) {
|
||||
isRedirectLoading.value = true
|
||||
// 显示loading状态,然后显示提示
|
||||
setTimeout(() => {
|
||||
coiMsgWarning('账号身份过期,请你重新登录')
|
||||
// 延迟隐藏loading,让过渡更自然
|
||||
setTimeout(() => {
|
||||
isRedirectLoading.value = false
|
||||
}, 200)
|
||||
}, 1000)
|
||||
}
|
||||
})
|
||||
|
||||
function checkUserAccount() {
|
||||
@ -257,6 +313,40 @@ function checkUserAccount() {
|
||||
--n-text-color: #f3f4f6;
|
||||
}
|
||||
|
||||
/* 过渡动画 */
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
/* Loading点动画 */
|
||||
.loading-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: v-bind(primaryColor);
|
||||
border-radius: 50%;
|
||||
animation: bounce 1.4s ease-in-out infinite both;
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%, 80%, 100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
40% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 640px) {
|
||||
.login-input {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user