feat: 登录界面UI优化 - 科技极简黑蓝紫风格

设计亮点:
1. 左右布局分区
   - 左侧:品牌展示区,深空黑渐变背景
   - 右侧:登录表单区,黑色主题

2. 配色方案
   - 主色调:深空黑(#0a0a0a) + 冰川蓝(#6B8AFF) + 薰衣紫(#A855F7)
   - 标题:紫色渐变
   - 提示文字:蓝色
   - 背景光点:蓝紫渐变

3. 交互特效
   - 背景50个光点随机分布,hover时联动放大发光
   - 品牌区脉冲动画背景光晕
   - 图标浮动动画
   - 按钮hover光扫过效果

4. 输入框设计
   - 极简圆角矩形(12px)
   - 边框:浅灰透明
   - 聚焦:蓝紫渐变光晕
   - 背景:深色半透明

5. 按钮设计
   - 纯黑底色 + 蓝紫渐变
   - 压印线条图案(光扫效果)
   - hover时提升阴影和上移

6. 品牌元素
   - SVG渐变Logo
   - 产品slogan:AI赋能·智创未来
   - 特性标签:智能创作、数字人技术、内容分析

响应式:
- 小屏自动切换为上下布局

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-25 01:34:46 +08:00
parent 31f01085a9
commit 46b98e78e5

View File

@@ -1,142 +1,188 @@
<template> <template>
<div class="login-container"> <div class="login-container">
<!-- 背景动画 --> <!-- 科技感背景光点 -->
<div class="bg-animation"> <div class="bg-stars">
<div class="floating-shapes"> <div
<div class="shape shape-1"></div> v-for="(star, index) in stars"
<div class="shape shape-2"></div> :key="index"
<div class="shape shape-3"></div> class="star"
<div class="shape shape-4"></div> :class="{ active: star.active }"
</div> :style="{
left: star.left + '%',
top: star.top + '%',
width: star.size + 'px',
height: star.size + 'px'
}"
@mouseenter="activateStar(index)"
@mouseleave="deactivateStar(index)"
></div>
</div> </div>
<!-- 登录表单卡片 --> <div class="login-content">
<div class="login-card"> <!-- 左侧品牌区 -->
<div class="card-header"> <div class="brand-panel">
<h1 class="title">欢迎回来</h1> <div class="brand-inner">
<p class="subtitle">登录您的账户以继续</p> <div class="brand-icon">
<svg width="80" height="80" viewBox="0 0 80 80">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6B8AFF;stop-opacity:1" />
<stop offset="100%" style="stop-color:#A855F7;stop-opacity:1" />
</linearGradient>
</defs>
<circle cx="40" cy="40" r="35" fill="none" stroke="url(#grad1)" stroke-width="2"/>
<path d="M 40 20 L 40 40 L 55 40" stroke="url(#grad1)" stroke-width="3" fill="none" stroke-linecap="round"/>
</svg>
</div>
<h1 class="brand-title">金牌内容大师</h1>
<p class="brand-subtitle">AI赋能 · 智创未来</p>
<div class="brand-features">
<div class="feature-item">
<span class="feature-icon"></span>
<span>智能创作</span>
</div>
<div class="feature-item">
<span class="feature-icon"></span>
<span>数字人技术</span>
</div>
<div class="feature-item">
<span class="feature-icon"></span>
<span>内容分析</span>
</div>
</div>
</div>
</div> </div>
<a-tabs v-model:activeKey="activeTab" centered class="login-tabs"> <!-- 右侧登录区 -->
<a-tab-pane key="sms" tab="短信登录"> <div class="login-panel">
<a-form <div class="login-card">
:model="smsForm" <div class="card-header">
:rules="smsRules" <h2 class="login-title">欢迎登录</h2>
ref="smsFormRef" <p class="login-subtitle">进入您的创作空间</p>
layout="vertical" </div>
>
<a-form-item name="mobile" label="手机号"> <a-tabs v-model:activeKey="activeTab" centered class="login-tabs">
<a-input <a-tab-pane key="sms" tab="短信登录">
v-model:value="smsForm.mobile" <a-form
size="large" :model="smsForm"
placeholder="请输入手机号" :rules="smsRules"
:maxlength="11" ref="smsFormRef"
layout="vertical"
> >
<template #prefix> <a-form-item name="mobile" label="手机号">
<PhoneOutlined /> <a-input
</template> v-model:value="smsForm.mobile"
</a-input> size="large"
</a-form-item> placeholder="请输入手机号"
:maxlength="11"
class="tech-input"
>
<template #prefix>
<PhoneOutlined class="input-icon" />
</template>
</a-input>
</a-form-item>
<a-form-item name="code" label="验证码"> <a-form-item name="code" label="验证码">
<div class="code-input"> <div class="code-input">
<a-input <a-input
v-model:value="smsForm.code" v-model:value="smsForm.code"
size="large" size="large"
placeholder="请输入验证码" placeholder="请输入验证码"
:maxlength="6" :maxlength="6"
> class="tech-input"
<template #prefix> >
<SafetyOutlined /> <template #prefix>
</template> <SafetyOutlined class="input-icon" />
</a-input> </template>
<a-button </a-input>
type="primary" <a-button
:disabled="codeCountdown > 0" type="primary"
:loading="sendingCode" :disabled="codeCountdown > 0"
@click="sendSmsCode" :loading="sendingCode"
class="send-code-btn" @click="sendSmsCode"
> class="send-code-btn"
{{ codeCountdown > 0 ? `${codeCountdown}s后重发` : '发送验证码' }} >
</a-button> {{ codeCountdown > 0 ? `${codeCountdown}s后重发` : '发送验证码' }}
</div> </a-button>
</a-form-item> </div>
</a-form-item>
<a-form-item> <a-form-item>
<a-button <a-button
type="primary" type="primary"
html-type="submit" html-type="submit"
size="large" size="large"
block block
:loading="loggingIn" :loading="loggingIn"
@click="handleSmsLogin" @click="handleSmsLogin"
class="login-btn" class="login-btn"
>
立即登录
</a-button>
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane key="password" tab="密码登录">
<a-form
:model="passwordForm"
:rules="passwordRules"
ref="passwordFormRef"
layout="vertical"
> >
登录 <a-form-item name="mobile" label="手机号">
</a-button> <a-input
</a-form-item> v-model:value="passwordForm.mobile"
</a-form> size="large"
</a-tab-pane> placeholder="请输入手机号"
:maxlength="11"
class="tech-input"
>
<template #prefix>
<PhoneOutlined class="input-icon" />
</template>
</a-input>
</a-form-item>
<a-tab-pane key="password" tab="密码登录"> <a-form-item name="password" label="密码">
<a-form <a-input-password
:model="passwordForm" v-model:value="passwordForm.password"
:rules="passwordRules" size="large"
ref="passwordFormRef" placeholder="请输入密码"
layout="vertical" @pressEnter="handlePasswordLogin"
> class="tech-input"
<a-form-item name="mobile" label="手机号"> >
<a-input <template #prefix>
v-model:value="passwordForm.mobile" <LockOutlined class="input-icon" />
size="large" </template>
placeholder="请输入手机号" </a-input-password>
:maxlength="11" </a-form-item>
>
<template #prefix>
<PhoneOutlined />
</template>
</a-input>
</a-form-item>
<a-form-item name="password" label="密码"> <a-form-item>
<a-input-password <a-button
v-model:value="passwordForm.password" type="primary"
size="large" html-type="submit"
placeholder="请输入密码" size="large"
@pressEnter="handlePasswordLogin" block
> :loading="loggingIn"
<template #prefix> @click="handlePasswordLogin"
<LockOutlined /> class="login-btn"
</template> >
</a-input-password> 立即登录
</a-form-item> </a-button>
</a-form-item>
<a-form-item> </a-form>
<a-button </a-tab-pane>
type="primary" </a-tabs>
html-type="submit" </div>
size="large"
block
:loading="loggingIn"
@click="handlePasswordLogin"
class="login-btn"
>
登录
</a-button>
</a-form-item>
</a-form>
</a-tab-pane>
</a-tabs>
<div class="card-footer">
<p class="tip">登录即表示您同意我们的服务条款和隐私政策</p>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, reactive } from 'vue' import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { import {
@@ -173,6 +219,9 @@ const codeCountdown = ref(0)
// 验证码倒计时 // 验证码倒计时
let countdownTimer = null let countdownTimer = null
// 背景光点
const stars = ref([])
// 表单验证规则 // 表单验证规则
const smsRules = { const smsRules = {
mobile: [ mobile: [
@@ -196,6 +245,29 @@ const passwordRules = {
] ]
} }
// 初始化背景光点
onMounted(() => {
const starCount = 50
for (let i = 0; i < starCount; i++) {
stars.value.push({
left: Math.random() * 100,
top: Math.random() * 100,
size: Math.random() * 3 + 1,
active: false
})
}
})
// 激活光点
function activateStar(index) {
stars.value[index].active = true
}
// 停用光点
function deactivateStar(index) {
stars.value[index].active = false
}
// 发送验证码 // 发送验证码
async function sendSmsCode() { async function sendSmsCode() {
try { try {
@@ -283,120 +355,185 @@ async function handlePasswordLogin() {
<style scoped> <style scoped>
.login-container { .login-container {
min-height: 100vh; min-height: 100vh;
background: #0a0a0a;
position: relative;
overflow: hidden;
}
/* 背景光点 */
.bg-stars {
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
}
.star {
position: absolute;
background: linear-gradient(135deg, #6B8AFF 0%, #A855F7 100%);
border-radius: 50%;
opacity: 0.3;
transition: all 0.3s ease;
cursor: pointer;
}
.star.active {
opacity: 0.8;
transform: scale(1.5);
box-shadow: 0 0 20px rgba(107, 138, 255, 0.6);
}
/* 主内容区 */
.login-content {
display: flex;
min-height: 100vh;
}
/* 左侧品牌区 */
.brand-panel {
flex: 1;
background: linear-gradient(135deg, #1a1a2e 0%, #0f0f1e 100%);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 60px;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
} }
/* 背景动画 */ .brand-panel::before {
.bg-animation { content: '';
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
}
.floating-shapes {
position: relative;
width: 100%;
height: 100%;
}
.shape {
position: absolute; position: absolute;
width: 500px;
height: 500px;
background: radial-gradient(circle, rgba(107, 138, 255, 0.1) 0%, transparent 70%);
border-radius: 50%; border-radius: 50%;
filter: blur(40px);
opacity: 0.3;
animation: float 20s infinite ease-in-out;
}
.shape-1 {
width: 300px;
height: 300px;
background: #ffffff;
top: -100px; top: -100px;
left: -100px; right: -100px;
animation-delay: 0s; animation: pulse 4s ease-in-out infinite;
} }
.shape-2 { .brand-panel::after {
content: '';
position: absolute;
width: 400px; width: 400px;
height: 400px; height: 400px;
background: #ffd700; background: radial-gradient(circle, rgba(168, 85, 247, 0.1) 0%, transparent 70%);
top: 50%; border-radius: 50%;
right: -150px; bottom: -50px;
animation-delay: -5s; left: -50px;
animation: pulse 4s ease-in-out infinite 2s;
} }
.shape-3 { @keyframes pulse {
width: 250px; 0%, 100% {
height: 250px; transform: scale(1);
background: #ff6b9d; opacity: 0.5;
bottom: -80px; }
left: 30%; 50% {
animation-delay: -10s; transform: scale(1.1);
opacity: 0.8;
}
} }
.shape-4 { .brand-inner {
width: 350px; position: relative;
height: 350px; z-index: 1;
background: #00f2fe; text-align: center;
top: 20%; max-width: 400px;
left: 10%; }
animation-delay: -15s;
.brand-icon {
margin-bottom: 30px;
animation: float 3s ease-in-out infinite;
} }
@keyframes float { @keyframes float {
0%, 100% { 0%, 100% {
transform: translate(0, 0) rotate(0deg); transform: translateY(0);
} }
33% { 50% {
transform: translate(100px, -100px) rotate(120deg); transform: translateY(-10px);
}
66% {
transform: translate(-80px, 80px) rotate(240deg);
} }
} }
/* 登录卡片 */ .brand-title {
font-size: 48px;
font-weight: 700;
background: linear-gradient(135deg, #6B8AFF 0%, #A855F7 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 15px;
}
.brand-subtitle {
font-size: 18px;
color: #6B8AFF;
margin-bottom: 50px;
opacity: 0.8;
}
.brand-features {
display: flex;
flex-direction: column;
gap: 20px;
}
.feature-item {
display: flex;
align-items: center;
gap: 12px;
color: #a0a0a0;
font-size: 16px;
}
.feature-icon {
color: #A855F7;
font-size: 20px;
}
/* 右侧登录区 */
.login-panel {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 60px;
background: #0a0a0a;
}
.login-card { .login-card {
width: 100%; width: 100%;
max-width: 450px; max-width: 420px;
background: rgba(255, 255, 255, 0.95); background: rgba(26, 26, 46, 0.6);
backdrop-filter: blur(20px); backdrop-filter: blur(20px);
border-radius: 20px; border-radius: 24px;
padding: 50px 40px; padding: 50px 40px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); border: 1px solid rgba(107, 138, 255, 0.2);
position: relative; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
z-index: 1;
border: 1px solid rgba(255, 255, 255, 0.3);
} }
.card-header { .card-header {
text-align: center; text-align: center;
margin-bottom: 30px; margin-bottom: 40px;
} }
.title { .login-title {
font-size: 32px; font-size: 32px;
font-weight: 700; font-weight: 700;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #A855F7;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 10px; margin-bottom: 10px;
} }
.subtitle { .login-subtitle {
color: #666;
font-size: 14px; font-size: 14px;
color: #6B8AFF;
margin: 0; margin: 0;
opacity: 0.8;
} }
/* 标签页样式 */ /* 标签页 */
.login-tabs { .login-tabs {
margin-bottom: 20px; margin-bottom: 20px;
} }
@@ -406,83 +543,155 @@ async function handlePasswordLogin() {
} }
.login-tabs :deep(.ant-tabs-tab) { .login-tabs :deep(.ant-tabs-tab) {
font-size: 16px; font-size: 15px;
font-weight: 500; font-weight: 500;
padding: 12px 20px; padding: 10px 24px;
color: #666;
} }
.login-tabs :deep(.ant-tabs-tab-active) { .login-tabs :deep(.ant-tabs-tab-active) {
color: #667eea; color: #A855F7;
} }
.login-tabs :deep(.ant-tabs-ink-bar) { .login-tabs :deep(.ant-tabs-ink-bar) {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); background: linear-gradient(135deg, #6B8AFF 0%, #A855F7 100%);
height: 3px;
} }
/* 表单样式 */ /* 输入框 */
:deep(.ant-form-item-label > label) { .tech-input {
font-weight: 500; border-radius: 12px;
color: #333; border: 1.5px solid rgba(255, 255, 255, 0.1);
} background: rgba(15, 15, 30, 0.5);
:deep(.ant-input-affix-wrapper) {
border-radius: 10px;
border: 2px solid #e8e8e8;
transition: all 0.3s; transition: all 0.3s;
} }
:deep(.ant-input-affix-wrapper:hover), .tech-input:hover {
:deep(.ant-input-affix-wrapper-focused) { border-color: rgba(107, 138, 255, 0.5);
border-color: #667eea; }
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
.tech-input:focus,
.tech-input-focused {
border-color: transparent;
background: rgba(15, 15, 30, 0.8);
box-shadow: 0 0 0 2px rgba(107, 138, 255, 0.2);
} }
:deep(.ant-input) { :deep(.ant-input) {
color: #fff;
font-size: 15px; font-size: 15px;
} }
/* 验证码输入组 */ :deep(.ant-input-prefix) {
color: rgba(107, 138, 255, 0.8);
}
:deep(.ant-form-item-label > label) {
font-weight: 500;
color: #a0a0a0;
}
:deep(.ant-input-affix-wrapper) {
border-radius: 12px;
border: 1.5px solid rgba(255, 255, 255, 0.1);
background: rgba(15, 15, 30, 0.5);
transition: all 0.3s;
}
:deep(.ant-input-affix-wrapper:hover) {
border-color: rgba(107, 138, 255, 0.5);
}
:deep(.ant-input-affix-wrapper-focused) {
border-color: transparent;
background: rgba(15, 15, 30, 0.8);
box-shadow: 0 0 0 2px rgba(107, 138, 255, 0.2);
}
/* 验证码输入 */
.code-input { .code-input {
display: flex; display: flex;
gap: 10px; gap: 12px;
} }
.send-code-btn { .send-code-btn {
border-radius: 10px; border-radius: 12px;
white-space: nowrap; white-space: nowrap;
flex-shrink: 0; flex-shrink: 0;
background: linear-gradient(135deg, rgba(107, 138, 255, 0.2) 0%, rgba(168, 85, 247, 0.2) 100%);
border: 1px solid rgba(107, 138, 255, 0.3);
color: #6B8AFF;
font-weight: 500;
}
.send-code-btn:hover {
background: linear-gradient(135deg, rgba(107, 138, 255, 0.3) 0%, rgba(168, 85, 247, 0.3) 100%);
border-color: rgba(107, 138, 255, 0.5);
} }
/* 登录按钮 */ /* 登录按钮 */
.login-btn { .login-btn {
height: 48px; height: 52px;
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
border-radius: 10px; border-radius: 12px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); background: linear-gradient(135deg, #6B8AFF 0%, #A855F7 100%);
border: none; border: none;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); box-shadow: 0 4px 20px rgba(107, 138, 255, 0.3);
transition: all 0.3s; position: relative;
overflow: hidden;
}
.login-btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
.login-btn:hover::before {
left: 100%;
} }
.login-btn:hover { .login-btn:hover {
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6); box-shadow: 0 6px 30px rgba(107, 138, 255, 0.5);
} }
.login-btn:active { .login-btn:active {
transform: translateY(0); transform: translateY(0);
} }
/* 底部提示 */ /* 图标 */
.card-footer { .input-icon {
text-align: center; font-size: 18px;
margin-top: 20px;
} }
.tip { /* 响应式 */
color: #999; @media (max-width: 992px) {
font-size: 12px; .login-content {
margin: 0; flex-direction: column;
}
.brand-panel {
padding: 40px 20px;
min-height: 200px;
}
.login-panel {
padding: 40px 20px;
}
.brand-title {
font-size: 36px;
}
.login-card {
padding: 30px 20px;
}
} }
</style> </style>