feat(login): 优化登录表单组件样式和用户体验
- 重新设计表单布局,添加欢迎语和用户选择区域 - 优化输入框样式,采用更现代化的圆角和边框设计 - 实现动态主题色系统,所有交互元素跟随系统主题色 - 改进按钮样式,使用渐变背景和悬停效果 - 优化验证码输入区域的视觉效果 - 完善暗色主题适配和响应式设计
This commit is contained in:
parent
2e69b2e9a0
commit
c292ed9454
@ -1,12 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FormInst } from 'naive-ui'
|
import type { FormInst } from 'naive-ui'
|
||||||
import { useAuthStore } from '@/store'
|
import { useAppStore, useAuthStore } from '@/store'
|
||||||
import { fetchCaptchaPng } from '@/service/api/auth'
|
import { fetchCaptchaPng } from '@/service/api/auth'
|
||||||
import { local } from '@/utils'
|
import { local } from '@/utils'
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
// 获取主题色
|
||||||
|
const primaryColor = computed(() => appStore.primaryColor)
|
||||||
|
const primaryColorHover = computed(() => appStore.theme.common.primaryColorHover)
|
||||||
|
const primaryColorPressed = computed(() => appStore.theme.common.primaryColorPressed)
|
||||||
|
|
||||||
function toOtherForm(type: any) {
|
function toOtherForm(type: any) {
|
||||||
emit('update:modelValue', type)
|
emit('update:modelValue', type)
|
||||||
@ -92,16 +98,39 @@ function checkUserAccount() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="space-y-6">
|
||||||
<n-h2 depth="3" class="text-center">
|
<!-- 头部问候语 -->
|
||||||
{{ $t('login.signInTitle') }}
|
<div class="text-center">
|
||||||
</n-h2>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">
|
||||||
<n-form ref="formRef" :rules="rules" :model="formValue" :show-label="false" size="large">
|
欢迎回来 👋
|
||||||
|
</h1>
|
||||||
|
<p class="text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
请输入您的详细信息以开始管理您的帐户
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 登录表单 -->
|
||||||
|
<n-form ref="formRef" :rules="rules" :model="formValue" :show-label="false" size="large" class="space-y-4">
|
||||||
|
<!-- 账号输入 -->
|
||||||
<n-form-item path="account">
|
<n-form-item path="account">
|
||||||
<n-input v-model:value="formValue.account" clearable :placeholder="$t('login.accountPlaceholder')" />
|
<n-input
|
||||||
|
v-model:value="formValue.account"
|
||||||
|
clearable
|
||||||
|
:placeholder="$t('login.accountPlaceholder')"
|
||||||
|
class="login-input"
|
||||||
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
|
|
||||||
|
<!-- 密码输入 -->
|
||||||
<n-form-item path="pwd">
|
<n-form-item path="pwd">
|
||||||
<n-input v-model:value="formValue.pwd" type="password" :placeholder="$t('login.passwordPlaceholder')" clearable show-password-on="click">
|
<n-input
|
||||||
|
v-model:value="formValue.pwd"
|
||||||
|
type="password"
|
||||||
|
:placeholder="$t('login.passwordPlaceholder')"
|
||||||
|
clearable
|
||||||
|
show-password-on="click"
|
||||||
|
class="login-input"
|
||||||
|
>
|
||||||
<template #password-invisible-icon>
|
<template #password-invisible-icon>
|
||||||
<icon-park-outline-preview-close-one />
|
<icon-park-outline-preview-close-one />
|
||||||
</template>
|
</template>
|
||||||
@ -110,17 +139,19 @@ function checkUserAccount() {
|
|||||||
</template>
|
</template>
|
||||||
</n-input>
|
</n-input>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
|
|
||||||
|
<!-- 验证码输入 -->
|
||||||
<n-form-item path="securityCode">
|
<n-form-item path="securityCode">
|
||||||
<div class="flex w-full gap-3">
|
<div class="flex w-full gap-3">
|
||||||
<n-input
|
<n-input
|
||||||
v-model:value="formValue.securityCode"
|
v-model:value="formValue.securityCode"
|
||||||
placeholder="请输入验证码"
|
placeholder="请输入验证码"
|
||||||
clearable
|
clearable
|
||||||
class="flex-1"
|
class="flex-1 login-input"
|
||||||
maxlength="5"
|
maxlength="5"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="w-32 h-12 cursor-pointer rounded border-2 border-gray-300 hover:border-blue-400 flex items-center justify-center overflow-hidden bg-white transition-colors duration-200"
|
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"
|
||||||
title="点击刷新验证码"
|
title="点击刷新验证码"
|
||||||
@click="getCaptcha"
|
@click="getCaptcha"
|
||||||
>
|
>
|
||||||
@ -130,51 +161,111 @@ function checkUserAccount() {
|
|||||||
alt="验证码"
|
alt="验证码"
|
||||||
class="w-full h-full object-contain"
|
class="w-full h-full object-contain"
|
||||||
>
|
>
|
||||||
<span v-else class="text-sm text-gray-500">点击获取验证码</span>
|
<span v-else class="text-sm text-gray-500 dark:text-gray-400">点击获取验证码</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-space vertical :size="20">
|
|
||||||
<div class="flex-y-center justify-between">
|
<!-- 记住密码和忘记密码 -->
|
||||||
<n-checkbox v-model:checked="isRemember">
|
<div class="flex items-center justify-between">
|
||||||
{{ $t('login.rememberMe') }}
|
<n-checkbox v-model:checked="isRemember" class="text-sm">
|
||||||
</n-checkbox>
|
{{ $t('login.rememberMe') }}
|
||||||
<n-button type="primary" text @click="toOtherForm('resetPwd')">
|
</n-checkbox>
|
||||||
{{ $t('login.forgotPassword') }}
|
<n-button type="primary" text class="text-sm" @click="toOtherForm('resetPwd')">
|
||||||
</n-button>
|
{{ $t('login.forgotPassword') }}
|
||||||
</div>
|
|
||||||
<n-button block type="primary" size="large" :loading="isLoading" :disabled="isLoading" @click="handleLogin">
|
|
||||||
{{ $t('login.signIn') }}
|
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-flex>
|
</div>
|
||||||
<n-text>{{ $t('login.noAccountText') }}</n-text>
|
|
||||||
<n-button type="primary" text @click="toOtherForm('register')">
|
<!-- 登录按钮 -->
|
||||||
{{ $t('login.signUp') }}
|
<n-button
|
||||||
</n-button>
|
block
|
||||||
</n-flex>
|
type="primary"
|
||||||
</n-space>
|
size="large"
|
||||||
|
:loading="isLoading"
|
||||||
|
:disabled="isLoading"
|
||||||
|
class="login-button"
|
||||||
|
@click="handleLogin"
|
||||||
|
>
|
||||||
|
{{ $t('login.signIn') }}
|
||||||
|
</n-button>
|
||||||
|
|
||||||
|
<!-- 注册链接 -->
|
||||||
|
<!-- <div class="text-center"> -->
|
||||||
|
<!-- <span class="text-sm text-gray-600 dark:text-gray-400">{{ $t('login.noAccountText') }}</span> -->
|
||||||
|
<!-- <n-button type="primary" text @click="toOtherForm('register')" class="text-sm ml-1"> -->
|
||||||
|
<!-- {{ $t('login.signUp') }} -->
|
||||||
|
<!-- </n-button> -->
|
||||||
|
<!-- </div> -->
|
||||||
</n-form>
|
</n-form>
|
||||||
<n-divider>
|
|
||||||
<span op-80>{{ $t('login.or') }}</span>
|
|
||||||
</n-divider>
|
|
||||||
<n-space justify="center">
|
|
||||||
<n-button circle>
|
|
||||||
<template #icon>
|
|
||||||
<n-icon><icon-park-outline-wechat /></n-icon>
|
|
||||||
</template>
|
|
||||||
</n-button>
|
|
||||||
<n-button circle>
|
|
||||||
<template #icon>
|
|
||||||
<n-icon><icon-park-outline-tencent-qq /></n-icon>
|
|
||||||
</template>
|
|
||||||
</n-button>
|
|
||||||
<n-button circle>
|
|
||||||
<template #icon>
|
|
||||||
<n-icon><icon-park-outline-github-one /></n-icon>
|
|
||||||
</template>
|
|
||||||
</n-button>
|
|
||||||
</n-space>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
.login-input {
|
||||||
|
--n-border-radius: 8px;
|
||||||
|
--n-border: 1px solid #e5e7eb;
|
||||||
|
--n-border-hover: 1px solid v-bind(primaryColor);
|
||||||
|
--n-border-focus: 1px solid v-bind(primaryColor);
|
||||||
|
--n-box-shadow-focus: 0 0 0 3px v-bind(`${primaryColor}19`);
|
||||||
|
--n-padding-left: 16px;
|
||||||
|
--n-padding-right: 16px;
|
||||||
|
--n-font-size: 14px;
|
||||||
|
--n-height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button {
|
||||||
|
--n-border-radius: 8px;
|
||||||
|
--n-height: 48px;
|
||||||
|
--n-font-size: 16px;
|
||||||
|
--n-font-weight: 600;
|
||||||
|
background: linear-gradient(135deg, v-bind(primaryColor) 0%, v-bind(primaryColorPressed) 100%);
|
||||||
|
border: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button:hover {
|
||||||
|
background: linear-gradient(135deg, v-bind(primaryColorHover) 0%, v-bind(primaryColorPressed) 100%);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 12px v-bind(`${primaryColor}4D`);
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-login-btn {
|
||||||
|
@apply w-12 h-12 rounded-full border-2 border-gray-200 dark:border-gray-600 flex items-center justify-center transition-all duration-200 text-gray-600 dark:text-gray-400;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: v-bind(primaryColor);
|
||||||
|
background: v-bind(`${primaryColor}0D`);
|
||||||
|
color: v-bind(primaryColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-login-btn:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha-button:hover {
|
||||||
|
border-color: v-bind(primaryColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 暗色主题适配 */
|
||||||
|
.dark .login-input {
|
||||||
|
--n-color: #374151;
|
||||||
|
--n-border: 1px solid #4b5563;
|
||||||
|
--n-border-hover: 1px solid v-bind(primaryColor);
|
||||||
|
--n-color-focus: #374151;
|
||||||
|
--n-text-color: #f3f4f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.login-input {
|
||||||
|
--n-height: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button {
|
||||||
|
--n-height: 44px;
|
||||||
|
--n-font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user