natasha пре 2 дана
родитељ
комит
355d0cbd85
2 измењених фајлова са 792 додато и 62 уклоњено
  1. BIN
      src/assets/login/logo.png
  2. 792 62
      src/views/login/index.vue

BIN
src/assets/login/logo.png


+ 792 - 62
src/views/login/index.vue

@@ -1,50 +1,195 @@
 <template>
   <div class="login">
     <main class="login-container">
-      <div class="title">登录</div>
-      <el-form ref="loginForm" :model="form" :rules="rules" label-position="right">
-        <el-form-item prop="server_address" label="服务器地址">
-          <el-input v-model="form.server_address" placeholder="请输入服务器地址" @change="updateServerAddress" />
-        </el-form-item>
-        <el-form-item prop="user_name" label="用户名">
-          <el-input v-model="form.user_name" />
-        </el-form-item>
-        <el-form-item prop="password" label="密码">
-          <el-input v-model="form.password" type="password" show-password />
-        </el-form-item>
-        <el-form-item prop="verification_code_image_text" class="verification-code" label="验证码">
-          <el-input v-model="form.verification_code_image_text" @keyup.enter.native="signIn" />
-          <el-image
-            v-if="image_content_base64.length > 0"
-            :src="`data:image/jpg;base64,${image_content_base64}`"
-            @click="updateVerificationCode"
-          />
-        </el-form-item>
-        <el-form-item prop="user_type" label="用户类型">
-          <el-radio-group v-model="form.user_type">
-            <el-radio label="USER">机构用户</el-radio>
-            <el-radio label="ORG_MANAGER">机构管理员</el-radio>
-            <el-radio label="ADMIN">系统管理员</el-radio>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item>
-          <el-button class="submit" type="primary" @click="signIn">登录</el-button>
-        </el-form-item>
-        <el-form-item>
-          <div class="protocol">
-            <span>
-              <el-checkbox v-model="isAgree" @change="changeAgree" /> 我已阅读并同意<span
-                style="color: #4d78ff; cursor: pointer"
-                @click="viewUserAgreement"
-                >《用户协议》</span
-              >
-            </span>
-            <span style="color: #4d78ff; cursor: pointer" @click="forgotPwd">
-              忘记密码?| <a @click="goRegister">注册</a>
-            </span>
+      <div class="brand-panel">
+        <div class="brand-content">
+          <div class="brand-logo">
+            <div class="logo-icon"><img src="@/assets/login/logo.png" alt="LOGO" /></div>
+            <div class="logo-text">智慧<span>梧桐</span></div>
           </div>
