| | |
| | | <template> |
| | | <div class="login-page"> |
| | | <div class="login-card"> |
| | | <div class="card-header"> |
| | | <div class="logo"> |
| | | <svg-icon icon-class="user" /> |
| | | <div class="login-wrapper"> |
| | | <!-- 顶部装饰条 --> |
| | | <div class="top-bar"></div> |
| | | |
| | | <!-- 主内容区 --> |
| | | <div class="main-content"> |
| | | <!-- 左侧:生产场景图 + 文字 --> |
| | | <div class="left-section"> |
| | | <div class="scene-container"> |
| | | <div class="factory-icon"> |
| | | <el-icon :size="80" color="var(--el-color-primary)"><FirstAidKit /></el-icon> |
| | | </div> |
| | | <div class="production-flow"> |
| | | <div class="flow-item"> |
| | | <div class="flow-dot"></div> |
| | | <span>原料入库</span> |
| | | </div> |
| | | <div class="flow-line"></div> |
| | | <div class="flow-item"> |
| | | <div class="flow-dot"></div> |
| | | <span>生产加工</span> |
| | | </div> |
| | | <div class="flow-line"></div> |
| | | <div class="flow-item"> |
| | | <div class="flow-dot"></div> |
| | | <span>质量检测</span> |
| | | </div> |
| | | <div class="flow-line"></div> |
| | | <div class="flow-item"> |
| | | <div class="flow-dot"></div> |
| | | <span>成品出库</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <h1>客户关系管理系统</h1> |
| | | <p>高效管理客户资源,驱动业务增长</p> |
| | | <div class="slogan"> |
| | | <h1>智能生产管理平台</h1> |
| | | <p>让生产更高效 · 让管理更智能</p> |
| | | </div> |
| | | </div> |
| | | |
| | | <el-form ref="loginRef" :model="loginForm" :rules="loginRules"> |
| | | <el-form-item prop="username"> |
| | | <el-input |
| | | v-model="loginForm.username" |
| | | type="text" |
| | | size="large" |
| | | auto-complete="off" |
| | | placeholder="请输入账号" |
| | | > |
| | | <template #prefix><el-icon><User /></el-icon></template> |
| | | </el-input> |
| | | </el-form-item> |
| | | <!-- 右侧:登录卡片 --> |
| | | <div class="right-section"> |
| | | <div class="login-card"> |
| | | <div class="card-decoration"> |
| | | <div class="deco-line"></div> |
| | | <div class="deco-line"></div> |
| | | </div> |
| | | |
| | | <div class="login-title"> |
| | | <div class="title-icon"> |
| | | <el-icon :size="32" color="#fff"><UserFilled /></el-icon> |
| | | </div> |
| | | <div class="title-text"> |
| | | <h2>账号登录</h2> |
| | | <span>Production Management System</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <el-form-item prop="password"> |
| | | <el-input |
| | | v-model="loginForm.password" |
| | | type="password" |
| | | size="large" |
| | | auto-complete="off" |
| | | placeholder="请输入密码" |
| | | show-password |
| | | @keyup.enter="handleLogin" |
| | | > |
| | | <template #prefix><el-icon><Lock /></el-icon></template> |
| | | </el-input> |
| | | </el-form-item> |
| | | <el-form ref="loginRef" :model="loginForm" :rules="loginRules"> |
| | | <el-form-item prop="username"> |
| | | <div class="custom-input"> |
| | | <span class="input-label">账号</span> |
| | | <el-input |
| | | v-model="loginForm.username" |
| | | type="text" |
| | | size="large" |
| | | auto-complete="off" |
| | | placeholder="请输入登录账号" |
| | | > |
| | | <template #prefix> |
| | | <el-icon><Avatar /></el-icon> |
| | | </template> |
| | | </el-input> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <div class="form-options"> |
| | | <el-checkbox v-model="loginForm.rememberMe">记住密码</el-checkbox> |
| | | <router-link v-if="register" :to="'/register'">立即注册</router-link> |
| | | <el-form-item prop="password"> |
| | | <div class="custom-input"> |
| | | <span class="input-label">密码</span> |
| | | <el-input |
| | | v-model="loginForm.password" |
| | | type="password" |
| | | size="large" |
| | | auto-complete="off" |
| | | placeholder="请输入登录密码" |
| | | show-password |
| | | @keyup.enter="handleLogin" |
| | | > |
| | | <template #prefix> |
| | | <el-icon><Key /></el-icon> |
| | | </template> |
| | | </el-input> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <div class="login-options"> |
| | | <el-checkbox v-model="loginForm.rememberMe"> |
| | | <span class="remember-text">记住登录状态</span> |
| | | </el-checkbox> |
| | | </div> |
| | | |
| | | <el-button |
| | | :loading="loading" |
| | | size="large" |
| | | type="primary" |
| | | class="submit-btn" |
| | | @click.prevent="handleLogin" |
| | | > |
| | | <el-icon v-if="!loading" class="btn-icon"><Right /></el-icon> |
| | | <span>{{ loading ? '登录中...' : '立即登录' }}</span> |
| | | </el-button> |
| | | </el-form> |
| | | </div> |
| | | |
| | | <el-button |
| | | :loading="loading" |
| | | size="large" |
| | | type="primary" |
| | | class="login-btn" |
| | | @click.prevent="handleLogin" |
| | | > |
| | | <span v-if="!loading">登 录</span> |
| | | <span v-else>登录中...</span> |
| | | </el-button> |
| | | </el-form> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="bg-pattern"> |
| | | <div class="pattern-item"></div> |
| | | <div class="pattern-item"></div> |
| | | <div class="pattern-item"></div> |
| | | <!-- 底部装饰 --> |
| | | <div class="bottom-decoration"> |
| | | <div class="wave"></div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | |
| | | |
| | | const loading = ref(false) |
| | | const captchaEnabled = ref(true) |
| | | const register = ref(false) |
| | | const redirect = ref(undefined) |
| | | |
| | | watch( |
| | |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .login-page { |
| | | .login-wrapper { |
| | | min-height: 100vh; |
| | | background: linear-gradient(180deg, #f8fafc 0%, #e2e8f0 100%); |
| | | position: relative; |
| | | overflow: hidden; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | // 顶部装饰条 |
| | | .top-bar { |
| | | height: 4px; |
| | | background: linear-gradient(90deg, var(--el-color-primary-dark-2) 0%, var(--el-color-primary) 50%, var(--el-color-primary-light-3) 100%); |
| | | } |
| | | |
| | | // 主内容区 |
| | | .main-content { |
| | | flex: 1; |
| | | display: flex; |
| | | padding: 60px 80px; |
| | | gap: 80px; |
| | | align-items: center; |
| | | max-width: 1400px; |
| | | margin: 0 auto; |
| | | width: 100%; |
| | | } |
| | | |
| | | // 左侧区域 |
| | | .left-section { |
| | | flex: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 50px; |
| | | } |
| | | |
| | | .scene-container { |
| | | background: #fff; |
| | | border-radius: 24px; |
| | | padding: 50px; |
| | | box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .factory-icon { |
| | | width: 140px; |
| | | height: 140px; |
| | | background: linear-gradient(135deg, var(--el-color-primary-light-9) 0%, var(--el-color-primary-light-8) 100%); |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: linear-gradient(135deg, var(--el-color-primary-light-9) 0%, var(--el-color-primary-light-8) 50%, var(--el-color-primary-light-9) 100%); |
| | | margin: 0 auto 40px; |
| | | border: 4px solid var(--el-color-primary-light-7); |
| | | } |
| | | |
| | | .production-flow { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .flow-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | gap: 8px; |
| | | |
| | | .flow-dot { |
| | | width: 12px; |
| | | height: 12px; |
| | | background: var(--el-color-primary); |
| | | border-radius: 50%; |
| | | box-shadow: 0 0 0 4px var(--el-color-primary-light-8); |
| | | } |
| | | |
| | | span { |
| | | font-size: 13px; |
| | | color: #64748b; |
| | | font-weight: 500; |
| | | } |
| | | } |
| | | |
| | | .flow-line { |
| | | width: 40px; |
| | | height: 2px; |
| | | background: linear-gradient(90deg, var(--el-color-primary-light-5), var(--el-color-primary)); |
| | | } |
| | | |
| | | .slogan { |
| | | text-align: center; |
| | | |
| | | h1 { |
| | | font-size: 36px; |
| | | font-weight: 700; |
| | | color: #1e293b; |
| | | margin: 0 0 12px; |
| | | letter-spacing: 2px; |
| | | } |
| | | |
| | | p { |
| | | font-size: 16px; |
| | | color: #64748b; |
| | | margin: 0; |
| | | letter-spacing: 1px; |
| | | } |
| | | } |
| | | |
| | | // 右侧区域 |
| | | .right-section { |
| | | width: 440px; |
| | | } |
| | | |
| | | .login-card { |
| | | background: #fff; |
| | | border-radius: 20px; |
| | | padding: 40px; |
| | | box-shadow: 0 10px 40px rgba(0, 0, 0, 0.08); |
| | | position: relative; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .bg-pattern { |
| | | .card-decoration { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | pointer-events: none; |
| | | width: 120px; |
| | | height: 120px; |
| | | |
| | | .pattern-item { |
| | | .deco-line { |
| | | position: absolute; |
| | | border-radius: 50%; |
| | | background: linear-gradient(135deg, var(--el-color-primary-light-7), var(--el-color-primary-light-9)); |
| | | } |
| | | background: var(--el-color-primary-light-7); |
| | | border-radius: 2px; |
| | | |
| | | .pattern-item:nth-child(1) { |
| | | width: 500px; |
| | | height: 500px; |
| | | top: -150px; |
| | | right: -100px; |
| | | } |
| | | &:nth-child(1) { |
| | | width: 80px; |
| | | height: 4px; |
| | | top: 30px; |
| | | right: -20px; |
| | | transform: rotate(-45deg); |
| | | } |
| | | |
| | | .pattern-item:nth-child(2) { |
| | | width: 350px; |
| | | height: 350px; |
| | | bottom: -100px; |
| | | left: -80px; |
| | | } |
| | | |
| | | .pattern-item:nth-child(3) { |
| | | width: 200px; |
| | | height: 200px; |
| | | top: 40%; |
| | | left: 15%; |
| | | background: linear-gradient(135deg, var(--el-color-primary-light-8), var(--el-color-primary-light-9)); |
| | | &:nth-child(2) { |
| | | width: 50px; |
| | | height: 4px; |
| | | top: 50px; |
| | | right: -10px; |
| | | transform: rotate(-45deg); |
| | | } |
| | | } |
| | | } |
| | | |
| | | .login-card { |
| | | width: 420px; |
| | | padding: 48px 40px; |
| | | background: rgba(255, 255, 255, 0.95); |
| | | border-radius: 20px; |
| | | box-shadow: |
| | | 0 4px 6px -1px rgba(0, 0, 0, 0.05), |
| | | 0 10px 15px -3px rgba(0, 0, 0, 0.08), |
| | | 0 20px 25px -5px rgba(0, 0, 0, 0.05); |
| | | backdrop-filter: blur(10px); |
| | | position: relative; |
| | | z-index: 1; |
| | | } |
| | | .login-title { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16px; |
| | | margin-bottom: 40px; |
| | | |
| | | .card-header { |
| | | text-align: center; |
| | | margin-bottom: 36px; |
| | | |
| | | .logo { |
| | | width: 72px; |
| | | height: 72px; |
| | | margin: 0 auto 20px; |
| | | .title-icon { |
| | | width: 56px; |
| | | height: 56px; |
| | | background: linear-gradient(135deg, var(--el-color-primary) 0%, var(--el-color-primary-light-3) 100%); |
| | | border-radius: 18px; |
| | | border-radius: 14px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 8px 20px var(--el-color-primary-light-5); |
| | | box-shadow: 0 4px 12px var(--el-color-primary-light-5); |
| | | } |
| | | |
| | | :deep(svg) { |
| | | width: 36px; |
| | | height: 36px; |
| | | color: #fff; |
| | | .title-text { |
| | | h2 { |
| | | font-size: 24px; |
| | | font-weight: 600; |
| | | color: #1e293b; |
| | | margin: 0 0 4px; |
| | | } |
| | | |
| | | span { |
| | | font-size: 12px; |
| | | color: #94a3b8; |
| | | text-transform: uppercase; |
| | | letter-spacing: 1px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .custom-input { |
| | | .input-label { |
| | | display: block; |
| | | font-size: 14px; |
| | | color: #475569; |
| | | margin-bottom: 8px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | :deep(.el-input__wrapper) { |
| | | border-radius: 10px; |
| | | box-shadow: 0 0 0 1px #e2e8f0; |
| | | padding: 0 16px; |
| | | height: 50px; |
| | | transition: all 0.3s; |
| | | |
| | | &:hover { |
| | | box-shadow: 0 0 0 1px var(--el-color-primary-light-5); |
| | | } |
| | | |
| | | &:focus-within { |
| | | box-shadow: 0 0 0 2px var(--el-color-primary); |
| | | } |
| | | } |
| | | |
| | | h1 { |
| | | font-size: 24px; |
| | | font-weight: 600; |
| | | color: #1f2937; |
| | | margin: 0 0 8px; |
| | | :deep(.el-input__inner) { |
| | | height: 50px; |
| | | font-size: 15px; |
| | | color: #334155; |
| | | |
| | | &::placeholder { |
| | | color: #cbd5e1; |
| | | } |
| | | } |
| | | |
| | | p { |
| | | font-size: 14px; |
| | | color: #6b7280; |
| | | margin: 0; |
| | | :deep(.el-input__prefix) { |
| | | color: var(--el-color-primary); |
| | | font-size: 18px; |
| | | margin-right: 10px; |
| | | } |
| | | } |
| | | |
| | | :deep(.el-form-item) { |
| | | margin-bottom: 20px; |
| | | margin-bottom: 24px; |
| | | } |
| | | |
| | | :deep(.el-input__wrapper) { |
| | | border-radius: 12px; |
| | | box-shadow: 0 0 0 1px #e5e7eb; |
| | | padding: 0 16px; |
| | | height: 48px; |
| | | transition: all 0.2s; |
| | | .login-options { |
| | | margin: 8px 0 32px; |
| | | |
| | | &:hover { |
| | | box-shadow: 0 0 0 1px var(--el-color-primary-light-5); |
| | | } |
| | | |
| | | &:focus-within { |
| | | box-shadow: 0 0 0 2px var(--el-color-primary); |
| | | } |
| | | } |
| | | |
| | | :deep(.el-input__inner) { |
| | | height: 48px; |
| | | font-size: 15px; |
| | | color: #374151; |
| | | |
| | | &::placeholder { |
| | | color: #9ca3af; |
| | | } |
| | | } |
| | | |
| | | :deep(.el-input__prefix) { |
| | | color: #9ca3af; |
| | | font-size: 18px; |
| | | } |
| | | |
| | | .form-options { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin: 8px 0 24px; |
| | | |
| | | :deep(.el-checkbox__label) { |
| | | color: #6b7280; |
| | | .remember-text { |
| | | font-size: 14px; |
| | | color: #64748b; |
| | | } |
| | | |
| | | :deep(.el-checkbox__input.is-checked .el-checkbox__inner) { |
| | | background-color: var(--el-color-primary); |
| | | border-color: var(--el-color-primary); |
| | | } |
| | | } |
| | | |
| | | a { |
| | | color: var(--el-color-primary); |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | text-decoration: none; |
| | | transition: color 0.2s; |
| | | .submit-btn { |
| | | width: 100%; |
| | | height: 52px; |
| | | border-radius: 10px; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | background: linear-gradient(135deg, var(--el-color-primary) 0%, var(--el-color-primary-light-2) 100%); |
| | | border: none; |
| | | box-shadow: 0 4px 16px var(--el-color-primary-light-5); |
| | | transition: all 0.3s; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8px; |
| | | |
| | | &:hover { |
| | | color: var(--el-color-primary-light-3); |
| | | } |
| | | &:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 6px 20px var(--el-color-primary-light-4); |
| | | } |
| | | |
| | | .btn-icon { |
| | | font-size: 18px; |
| | | } |
| | | } |
| | | |
| | | .login-btn { |
| | | width: 100%; |
| | | height: 48px; |
| | | border-radius: 12px; |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | background: linear-gradient(135deg, var(--el-color-primary) 0%, var(--el-color-primary-light-3) 100%); |
| | | border: none; |
| | | box-shadow: 0 4px 14px var(--el-color-primary-light-5); |
| | | transition: all 0.2s; |
| | | .card-footer { |
| | | margin-top: 32px; |
| | | padding-top: 24px; |
| | | border-top: 1px solid #f1f5f9; |
| | | text-align: center; |
| | | |
| | | &:hover { |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 6px 20px var(--el-color-primary-light-4); |
| | | p { |
| | | color: #94a3b8; |
| | | font-size: 13px; |
| | | margin: 0; |
| | | } |
| | | } |
| | | |
| | | // 底部装饰 |
| | | .bottom-decoration { |
| | | height: 80px; |
| | | position: relative; |
| | | |
| | | .wave { |
| | | position: absolute; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | height: 60px; |
| | | background: linear-gradient(90deg, var(--el-color-primary-light-9) 0%, var(--el-color-primary-light-8) 100%); |
| | | clip-path: polygon(0 100%, 100% 100%, 100% 30%, 75% 60%, 50% 40%, 25% 70%, 0 50%); |
| | | } |
| | | } |
| | | |
| | | // 响应式 |
| | | @media (max-width: 1100px) { |
| | | .main-content { |
| | | flex-direction: column; |
| | | padding: 40px; |
| | | gap: 40px; |
| | | } |
| | | |
| | | .left-section { |
| | | order: 2; |
| | | } |
| | | |
| | | .right-section { |
| | | width: 100%; |
| | | max-width: 440px; |
| | | order: 1; |
| | | } |
| | | |
| | | .slogan { |
| | | display: none; |
| | | } |
| | | } |
| | | |
| | | @media (max-width: 480px) { |
| | | .login-card { |
| | | width: 90%; |
| | | padding: 36px 24px; |
| | | .main-content { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .card-header { |
| | | h1 { |
| | | font-size: 20px; |
| | | .login-card { |
| | | padding: 30px 24px; |
| | | } |
| | | |
| | | .scene-container { |
| | | padding: 30px 20px; |
| | | } |
| | | |
| | | .production-flow { |
| | | flex-wrap: wrap; |
| | | gap: 16px; |
| | | |
| | | .flow-line { |
| | | display: none; |
| | | } |
| | | } |
| | | } |