990 lines
24 KiB
Vue
990 lines
24 KiB
Vue
<template>
|
||
<div class="login">
|
||
<div class="bg-aurora" />
|
||
<div class="grid-overlay" />
|
||
|
||
<div class="login-wrap">
|
||
<div class="login-card">
|
||
<section class="brand-panel">
|
||
<div class="brand-panel__mask" />
|
||
<div class="brand-top">
|
||
<img :src="brandLogo" alt="遨游龙翔游戏平台" class="brand-logo-img" />
|
||
</div>
|
||
|
||
<div class="brand-hero">
|
||
<h1>
|
||
渠道商务
|
||
<br />
|
||
<span class="hl">一站式数据中枢</span>
|
||
</h1>
|
||
<p>
|
||
聚合渠道注册、充值、活跃与预警数据,让每一笔商务合作清晰可查、可控、可追溯。
|
||
</p>
|
||
|
||
<div class="feature-list">
|
||
<div v-for="item in featureList" :key="item.text" class="feature-item">
|
||
<span class="feature-ico">
|
||
<!-- 趋势看板 -->
|
||
<svg
|
||
v-if="item.icon === 'trend'"
|
||
width="17"
|
||
height="17"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
>
|
||
<path
|
||
d="M4 19V9m5 10V5m5 14v-7m5 7V3"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
/>
|
||
</svg>
|
||
<!-- 商务层级 -->
|
||
<svg
|
||
v-else-if="item.icon === 'user'"
|
||
width="17"
|
||
height="17"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
>
|
||
<circle cx="12" cy="8" r="4" stroke="currentColor" stroke-width="2" />
|
||
<path
|
||
d="M4 20c0-3.3 3.6-6 8-6s8 2.7 8 6"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
/>
|
||
</svg>
|
||
<!-- 风险监控 -->
|
||
<svg
|
||
v-else
|
||
width="17"
|
||
height="17"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
>
|
||
<path
|
||
d="M12 3l8 4v5c0 4.5-3.2 7.8-8 9-4.8-1.2-8-4.5-8-9V7l8-4z"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linejoin="round"
|
||
/>
|
||
<path
|
||
d="M9 12l2 2 4-4"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
/>
|
||
</svg>
|
||
</span>
|
||
<span>{{ item.text }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="brand-foot">
|
||
<p>© {{ currentYear }} 遨游龙翔游戏平台 · 内部商务系统</p>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="form-panel">
|
||
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
|
||
<div class="form-head">
|
||
<div>
|
||
<h3 class="form-title">欢迎登录</h3>
|
||
<p class="form-desc">请输入您的商务账号以继续</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-body">
|
||
<div class="field-block">
|
||
<label class="field-label">商务账号</label>
|
||
<el-form-item prop="username" class="form-item">
|
||
<el-input
|
||
v-model="loginForm.username"
|
||
type="text"
|
||
size="large"
|
||
auto-complete="off"
|
||
placeholder="请输入商务账号"
|
||
>
|
||
<template #prefix><Icon icon="lucide:user-round" class="el-input__icon input-icon" /></template>
|
||
</el-input>
|
||
</el-form-item>
|
||
</div>
|
||
|
||
<div class="field-block">
|
||
<label class="field-label">登录密码</label>
|
||
<el-form-item prop="password" class="form-item">
|
||
<el-input
|
||
v-model="loginForm.password"
|
||
type="password"
|
||
size="large"
|
||
autocomplete="new-password"
|
||
:readonly="passwordReadonly"
|
||
show-password
|
||
placeholder="请输入登录密码"
|
||
@focus="passwordReadonly = false"
|
||
@keyup.enter="handleLogin"
|
||
>
|
||
<template #prefix><Icon icon="lucide:lock-keyhole" class="el-input__icon input-icon" /></template>
|
||
</el-input>
|
||
</el-form-item>
|
||
</div>
|
||
|
||
<div v-if="captchaEnabled" class="field-block">
|
||
<label class="field-label">验证码</label>
|
||
<el-form-item prop="code" class="form-item captcha-row">
|
||
<el-input
|
||
v-model="loginForm.code"
|
||
size="large"
|
||
auto-complete="off"
|
||
placeholder="请输入验证码"
|
||
@keyup.enter="handleLogin"
|
||
>
|
||
<template #prefix><Icon icon="lucide:shield-check" class="el-input__icon input-icon" /></template>
|
||
</el-input>
|
||
<div v-if="codeUrl" class="login-code">
|
||
<img :src="codeUrl" class="login-code-img" @click="getCode" />
|
||
</div>
|
||
</el-form-item>
|
||
</div>
|
||
|
||
<div class="form-meta">
|
||
<el-checkbox v-model="loginForm.rememberMe">记住账号</el-checkbox>
|
||
<button type="button" class="link-type link-button" @click="handleOpenForgotPassword">
|
||
忘记密码?
|
||
</button>
|
||
</div>
|
||
|
||
<el-form-item class="submit-row">
|
||
<el-button
|
||
:loading="loading"
|
||
size="large"
|
||
type="primary"
|
||
class="submit-button"
|
||
@click.prevent="handleLogin"
|
||
>
|
||
<span v-if="!loading">登录</span>
|
||
<span v-else>登录中…</span>
|
||
</el-button>
|
||
</el-form-item>
|
||
|
||
</div>
|
||
</el-form>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
|
||
<el-dialog
|
||
v-model="forgotPasswordVisible"
|
||
title="找回密码"
|
||
width="400px"
|
||
class="forgot-password-dialog"
|
||
header-class="forgot-password-dialog__header"
|
||
body-class="forgot-password-dialog__body"
|
||
destroy-on-close
|
||
@closed="handleForgotPasswordClosed"
|
||
>
|
||
<div class="forgot-password-panel">
|
||
<!-- 步骤1:输入商务账号 -->
|
||
<template v-if="forgotPasswordStep === 'account'">
|
||
<el-form ref="forgotAccountRef" :model="forgotPasswordForm" :rules="forgotAccountRules" label-position="top" hide-required-asterisk>
|
||
<el-form-item
|
||
label="商务账号"
|
||
prop="username"
|
||
class="forgot-password-field"
|
||
:error="forgotAccountError || undefined"
|
||
>
|
||
<el-input
|
||
v-model="forgotPasswordForm.username"
|
||
size="large"
|
||
placeholder="请输入您注册的商务账号"
|
||
@input="forgotAccountError = ''"
|
||
@keyup.enter="handleForgotAccountNext"
|
||
>
|
||
<template #prefix><Icon icon="lucide:user-round" class="el-input__icon input-icon" /></template>
|
||
</el-input>
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<el-button
|
||
type="primary"
|
||
class="forgot-password-submit"
|
||
:loading="forgotSubmitting"
|
||
@click="handleForgotAccountNext"
|
||
>
|
||
下一步
|
||
</el-button>
|
||
</template>
|
||
|
||
<!-- 步骤2:短信验证码(账号校验通过后) -->
|
||
<template v-else-if="forgotPasswordStep === 'sms'">
|
||
<p class="forgot-sms-tip">
|
||
验证码已发送至手机号
|
||
<span class="forgot-sms-phone">{{ forgotMaskedPhone }}</span>
|
||
</p>
|
||
|
||
<el-form ref="forgotSmsRef" :model="forgotPasswordForm" :rules="forgotSmsRules" label-position="top" hide-required-asterisk>
|
||
<el-form-item label="短信验证码" prop="smsCode" class="forgot-password-field">
|
||
<div class="forgot-sms-row">
|
||
<el-input
|
||
v-model="forgotPasswordForm.smsCode"
|
||
size="large"
|
||
maxlength="6"
|
||
placeholder="请输入 6 位验证码"
|
||
@keyup.enter="handleForgotSmsNext"
|
||
>
|
||
<template #prefix><Icon icon="lucide:shield-check" class="el-input__icon input-icon" /></template>
|
||
</el-input>
|
||
<el-button
|
||
class="forgot-sms-send"
|
||
:disabled="smsCountdown > 0 || forgotSubmitting"
|
||
:loading="forgotSubmitting && smsCountdown <= 0"
|
||
@click="handleSendSmsCode"
|
||
>
|
||
{{ smsCountdown > 0 ? `${smsCountdown}s` : '获取验证码' }}
|
||
</el-button>
|
||
</div>
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<el-button
|
||
type="primary"
|
||
class="forgot-password-submit"
|
||
:loading="forgotSubmitting"
|
||
@click="handleForgotSmsNext"
|
||
>
|
||
下一步
|
||
</el-button>
|
||
</template>
|
||
|
||
<!-- 步骤3:设置新密码(验证码校验通过后) -->
|
||
<template v-else>
|
||
<el-form ref="forgotResetRef" :model="forgotPasswordForm" :rules="forgotResetRules" label-position="top">
|
||
<el-form-item label="新密码" prop="newPassword" class="forgot-password-field">
|
||
<el-input
|
||
v-model="forgotPasswordForm.newPassword"
|
||
type="password"
|
||
size="large"
|
||
show-password
|
||
maxlength="15"
|
||
placeholder="6-15 位字符"
|
||
@keyup.enter="handleForgotResetSubmit"
|
||
>
|
||
<template #prefix><Icon icon="lucide:lock-keyhole" class="el-input__icon input-icon" /></template>
|
||
</el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="确认新密码" prop="confirmPassword" class="forgot-password-field">
|
||
<el-input
|
||
v-model="forgotPasswordForm.confirmPassword"
|
||
type="password"
|
||
size="large"
|
||
show-password
|
||
maxlength="15"
|
||
placeholder="请再次输入新密码"
|
||
@keyup.enter="handleForgotResetSubmit"
|
||
>
|
||
<template #prefix><Icon icon="lucide:lock-keyhole" class="el-input__icon input-icon" /></template>
|
||
</el-input>
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<el-button
|
||
type="primary"
|
||
class="forgot-password-submit"
|
||
:loading="forgotSubmitting"
|
||
@click="handleForgotResetSubmit"
|
||
>
|
||
完成
|
||
</el-button>
|
||
</template>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<div class="el-login-footer">
|
||
<span>Copyright © 2018-{{ currentYear }} {{ title }} All Rights Reserved.</span>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { Icon } from '@iconify/vue'
|
||
import brandLogo from '@/assets/business-login-logo.png'
|
||
import { useBusinessLogin } from './useBusinessLogin'
|
||
|
||
defineOptions({ name: 'Login' })
|
||
const {
|
||
title, currentYear, featureList, loginRef, loginForm, loginRules, loading, passwordReadonly, captchaEnabled, codeUrl, getCode, handleLogin,
|
||
forgotPasswordVisible, forgotPasswordStep, forgotAccountRef, forgotSmsRef, forgotResetRef, forgotPasswordForm, forgotAccountError, forgotMaskedPhone,
|
||
forgotSubmitting, smsCountdown, forgotAccountRules, forgotSmsRules, forgotResetRules, handleOpenForgotPassword, handleForgotPasswordClosed,
|
||
handleForgotAccountNext, handleSendSmsCode, handleForgotSmsNext, handleForgotResetSubmit
|
||
} = useBusinessLogin()
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.login {
|
||
--login-bg-primary: #eef1f7;
|
||
--login-bg-secondary: rgba(255, 255, 255, 0.92);
|
||
--login-bg-card: #ffffff;
|
||
--login-bg-input: #f3f5f9;
|
||
--login-bg-hover: #e9eef9;
|
||
--login-text-primary: #1d2330;
|
||
--login-text-secondary: #5b6172;
|
||
--login-text-muted: #9aa1b2;
|
||
--login-accent: #3b7bff;
|
||
--login-accent-hover: #2f6af0;
|
||
--login-accent-soft: rgba(59, 123, 255, 0.1);
|
||
--login-border: rgba(17, 24, 39, 0.08);
|
||
--login-shadow: 0 30px 80px rgba(31, 41, 55, 0.16);
|
||
--login-radius: 24px;
|
||
position: relative;
|
||
min-height: 100%;
|
||
overflow: hidden;
|
||
background: var(--login-bg-primary);
|
||
}
|
||
|
||
.bg-aurora,
|
||
.grid-overlay {
|
||
position: fixed;
|
||
inset: 0;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.bg-aurora {
|
||
z-index: 0;
|
||
|
||
&::before,
|
||
&::after {
|
||
content: '';
|
||
position: absolute;
|
||
border-radius: 50%;
|
||
filter: blur(90px);
|
||
opacity: 0.42;
|
||
animation: aurora-float 18s ease-in-out infinite;
|
||
}
|
||
|
||
&::before {
|
||
top: -140px;
|
||
left: -120px;
|
||
width: 560px;
|
||
height: 560px;
|
||
background: radial-gradient(circle at 30% 30%, rgba(79, 140, 255, 0.45), transparent 70%);
|
||
}
|
||
|
||
&::after {
|
||
right: -160px;
|
||
bottom: -180px;
|
||
width: 620px;
|
||
height: 620px;
|
||
background: radial-gradient(circle at 70% 70%, rgba(124, 92, 255, 0.36), transparent 70%);
|
||
animation-delay: -9s;
|
||
}
|
||
}
|
||
|
||
.grid-overlay {
|
||
z-index: 1;
|
||
background-image:
|
||
linear-gradient(rgba(0, 0, 0, 0.035) 1px, transparent 1px),
|
||
linear-gradient(90deg, rgba(0, 0, 0, 0.035) 1px, transparent 1px);
|
||
background-size: 46px 46px;
|
||
mask-image: radial-gradient(circle at 50% 50%, #000 30%, transparent 80%);
|
||
}
|
||
|
||
.login-wrap {
|
||
position: relative;
|
||
z-index: 2;
|
||
min-height: 100vh;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 28px 24px 92px;
|
||
}
|
||
|
||
.login-card {
|
||
width: min(960px, 100%);
|
||
height: 560px;
|
||
min-height: 560px;
|
||
display: grid;
|
||
grid-template-columns: 499px minmax(0, 1fr);
|
||
background: var(--login-bg-secondary);
|
||
border: 1px solid rgba(255, 255, 255, 0.7);
|
||
border-radius: var(--login-radius);
|
||
box-shadow: var(--login-shadow);
|
||
backdrop-filter: blur(22px);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.brand-panel {
|
||
position: relative;
|
||
height: 558px;
|
||
padding: 44px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
color: var(--login-text-primary);
|
||
background: linear-gradient(150deg, rgba(79, 140, 255, .10), rgba(124, 92, 255, .06) 60%, transparent);
|
||
|
||
border-right: 1px solid rgba(17, 24, 39, 0.06);
|
||
}
|
||
|
||
.brand-panel__mask {
|
||
position: absolute;
|
||
inset: 0;
|
||
}
|
||
|
||
.brand-top,
|
||
.brand-hero,
|
||
.brand-foot {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.brand-top {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.brand-logo-img {
|
||
display: block;
|
||
max-width: min(100%, 420px);
|
||
max-height: 110px;
|
||
object-fit: contain;
|
||
}
|
||
|
||
.brand-hero {
|
||
margin-top: auto;
|
||
|
||
h1 {
|
||
margin: 18px 0 7px;
|
||
font-size: clamp(34px, 3.4vw, 35px);
|
||
line-height: 1.18;
|
||
letter-spacing: -0.03em;
|
||
}
|
||
|
||
p {
|
||
max-width: 420px;
|
||
margin: 0;
|
||
color: var(--login-text-secondary);
|
||
font-size: 15px;
|
||
line-height: 1.9;
|
||
}
|
||
}
|
||
|
||
.brand-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 8px 14px;
|
||
border-radius: 999px;
|
||
background: rgba(255, 255, 255, 0.7);
|
||
border: 1px solid rgba(59, 123, 255, 0.12);
|
||
color: var(--login-accent);
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.hl {
|
||
color: var(--login-accent);
|
||
}
|
||
|
||
.feature-list {
|
||
margin-top: 28px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 14px;
|
||
}
|
||
|
||
.feature-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
color: var(--login-text-secondary);
|
||
font-size: 14px;
|
||
}
|
||
|
||
.feature-ico {
|
||
width: 34px;
|
||
height: 34px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 10px;
|
||
background: var(--login-accent-soft);
|
||
color: var(--login-accent);
|
||
flex: none;
|
||
}
|
||
|
||
.brand-foot {
|
||
margin-top: 36px;
|
||
|
||
p {
|
||
margin: 18px 0 0;
|
||
color: var(--login-text-muted);
|
||
font-size: 12px;
|
||
}
|
||
}
|
||
|
||
.brand-metrics {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||
gap: 12px;
|
||
}
|
||
|
||
.metric-card {
|
||
padding: 18px 16px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
border-radius: 18px;
|
||
background: rgba(255, 255, 255, 0.72);
|
||
border: 1px solid rgba(255, 255, 255, 0.72);
|
||
|
||
strong {
|
||
color: var(--login-text-primary);
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
span {
|
||
color: var(--login-text-secondary);
|
||
font-size: 13px;
|
||
}
|
||
}
|
||
|
||
.form-panel {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 44px;
|
||
background: rgba(255, 255, 255, 0.82);
|
||
}
|
||
|
||
.login-form {
|
||
width: 100%;
|
||
max-width: 100%;
|
||
}
|
||
|
||
.form-head {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
margin-bottom: 28px;
|
||
}
|
||
|
||
.form-kicker {
|
||
margin: 0 0 8px;
|
||
color: var(--login-accent);
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.form-title {
|
||
margin: 0;
|
||
color: var(--login-text-primary);
|
||
font-size: 30px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.form-desc {
|
||
margin: 8px 0 0;
|
||
color: var(--login-text-muted);
|
||
font-size: 14px;
|
||
line-height: 1.8;
|
||
}
|
||
|
||
.form-body {
|
||
border-radius: 24px;
|
||
background: rgba(255, 255, 255, 0.72);
|
||
}
|
||
|
||
.field-block + .field-block {
|
||
margin-top: 18px;
|
||
}
|
||
|
||
.field-label {
|
||
display: block;
|
||
margin-bottom: 8px;
|
||
color: var(--login-text-secondary);
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.form-item {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.login-form .el-input {
|
||
height: 48px;
|
||
}
|
||
|
||
.login-form .input-icon {
|
||
width: 15px;
|
||
height: 46px;
|
||
margin-left: 0;
|
||
color: var(--login-text-muted);
|
||
}
|
||
|
||
.login-form :deep(.el-input__wrapper) {
|
||
min-height: 48px;
|
||
padding: 0 14px;
|
||
border-radius: 14px;
|
||
background: var(--login-bg-input);
|
||
box-shadow: 0 0 0 1px var(--login-border) inset;
|
||
}
|
||
|
||
.login-form :deep(.el-input__wrapper.is-focus) {
|
||
box-shadow:
|
||
0 0 0 1px rgba(59, 123, 255, 0.28) inset,
|
||
0 0 0 4px rgba(59, 123, 255, 0.12);
|
||
}
|
||
|
||
.login-form :deep(.el-input__inner) {
|
||
color: var(--login-text-primary);
|
||
}
|
||
|
||
.login-form :deep(.el-input__inner::placeholder) {
|
||
color: var(--login-text-muted);
|
||
}
|
||
|
||
.login-form :deep(.el-input__suffix-inner) {
|
||
color: var(--login-text-muted);
|
||
}
|
||
|
||
.captcha-row {
|
||
:deep(.el-form-item__content) {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) 124px;
|
||
gap: 12px;
|
||
}
|
||
}
|
||
|
||
.login-code {
|
||
height: 48px;
|
||
overflow: hidden;
|
||
border-radius: 14px;
|
||
border: 1px solid var(--login-border);
|
||
background: var(--login-bg-card);
|
||
|
||
img {
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
cursor: pointer;
|
||
object-fit: cover;
|
||
}
|
||
}
|
||
|
||
.login-code-img {
|
||
padding-left: 0;
|
||
}
|
||
|
||
.form-meta {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
margin: 18px 0 24px;
|
||
}
|
||
|
||
.login-form :deep(.el-checkbox__label) {
|
||
color: var(--login-text-secondary);
|
||
}
|
||
|
||
.login-form :deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
|
||
background-color: var(--login-accent);
|
||
border-color: var(--login-accent);
|
||
}
|
||
|
||
.link-type {
|
||
color: #2f6af0;
|
||
font-size: 13px;
|
||
text-decoration: none;
|
||
|
||
&:hover {
|
||
color: #2f6af0;
|
||
}
|
||
}
|
||
|
||
.link-button {
|
||
padding: 0;
|
||
background: transparent;
|
||
border: none;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.submit-row {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.submit-button {
|
||
width: 100%;
|
||
height: 48px;
|
||
border: none;
|
||
border-radius: 14px;
|
||
background: linear-gradient(135deg, var(--login-accent), #6ba0ff);
|
||
box-shadow: none;
|
||
|
||
&:hover,
|
||
&:focus {
|
||
box-shadow: none;
|
||
}
|
||
}
|
||
|
||
.divider {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
margin: 24px 0 18px;
|
||
color: var(--login-text-muted);
|
||
font-size: 12px;
|
||
|
||
&::before,
|
||
&::after {
|
||
content: '';
|
||
flex: 1;
|
||
height: 1px;
|
||
background: rgba(17, 24, 39, 0.08);
|
||
}
|
||
}
|
||
|
||
.social-actions {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
}
|
||
|
||
.login-form :deep(.el-button.is-circle) {
|
||
width: 42px;
|
||
height: 42px;
|
||
margin: 0;
|
||
background: var(--login-bg-card);
|
||
border: 1px solid var(--login-border);
|
||
color: var(--login-text-secondary);
|
||
}
|
||
|
||
.login-form :deep(.el-button.is-circle:hover) {
|
||
background: var(--login-bg-hover);
|
||
border-color: rgba(59, 123, 255, 0.24);
|
||
color: var(--login-accent);
|
||
}
|
||
|
||
.login-form :deep(.el-form-item__error) {
|
||
padding-top: 7px;
|
||
}
|
||
|
||
.el-login-footer {
|
||
position: fixed;
|
||
right: 0;
|
||
bottom: 0;
|
||
left: 0;
|
||
z-index: 2;
|
||
height: 42px;
|
||
line-height: 42px;
|
||
text-align: center;
|
||
color: rgba(91, 97, 114, 0.78);
|
||
font-size: 12px;
|
||
}
|
||
|
||
/* 弹窗根节点经 Teleport 挂到 body,用 :global 覆盖全局 dialog 样式 */
|
||
:global(.forgot-password-dialog.el-dialog) {
|
||
border-radius: 18px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
:global(.forgot-password-dialog__header) {
|
||
margin-right: 0 !important;
|
||
padding: 24px 28px !important;
|
||
border-bottom: 1px solid rgba(17, 24, 39, 0.08);
|
||
}
|
||
|
||
:global(.forgot-password-dialog__header .el-dialog__title) {
|
||
color: #1d2330;
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
:global(.forgot-password-dialog__header .el-dialog__headerbtn) {
|
||
top: 22px;
|
||
right: 20px;
|
||
}
|
||
|
||
:global(.forgot-password-dialog__body) {
|
||
padding: 22px 28px 36px !important;
|
||
max-height: none !important;
|
||
overflow: visible !important;
|
||
}
|
||
|
||
/* 插槽内节点带 scoped 标记,可稳定约束输入框高度 */
|
||
.forgot-password-panel {
|
||
:deep(.el-form-item__label) {
|
||
padding-bottom: 10px;
|
||
color: var(--login-text-secondary);
|
||
font-size: 14px;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
/* 生产构建中 EP `.el-input .el-input__icon{height:inherit}` 会压过 SvgIcon 的 1em,
|
||
SVG 回退为默认约 150px 高,把输入框撑变形;与登录表单同样锁死尺寸 */
|
||
:deep(.el-input) {
|
||
height: 40px;
|
||
}
|
||
|
||
:deep(.el-input__wrapper) {
|
||
min-height: 40px;
|
||
height: 40px;
|
||
border-radius: 12px;
|
||
box-shadow: none;
|
||
background: var(--login-bg-input, #f3f5f9);
|
||
}
|
||
|
||
:deep(.el-input__wrapper.is-focus) {
|
||
box-shadow: 0 0 0 1px rgba(59, 123, 255, 0.35) inset;
|
||
}
|
||
|
||
:deep(.el-input__inner::placeholder) {
|
||
color: var(--login-text-muted, #9aa1b2);
|
||
}
|
||
|
||
:deep(.el-input .input-icon) {
|
||
width: 15px;
|
||
height: 20px;
|
||
margin-left: 0;
|
||
}
|
||
}
|
||
|
||
.forgot-password-field {
|
||
margin-bottom: 18px;
|
||
}
|
||
|
||
.forgot-sms-tip {
|
||
margin: 0 0 18px;
|
||
color: var(--login-text-secondary);
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.forgot-sms-phone {
|
||
margin-left: 4px;
|
||
color: var(--login-accent);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.forgot-sms-row {
|
||
display: grid;
|
||
grid-template-columns: 1fr auto;
|
||
gap: 10px;
|
||
width: 100%;
|
||
align-items: center;
|
||
}
|
||
|
||
.forgot-sms-send {
|
||
height: 40px;
|
||
padding: 0 14px;
|
||
border: none;
|
||
border-radius: 10px;
|
||
background: #eaf1ff;
|
||
color: var(--login-accent);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.forgot-sms-send:hover,
|
||
.forgot-sms-send:focus {
|
||
background: #dde9ff;
|
||
color: var(--login-accent);
|
||
}
|
||
|
||
.forgot-sms-send.is-disabled,
|
||
.forgot-sms-send:disabled {
|
||
background: #f0f3f8;
|
||
color: var(--login-text-muted);
|
||
}
|
||
|
||
.forgot-password-submit {
|
||
width: 100%;
|
||
height: 48px;
|
||
border: none;
|
||
border-radius: 14px;
|
||
background: linear-gradient(135deg, var(--login-accent), #6ba0ff);
|
||
box-shadow: none;
|
||
|
||
&:hover,
|
||
&:focus {
|
||
box-shadow: none;
|
||
}
|
||
}
|
||
|
||
.forgot-password-tips {
|
||
margin: 16px 0 0;
|
||
color: #99a1b3;
|
||
font-size: 12px;
|
||
line-height: 1.8;
|
||
}
|
||
|
||
@keyframes aurora-float {
|
||
0%,
|
||
100% {
|
||
transform: translate(0, 0);
|
||
}
|
||
|
||
50% {
|
||
transform: translate(40px, 30px);
|
||
}
|
||
}
|
||
|
||
@media (max-width: 960px) {
|
||
.login-wrap {
|
||
padding: 20px 16px 82px;
|
||
}
|
||
|
||
.login-card {
|
||
width: min(960px, 100%);
|
||
height: auto;
|
||
min-height: 0;
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.brand-panel {
|
||
height: auto;
|
||
min-height: 360px;
|
||
}
|
||
|
||
.brand-metrics {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 760px) {
|
||
.brand-panel {
|
||
display: none;
|
||
}
|
||
|
||
.form-panel {
|
||
padding: 28px 20px;
|
||
}
|
||
|
||
.form-body {
|
||
padding: 22px 18px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 560px) {
|
||
.form-head {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.captcha-row :deep(.el-form-item__content) {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.social-actions {
|
||
justify-content: flex-start;
|
||
}
|
||
}
|
||
</style>
|