| | |
| | | <template> |
| | | <div class="login-container"> |
| | | <!-- <div class="login-background">--> |
| | | <!-- <div class="gradient-overlay"></div>--> |
| | | <!-- </div>--> |
| | | <!-- 动态背景 --> |
| | | <div class="background-animation"> |
| | | <div class="floating-shapes"> |
| | | <div class="shape shape-1"></div> |
| | | <div class="shape shape-2"></div> |
| | | <div class="shape shape-3"></div> |
| | | <div class="shape shape-4"></div> |
| | | </div> |
| | | <div class="network-lines"></div> |
| | | <div class="gradient-overlay"></div> |
| | | </div> |
| | | |
| | | <el-card class="login-card"> |
| | | <!-- 主登录区域 --> |
| | | <div class="login-content"> |
| | | <!-- 左侧品牌展示区 --> |
| | | <div class="brand-section"> |
| | | <img |
| | | src="~@/assets/images/img.png" |
| | | class="brand-logo" |
| | | alt="系统logo" |
| | | /> |
| | | <h1 class="system-name">接口自动化平台</h1> |
| | | <div class="brand-logo-container"> |
| | | <div class="logo-icon"> |
| | | <svg viewBox="0 0 100 100" class="api-icon"> |
| | | <!-- 主API连接图标 --> |
| | | <path d="M20,30 L50,10 L80,30 L80,70 L50,90 L20,70 Z" fill="rgba(0,212,255,0.1)" stroke="rgba(0,212,255,0.6)" stroke-width="2"/> |
| | | <!-- 内部连接层 --> |
| | | <path d="M30,40 L50,25 L70,40 L70,60 L50,75 L30,60 Z" fill="rgba(148,0,211,0.1)" stroke="rgba(148,0,211,0.6)" stroke-width="1.5"/> |
| | | <!-- 核心数据点 --> |
| | | <path d="M40,50 L50,40 L60,50 L60,55 L50,60 L40,55 Z" fill="rgba(255,255,255,0.2)" stroke="rgba(255,255,255,0.8)" stroke-width="1"/> |
| | | <!-- 动态连接线 --> |
| | | <line x1="35" y1="45" x2="45" y2="52" stroke="#00d4ff" stroke-width="1.5" stroke-dasharray="2,2"/> |
| | | <line x1="55" y1="45" x2="65" y2="52" stroke="#9400d3" stroke-width="1.5" stroke-dasharray="2,2"/> |
| | | <!-- 中心数据流点 --> |
| | | <circle cx="50" cy="50" r="4" fill="#00d4ff"> |
| | | <animate attributeName="r" values="4;6;4" dur="2s" repeatCount="indefinite"/> |
| | | <animate attributeName="opacity" values="1;0.7;1" dur="2s" repeatCount="indefinite"/> |
| | | </circle> |
| | | <!-- 外围数据点 --> |
| | | <circle cx="35" cy="35" r="2" fill="#00d4ff"> |
| | | <animate attributeName="opacity" values="0.5;1;0.5" dur="1.5s" repeatCount="indefinite"/> |
| | | </circle> |
| | | <circle cx="65" cy="35" r="2" fill="#9400d3"> |
| | | <animate attributeName="opacity" values="0.5;1;0.5" dur="1.5s" repeatCount="indefinite" begin="0.5s"/> |
| | | </circle> |
| | | <circle cx="35" cy="65" r="2" fill="#9400d3"> |
| | | <animate attributeName="opacity" values="0.5;1;0.5" dur="1.5s" repeatCount="indefinite" begin="1s"/> |
| | | </circle> |
| | | <circle cx="65" cy="65" r="2" fill="#00d4ff"> |
| | | <animate attributeName="opacity" values="0.5;1;0.5" dur="1.5s" repeatCount="indefinite" begin="1.5s"/> |
| | | </circle> |
| | | </svg> |
| | | </div> |
| | | <h1 class="system-name">APITest Pro</h1> |
| | | <p class="system-desc">智能接口自动化测试平台</p> |
| | | </div> |
| | | |
| | | <div class="feature-list"> |
| | | <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> |
| | | |
| | | <el-form |
| | | ref="loginForm" |
| | | @submit.native.prevent="submitForm" |
| | | class="login-form" |
| | | > |
| | | <el-form-item> |
| | | <el-input |
| | | v-model="loginForm.username" |
| | | placeholder="账号" |
| | | prefix-icon="el-icon-user-solid" |
| | | class="custom-input" |
| | | :class="{ 'input-error': usernameInvalid }" |
| | | @blur="validateUserName" |
| | | /> |
| | | <transition name="el-zoom-in-top"> |
| | | <div v-if="usernameInvalid" class="error-msg">{{ usernameInvalid }}</div> |
| | | </transition> |
| | | </el-form-item> |
| | | <!-- 右侧登录表单 --> |
| | | <div class="login-form-section"> |
| | | <div class="form-container"> |
| | | <div class="form-header"> |
| | | <h2>欢迎登录</h2> |
| | | <p>请使用您的账号密码登录系统</p> |
| | | </div> |
| | | |
| | | <el-form-item> |
| | | <el-input |
| | | v-model="loginForm.password" |
| | | type="password" |
| | | placeholder="密码" |
| | | prefix-icon="el-icon-lock" |
| | | show-password |
| | | class="custom-input" |
| | | :class="{ 'input-error': passwordInvalid }" |
| | | @blur="validatePassword" |
| | | /> |
| | | <transition name="el-zoom-in-top"> |
| | | <div v-if="passwordInvalid" class="error-msg">{{ passwordInvalid }}</div> |
| | | </transition> |
| | | </el-form-item> |
| | | <el-form |
| | | ref="loginForm" |
| | | @submit.native.prevent="submitForm" |
| | | class="login-form" |
| | | > |
| | | <div class="input-group"> |
| | | <label class="input-label">用户名</label> |
| | | <el-input |
| | | v-model="loginForm.username" |
| | | placeholder="请输入用户名" |
| | | prefix-icon="el-icon-user" |
| | | class="modern-input" |
| | | :class="{ 'input-error': usernameInvalid }" |
| | | @blur="validateUserName" |
| | | /> |
| | | <transition name="slide-fade"> |
| | | <div v-if="usernameInvalid" class="error-msg">{{ usernameInvalid }}</div> |
| | | </transition> |
| | | </div> |
| | | |
| | | <el-button |
| | | type="primary" |
| | | class="login-button" |
| | | :loading="isLoading" |
| | | @click="submitForm" |
| | | > |
| | | {{ isLoading ? '登录中...' : '立即登录' }} |
| | | </el-button> |
| | | </el-form> |
| | | </el-card> |
| | | <div class="input-group"> |
| | | <label class="input-label">密码</label> |
| | | <el-input |
| | | v-model="loginForm.password" |
| | | type="password" |
| | | placeholder="请输入密码" |
| | | prefix-icon="el-icon-lock" |
| | | show-password |
| | | class="modern-input" |
| | | :class="{ 'input-error': passwordInvalid }" |
| | | @blur="validatePassword" |
| | | /> |
| | | <transition name="slide-fade"> |
| | | <div v-if="passwordInvalid" class="error-msg">{{ passwordInvalid }}</div> |
| | | </transition> |
| | | </div> |
| | | |
| | | <el-button |
| | | type="primary" |
| | | class="login-button" |
| | | :loading="isLoading" |
| | | @click="submitForm" |
| | | > |
| | | {{ isLoading ? '登录中...' : '登录系统' }} |
| | | </el-button> |
| | | </el-form> |
| | | |
| | | <div class="form-footer"> |
| | | <p>还没有账号?<a href="#" class="register-link">联系管理员注册</a></p> |
| | | <p class="copyright">© 2025 APITest Pro 智能接口自动化平台</p> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | if (resp.success) { |
| | | // 显示登录信息提示 |
| | | this.showLoginNotification(resp); |
| | | |
| | | |
| | | // 原有路由跳转和存储逻辑 |
| | | this.$router.push({ name: "ProjectList" }); |
| | | this.$store.commit("isLogin", resp.token); |
| | |
| | | }); |
| | | } |
| | | }, |
| | | |
| | | |
| | | showLoginNotification(resp) { |
| | | const currentTime = new Date().toLocaleString(); |
| | | |
| | | |
| | | this.$notify({ |
| | | title: '登录成功', |
| | | message: `登录时间: ${currentTime}`, |
| | |
| | | <style scoped> |
| | | .login-container { |
| | | min-height: 100vh; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | background: linear-gradient(135deg, #0c0e27 0%, #1a1f3d 50%, #2d1b69 100%); |
| | | position: relative; |
| | | overflow: hidden; |
| | | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| | | } |
| | | |
| | | .login-background { |
| | | /* 动态背景动画 */ |
| | | .background-animation { |
| | | position: absolute; |
| | | width: 100%; |
| | | height: 100%; |
| | | background: rgba(255, 255, 255, 0.1); |
| | | opacity: 0.1; |
| | | top: 0; |
| | | left: 0; |
| | | } |
| | | |
| | | .floating-shapes { |
| | | position: absolute; |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .shape { |
| | | position: absolute; |
| | | border-radius: 50%; |
| | | background: linear-gradient(45deg, rgba(0, 212, 255, 0.1), rgba(148, 0, 211, 0.1)); |
| | | animation: float 20s infinite linear; |
| | | } |
| | | |
| | | .shape-1 { |
| | | width: 100px; |
| | | height: 100px; |
| | | top: 10%; |
| | | left: 10%; |
| | | animation-delay: 0s; |
| | | } |
| | | |
| | | .shape-2 { |
| | | width: 150px; |
| | | height: 150px; |
| | | top: 60%; |
| | | right: 10%; |
| | | animation-delay: -5s; |
| | | } |
| | | |
| | | .shape-3 { |
| | | width: 80px; |
| | | height: 80px; |
| | | bottom: 20%; |
| | | left: 20%; |
| | | animation-delay: -10s; |
| | | } |
| | | |
| | | .shape-4 { |
| | | width: 120px; |
| | | height: 120px; |
| | | top: 30%; |
| | | right: 30%; |
| | | animation-delay: -15s; |
| | | } |
| | | |
| | | .network-lines { |
| | | position: absolute; |
| | | width: 100%; |
| | | height: 100%; |
| | | background-image: |
| | | linear-gradient(90deg, transparent 24px, rgba(0, 212, 255, 0.03) 25px, rgba(0, 212, 255, 0.03) 26px, transparent 27px, transparent 74px, rgba(148, 0, 211, 0.03) 75px, rgba(148, 0, 211, 0.03) 76px, transparent 77px), |
| | | linear-gradient(0deg, transparent 24px, rgba(0, 212, 255, 0.03) 25px, rgba(0, 212, 255, 0.03) 26px, transparent 27px, transparent 74px, rgba(148, 0, 211, 0.03) 75px, rgba(148, 0, 211, 0.03) 76px, transparent 77px); |
| | | background-size: 100px 100px; |
| | | animation: gridMove 40s linear infinite; |
| | | } |
| | | |
| | | .gradient-overlay { |
| | | position: absolute; |
| | | width: 100%; |
| | | height: 100%; |
| | | background: linear-gradient(135deg, rgba(102, 126, 234, 0.2) 0%, rgba(118, 75, 162, 0.2) 100%); |
| | | background: radial-gradient(circle at 20% 80%, rgba(0, 212, 255, 0.1) 0%, transparent 50%), |
| | | radial-gradient(circle at 80% 20%, rgba(148, 0, 211, 0.1) 0%, transparent 50%); |
| | | } |
| | | |
| | | .login-card { |
| | | width: 420px; |
| | | border-radius: 12px; |
| | | box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15); |
| | | border: none; |
| | | z-index: 1; |
| | | background: rgba(255, 255, 255, 0.95); |
| | | /* 主内容区域 */ |
| | | .login-content { |
| | | display: flex; |
| | | min-height: 100vh; |
| | | max-width: 1400px; |
| | | margin: 0 auto; |
| | | position: relative; |
| | | z-index: 2; |
| | | } |
| | | |
| | | /* 左侧品牌区域 */ |
| | | .brand-section { |
| | | flex: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | padding: 0 4rem; |
| | | color: white; |
| | | } |
| | | |
| | | .brand-logo-container { |
| | | margin-bottom: 3rem; |
| | | } |
| | | |
| | | .logo-icon { |
| | | width: 120px; |
| | | height: 120px; |
| | | margin-bottom: 1.5rem; |
| | | filter: drop-shadow(0 0 20px rgba(0, 212, 255, 0.5)); |
| | | } |
| | | |
| | | .api-icon { |
| | | width: 100%; |
| | | height: 100%; |
| | | animation: pulse 3s ease-in-out infinite; |
| | | } |
| | | |
| | | .system-name { |
| | | font-size: 3rem; |
| | | font-weight: 700; |
| | | margin-bottom: 0.5rem; |
| | | background: linear-gradient(135deg, #00d4ff 0%, #9400d3 100%); |
| | | -webkit-background-clip: text; |
| | | -webkit-text-fill-color: transparent; |
| | | background-clip: text; |
| | | } |
| | | |
| | | .system-desc { |
| | | font-size: 1.2rem; |
| | | opacity: 0.8; |
| | | margin: 0; |
| | | } |
| | | |
| | | .feature-list { |
| | | margin-top: 2rem; |
| | | } |
| | | |
| | | .feature-item { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 1rem; |
| | | font-size: 1.1rem; |
| | | opacity: 0.9; |
| | | transition: opacity 0.3s ease; |
| | | } |
| | | |
| | | .feature-item:hover { |
| | | opacity: 1; |
| | | } |
| | | |
| | | .feature-icon { |
| | | font-size: 1.5rem; |
| | | margin-right: 0.8rem; |
| | | filter: drop-shadow(0 0 10px rgba(0, 212, 255, 0.5)); |
| | | } |
| | | |
| | | /* 右侧表单区域 */ |
| | | .login-form-section { |
| | | flex: 0 0 500px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 2rem; |
| | | } |
| | | |
| | | .form-container { |
| | | background: rgba(255, 255, 255, 0.95); |
| | | backdrop-filter: blur(20px); |
| | | border-radius: 20px; |
| | | padding: 3rem 2.5rem; |
| | | width: 100%; |
| | | max-width: 400px; |
| | | box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3); |
| | | border: 1px solid rgba(255, 255, 255, 0.2); |
| | | } |
| | | |
| | | .form-header { |
| | | text-align: center; |
| | | margin-bottom: 2rem; |
| | | margin-bottom: 2.5rem; |
| | | } |
| | | |
| | | .brand-logo { |
| | | width: 120px; /* 尺寸放大50% */ |
| | | height: auto; |
| | | margin-bottom: 1.5rem; /* 增加下边距保持间距 */ |
| | | transition: transform 0.3s ease; /* 添加悬停动效 */ |
| | | .form-header h2 { |
| | | font-size: 2rem; |
| | | font-weight: 600; |
| | | margin-bottom: 0.5rem; |
| | | color: #1a1f3d; |
| | | } |
| | | |
| | | /* 可选悬停效果 */ |
| | | .brand-logo:hover { |
| | | transform: scale(1.05); |
| | | .form-header p { |
| | | color: #666; |
| | | margin: 0; |
| | | } |
| | | |
| | | .input-group { |
| | | margin-bottom: 1.5rem; |
| | | } |
| | | |
| | | .input-label { |
| | | display: block; |
| | | margin-bottom: 0.5rem; |
| | | font-weight: 500; |
| | | color: #333; |
| | | font-size: 0.9rem; |
| | | } |
| | | |
| | | .modern-input { |
| | | width: 100%; |
| | | } |
| | | |
| | | .modern-input >>> .el-input__inner { |
| | | height: 48px; |
| | | border-radius: 12px; |
| | | border: 2px solid #e1e5e9; |
| | | font-size: 1rem; |
| | | transition: all 0.3s ease; |
| | | background: #f8f9fa; |
| | | } |
| | | |
| | | .modern-input >>> .el-input__inner:focus { |
| | | border-color: #00d4ff; |
| | | box-shadow: 0 0 0 3px rgba(0, 212, 255, 0.1); |
| | | background: white; |
| | | } |
| | | |
| | | .modern-input.input-error >>> .el-input__inner { |
| | | border-color: #ff4757; |
| | | } |
| | | |
| | | .login-button { |
| | | width: 100%; |
| | | height: 50px; |
| | | border-radius: 12px; |
| | | font-size: 1.1rem; |
| | | font-weight: 600; |
| | | background: linear-gradient(135deg, #00d4ff 0%, #9400d3 100%); |
| | | border: none; |
| | | transition: all 0.3s ease; |
| | | margin-top: 0.5rem; |
| | | } |
| | | |
| | | .login-button:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 10px 25px rgba(0, 212, 255, 0.4); |
| | | } |
| | | |
| | | .login-button:active { |
| | | transform: translateY(0); |
| | | } |
| | | |
| | | .error-msg { |
| | | color: #ff4757; |
| | | font-size: 0.85rem; |
| | | margin-top: 0.3rem; |
| | | animation: slideIn 0.3s ease; |
| | | } |
| | | |
| | | .form-footer { |
| | | margin-top: 2rem; |
| | | text-align: center; |
| | | } |
| | | |
| | | .form-footer p { |
| | | margin: 0.5rem 0; |
| | | color: #666; |
| | | font-size: 0.9rem; |
| | | } |
| | | |
| | | .register-link { |
| | | color: #00d4ff; |
| | | text-decoration: none; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .register-link:hover { |
| | | text-decoration: underline; |
| | | } |
| | | |
| | | .copyright { |
| | | opacity: 0.6; |
| | | font-size: 0.8rem; |
| | | } |
| | | |
| | | /* 动画效果 */ |
| | | @keyframes float { |
| | | 0%, 100% { |
| | | transform: translateY(0px) rotate(0deg); |
| | | } |
| | | 33% { |
| | | transform: translateY(-20px) rotate(120deg); |
| | | } |
| | | 66% { |
| | | transform: translateY(10px) rotate(240deg); |
| | | } |
| | | } |
| | | |
| | | @keyframes gridMove { |
| | | 0% { |
| | | transform: translate(0, 0); |
| | | } |
| | | 100% { |
| | | transform: translate(100px, 100px); |
| | | } |
| | | } |
| | | |
| | | @keyframes pulse { |
| | | 0%, 100% { |
| | | transform: scale(1); |
| | | opacity: 1; |
| | | } |
| | | 50% { |
| | | transform: scale(1.05); |
| | | opacity: 0.8; |
| | | } |
| | | } |
| | | |
| | | @keyframes slideIn { |
| | | from { |
| | | opacity: 0; |
| | | transform: translateY(-10px); |
| | | } |
| | | to { |
| | | opacity: 1; |
| | | transform: translateY(0); |
| | | } |
| | | } |
| | | |
| | | .slide-fade-enter-active { |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .slide-fade-leave-active { |
| | | transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1); |
| | | } |
| | | |
| | | .slide-fade-enter, .slide-fade-leave-to { |
| | | transform: translateY(-10px); |
| | | opacity: 0; |
| | | } |
| | | |
| | | /* 响应式设计 */ |
| | | @media (max-width: 1024px) { |
| | | .login-content { |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .brand-section { |
| | | padding: 2rem; |
| | | text-align: center; |
| | | } |
| | | |
| | | .login-form-section { |
| | | flex: none; |
| | | width: 100%; |
| | | } |
| | | } |
| | | |
| | | @media (max-width: 768px) { |
| | | .form-container { |
| | | margin: 1rem; |
| | | padding: 2rem 1.5rem; |
| | | } |
| | | |
| | | .system-name { |
| | | font-size: 2.5rem; |
| | | } |
| | | } |
| | | |
| | | |
| | | .system-name { |
| | | font-size: 2rem; /* 同步放大系统名称字号 */ |
| | | margin-top: 0.5rem; /* 增加与LOGO的间距 */ |