-        </el-form-item>
-      </el-form>
+          <div class="brand-title-group">
+            <div class="brand-position">数字教材编辑器</div>
+            <div class="brand-desc">专业化数字教材编校平台</div>
+          </div>
+          <div class="flow-zone">
+            <div class="flow-track">
+              <div class="flow-node">
+                <div class="flow-dot dot-1"></div>
+                <div class="flow-card">
+                  <span class="fc-icon"
+                    ><svg viewBox="0 0 24 24" fill="none" stroke="#2D3E8F">
+                      <path d="M12 20h9" />
+                      <path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" /></svg
+                  ></span>
+                  <div class="fc-title">内容创作</div>
+                  <div class="fc-sub">结构化编写</div>
+                </div>
+              </div>
+              <div class="flow-node">
+                <div class="flow-dot dot-2"></div>
+                <div class="flow-card">
+                  <span class="fc-icon"
+                    ><svg viewBox="0 0 24 24" fill="none" stroke="#3B52C4">
+                      <rect x="3" y="3" width="7" height="7" rx="1" />
+                      <rect x="14" y="3" width="7" height="7" rx="1" />
+                      <rect x="3" y="14" width="7" height="7" rx="1" />
+                      <circle cx="17.5" cy="17.5" r="3.5" /></svg
+                  ></span>
+                  <div class="fc-title">组件编排</div>
+                  <div class="fc-sub">模块化搭建</div>
+                </div>
+              </div>
+              <div class="flow-node">
+                <div class="flow-dot dot-3"></div>
+                <div class="flow-card">
+                  <span class="fc-icon"
+                    ><svg viewBox="0 0 24 24" fill="none" stroke="#4A7DD4">
+                      <path d="M12 2v4" />
+                      <path d="m16.2 7.8 2.9-2.9" />
+                      <path d="M18 12h4" />
+                      <path d="m16.2 16.2 2.9 2.9" />
+                      <path d="M12 18v4" />
+                      <path d="m4.9 19.1 2.9-2.9" />
+                      <path d="M2 12h4" />
+                      <path d="m4.9 4.9 2.9 2.9" /></svg
+                  ></span>
+                  <div class="fc-title">自动生成</div>
+                  <div class="fc-sub">AI赋能</div>
+                </div>
+              </div>
+              <div class="flow-node">
+                <div class="flow-dot dot-4"></div>
+                <div class="flow-card">
+                  <span class="fc-icon"
+                    ><svg viewBox="0 0 24 24" fill="none" stroke="#4DC9F6">
+                      <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
+                      <circle cx="12" cy="12" r="3" />
+                      <path d="m9 12 2 2 4-4" /></svg
+                  ></span>
+                  <div class="fc-title">专业审校</div>
+                  <div class="fc-sub">质量把关</div>
+                </div>
+              </div>
+              <div class="flow-node">
+                <div class="flow-dot dot-5"></div>
+                <div class="flow-card">
+                  <span class="fc-icon"
+                    ><svg viewBox="0 0 24 24" fill="none" stroke="#38B8E8">
+                      <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
+                      <polyline points="17 8 12 3 7 8" />
+                      <line x1="12" y1="3" x2="12" y2="15" /></svg
+                  ></span>
+                  <div class="fc-title">一键发布</div>
+                  <div class="fc-sub">多平台分发</div>
+                </div>
+              </div>
+              <div class="flow-node">
+                <div class="flow-dot dot-6"></div>
+                <div class="flow-card">
+                  <span class="fc-icon"
+                    ><svg viewBox="0 0 24 24" fill="none" stroke="#2D9BD4">
+                      <path d="M22 10v6M2 10l10-5 10 5-10 5z" />
+                      <path d="M6 12v5c3 3 9 3 12 0v-5" /></svg
+                  ></span>
+                  <div class="fc-title">教学应用</div>
+                  <div class="fc-sub">智慧教学</div>
+                </div>
+              </div>
+              <div class="flow-arrow a1">›</div>
+              <div class="flow-arrow a2">›</div>
+              <div class="flow-arrow a3">›</div>
+              <div class="flow-arrow a4">›</div>
+              <div class="flow-arrow a5">›</div>
+            </div>
+            <div class="flow-progress"><div class="fp-fill"></div></div>
+          </div>
+        </div>
+      </div>
+      <div class="login-panel">
+        <div class="login-card">
+          <div class="lc-header">
+            <div class="lc-logo"><img src="@/assets/login/logo.png" alt="LOGO" /></div>
+            <h2>欢迎登录</h2>
+            <p class="sub">登录您的账号</p>
+          </div>
+          <div class="role-tabs" id="roleTabs">
+            <div class="role-tab" :class="[form.user_type === 'USER' ? 'active' : '']" @click="switchRole('USER')">
+              机构用户
+            </div>
+            <div
+              class="role-tab"
+              :class="[form.user_type === 'ORG_MANAGER' ? 'active' : '']"
+              @click="switchRole('ORG_MANAGER')"
+            >
+              机构管理员
+            </div>
+            <div class="role-tab" :class="[form.user_type === 'ADMIN' ? 'active' : '']" @click="switchRole('ADMIN')">
+              系统管理员
+            </div>
+          </div>
+          <el-form ref="loginForm" :model="form" :rules="rules" label-position="right">
+            <el-form-item prop="server_address" label="服务器地址">
+              <el-input
+                v-model="form.server_address"
+                placeholder="请输入服务器地址"
+                prefix-icon="el-icon-monitor"
+                @change="updateServerAddress"
+              />
+            </el-form-item>
+            <el-form-item prop="user_name" label="账号">
+              <el-input v-model="form.user_name" prefix-icon="el-icon-user" placeholder="请输入账号" />
+            </el-form-item>
+            <el-form-item prop="password" label="密码">
+              <el-input
+                v-model="form.password"
+                type="password"
+                show-password
+                prefix-icon="el-icon-lock"
+                placeholder="请输入密码"
+              />
+            </el-form-item>
+            <el-form-item prop="verification_code_image_text" class="verification-code" label="验证码">
+              <el-input
+                v-model="form.verification_code_image_text"
+                @keyup.enter.native="signIn"
+                prefix-icon="el-icon-finished"
+                placeholder="请输入验证码"
+              />
+              <el-image
+                v-if="image_content_base64.length > 0"
+                :src="`data:image/jpg;base64,${image_content_base64}`"
+                @click="updateVerificationCode"
+              />
+            </el-form-item>
+            <!-- <el-form-item prop="user_type" label="用户类型">
+              <el-radio-group v-model="form.user_type">
+                <el-radio label="USER">机构用户</el-radio>
+                <el-radio label="ORG_MANAGER">机构管理员</el-radio>
+                <el-radio label="ADMIN">系统管理员</el-radio>
+              </el-radio-group>
+            </el-form-item> -->
+            <el-form-item style="margin-bottom: 6px">
+              <div class="protocol">
+                <span>
+                  <el-checkbox v-model="isAgree" @change="changeAgree" /> 我已阅读并同意<span
+                    style="font-weight: 500; color: #2d3e8f; cursor: pointer"
+                    @click="viewUserAgreement"
+                    >《用户协议》</span
+                  >
+                </span>
+              </div>
+            </el-form-item>
+            <el-form-item style="margin-bottom: 6px">
+              <el-button class="submit" type="primary" @click="signIn">登 录</el-button>
+            </el-form-item>
+            <el-form-item style="text-align: center">
+              <!-- | <a @click="goRegister">注册</a> -->
+
+              <span style=" font-size: 12px;color: #94a3b8; cursor: pointer" @click="forgotPwd"> 忘记密码 </span>
+            </el-form-item>
+          </el-form>
+        </div>
+      </div>
     </main>
     <el-dialog
       :visible.sync="showUseragreement"
