feat: unify login UI and improve kdocs defaults
This commit is contained in:
@@ -240,56 +240,80 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="auth-wrap">
|
||||
<el-card shadow="never" class="auth-card" :body-style="{ padding: '22px' }">
|
||||
<div class="brand">
|
||||
<div class="brand-title">知识管理平台</div>
|
||||
<div class="brand-sub app-muted">用户登录</div>
|
||||
<div class="login-page">
|
||||
<div class="login-container">
|
||||
<div class="login-header">
|
||||
<span class="login-badge">用户登录</span>
|
||||
<h1>用户登录系统</h1>
|
||||
<p>知识管理平台</p>
|
||||
</div>
|
||||
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="用户名">
|
||||
<el-input v-model="form.username" placeholder="请输入用户名" autocomplete="username" />
|
||||
</el-form-item>
|
||||
<el-form-item label="密码">
|
||||
<div class="form-group">
|
||||
<label for="username">用户账号</label>
|
||||
<el-input
|
||||
id="username"
|
||||
v-model="form.username"
|
||||
class="login-input"
|
||||
placeholder="请输入用户名"
|
||||
autocomplete="username"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">密码</label>
|
||||
<el-input
|
||||
id="password"
|
||||
v-model="form.password"
|
||||
class="login-input"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="请输入密码"
|
||||
autocomplete="current-password"
|
||||
@keyup.enter="onSubmit"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="needCaptcha" class="form-group">
|
||||
<label for="captcha">验证码</label>
|
||||
<div class="captcha-row">
|
||||
<el-input
|
||||
v-model="form.password"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="请输入密码"
|
||||
autocomplete="current-password"
|
||||
id="captcha"
|
||||
v-model="form.captcha"
|
||||
class="login-input captcha-input"
|
||||
placeholder="请输入验证码"
|
||||
@keyup.enter="onSubmit"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="needCaptcha" label="验证码">
|
||||
<div class="captcha-row">
|
||||
<el-input v-model="form.captcha" placeholder="请输入验证码" @keyup.enter="onSubmit" />
|
||||
<img
|
||||
v-if="captchaImage"
|
||||
class="captcha-img"
|
||||
:src="captchaImage"
|
||||
alt="验证码"
|
||||
title="点击刷新"
|
||||
@click="refreshLoginCaptcha"
|
||||
/>
|
||||
<el-button @click="refreshLoginCaptcha">刷新</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div class="links">
|
||||
<el-button text type="primary" @click="openForgot">忘记密码?</el-button>
|
||||
<el-button v-if="showResendLink" text type="primary" @click="openResend">重发验证邮件</el-button>
|
||||
<img
|
||||
v-if="captchaImage"
|
||||
class="captcha-img"
|
||||
:src="captchaImage"
|
||||
alt="验证码"
|
||||
title="点击刷新"
|
||||
@click="refreshLoginCaptcha"
|
||||
/>
|
||||
<button type="button" class="captcha-refresh" @click="refreshLoginCaptcha">刷新</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-button type="primary" class="submit-btn" :loading="loading" @click="onSubmit">登录</el-button>
|
||||
<button type="button" class="btn-login" :disabled="loading" @click="onSubmit">
|
||||
{{ loading ? '登录中...' : '登录系统' }}
|
||||
</button>
|
||||
|
||||
<div class="foot">
|
||||
<span class="app-muted">还没有账号?</span>
|
||||
<el-button link type="primary" @click="goRegister">立即注册</el-button>
|
||||
<div class="action-links">
|
||||
<button type="button" class="link-btn" @click="openForgot">忘记密码?</button>
|
||||
<button v-if="showResendLink" type="button" class="link-btn" @click="openResend">重发验证邮件</button>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<div class="register-row">
|
||||
<span>还没有账号?</span>
|
||||
<button type="button" class="link-btn" @click="goRegister">立即注册</button>
|
||||
</div>
|
||||
|
||||
<div class="back-link">
|
||||
<span>管理员请前往</span>
|
||||
<a href="/yuyx">后台登录</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="forgotOpen" title="找回密码" width="min(560px, 92vw)">
|
||||
<el-alert
|
||||
@@ -376,61 +400,181 @@ onMounted(async () => {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.auth-wrap {
|
||||
.login-page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
background: linear-gradient(135deg, #eef2ff 0%, #f6f7fb 45%, #ecfeff 100%);
|
||||
}
|
||||
|
||||
.auth-card {
|
||||
.login-page::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background:
|
||||
radial-gradient(800px 500px at 15% 20%, rgba(59, 130, 246, 0.18), transparent 60%),
|
||||
radial-gradient(700px 420px at 85% 70%, rgba(124, 58, 237, 0.16), transparent 55%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
border-radius: var(--app-radius);
|
||||
border: 1px solid var(--app-border);
|
||||
box-shadow: var(--app-shadow);
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 18px 60px rgba(17, 24, 39, 0.15);
|
||||
border: 1px solid rgba(17, 24, 39, 0.08);
|
||||
padding: 38px 34px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.brand {
|
||||
.login-header {
|
||||
text-align: center;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.login-badge {
|
||||
display: inline-block;
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
color: #1d4ed8;
|
||||
padding: 6px 14px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.brand-title {
|
||||
font-size: 18px;
|
||||
font-weight: 900;
|
||||
.login-header h1 {
|
||||
font-size: 24px;
|
||||
color: #111827;
|
||||
margin: 0 0 10px;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.brand-sub {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
.login-header p {
|
||||
margin: 0;
|
||||
color: #6b7280;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
margin: 2px 0 10px;
|
||||
flex-wrap: wrap;
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: #111827;
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.login-input :deep(.el-input__wrapper) {
|
||||
border-radius: 10px;
|
||||
min-height: 44px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
box-shadow: 0 0 0 1px rgba(17, 24, 39, 0.14) inset;
|
||||
transition: box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.login-input :deep(.el-input__wrapper.is-focus) {
|
||||
box-shadow: 0 0 0 1px rgba(59, 130, 246, 0.7) inset, 0 0 0 4px rgba(59, 130, 246, 0.16);
|
||||
}
|
||||
|
||||
.login-input :deep(.el-input__inner) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.btn-login {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
background: linear-gradient(135deg, #2563eb 0%, #7c3aed 100%);
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
cursor: pointer;
|
||||
transition: transform 0.15s, filter 0.15s;
|
||||
}
|
||||
|
||||
.foot {
|
||||
.btn-login:hover:not(:disabled) {
|
||||
transform: translateY(-2px);
|
||||
filter: brightness(1.02);
|
||||
}
|
||||
|
||||
.btn-login:active:not(:disabled) {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.btn-login:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.action-links {
|
||||
margin-top: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.link-btn {
|
||||
border: none;
|
||||
background: none;
|
||||
color: #2563eb;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.link-btn:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.register-row {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #6b7280;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
text-align: center;
|
||||
margin-top: 18px;
|
||||
color: #6b7280;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.back-link a {
|
||||
margin-left: 6px;
|
||||
color: #2563eb;
|
||||
text-decoration: none;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.back-link a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.dialog-form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.alert {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.captcha-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -438,17 +582,62 @@ onMounted(async () => {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.captcha-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.captcha-img {
|
||||
height: 40px;
|
||||
border: 1px solid var(--app-border);
|
||||
height: 46px;
|
||||
border: 1px solid rgba(17, 24, 39, 0.14);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.captcha-refresh {
|
||||
height: 44px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid rgba(17, 24, 39, 0.14);
|
||||
border-radius: 10px;
|
||||
background: #f8fafc;
|
||||
color: #111827;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.captcha-refresh:hover {
|
||||
background: #f1f5f9;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.login-page {
|
||||
align-items: flex-start;
|
||||
padding: 20px 12px 12px;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
max-width: 100%;
|
||||
padding: 28px 20px;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.login-header h1 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.btn-login {
|
||||
padding: 13px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.captcha-img {
|
||||
height: 38px;
|
||||
height: 42px;
|
||||
}
|
||||
|
||||
.captcha-refresh {
|
||||
height: 42px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user