Explorar o código

style(Login): 对齐设计图,调整登录框尺寸、输入框、按钮及标题样式,清理CSS注释

sequoia tungfang hai 2 semanas
pai
achega
f7b72e6f11

BIN=BIN
src/assets/login/Light-green.png


BIN=BIN
src/assets/login/Light-red.png


BIN=BIN
src/assets/login/Light-yellow.png


BIN=BIN
src/assets/login/globe-base.png


BIN=BIN
src/assets/login/light-bg.png


BIN=BIN
src/assets/login/light-small-green.png


BIN=BIN
src/assets/login/light-small-red.png


BIN=BIN
src/assets/login/light-small-yellow.png


BIN=BIN
src/assets/login/login-bg.mp4


BIN=BIN
src/assets/login/platform-disc.png


BIN=BIN
src/assets/login/title.png


BIN=BIN
src/assets/login/traffic-light-main.png


BIN=BIN
src/assets/login/traffic-light-small.png


+ 369 - 0
src/views/Login copy.vue

@@ -0,0 +1,369 @@
+<template>
+  <LoginLayout >
+
+    <!-- 背景 -->
+    <template #background>
+      <div class="login-bg"></div>
+      <!-- 红绿灯动画 -->
+      <div class="traffic-light-system">
+        <!-- 主红绿灯(直行) -->
+        <div class="traffic-light main-light">
+          <div class="light red" :class="{ active: mainLight === 'red' }"></div>
+          <div class="light yellow" :class="{ active: mainLight === 'yellow' }"></div>
+          <div class="light green" :class="{ active: mainLight === 'green' }"></div>
+        </div>
+        <!-- 左拐灯 -->
+        <div class="left-turn-light">
+          <div class="arrow-light">
+            <img 
+              v-if="leftLight === 'red'" 
+              :src="require('@/assets/icon_red_left.png')" 
+              alt="左拐红灯"
+            />
+            <img 
+              v-else-if="leftLight === 'yellow'" 
+              :src="require('@/assets/icon_yellow_left.png')" 
+              alt="左拐黄灯"
+            />
+            <img 
+              v-else 
+              :src="require('@/assets/icon_green_left.png')" 
+              alt="左拐绿灯"
+            />
+          </div>
+        </div>
+        <!-- 人行道灯 -->
+        <div class="pedestrian-light">
+          <div class="person-light" :class="{ active: pedestrianLight === 'red' }">
+            <img :src="require('@/assets/icon_red_person.png')" alt="红灯" class="person-img" />
+          </div>
+          <div class="person-light" :class="{ active: pedestrianLight === 'green' }">
+            <img :src="require('@/assets/icon_green_person.png')" alt="绿灯" class="person-img" />
+          </div>
+        </div>
+      </div>
+      <!-- Canvas 波浪动画 -->
+      <div class="wave-canvas-container">
+        <WaveCanvas />
+      </div>
+    </template>
+
+    <template #main>
+      <div class="page">
+        <div class="content">
+          <!-- 左侧酷炫区域 -->
+          <div class="left">
+            
+          </div>
+    
+          <!-- 右侧登录面板 -->
+          <div class="right">
+            <div class="login-container">
+              <LoginForm></LoginForm>
+            </div>
+          </div>
+        </div>
+
+        <div class="copyright">
+          <img class="copyright-logo" :src="require('@/assets/images/logo.png')" />
+          <div>北京东土正创科技有限公司</div>
+        </div>
+      </div>
+    </template>
+
+  </LoginLayout>
+</template>
+
+<script>
+import LoginLayout from "@/layouts/LoginLayout.vue";
+import LoginForm from "@/components/ui/LoginForm.vue";
+import WaveCanvas from "@/components/WaveCanvas.vue";
+
+export default {
+  name: "LoginPage",
+  components: { 
+    LoginLayout,
+    LoginForm,
+    WaveCanvas
+  },
+  created() {
+    // 提前预加载 Cesium 瓦片
+    import('@/utils/cesiumPreloader').then(m => m.default.start());
+  },
+  data() {
+    return {
+      mainLight: 'green',      // 直行灯: green, yellow, red
+      leftLight: 'red',        // 左拐灯: green, yellow, red
+      pedestrianLight: 'green', // 人行道灯: green, red
+      lightTimer: null,
+      currentPhase: 0,         // 0: 直行绿, 1: 黄灯, 2: 左拐绿
+    };
+  },
+  computed: {
+    
+  },
+  mounted() {
+    this.startTrafficLightCycle();
+  },
+  beforeDestroy() {
+    if (this.lightTimer) {
+      clearTimeout(this.lightTimer);
+    }
+  },
+
+  methods: {
+    startTrafficLightCycle() {
+      // 状态0: 直行绿灯10秒 + 人行道绿灯 + 左拐红灯
+      this.currentPhase = 0;
+      this.mainLight = 'green';
+      this.leftLight = 'red';
+      this.pedestrianLight = 'green';
+      
+      this.lightTimer = setTimeout(() => {
+        this.switchToYellowPhase();
+      }, 10000);
+    },
+    
+    switchToYellowPhase() {
+      // 状态1: 黄灯3秒 + 直行灭(实际变黄)+ 左拐红 + 人行道红
+      this.currentPhase = 1;
+      this.mainLight = 'yellow';
+      this.leftLight = 'red';
+      this.pedestrianLight = 'red';
+      
+      this.lightTimer = setTimeout(() => {
+        this.switchToLeftTurnPhase();
+      }, 3000);
+    },
+    
+    switchToLeftTurnPhase() {
+      // 状态2: 左拐绿灯10秒 + 直行红灯 + 人行道红灯
+      this.currentPhase = 2;
+      this.mainLight = 'red';
+      this.leftLight = 'green';
+      this.pedestrianLight = 'red';
+      
+      this.lightTimer = setTimeout(() => {
+        this.switchToYellowPhase2();
+      }, 10000);
+    },
+    
+    switchToYellowPhase2() {
+      // 状态3: 黄灯3秒 + 左拐黄灯 + 直行红灯 + 人行道红灯
+      this.currentPhase = 3;
+      this.mainLight = 'red';
+      this.leftLight = 'yellow';
+      this.pedestrianLight = 'red';
+      
+      this.lightTimer = setTimeout(() => {
+        this.startTrafficLightCycle(); // 重新开始循环
+      }, 3000);
+    }
+  }
+};
+</script>
+
+<style scoped>
+.login-bg {
+  background: url('@/assets/images/login-background.png') no-repeat center/cover;
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  top: 0;
+  left: 0;
+}
+.login-container {
+  /* * 【自适应魔法】:计算当前屏幕与 1920 宽度的比例。
+   * clamp(最小缩放比例, 动态计算比例, 最大缩放比例)
+   * 当屏幕小于 1920 时,--s 会小于 1;当屏幕等于 1920 时,--s 为 1。
+   */
+  --s: clamp(0.5, 100vw / 1920, 1.2);
+
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;        
+  justify-content: flex-end;  
+  
+  /* 右侧边距也用 clamp 做动态响应:最小 20px,推荐 8vw,最大 150px */
+  padding-right: clamp(20px, 8vw, 150px); 
+  box-sizing: border-box;
+  pointer-events: auto;       
+}
+
+.page{
+  width: 100vw; height: 100vh;
+  position: relative;
+  overflow: hidden;
+  --s: 1;
+ }
+
+.content{
+  position:absolute;
+  inset: 0;
+  display:grid;
+  grid-template-columns: 1fr 1fr;
+  gap: clamp(calc(var(--s) * 14px), 1.8vw, calc(var(--s) * 28px));
+  padding: clamp(calc(var(--s) * 14px), 2.4vw, calc(var(--s) * 30px));
+  padding-top: calc(var(--s) * 60px); /* 给头部留空间 */
+  align-items:center;
+  z-index: 1;
+}
+
+/* 左侧 */
+.left{ min-width: 0; }
+
+/* 右侧面板 */
+.right{
+  min-width: 0;
+  display:flex;
+  justify-content:center;
+}
+
+/* 版权信息 */
+.copyright {
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+  font-size: 18px;
+  color: #FFF;
+  line-height: 30px;
+  text-align: center;
+  position: absolute;
+  bottom: 20px;
+  left: 0;
+  justify-content: center;
+  width: 100%;
+}
+
+.copyright-logo {
+  width: 100px;
+  margin-bottom: 10px;
+}
+
+/* ================= 红绿灯系统 ================= */
+.traffic-light-system {
+  position: absolute;
+  left: 30%;
+  top: 25%;
+  z-index: 10;
+}
+
+/* 主红绿灯(直行) */
+.traffic-light {
+  position: absolute;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  padding: 6px;
+}
+
+.main-light {
+  left: 67px;
+  top: 0px;
+}
+
+.left-light {
+  left: 0;
+  top: 0;
+}
+
+/* 左拐箭头灯 */
+.left-turn-light {
+  position: absolute;
+  left: 4px;
+  top: 4px;
+}
+
+.arrow-light {
+  width: 36px;
+  height: 36px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  transition: all 0.3s ease;
+}
+
+.arrow-light img {
+  width: 100%;
+  height: 100%;
+  object-fit: contain;
+  filter: drop-shadow(none);
+}
+
+.light {
+  width: 30px;
+  height: 30px;
+  border-radius: 50%;
+  opacity: 0.15;
+  transition: all 0.3s ease;
+  background: radial-gradient(circle at 30% 30%, rgba(100, 100, 100, 0.5), rgba(50, 50, 50, 0.3));
+}
+
+.light.red {
+  background: radial-gradient(circle at 30% 30%, #ff4444, #cc0000);
+}
+
+.light.red.active {
+  opacity: 1;
+  box-shadow: 0 0 15px #ff4444, 0 0 30px #ff4444, inset 0 0 10px rgba(255, 100, 100, 0.5);
+}
+
+.light.yellow {
+  background: radial-gradient(circle at 30% 30%, #ffcc00, #ff9900);
+}
+
+.light.yellow.active {
+  opacity: 1;
+  box-shadow: 0 0 15px #ffcc00, 0 0 30px #ffcc00, inset 0 0 10px rgba(255, 200, 100, 0.5);
+}
+
+.light.green {
+  background: radial-gradient(circle at 30% 30%, #00ff44, #00cc33);
+}
+
+.light.green.active {
+  opacity: 1;
+  box-shadow: 0 0 15px #00ff44, 0 0 30px #00ff44, inset 0 0 10px rgba(100, 255, 150, 0.5);
+}
+
+/* 人行道灯 */
+.pedestrian-light {
+  position: absolute;
+  left: 240px;
+  top: 125px;
+  width: 36px;
+  height: 80px;
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+
+.person-light {
+  width: 36px;
+  height: 36px;
+  opacity: 0;
+  transition: all 0.3s ease;
+}
+
+.person-light.active {
+  opacity: 1;
+}
+
+.person-img {
+  width: 100%;
+  height: 100%;
+  object-fit: contain;
+}
+
+/* ================= Canvas 波浪容器 ================= */
+.wave-canvas-container {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  height: 400px;
+  z-index: 5;
+  pointer-events: none;
+}
+</style>

+ 241 - 294
src/views/Login.vue

@@ -1,369 +1,316 @@
 <template>
-  <LoginLayout >
-
-    <!-- 背景 -->
-    <template #background>
-      <div class="login-bg"></div>
-      <!-- 红绿灯动画 -->
-      <div class="traffic-light-system">
-        <!-- 主红绿灯(直行) -->
-        <div class="traffic-light main-light">
-          <div class="light red" :class="{ active: mainLight === 'red' }"></div>
-          <div class="light yellow" :class="{ active: mainLight === 'yellow' }"></div>
-          <div class="light green" :class="{ active: mainLight === 'green' }"></div>
-        </div>
-        <!-- 左拐灯 -->
-        <div class="left-turn-light">
-          <div class="arrow-light">
-            <img 
-              v-if="leftLight === 'red'" 
-              :src="require('@/assets/icon_red_left.png')" 
-              alt="左拐红灯"
-            />
-            <img 
-              v-else-if="leftLight === 'yellow'" 
-              :src="require('@/assets/icon_yellow_left.png')" 
-              alt="左拐黄灯"
-            />
-            <img 
-              v-else 
-              :src="require('@/assets/icon_green_left.png')" 
-              alt="左拐绿灯"
-            />
-          </div>
-        </div>
-        <!-- 人行道灯 -->
-        <div class="pedestrian-light">
-          <div class="person-light" :class="{ active: pedestrianLight === 'red' }">
-            <img :src="require('@/assets/icon_red_person.png')" alt="红灯" class="person-img" />
-          </div>
-          <div class="person-light" :class="{ active: pedestrianLight === 'green' }">
-            <img :src="require('@/assets/icon_green_person.png')" alt="绿灯" class="person-img" />
+  <div class="login-root">
+
+    <!-- 视频背景 -->
+    <video class="bg-video" autoplay muted loop playsinline @error="videoBgFailed = true">
+      <source src="@/assets/login/login-bg.mp4" type="video/mp4" />
+    </video>
+    <div v-if="videoBgFailed" class="bg-fallback"></div>
+
+    <!-- 顶部居中 KYLAND Logo -->
+    <header class="top-bar">
+      <img class="kyland-logo" src="@/assets/images/logo.png" alt="KYLAND" />
+    </header>
+
+    <!-- 主内容区:左右两栏 -->
+    <div class="main-content">
+
+      <!-- 左侧 -->
+      <div class="left-panel">
+        <div class="login-box">
+          <!-- 标题 -->
+          <div class="title-wrap">
+            <img class="title-img" src="@/assets/login/title.png" alt="交通信号控制平台—灵·智" />
           </div>
-        </div>
-      </div>
-      <!-- Canvas 波浪动画 -->
-      <div class="wave-canvas-container">
-        <WaveCanvas />
-      </div>
-    </template>
-
-    <template #main>
-      <div class="page">
-        <div class="content">
-          <!-- 左侧酷炫区域 -->
-          <div class="left">
-            
-          </div>
-    
-          <!-- 右侧登录面板 -->
-          <div class="right">
-            <div class="login-container">
-              <LoginForm></LoginForm>
+
+          <!-- 表单 -->
+          <div class="login-form">
+            <div class="field">
+              <img class="field-icon" src="@/assets/i_user.png" alt="" />
+              <span class="field-label">用户名</span>
+              <input class="inp" v-model.trim="username" placeholder="" />
+            </div>
+
+            <div class="field">
+              <img class="field-icon" src="@/assets/i_lock.png" alt="" />
+              <span class="field-label">密码</span>
+              <input class="inp" type="password" v-model.trim="password" placeholder="" />
             </div>
-          </div>
-        </div>
 
-        <div class="copyright">
-          <img class="copyright-logo" :src="require('@/assets/images/logo.png')" />
-          <div>北京东土正创科技有限公司</div>
+            <div class="field-row" v-if="showCaptcha">
+              <div class="field cap-field">
+                <img class="field-icon" src="@/assets/i_captcha.png" alt="" />
+                <span class="field-label">验证码</span>
+                <input class="inp" v-model.trim="captchaInput" placeholder="" />
+              </div>
+              <CaptchaCanvas class="captcha-canvas" v-model="captchaCode" />
+            </div>
+
+            <div class="hint" v-if="hint">{{ hint }}</div>
+
+            <button class="btn-login" @click="onLogin" :disabled="loading">
+              {{ loading ? '登录中...' : '登录' }}
+            </button>
+          </div>
         </div>
       </div>
-    </template>
 
-  </LoginLayout>
+      <!-- 右侧留空(球体动画由视频背景实现) -->
+      <div class="right-panel"></div>
+
+    </div>
+
+    <!-- 版权 -->
+    <footer class="copyright">北京东土正创科技有限公司</footer>
+
+  </div>
 </template>
 
 <script>
-import LoginLayout from "@/layouts/LoginLayout.vue";
-import LoginForm from "@/components/ui/LoginForm.vue";
-import WaveCanvas from "@/components/WaveCanvas.vue";
+import CaptchaCanvas from "@/components/CaptchaCanvas.vue";
+import { apiLogin } from "@/api";
 
 export default {
   name: "LoginPage",
-  components: { 
-    LoginLayout,
-    LoginForm,
-    WaveCanvas
-  },
+  components: { CaptchaCanvas },
   created() {
-    // 提前预加载 Cesium 瓦片
     import('@/utils/cesiumPreloader').then(m => m.default.start());
   },
   data() {
     return {
-      mainLight: 'green',      // 直行灯: green, yellow, red
-      leftLight: 'red',        // 左拐灯: green, yellow, red
-      pedestrianLight: 'green', // 人行道灯: green, red
-      lightTimer: null,
-      currentPhase: 0,         // 0: 直行绿, 1: 黄灯, 2: 左拐绿
+      videoBgFailed: false,
+      username: "admin",
+      password: "123456",
+      captchaCode: "",
+      captchaInput: "",
+      hint: "",
+      loading: false,
+      loginFailedCount: Number(sessionStorage.getItem("loginFailedCount")) || 0,
     };
   },
   computed: {
-    
-  },
-  mounted() {
-    this.startTrafficLightCycle();
+    showCaptcha() { return this.loginFailedCount >= 3; },
   },
-  beforeDestroy() {
-    if (this.lightTimer) {
-      clearTimeout(this.lightTimer);
-    }
-  },
-
   methods: {
-    startTrafficLightCycle() {
-      // 状态0: 直行绿灯10秒 + 人行道绿灯 + 左拐红灯
-      this.currentPhase = 0;
-      this.mainLight = 'green';
-      this.leftLight = 'red';
-      this.pedestrianLight = 'green';
-      
-      this.lightTimer = setTimeout(() => {
-        this.switchToYellowPhase();
-      }, 10000);
-    },
-    
-    switchToYellowPhase() {
-      // 状态1: 黄灯3秒 + 直行灭(实际变黄)+ 左拐红 + 人行道红
-      this.currentPhase = 1;
-      this.mainLight = 'yellow';
-      this.leftLight = 'red';
-      this.pedestrianLight = 'red';
-      
-      this.lightTimer = setTimeout(() => {
-        this.switchToLeftTurnPhase();
-      }, 3000);
+    async onLogin() {
+      this.hint = "";
+      if (this.showCaptcha) {
+        if (!this.captchaInput) { this.hint = "请输入验证码"; return; }
+        if (this.captchaInput.toLowerCase() !== this.captchaCode.toLowerCase()) {
+          this.hint = "验证码错误"; this.captchaInput = ""; return;
+        }
+      }
+      this.loading = true;
+      try {
+        const data = await apiLogin({ username: this.username, password: this.password, captcha: this.captchaInput });
+        this.loginFailedCount = 0;
+        sessionStorage.removeItem("loginFailedCount");
+        localStorage.setItem("token", data.token);
+        setTimeout(() => {
+          this.$router.push('/transition').catch(() => {});
+        }, 300);
+      } catch (e) {
+        this.hint = e.message || "登录失败";
+        this.loginFailedCount++;
+        sessionStorage.setItem("loginFailedCount", this.loginFailedCount);
+        this.captchaInput = "";
+      } finally {
+        this.loading = false;
+      }
     },
-    
-    switchToLeftTurnPhase() {
-      // 状态2: 左拐绿灯10秒 + 直行红灯 + 人行道红灯
-      this.currentPhase = 2;
-      this.mainLight = 'red';
-      this.leftLight = 'green';
-      this.pedestrianLight = 'red';
-      
-      this.lightTimer = setTimeout(() => {
-        this.switchToYellowPhase2();
-      }, 10000);
-    },
-    
-    switchToYellowPhase2() {
-      // 状态3: 黄灯3秒 + 左拐黄灯 + 直行红灯 + 人行道红灯
-      this.currentPhase = 3;
-      this.mainLight = 'red';
-      this.leftLight = 'yellow';
-      this.pedestrianLight = 'red';
-      
-      this.lightTimer = setTimeout(() => {
-        this.startTrafficLightCycle(); // 重新开始循环
-      }, 3000);
-    }
-  }
+  },
 };
 </script>
 
 <style scoped>
-.login-bg {
-  background: url('@/assets/images/login-background.png') no-repeat center/cover;
-  width: 100%;
-  height: 100%;
-  position: absolute;
-  top: 0;
-  left: 0;
-}
-.login-container {
-  /* * 【自适应魔法】:计算当前屏幕与 1920 宽度的比例。
-   * clamp(最小缩放比例, 动态计算比例, 最大缩放比例)
-   * 当屏幕小于 1920 时,--s 会小于 1;当屏幕等于 1920 时,--s 为 1。
-   */
-  --s: clamp(0.5, 100vw / 1920, 1.2);
-
-  width: 100%;
-  height: 100%;
-  display: flex;
-  align-items: center;        
-  justify-content: flex-end;  
-  
-  /* 右侧边距也用 clamp 做动态响应:最小 20px,推荐 8vw,最大 150px */
-  padding-right: clamp(20px, 8vw, 150px); 
-  box-sizing: border-box;
-  pointer-events: auto;       
-}
-
-.page{
-  width: 100vw; height: 100vh;
+.login-root {
+  width: 100vw;
+  height: 100vh;
   position: relative;
   overflow: hidden;
-  --s: 1;
- }
-
-.content{
-  position:absolute;
-  inset: 0;
-  display:grid;
-  grid-template-columns: 1fr 1fr;
-  gap: clamp(calc(var(--s) * 14px), 1.8vw, calc(var(--s) * 28px));
-  padding: clamp(calc(var(--s) * 14px), 2.4vw, calc(var(--s) * 30px));
-  padding-top: calc(var(--s) * 60px); /* 给头部留空间 */
-  align-items:center;
-  z-index: 1;
-}
-
-/* 左侧 */
-.left{ min-width: 0; }
-
-/* 右侧面板 */
-.right{
-  min-width: 0;
-  display:flex;
-  justify-content:center;
-}
-
-/* 版权信息 */
-.copyright {
+  background: #050a17;
   display: flex;
-  align-items: center;
   flex-direction: column;
-  font-size: 18px;
-  color: #FFF;
-  line-height: 30px;
-  text-align: center;
-  position: absolute;
-  bottom: 20px;
-  left: 0;
-  justify-content: center;
-  width: 100%;
-}
-
-.copyright-logo {
-  width: 100px;
-  margin-bottom: 10px;
 }
 
-/* ================= 红绿灯系统 ================= */
-.traffic-light-system {
+.bg-video {
   position: absolute;
-  left: 30%;
-  top: 25%;
-  z-index: 10;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  min-width: 100%;
+  min-height: 100%;
+  width: auto;
+  height: auto;
+  object-fit: cover;
+  z-index: 0;
 }
-
-/* 主红绿灯(直行) */
-.traffic-light {
+.bg-fallback {
   position: absolute;
-  display: flex;
-  flex-direction: column;
-  gap: 12px;
-  padding: 6px;
+  inset: 0;
+  background: url('@/assets/images/login-background.png') no-repeat center / cover;
+  z-index: 0;
 }
 
-.main-light {
-  left: 67px;
-  top: 0px;
+.top-bar {
+  position: relative;
+  z-index: 2;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 10vh;
+  flex-shrink: 0;
 }
-
-.left-light {
-  left: 0;
-  top: 0;
+.kyland-logo {
+  height: clamp(30px, 5vh, 56px);
+  width: auto;
 }
 
-/* 左拐箭头灯 */
-.left-turn-light {
-  position: absolute;
-  left: 4px;
-  top: 4px;
+.main-content {
+  position: relative;
+  z-index: 2;
+  flex: 1;
+  display: grid;
+  grid-template-columns: 45fr 55fr;
+  align-items: center;
+  padding: 0 8vw;
+  min-height: 0;
 }
 
-.arrow-light {
-  width: 36px;
-  height: 36px;
+.left-panel {
   display: flex;
-  align-items: center;
+  flex-direction: column;
   justify-content: center;
-  transition: all 0.3s ease;
+  align-items: center;
 }
 
-.arrow-light img {
-  width: 100%;
-  height: 100%;
-  object-fit: contain;
-  filter: drop-shadow(none);
+.login-box {
+  width: 566px;
+  height: 353px;
+  background: transparent;
+  border: none;
+  box-shadow: none;
+  padding: 0;
+  box-sizing: border-box;
+  display: flex;
+  flex-direction: column;
 }
 
-.light {
-  width: 30px;
-  height: 30px;
-  border-radius: 50%;
-  opacity: 0.15;
-  transition: all 0.3s ease;
-  background: radial-gradient(circle at 30% 30%, rgba(100, 100, 100, 0.5), rgba(50, 50, 50, 0.3));
+.title-wrap {
+  display: flex;
+  align-items: center;
+  margin-bottom: 22px;
+  flex-shrink: 0;
 }
-
-.light.red {
-  background: radial-gradient(circle at 30% 30%, #ff4444, #cc0000);
+.title-img {
+  width: auto;
+  max-width: 100%;
 }
 
-.light.red.active {
-  opacity: 1;
-  box-shadow: 0 0 15px #ff4444, 0 0 30px #ff4444, inset 0 0 10px rgba(255, 100, 100, 0.5);
+.login-form {
+  display: flex;
+  flex-direction: column;
+  flex: 1;
 }
 
-.light.yellow {
-  background: radial-gradient(circle at 30% 30%, #ffcc00, #ff9900);
+.field {
+  display: flex;
+  align-items: center;
+  height: 60px;
+  width: 100%;
+  padding: 0 20px;
+  background: rgba(255, 255, 255, 0.08);
+  border: 1px solid rgba(255, 255, 255, 0.15);
+  border-radius: 16px;
+  margin-bottom: 36px;
+  transition: border-color 0.2s, background 0.2s;
+  box-sizing: border-box;
+}
+.field:focus-within {
+  border-color: rgba(80, 180, 255, 0.5);
+  background: rgba(255, 255, 255, 0.12);
 }
 
-.light.yellow.active {
-  opacity: 1;
-  box-shadow: 0 0 15px #ffcc00, 0 0 30px #ffcc00, inset 0 0 10px rgba(255, 200, 100, 0.5);
+.field-icon {
+  width: 22px;
+  height: 22px;
+  object-fit: contain;
+  opacity: 0.7;
+  flex-shrink: 0;
+  margin-right: 10px;
 }
 
-.light.green {
-  background: radial-gradient(circle at 30% 30%, #00ff44, #00cc33);
+.field-label {
+  color: rgba(180, 215, 255, 0.75);
+  font-size: 16px;
+  white-space: nowrap;
+  flex-shrink: 0;
+  margin-right: 12px;
 }
 
-.light.green.active {
-  opacity: 1;
-  box-shadow: 0 0 15px #00ff44, 0 0 30px #00ff44, inset 0 0 10px rgba(100, 255, 150, 0.5);
+.inp {
+  flex: 1;
+  height: 100%;
+  border: none;
+  outline: none;
+  background: transparent;
+  color: #fff;
+  font-size: 16px;
 }
+.inp::placeholder { color: rgba(140, 180, 255, 0.3); }
 
-/* 人行道灯 */
-.pedestrian-light {
-  position: absolute;
-  left: 240px;
-  top: 125px;
-  width: 36px;
-  height: 80px;
+.field-row {
   display: flex;
-  flex-direction: column;
-  gap: 8px;
+  gap: 12px;
+  margin-bottom: 36px;
 }
-
-.person-light {
-  width: 36px;
-  height: 36px;
-  opacity: 0;
-  transition: all 0.3s ease;
+.cap-field { flex: 1; margin-bottom: 0; }
+.captcha-canvas {
+  width: 120px;
+  height: 60px;
+  border-radius: 16px;
+  overflow: hidden;
+  border: 1px solid rgba(80, 160, 255, 0.35);
+  flex-shrink: 0;
 }
 
-.person-light.active {
-  opacity: 1;
+.hint {
+  color: #ff4d4f;
+  font-size: 13px;
+  margin-bottom: 8px;
+  margin-top: -24px;
 }
 
-.person-img {
+.btn-login {
   width: 100%;
+  height: 64px;
+  background: linear-gradient(180deg, #3dd4f8 0%, #0fa8e8 50%, #0a90d8 100%);
+  border: none;
+  border-radius: 24px;
+  color: #fff;
+  font-size: 22px;
+  letter-spacing: 0.4em;
+  cursor: pointer;
+  transition: filter 0.2s;
+  flex-shrink: 0;
+  box-shadow: 0 4px 20px rgba(10, 160, 230, 0.5);
+}
+.btn-login:hover  { filter: brightness(1.1); }
+.btn-login:disabled { opacity: 0.6; cursor: not-allowed; }
+
+.right-panel {
   height: 100%;
-  object-fit: contain;
 }
 
-/* ================= Canvas 波浪容器 ================= */
-.wave-canvas-container {
-  position: absolute;
-  bottom: 0;
-  left: 0;
-  width: 100%;
-  height: 400px;
-  z-index: 5;
-  pointer-events: none;
+.copyright {
+  position: relative;
+  z-index: 2;
+  text-align: center;
+  color: rgba(255, 255, 255, 0.85);
+  font-size: clamp(12px, 1vw, 18px);
+  padding-bottom: clamp(10px, 1.8vh, 22px);
+  flex-shrink: 0;
+  text-shadow: 0 0 8px rgba(0, 150, 255, 0.6), 0 1px 3px rgba(0, 0, 0, 0.8);
+  letter-spacing: 1px;
 }
 </style>