@@ -181,26 +326,30 @@ export default {
     cancelFot() {
       this.forgotPwdFlag = false;
     },
+    // 切换角色
+    switchRole(role) {
+      this.form.user_type = role;
+    },
   },
 };
 </script>
 
 <style lang="scss" scoped>
 .login {
-  position: relative;
   width: 100%;
   height: 100%;
   overflow: hidden;
-  background: #2148c0 url('~@/assets/login/login-bg.png') no-repeat center center / cover;
+  font-family:
+    -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', sans-serif;
+  background: #f7f9fe;
 
   &-container {
-    position: absolute;
-    top: 50%;
-    left: 50%;
-    margin-top: -350px;
-    margin-left: -200px;
+    display: flex;
+    height: 100vh;
+    min-height: 100vh;
+    overflow: hidden;
 
-    .title {
+    position:relative .title {
       margin-bottom: 28px;
       font-size: 24px;
       color: #fff;
@@ -208,18 +357,15 @@ export default {
     }
 
     :deep .el-form {
-      max-width: 400px;
-      padding: 24px 32px;
       margin: 0 auto;
-      background-color: #fff;
-      border-radius: 16px;
 
       &-item {
         &__label {
-          margin-bottom: 4px;
-          font-size: 18px;
-          font-weight: bold;
-          color: #000;
+          margin-bottom: 6px;
+          font-size: 12px;
+          font-weight: 600;
+          line-height: 1;
+          color: #1f2937;
 
           &::before {
             display: none;
@@ -230,8 +376,36 @@ export default {
       .el-input {
         &__inner {
           height: 40px;
+          padding: 0 14px 0 42px;
+          font-family: inherit;
+          font-size: 14px;
           line-height: 40px;
-          background-color: #fff;
+          color: #1f2937;
+          background: #f8fafc;
+          border: 1.5px solid #e2e8f0;
+          border-radius: 10px;
+          outline: none;
+          transition:
+            border-color 0.2s,
+            box-shadow 0.2s,
+            background 0.2s;
+
+          &::placeholder {
+            color: #94a3b8;
+          }
+
+          &:focus {
+            background: #fff;
+            border-color: #2d3e8f;
+            box-shadow: 0 0 0 4px rgba(45, 62, 143, 12%);
+          }
+        }
+
+        .el-input__prefix {
+          left: 10px;
+          font-size: 16px;
+          font-weight: 600;
+          color: #94a3b8;
         }
       }
 
@@ -249,26 +423,62 @@ export default {
         }
 
         .el-image {
-          height: 38px;
+          height: 40px;
           vertical-align: bottom;
           cursor: pointer;
+          background: linear-gradient(135deg, #e8f4fd, #f0e8fd);
+          border: 1.5px solid #e2e8f0;
+          border-radius: 10px;
+          transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1);
         }
       }
 
       .submit {
         width: 100%;
+        height: 46px;
+        font-family: inherit;
+        font-size: 15px;
+        font-weight: 600;
+        color: #fff;
+        letter-spacing: 0.5px;
+        cursor: pointer;
+        background: linear-gradient(135deg, #2d3e8f, #4a7dd4);
+        border: none;
+        border-radius: 10px;
+        box-shadow: 0 4px 14px rgba(45, 62, 143, 25%);
+        transition: all 0.2s;
+
+        &:hover {
+          background: linear-gradient(135deg, #1e2d6e, #3b52c4);
+          box-shadow: 0 6px 22px rgba(45, 62, 143, 35%);
+          transform: translateY(-1px);
+        }
+
+        &:active {
+          transform: translateY(0);
+        }
+
+        &:focus {
+          outline: none;
+        }
       }
 
       .protocol {
         display: flex;
         justify-content: space-between;
         font-size: 12px;
-        color: #000;
+        color: #94a3b8;
       }
 
       .el-form-item--small.el-form-item:last-child {
         margin-bottom: 0;
       }
+
+      .el-checkbox__input.is-checked .el-checkbox__inner {
+        accent-color: #2d3e8f;
+        background-color: #2d3e8f;
+        border-color: #2d3e8f;
+      }
     }
 
     .not-tips {
@@ -279,6 +489,526 @@ export default {
       cursor: pointer;
     }
   }
+
+  .brand-panel {
+    position: relative;
+    z-index: 1;
+    display: flex;
+    flex: 0 0 62%;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    width: 62%;
+    padding: 40px 55px;
+    overflow: hidden;
+  }
+
+  .brand-content {
+    position: relative;
+    z-index: 2;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    width: 100%;
+    max-width: 620px;
+  }
+
+  .brand-logo {
+    display: flex;
+    gap: 14px;
+    align-items: center;
+    margin-bottom: 12px;
+    animation: fadeDown 0.7s cubic-bezier(0.22, 1, 0.36, 1);
+  }
+
+  @keyframes fadeDown {
+    from {
+      opacity: 0;
+      transform: translateY(-10px);
+    }
+
+    to {
+      opacity: 1;
+      transform: translateY(0);
+    }
+  }
+
+  .brand-logo .logo-icon {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 52px;
+    height: 52px;
+  }
+
+  .brand-logo .logo-icon img {
+    width: 100%;
+    height: 100%;
+    object-fit: contain;
+  }
+
+  .brand-logo .logo-text {
+    font-size: 27px;
+    font-weight: 700;
+    color: #1f2937;
+    letter-spacing: 0.3px;
+  }
+
+  .brand-logo .logo-text span {
+    color: #2d3e8f;
+  }
+
+  .brand-title-group {
+    display: flex;
+    flex-direction: column;
+    gap: 2px;
+    align-items: center;
+    margin-top: 4px;
+  }
+
+  .brand-position {
+    margin-bottom: 4px;
+    font-size: 13px;
+    font-weight: 500;
+    color: #64748b;
+    letter-spacing: 0.8px;
+    animation: fadeDown 0.7s 0.04s cubic-bezier(0.22, 1, 0.36, 1) both;
+  }
+
+  .brand-desc {
+    margin-bottom: 40px;
+    font-size: 13px;
+    color: #64748b;
+    animation: fadeDown 0.7s 0.08s cubic-bezier(0.22, 1, 0.36, 1) both;
+  }
+
+  .flow-zone {
+    position: relative;
+    width: 100%;
+    max-width: 580px;
+    margin-bottom: 38px;
+    animation: fadeDown 0.7s 0.16s cubic-bezier(0.22, 1, 0.36, 1) both;
+  }
+
+  .flow-track {
+    position: relative;
+    display: flex;
+    align-items: center;
+    height: 155px;
+    padding: 0 12px;
+  }
+
+  .flow-track::before {
+    position: absolute;
+    top: 44%;
+    right: 28px;
+    left: 28px;
+    height: 2px;
+    content: '';
+    background: linear-gradient(
+      90deg,
+      rgba(45, 62, 143, 6%),
+      rgba(45, 62, 143, 18%) 18%,
+      rgba(45, 62, 143, 22%) 36%,
+      rgba(77, 201, 246, 20%) 54%,
+      rgba(77, 201, 246, 15%) 72%,
+      rgba(77, 201, 246, 10%) 90%,
+      rgba(77, 201, 246, 4%) 100%
+    );
+    border-radius: 1px;
+    transform: translateY(-50%);
+    animation: trackGlow 3s ease-in-out infinite;
+  }
+
+  @keyframes trackGlow {
+    0%,
+    100% {
+      opacity: 0.6;
+    }
+
+    50% {
+      opacity: 1;
+    }
+  }
+
+  .flow-node {
+    position: relative;
+    z-index: 2;
+    display: flex;
+    flex: 1;
+    flex-direction: column;
+    align-items: center;
+    animation: nodeRise 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
+  }
+
+  .flow-node:nth-child(1) {
+    animation-delay: 0.15s;
+  }
+
+  .flow-node:nth-child(2) {
+    animation-delay: 0.27s;
+  }
+
+  .flow-node:nth-child(3) {
+    animation-delay: 0.39s;
+  }
+
+  .flow-node:nth-child(4) {
+    animation-delay: 0.51s;
+  }
+
+  .flow-node:nth-child(5) {
+    animation-delay: 0.63s;
+  }
+
+  .flow-node:nth-child(6) {
+    animation-delay: 0.75s;
+  }
+
+  @keyframes nodeRise {
+    from {
+      opacity: 0;
+      transform: translateY(20px);
+    }
+
+    to {
+      opacity: 1;
+      transform: translateY(0);
+    }
+  }
+
+  .flow-dot {
+    position: relative;
+    z-index: 3;
+    width: 14px;
+    height: 14px;
+    border: 2.5px solid #f7f9fe;
+    border-radius: 50%;
+    box-shadow:
+      0 0 0 4px var(--sd, transparent),
+      0 2px 8px rgba(0, 0, 0, 6%);
+    animation: dotGlow 3s ease-in-out infinite;
+    animation-delay: var(--dg, 0s);
+  }
+
+  @keyframes dotGlow {
+    0%,
+    100% {
+      box-shadow:
+        0 0 0 4px var(--sd, transparent),
+        0 2px 8px rgba(0, 0, 0, 6%);
+    }
+
+    50% {
+      box-shadow:
+        0 0 0 8px var(--sd, transparent),
+        0 4px 16px rgba(0, 0, 0, 10%);
+    }
+  }
+
+  .dot-1 {
+    background: #2d3e8f;
+
+    --sd: rgba(45, 62, 143, 20%);
+    --dg: 0s;
+  }
+
+  .dot-2 {
+    background: #3b52c4;
+
+    --sd: rgba(59, 82, 196, 20%);
+    --dg: 0.5s;
+  }
+
+  .dot-3 {
+    background: #4a7dd4;
+
+    --sd: rgba(74, 125, 212, 20%);
+    --dg: 1s;
+  }
+
+  .dot-4 {
+    background: #4dc9f6;
+
+    --sd: rgba(77, 201, 246, 20%);
+    --dg: 1.5s;
+  }
+
+  .dot-5 {
+    background: #38b8e8;
+
+    --sd: rgba(56, 184, 232, 20%);
+    --dg: 2s;
+  }
+
+  .dot-6 {
+    background: #2d9bd4;
+
+    --sd: rgba(45, 155, 212, 20%);
+    --dg: 2.5s;
+  }
+
+  .flow-card {
+    min-width: 80px;
+    padding: 10px 12px;
+    margin-top: 14px;
+    text-align: center;
+    cursor: default;
+    background: rgba(255, 255, 255, 88%);
+    backdrop-filter: blur(18px);
+    border: 1px solid rgba(255, 255, 255, 85%);
+    border-radius: 11px;
+    box-shadow:
+      0 3px 14px rgba(0, 0, 0, 4%),
+      0 1px 3px rgba(0, 0, 0, 2%);
+    transition: all 0.38s cubic-bezier(0.22, 1, 0.36, 1);
+  }
+
+  .flow-card:hover {
+    box-shadow:
+      0 10px 28px rgba(45, 62, 143, 8%),
+      0 4px 12px rgba(0, 0, 0, 4%);
+    transform: translateY(-5px);
+  }
+
+  .flow-card .fc-icon {
+    display: block;
+    margin-bottom: 4px;
+    font-size: 22px;
+    transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1);
+  }
+
+  .flow-card:hover .fc-icon {
+    transform: scale(1.1);
+  }
+
+  .flow-card .fc-icon svg {
+    width: 22px;
+    height: 22px;
+    stroke-linecap: round;
+    stroke-linejoin: round;
+    stroke-width: 1.8;
+  }
+
+  .flow-card .fc-title {
+    margin-bottom: 1px;
+    font-size: 11px;
+    font-weight: 600;
+    color: #1f2937;
+    white-space: nowrap;
+  }
+
+  .flow-card .fc-sub {
+    font-size: 9px;
+    color: #94a3b8;
+    white-space: nowrap;
+  }
+
+  .flow-arrow {
+    position: absolute;
+    top: 44%;
+    z-index: 2;
+    font-size: 12px;
+    color: rgba(45, 62, 143, 18%);
+    pointer-events: none;
+    transform: translateY(-50%);
+  }
+
+  .flow-arrow.a1 {
+    left: 12%;
+  }
+
+  .flow-arrow.a2 {
+    left: 28%;
+  }
+
+  .flow-arrow.a3 {
+    left: 44%;
+  }
+
+  .flow-arrow.a4 {
+    left: 60%;
+  }
+
+  .flow-arrow.a5 {
+    left: 76%;
+  }
+
+  .flow-progress {
+    position: absolute;
+    right: 28px;
+    bottom: -28px;
+    left: 28px;
+    height: 3px;
+    overflow: hidden;
+    background: #e5e7eb;
+    border-radius: 2px;
+  }
+
+  .flow-progress .fp-fill {
+    height: 100%;
+    background: linear-gradient(90deg, #2d3e8f, #3b52c4, #4a7dd4, #4dc9f6, #38b8e8, #2d9bd4);
+    border-radius: 2px;
+    animation: progressFill 4.5s ease-in-out infinite;
+  }
+
+  @keyframes progressFill {
+    0% {
+      width: 0%;
+    }
+
+    50% {
+      width: 100%;
+    }
+
+    100% {
+      width: 100%;
+    }
+  }
+
+  .flow-labels {
+    display: flex;
+    justify-content: space-around;
+    padding: 0 8px;
+    margin-top: 14px;
+  }
+
+  .flow-label {
+    font-size: 10px;
+    color: #94a3b8;
+    text-align: center;
+    animation: nodeRise 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
+  }
+
+  .flow-label:nth-child(1) {
+    animation-delay: 0.25s;
+  }
+
+  .flow-label:nth-child(2) {
+    animation-delay: 0.37s;
+  }
+
+  .flow-label:nth-child(3) {
+    animation-delay: 0.49s;
+  }
+
+  .flow-label:nth-child(4) {
+    animation-delay: 0.61s;
+  }
+
+  .flow-label:nth-child(5) {
+    animation-delay: 0.73s;
+  }
+
+  .flow-label:nth-child(6) {
+    animation-delay: 0.85s;
+  }
+
+  .login-panel {
+    position: relative;
+    z-index: 1;
+    display: flex;
+    flex: 0 0 38%;
+    align-items: center;
+    justify-content: center;
+    width: 38%;
+    padding: 40px;
+    overflow: hidden;
+  }
+
+  .login-card {
+    position: relative;
+    z-index: 2;
+    width: 100%;
+    max-width: 420px;
+    padding: 32px 36px 28px;
+    background: #fff;
+    border: 1px solid rgba(226, 232, 240, 60%);
+    border-radius: 16px;
+    box-shadow:
+      0 8px 40px rgba(0, 0, 0, 6%),
+      0 2px 12px rgba(0, 0, 0, 3%);
+    animation: fadeUp 0.7s 0.2s cubic-bezier(0.22, 1, 0.36, 1) both;
+  }
+
+  @keyframes fadeUp {
+    from {
+      opacity: 0;
+      transform: translateY(16px);
+    }
+
+    to {
+      opacity: 1;
+      transform: translateY(0);
+    }
+  }
+
+  .login-card .lc-header {
+    margin-bottom: 28px;
+    text-align: center;
+  }
+
+  .login-card .lc-logo {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 44px;
+    height: 44px;
+    margin: 0 auto 16px;
+    overflow: hidden;
+    border-radius: 12px;
+  }
+
+  .login-card .lc-logo img {
+    width: 100%;
+    height: 100%;
+    object-fit: contain;
+  }
+
+  .login-card h2 {
+    margin-bottom: 4px;
+    font-size: 22px;
+    font-weight: 700;
+    color: #1f2937;
+  }
+
+  .login-card .sub {
+    font-size: 13px;
+    color: #94a3b8;
+  }
+
+  /* 角色选择 */
+  .role-tabs {
+    display: flex;
+    gap: 0;
+    padding: 3px;
+    margin-bottom: 22px;
+    background: #f1f5f9;
+    border-radius: 8px;
+  }
+
+  .role-tabs .role-tab {
+    flex: 1;
+    padding: 8px 0;
+    font-size: 13px;
+    font-weight: 500;
+    color: #64748b;
+    text-align: center;
+    cursor: pointer;
+    user-select: none;
+    border-radius: 6px;
+    transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1);
+  }
+
+  .role-tabs .role-tab.active {
+    color: #2d3e8f;
+    background: #fff;
+    box-shadow: 0 1px 4px rgba(0, 0, 0, 8%);
+  }
+
+  .role-tabs .role-tab:hover:not(.active) {
+    color: #1f2937;
+  }
 }
 </style>
 <style lang="scss">