gaoluyang
2026-05-18 344d05bd35ebd7af329772d31868bdfdb05665b8
src/views/login.vue
@@ -1,63 +1,123 @@
<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>
@@ -86,7 +146,6 @@
const loading = ref(false)
const captchaEnabled = ref(true)
const register = ref(false)
const redirect = ref(undefined)
watch(
@@ -151,193 +210,347 @@
</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;
    }
  }
}