|
@@ -0,0 +1,411 @@
|
|
|
|
|
+<!DOCTYPE html>
|
|
|
|
|
+<html lang="zh-CN">
|
|
|
|
|
+<head>
|
|
|
|
|
+ <meta charset="UTF-8" />
|
|
|
|
|
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
|
|
|
|
+ <title>绿水青山 · 登录</title>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- Tailwind CSS(CDN,国内可访问) -->
|
|
|
|
|
+ <script src="https://cdn.tailwindcss.com"></script>
|
|
|
|
|
+ <!-- Font Awesome 图标(国内 jsdelivr 镜像) -->
|
|
|
|
|
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" />
|
|
|
|
|
+
|
|
|
|
|
+ <script>
|
|
|
|
|
+ // Tailwind 主题扩展:品牌色 / 嫩芽色 / 山水色
|
|
|
|
|
+ tailwind.config = {
|
|
|
|
|
+ theme: {
|
|
|
|
|
+ extend: {
|
|
|
|
|
+ colors: {
|
|
|
|
|
+ brand: '#368F6F', // 主标题 / 按钮起点(青山绿)
|
|
|
|
|
+ stream: '#5AB8D0', // 按钮终点(溪流水蓝)
|
|
|
|
|
+ mist: '#709484', // 副标题(晨雾灰绿)
|
|
|
|
|
+ sprout: '#9BCB8A', // 嫩芽(卡片边框)
|
|
|
|
|
+ gold: '#E9C46A', // 金色微光
|
|
|
|
|
+ },
|
|
|
|
|
+ fontFamily: {
|
|
|
|
|
+ sans: ['"PingFang SC"', '"Microsoft YaHei"', '"Hiragino Sans GB"', 'system-ui', 'sans-serif'],
|
|
|
|
|
+ },
|
|
|
|
|
+ boxShadow: {
|
|
|
|
|
+ glass: '0 18px 50px -12px rgba(54, 143, 111, 0.28)',
|
|
|
|
|
+ btn: '0 14px 30px -8px rgba(54, 143, 111, 0.45)',
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+ </script>
|
|
|
|
|
+
|
|
|
|
|
+ <style>
|
|
|
|
|
+ /* ============ 基础 ============ */
|
|
|
|
|
+ html, body { height: 100%; }
|
|
|
|
|
+ body {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ -webkit-tap-highlight-color: transparent;
|
|
|
|
|
+ font-family: "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;
|
|
|
|
|
+ background: #eef4f1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 手机外壳:移动端竖屏、居中 */
|
|
|
|
|
+ .phone {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ max-width: 28rem; /* max-w-md */
|
|
|
|
|
+ min-height: 100vh;
|
|
|
|
|
+ margin: 0 auto; /* mx-auto */
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ background:
|
|
|
|
|
+ linear-gradient(180deg, #f7fbf9 0%, #eef6f1 38%, #e7f0ec 100%);
|
|
|
|
|
+ isolation: isolate;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* ============ 顶部浅雾渐变天幕 ============ */
|
|
|
|
|
+ .sky {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ inset: 0 0 auto 0;
|
|
|
|
|
+ height: 46%;
|
|
|
|
|
+ background:
|
|
|
|
|
+ linear-gradient(160deg, #fbfefd 0%, #eaf4ef 45%, #dbe8ec 100%);
|
|
|
|
|
+ z-index: -3;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 金色微光(标题后方的柔光晕) */
|
|
|
|
|
+ .gold-glow {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: 7%;
|
|
|
|
|
+ left: 50%;
|
|
|
|
|
+ width: 260px;
|
|
|
|
|
+ height: 260px;
|
|
|
|
|
+ transform: translateX(-50%);
|
|
|
|
|
+ background: radial-gradient(circle, rgba(233,196,106,0.40) 0%, rgba(233,196,106,0.12) 42%, rgba(233,196,106,0) 70%);
|
|
|
|
|
+ filter: blur(6px);
|
|
|
|
|
+ z-index: -2;
|
|
|
|
|
+ animation: breathe 6s ease-in-out infinite;
|
|
|
|
|
+ }
|
|
|
|
|
+ @keyframes breathe {
|
|
|
|
|
+ 0%, 100% { opacity: 0.7; transform: translateX(-50%) scale(1); }
|
|
|
|
|
+ 50% { opacity: 1; transform: translateX(-50%) scale(1.08); }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 云雾朦胧斑 */
|
|
|
|
|
+ .mist-blob {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ border-radius: 9999px;
|
|
|
|
|
+ filter: blur(26px);
|
|
|
|
|
+ z-index: -2;
|
|
|
|
|
+ opacity: 0.55;
|
|
|
|
|
+ animation: drift 14s ease-in-out infinite;
|
|
|
|
|
+ }
|
|
|
|
|
+ .mist-1 { width: 200px; height: 120px; left: -60px; top: 6%; background: rgba(255,255,255,0.9); }
|
|
|
|
|
+ .mist-2 { width: 240px; height: 140px; right: -90px; top: 14%; background: rgba(214,235,230,0.9); animation-delay: -4s; }
|
|
|
|
|
+ .mist-3 { width: 180px; height: 100px; left: 30%; top: 2%; background: rgba(255,255,255,0.7); animation-delay: -8s; }
|
|
|
|
|
+ @keyframes drift {
|
|
|
|
|
+ 0%, 100% { transform: translate3d(0, 0, 0); }
|
|
|
|
|
+ 50% { transform: translate3d(18px, 10px, 0); }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* ============ 远山剪影 / 溪流(SVG 共用) ============ */
|
|
|
|
|
+ .scene { position: absolute; left: 0; right: 0; width: 100%; pointer-events: none; }
|
|
|
|
|
+ .scene-top { top: 30%; z-index: -1; opacity: 0.5; }
|
|
|
|
|
+ .scene-bottom { bottom: 0; z-index: -1; }
|
|
|
|
|
+
|
|
|
|
|
+ /* ============ 飘落绿叶粒子 ============ */
|
|
|
|
|
+ .leaf {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: -40px;
|
|
|
|
|
+ width: 18px;
|
|
|
|
|
+ height: 18px;
|
|
|
|
|
+ border-radius: 2px 16px 2px 16px;
|
|
|
|
|
+ transform: rotate(45deg);
|
|
|
|
|
+ z-index: 5;
|
|
|
|
|
+ opacity: 0.85;
|
|
|
|
|
+ animation: fall linear infinite;
|
|
|
|
|
+ }
|
|
|
|
|
+ @keyframes fall {
|
|
|
|
|
+ 0% { transform: translate3d(0, -40px, 0) rotate(0deg); opacity: 0; }
|
|
|
|
|
+ 10% { opacity: 0.9; }
|
|
|
|
|
+ 90% { opacity: 0.8; }
|
|
|
|
|
+ 100% { transform: translate3d(var(--dx, -30px), 105vh, 0) rotate(540deg); opacity: 0; }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* ============ 磨砂玻璃卡片 ============ */
|
|
|
|
|
+ .glass-card {
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.80);
|
|
|
|
|
+ -webkit-backdrop-filter: blur(12px);
|
|
|
|
|
+ backdrop-filter: blur(12px);
|
|
|
|
|
+ border: 1px solid rgba(155, 203, 138, 0.30);
|
|
|
|
|
+ border-radius: 24px;
|
|
|
|
|
+ box-shadow: 0 18px 50px -12px rgba(54, 143, 111, 0.28);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 输入框 */
|
|
|
|
|
+ .field {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ height: 52px;
|
|
|
|
|
+ padding: 0 16px;
|
|
|
|
|
+ border-radius: 16px;
|
|
|
|
|
+ background: rgba(246, 250, 247, 0.9);
|
|
|
|
|
+ border: 1px solid rgba(155, 203, 138, 0.35);
|
|
|
|
|
+ transition: border-color .2s ease, box-shadow .2s ease, background .2s ease;
|
|
|
|
|
+ }
|
|
|
|
|
+ .field:focus-within {
|
|
|
|
|
+ background: #ffffff;
|
|
|
|
|
+ border-color: #368F6F;
|
|
|
|
|
+ box-shadow: 0 0 0 4px rgba(54, 143, 111, 0.12);
|
|
|
|
|
+ }
|
|
|
|
|
+ .field input {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ outline: none;
|
|
|
|
|
+ background: transparent;
|
|
|
|
|
+ font-size: 15px;
|
|
|
|
|
+ color: #26403a;
|
|
|
|
|
+ }
|
|
|
|
|
+ .field input::placeholder { color: #9fb4aa; }
|
|
|
|
|
+ .field .fa { width: 20px; text-align: center; color: #7d978c; }
|
|
|
|
|
+ .field .toggle { color: #9fb4aa; cursor: pointer; padding: 6px; }
|
|
|
|
|
+
|
|
|
|
|
+ /* ============ 登录按钮(渐变 + 水波纹边缘) ============ */
|
|
|
|
|
+ .login-btn {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ height: 56px;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ border-radius: 24px;
|
|
|
|
|
+ color: #ffffff;
|
|
|
|
|
+ font-size: 17px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ letter-spacing: 4px;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ background: linear-gradient(135deg, #368F6F 0%, #5AB8D0 100%);
|
|
|
|
|
+ box-shadow: 0 14px 30px -8px rgba(54, 143, 111, 0.45);
|
|
|
|
|
+ transition: transform .15s ease, box-shadow .2s ease, opacity .2s ease;
|
|
|
|
|
+ }
|
|
|
|
|
+ .login-btn:active { transform: scale(0.985); }
|
|
|
|
|
+ .login-btn:disabled { opacity: 0.7; cursor: default; }
|
|
|
|
|
+
|
|
|
|
|
+ /* 持续呼吸的水波纹边缘 */
|
|
|
|
|
+ .login-btn::before {
|
|
|
|
|
+ content: "";
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ inset: -3px;
|
|
|
|
|
+ border-radius: 26px;
|
|
|
|
|
+ border: 2px solid rgba(90, 184, 208, 0.55);
|
|
|
|
|
+ animation: ripple 2.6s ease-out infinite;
|
|
|
|
|
+ pointer-events: none;
|
|
|
|
|
+ }
|
|
|
|
|
+ @keyframes ripple {
|
|
|
|
|
+ 0% { transform: scale(0.96); opacity: 0.7; }
|
|
|
|
|
+ 100% { transform: scale(1.08); opacity: 0; }
|
|
|
|
|
+ }
|
|
|
|
|
+ /* 点击水波圆斑 */
|
|
|
|
|
+ .login-btn .ink {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ transform: scale(0);
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.45);
|
|
|
|
|
+ animation: ink .6s ease-out;
|
|
|
|
|
+ pointer-events: none;
|
|
|
|
|
+ }
|
|
|
|
|
+ @keyframes ink {
|
|
|
|
|
+ to { transform: scale(2.6); opacity: 0; }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* ============ 底部草叶浮动 ============ */
|
|
|
|
|
+ .float-slow { animation: float 6s ease-in-out infinite; }
|
|
|
|
|
+ .float-slower { animation: float 8s ease-in-out infinite; animation-delay: -2s; }
|
|
|
|
|
+ @keyframes float {
|
|
|
|
|
+ 0%, 100% { transform: translateY(0); }
|
|
|
|
|
+ 50% { transform: translateY(-8px); }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 链接 */
|
|
|
|
|
+ .link { color: #368F6F; font-size: 13px; }
|
|
|
|
|
+ .link:active { opacity: 0.7; }
|
|
|
|
|
+ </style>
|
|
|
|
|
+</head>
|
|
|
|
|
+
|
|
|
|
|
+<body>
|
|
|
|
|
+ <!-- ============ 手机容器 ============ -->
|
|
|
|
|
+ <main class="phone flex flex-col">
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 天幕 / 光 / 雾(装饰层) -->
|
|
|
|
|
+ <div class="sky"></div>
|
|
|
|
|
+ <div class="gold-glow"></div>
|
|
|
|
|
+ <div class="mist-blob mist-1"></div>
|
|
|
|
|
+ <div class="mist-blob mist-2"></div>
|
|
|
|
|
+ <div class="mist-blob mist-3"></div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 顶部远山剪影 -->
|
|
|
|
|
+ <svg class="scene scene-top" viewBox="0 0 400 120" preserveAspectRatio="none" aria-hidden="true">
|
|
|
|
|
+ <path d="M0,120 L0,70 Q60,20 120,68 Q180,16 240,66 Q310,30 400,72 L400,120 Z" fill="#7fb3a0" opacity="0.35"/>
|
|
|
|
|
+ <path d="M0,120 L0,86 Q70,48 140,84 Q210,46 280,86 Q340,58 400,86 L400,120 Z" fill="#5e9c86" opacity="0.40"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 顶部溪流曲线 -->
|
|
|
|
|
+ <svg class="scene scene-top" style="top:40%; opacity:0.45" viewBox="0 0 400 60" preserveAspectRatio="none" aria-hidden="true">
|
|
|
|
|
+ <path d="M0,30 C60,10 90,50 150,30 S260,10 320,30 S380,42 400,30" fill="none" stroke="#5AB8D0" stroke-width="2" opacity="0.5"/>
|
|
|
|
|
+ <path d="M0,44 C70,26 100,58 160,42 S270,24 400,44" fill="none" stroke="#5AB8D0" stroke-width="1.4" opacity="0.35"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 飘落绿叶粒子 -->
|
|
|
|
|
+ <i class="leaf" style="left:8%; --dx:-40px; background:linear-gradient(135deg,#7cc68a,#368F6F); animation-duration:11s; animation-delay:-1s;"></i>
|
|
|
|
|
+ <i class="leaf" style="left:22%; --dx:30px; background:linear-gradient(135deg,#9BCB8A,#5ab07a); width:14px;height:14px; animation-duration:14s; animation-delay:-6s;"></i>
|
|
|
|
|
+ <i class="leaf" style="left:40%; --dx:-20px; background:linear-gradient(135deg,#6fbf8f,#2f7f62); width:20px;height:20px; animation-duration:12s; animation-delay:-3s;"></i>
|
|
|
|
|
+ <i class="leaf" style="left:58%; --dx:40px; background:linear-gradient(135deg,#a9d89c,#368F6F); width:13px;height:13px; animation-duration:15s; animation-delay:-9s;"></i>
|
|
|
|
|
+ <i class="leaf" style="left:74%; --dx:-34px; background:linear-gradient(135deg,#8bcf9a,#3f9b73); width:16px;height:16px; animation-duration:13s; animation-delay:-4s;"></i>
|
|
|
|
|
+ <i class="leaf" style="left:90%; --dx:24px; background:linear-gradient(135deg,#9BCB8A,#5ab07a); width:12px;height:12px; animation-duration:16s; animation-delay:-11s;"></i>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- ============ 内容层 ============ -->
|
|
|
|
|
+ <div class="relative z-10 flex flex-col flex-1 px-7 pt-16 pb-8">
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 品牌区 -->
|
|
|
|
|
+ <header class="flex flex-col items-center">
|
|
|
|
|
+ <!-- Logo:山形 + 水滴 + 三片叶子 融合 -->
|
|
|
|
|
+ <div class="w-20 h-20 rounded-[22px] bg-white shadow-glass flex items-center justify-center">
|
|
|
|
|
+ <svg viewBox="0 0 64 64" width="54" height="54" aria-label="绿水青山">
|
|
|
|
|
+ <defs>
|
|
|
|
|
+ <linearGradient id="leafGrad" x1="0" y1="0" x2="1" y2="1">
|
|
|
|
|
+ <stop offset="0" stop-color="#7fd08a"/>
|
|
|
|
|
+ <stop offset="1" stop-color="#368F6F"/>
|
|
|
|
|
+ </linearGradient>
|
|
|
|
|
+ <linearGradient id="dropGrad" x1="0" y1="0" x2="0" y2="1">
|
|
|
|
|
+ <stop offset="0" stop-color="#8fd0e0"/>
|
|
|
|
|
+ <stop offset="1" stop-color="#5AB8D0"/>
|
|
|
|
|
+ </linearGradient>
|
|
|
|
|
+ </defs>
|
|
|
|
|
+ <!-- 山形 -->
|
|
|
|
|
+ <path d="M6 46 L24 22 L34 34 L44 20 L58 46 Z" fill="#e8f3ec" stroke="#cfe6d6" stroke-width="1.5" stroke-linejoin="round"/>
|
|
|
|
|
+ <!-- 水滴 -->
|
|
|
|
|
+ <path d="M32 14 C32 14 24 23 24 30 a8 8 0 0 0 16 0 C40 23 32 14 32 14 Z" fill="url(#dropGrad)" opacity="0.92"/>
|
|
|
|
|
+ <!-- 三片叶子 -->
|
|
|
|
|
+ <path d="M32 50 C24 44 20 36 22 28 C30 30 34 38 32 50 Z" fill="url(#leafGrad)"/>
|
|
|
|
|
+ <path d="M32 50 C40 44 44 36 42 28 C34 30 30 38 32 50 Z" fill="url(#leafGrad)" opacity="0.9"/>
|
|
|
|
|
+ <path d="M32 52 C30 42 32 34 38 28 C40 36 38 46 32 52 Z" fill="#2f7f62" opacity="0.55"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <h1 class="mt-5 text-[32px] font-extrabold tracking-[6px] text-brand">绿水青山</h1>
|
|
|
|
|
+ <p class="mt-2 text-sm tracking-[3px] text-mist">与自然同行</p>
|
|
|
|
|
+ </header>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 登录卡片 -->
|
|
|
|
|
+ <section class="glass-card mt-10 px-6 py-7">
|
|
|
|
|
+ <h2 class="text-center text-lg font-bold text-brand">欢迎回来</h2>
|
|
|
|
|
+
|
|
|
|
|
+ <form class="mt-6 space-y-4" id="loginForm" novalidate>
|
|
|
|
|
+ <!-- 账号 -->
|
|
|
|
|
+ <div class="field">
|
|
|
|
|
+ <i class="fa fa-user mr-3"></i>
|
|
|
|
|
+ <input type="text" id="account" name="account" placeholder="请输入账号" autocomplete="username" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 密码 -->
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <div class="field">
|
|
|
|
|
+ <i class="fa fa-lock mr-3"></i>
|
|
|
|
|
+ <input type="password" id="password" name="password" placeholder="请输入密码" autocomplete="current-password" />
|
|
|
|
|
+ <i class="fa fa-eye toggle" id="togglePwd" aria-label="显示密码"></i>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="mt-2 flex justify-end">
|
|
|
|
|
+ <a class="link" href="javascript:void(0)" id="forgot">忘记密码?</a>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 登录按钮 -->
|
|
|
|
|
+ <button type="submit" class="login-btn mt-2" id="loginBtn">登 录</button>
|
|
|
|
|
+ </form>
|
|
|
|
|
+ </section>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 协议 / 其他入口 -->
|
|
|
|
|
+ <p class="mt-6 text-center text-xs text-mist/80">
|
|
|
|
|
+ 登录即代表同意 <span class="link">服务协议</span> 与 <span class="link">隐私政策</span>
|
|
|
|
|
+ </p>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="flex-1"></div>
|
|
|
|
|
+ <p class="mt-6 text-center text-[11px] text-mist/60">v1.0.0 · 绿水青山环境服务</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- ============ 底部山水(低透明度 + 浮动) ============ -->
|
|
|
|
|
+ <svg class="scene scene-bottom float-slower" style="opacity:0.55" viewBox="0 0 400 140" preserveAspectRatio="none" aria-hidden="true">
|
|
|
|
|
+ <path d="M0,140 L0,78 Q70,30 150,78 Q230,26 320,80 Q360,56 400,82 L400,140 Z" fill="#7fb3a0" opacity="0.30"/>
|
|
|
|
|
+ <path d="M0,140 L0,98 Q80,64 160,100 Q250,58 400,100 L400,140 Z" fill="#368F6F" opacity="0.22"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 底部溪流波纹 -->
|
|
|
|
|
+ <svg class="scene scene-bottom float-slow" style="opacity:0.5" viewBox="0 0 400 70" preserveAspectRatio="none" aria-hidden="true">
|
|
|
|
|
+ <path d="M0,26 C60,8 100,44 160,26 S280,8 400,30" fill="none" stroke="#5AB8D0" stroke-width="2" opacity="0.45"/>
|
|
|
|
|
+ <path d="M0,44 C70,26 110,58 180,42 S300,26 400,46" fill="none" stroke="#5AB8D0" stroke-width="1.4" opacity="0.32"/>
|
|
|
|
|
+ <path d="M0,58 C80,46 120,66 200,56 S320,46 400,60" fill="none" stroke="#5AB8D0" stroke-width="1" opacity="0.22"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 底部草叶纹理 / 绿叶点缀 -->
|
|
|
|
|
+ <div class="scene-bottom" style="bottom:14px; z-index:0; opacity:0.8;">
|
|
|
|
|
+ <i class="leaf float-slow" style="top:auto; bottom:6px; left:24px; position:absolute; width:16px;height:16px; background:linear-gradient(135deg,#9BCB8A,#368F6F);"></i>
|
|
|
|
|
+ <i class="leaf float-slower" style="top:auto; bottom:2px; right:30px; position:absolute; width:14px;height:14px; background:linear-gradient(135deg,#8bcf9a,#2f7f62);"></i>
|
|
|
|
|
+ <i class="leaf float-slow" style="top:auto; bottom:22px; right:120px; position:absolute; width:12px;height:12px; background:linear-gradient(135deg,#a9d89c,#5ab07a);"></i>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </main>
|
|
|
|
|
+
|
|
|
|
|
+ <script>
|
|
|
|
|
+ // 密码显隐切换
|
|
|
|
|
+ const pwd = document.getElementById('password')
|
|
|
|
|
+ const toggle = document.getElementById('togglePwd')
|
|
|
|
|
+ toggle.addEventListener('click', () => {
|
|
|
|
|
+ const show = pwd.type === 'password'
|
|
|
|
|
+ pwd.type = show ? 'text' : 'password'
|
|
|
|
|
+ toggle.classList.toggle('fa-eye', !show)
|
|
|
|
|
+ toggle.classList.toggle('fa-eye-slash', show)
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 忘记密码
|
|
|
|
|
+ document.getElementById('forgot').addEventListener('click', () => {
|
|
|
|
|
+ alert('请联系管理员重置密码')
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 登录提交 + 按钮水波纹 + 加载态
|
|
|
|
|
+ const form = document.getElementById('loginForm')
|
|
|
|
|
+ const btn = document.getElementById('loginBtn')
|
|
|
|
|
+
|
|
|
|
|
+ function spawnInk(x, y) {
|
|
|
|
|
+ const ink = document.createElement('span')
|
|
|
|
|
+ ink.className = 'ink'
|
|
|
|
|
+ const size = 120
|
|
|
|
|
+ ink.style.width = ink.style.height = size + 'px'
|
|
|
|
|
+ ink.style.left = (x - size / 2) + 'px'
|
|
|
|
|
+ ink.style.top = (y - size / 2) + 'px'
|
|
|
|
|
+ btn.appendChild(ink)
|
|
|
|
|
+ setTimeout(() => ink.remove(), 600)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ form.addEventListener('submit', (e) => {
|
|
|
|
|
+ e.preventDefault()
|
|
|
|
|
+ const account = document.getElementById('account').value.trim()
|
|
|
|
|
+ if (!account || !pwd.value) {
|
|
|
|
|
+ // 简单校验反馈
|
|
|
|
|
+ btn.animate(
|
|
|
|
|
+ [{ transform: 'translateX(0)' }, { transform: 'translateX(-6px)' }, { transform: 'translateX(6px)' }, { transform: 'translateX(0)' }],
|
|
|
|
|
+ { duration: 240, iterations: 2 }
|
|
|
|
|
+ )
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ const rect = btn.getBoundingClientRect()
|
|
|
|
|
+ // 点击位置水波(若来自键盘则居中)
|
|
|
|
|
+ const x = (e.clientX || 0) ? e.clientX - rect.left : rect.width / 2
|
|
|
|
|
+ const y = (e.clientY || 0) ? e.clientY - rect.top : rect.height / 2
|
|
|
|
|
+ spawnInk(x, y)
|
|
|
|
|
+
|
|
|
|
|
+ btn.disabled = true
|
|
|
|
|
+ const raw = btn.textContent
|
|
|
|
|
+ btn.textContent = '登录中…'
|
|
|
|
|
+ // 模拟登录请求
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ btn.disabled = false
|
|
|
|
|
+ btn.textContent = raw
|
|
|
|
|
+ alert('登录成功,欢迎回来,' + account + '!')
|
|
|
|
|
+ }, 900)
|
|
|
|
|
+ })
|
|
|
|
|
+ </script>
|
|
|
|
|
+</body>
|
|
|
|
|
+</html>
|