login-page.html 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
  6. <title>绿水青山 · 登录</title>
  7. <!-- Tailwind CSS(CDN,国内可访问) -->
  8. <script src="https://cdn.tailwindcss.com"></script>
  9. <!-- Font Awesome 图标(国内 jsdelivr 镜像) -->
  10. <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" />
  11. <script>
  12. // Tailwind 主题扩展:品牌色 / 嫩芽色 / 山水色
  13. tailwind.config = {
  14. theme: {
  15. extend: {
  16. colors: {
  17. brand: '#368F6F', // 主标题 / 按钮起点(青山绿)
  18. stream: '#5AB8D0', // 按钮终点(溪流水蓝)
  19. mist: '#709484', // 副标题(晨雾灰绿)
  20. sprout: '#9BCB8A', // 嫩芽(卡片边框)
  21. gold: '#E9C46A', // 金色微光
  22. },
  23. fontFamily: {
  24. sans: ['"PingFang SC"', '"Microsoft YaHei"', '"Hiragino Sans GB"', 'system-ui', 'sans-serif'],
  25. },
  26. boxShadow: {
  27. glass: '0 18px 50px -12px rgba(54, 143, 111, 0.28)',
  28. btn: '0 14px 30px -8px rgba(54, 143, 111, 0.45)',
  29. },
  30. },
  31. },
  32. }
  33. </script>
  34. <style>
  35. /* ============ 基础 ============ */
  36. html, body { height: 100%; }
  37. body {
  38. margin: 0;
  39. -webkit-tap-highlight-color: transparent;
  40. font-family: "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;
  41. background: #eef4f1;
  42. }
  43. /* 手机外壳:移动端竖屏、居中 */
  44. .phone {
  45. position: relative;
  46. width: 100%;
  47. max-width: 28rem; /* max-w-md */
  48. min-height: 100vh;
  49. margin: 0 auto; /* mx-auto */
  50. overflow: hidden;
  51. background:
  52. linear-gradient(180deg, #f7fbf9 0%, #eef6f1 38%, #e7f0ec 100%);
  53. isolation: isolate;
  54. }
  55. /* ============ 顶部浅雾渐变天幕 ============ */
  56. .sky {
  57. position: absolute;
  58. inset: 0 0 auto 0;
  59. height: 46%;
  60. background:
  61. linear-gradient(160deg, #fbfefd 0%, #eaf4ef 45%, #dbe8ec 100%);
  62. z-index: -3;
  63. }
  64. /* 金色微光(标题后方的柔光晕) */
  65. .gold-glow {
  66. position: absolute;
  67. top: 7%;
  68. left: 50%;
  69. width: 260px;
  70. height: 260px;
  71. transform: translateX(-50%);
  72. background: radial-gradient(circle, rgba(233,196,106,0.40) 0%, rgba(233,196,106,0.12) 42%, rgba(233,196,106,0) 70%);
  73. filter: blur(6px);
  74. z-index: -2;
  75. animation: breathe 6s ease-in-out infinite;
  76. }
  77. @keyframes breathe {
  78. 0%, 100% { opacity: 0.7; transform: translateX(-50%) scale(1); }
  79. 50% { opacity: 1; transform: translateX(-50%) scale(1.08); }
  80. }
  81. /* 云雾朦胧斑 */
  82. .mist-blob {
  83. position: absolute;
  84. border-radius: 9999px;
  85. filter: blur(26px);
  86. z-index: -2;
  87. opacity: 0.55;
  88. animation: drift 14s ease-in-out infinite;
  89. }
  90. .mist-1 { width: 200px; height: 120px; left: -60px; top: 6%; background: rgba(255,255,255,0.9); }
  91. .mist-2 { width: 240px; height: 140px; right: -90px; top: 14%; background: rgba(214,235,230,0.9); animation-delay: -4s; }
  92. .mist-3 { width: 180px; height: 100px; left: 30%; top: 2%; background: rgba(255,255,255,0.7); animation-delay: -8s; }
  93. @keyframes drift {
  94. 0%, 100% { transform: translate3d(0, 0, 0); }
  95. 50% { transform: translate3d(18px, 10px, 0); }
  96. }
  97. /* ============ 远山剪影 / 溪流(SVG 共用) ============ */
  98. .scene { position: absolute; left: 0; right: 0; width: 100%; pointer-events: none; }
  99. .scene-top { top: 30%; z-index: -1; opacity: 0.5; }
  100. .scene-bottom { bottom: 0; z-index: -1; }
  101. /* ============ 飘落绿叶粒子 ============ */
  102. .leaf {
  103. position: absolute;
  104. top: -40px;
  105. width: 18px;
  106. height: 18px;
  107. border-radius: 2px 16px 2px 16px;
  108. transform: rotate(45deg);
  109. z-index: 5;
  110. opacity: 0.85;
  111. animation: fall linear infinite;
  112. }
  113. @keyframes fall {
  114. 0% { transform: translate3d(0, -40px, 0) rotate(0deg); opacity: 0; }
  115. 10% { opacity: 0.9; }
  116. 90% { opacity: 0.8; }
  117. 100% { transform: translate3d(var(--dx, -30px), 105vh, 0) rotate(540deg); opacity: 0; }
  118. }
  119. /* ============ 磨砂玻璃卡片 ============ */
  120. .glass-card {
  121. background: rgba(255, 255, 255, 0.80);
  122. -webkit-backdrop-filter: blur(12px);
  123. backdrop-filter: blur(12px);
  124. border: 1px solid rgba(155, 203, 138, 0.30);
  125. border-radius: 24px;
  126. box-shadow: 0 18px 50px -12px rgba(54, 143, 111, 0.28);
  127. }
  128. /* 输入框 */
  129. .field {
  130. display: flex;
  131. align-items: center;
  132. height: 52px;
  133. padding: 0 16px;
  134. border-radius: 16px;
  135. background: rgba(246, 250, 247, 0.9);
  136. border: 1px solid rgba(155, 203, 138, 0.35);
  137. transition: border-color .2s ease, box-shadow .2s ease, background .2s ease;
  138. }
  139. .field:focus-within {
  140. background: #ffffff;
  141. border-color: #368F6F;
  142. box-shadow: 0 0 0 4px rgba(54, 143, 111, 0.12);
  143. }
  144. .field input {
  145. flex: 1;
  146. height: 100%;
  147. border: none;
  148. outline: none;
  149. background: transparent;
  150. font-size: 15px;
  151. color: #26403a;
  152. }
  153. .field input::placeholder { color: #9fb4aa; }
  154. .field .fa { width: 20px; text-align: center; color: #7d978c; }
  155. .field .toggle { color: #9fb4aa; cursor: pointer; padding: 6px; }
  156. /* ============ 登录按钮(渐变 + 水波纹边缘) ============ */
  157. .login-btn {
  158. position: relative;
  159. height: 56px;
  160. width: 100%;
  161. border: none;
  162. border-radius: 24px;
  163. color: #ffffff;
  164. font-size: 17px;
  165. font-weight: 700;
  166. letter-spacing: 4px;
  167. cursor: pointer;
  168. overflow: hidden;
  169. background: linear-gradient(135deg, #368F6F 0%, #5AB8D0 100%);
  170. box-shadow: 0 14px 30px -8px rgba(54, 143, 111, 0.45);
  171. transition: transform .15s ease, box-shadow .2s ease, opacity .2s ease;
  172. }
  173. .login-btn:active { transform: scale(0.985); }
  174. .login-btn:disabled { opacity: 0.7; cursor: default; }
  175. /* 持续呼吸的水波纹边缘 */
  176. .login-btn::before {
  177. content: "";
  178. position: absolute;
  179. inset: -3px;
  180. border-radius: 26px;
  181. border: 2px solid rgba(90, 184, 208, 0.55);
  182. animation: ripple 2.6s ease-out infinite;
  183. pointer-events: none;
  184. }
  185. @keyframes ripple {
  186. 0% { transform: scale(0.96); opacity: 0.7; }
  187. 100% { transform: scale(1.08); opacity: 0; }
  188. }
  189. /* 点击水波圆斑 */
  190. .login-btn .ink {
  191. position: absolute;
  192. border-radius: 50%;
  193. transform: scale(0);
  194. background: rgba(255, 255, 255, 0.45);
  195. animation: ink .6s ease-out;
  196. pointer-events: none;
  197. }
  198. @keyframes ink {
  199. to { transform: scale(2.6); opacity: 0; }
  200. }
  201. /* ============ 底部草叶浮动 ============ */
  202. .float-slow { animation: float 6s ease-in-out infinite; }
  203. .float-slower { animation: float 8s ease-in-out infinite; animation-delay: -2s; }
  204. @keyframes float {
  205. 0%, 100% { transform: translateY(0); }
  206. 50% { transform: translateY(-8px); }
  207. }
  208. /* 链接 */
  209. .link { color: #368F6F; font-size: 13px; }
  210. .link:active { opacity: 0.7; }
  211. </style>
  212. </head>
  213. <body>
  214. <!-- ============ 手机容器 ============ -->
  215. <main class="phone flex flex-col">
  216. <!-- 天幕 / 光 / 雾(装饰层) -->
  217. <div class="sky"></div>
  218. <div class="gold-glow"></div>
  219. <div class="mist-blob mist-1"></div>
  220. <div class="mist-blob mist-2"></div>
  221. <div class="mist-blob mist-3"></div>
  222. <!-- 顶部远山剪影 -->
  223. <svg class="scene scene-top" viewBox="0 0 400 120" preserveAspectRatio="none" aria-hidden="true">
  224. <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"/>
  225. <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"/>
  226. </svg>
  227. <!-- 顶部溪流曲线 -->
  228. <svg class="scene scene-top" style="top:40%; opacity:0.45" viewBox="0 0 400 60" preserveAspectRatio="none" aria-hidden="true">
  229. <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"/>
  230. <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"/>
  231. </svg>
  232. <!-- 飘落绿叶粒子 -->
  233. <i class="leaf" style="left:8%; --dx:-40px; background:linear-gradient(135deg,#7cc68a,#368F6F); animation-duration:11s; animation-delay:-1s;"></i>
  234. <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>
  235. <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>
  236. <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>
  237. <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>
  238. <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>
  239. <!-- ============ 内容层 ============ -->
  240. <div class="relative z-10 flex flex-col flex-1 px-7 pt-16 pb-8">
  241. <!-- 品牌区 -->
  242. <header class="flex flex-col items-center">
  243. <!-- Logo:山形 + 水滴 + 三片叶子 融合 -->
  244. <div class="w-20 h-20 rounded-[22px] bg-white shadow-glass flex items-center justify-center">
  245. <svg viewBox="0 0 64 64" width="54" height="54" aria-label="绿水青山">
  246. <defs>
  247. <linearGradient id="leafGrad" x1="0" y1="0" x2="1" y2="1">
  248. <stop offset="0" stop-color="#7fd08a"/>
  249. <stop offset="1" stop-color="#368F6F"/>
  250. </linearGradient>
  251. <linearGradient id="dropGrad" x1="0" y1="0" x2="0" y2="1">
  252. <stop offset="0" stop-color="#8fd0e0"/>
  253. <stop offset="1" stop-color="#5AB8D0"/>
  254. </linearGradient>
  255. </defs>
  256. <!-- 山形 -->
  257. <path d="M6 46 L24 22 L34 34 L44 20 L58 46 Z" fill="#e8f3ec" stroke="#cfe6d6" stroke-width="1.5" stroke-linejoin="round"/>
  258. <!-- 水滴 -->
  259. <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"/>
  260. <!-- 三片叶子 -->
  261. <path d="M32 50 C24 44 20 36 22 28 C30 30 34 38 32 50 Z" fill="url(#leafGrad)"/>
  262. <path d="M32 50 C40 44 44 36 42 28 C34 30 30 38 32 50 Z" fill="url(#leafGrad)" opacity="0.9"/>
  263. <path d="M32 52 C30 42 32 34 38 28 C40 36 38 46 32 52 Z" fill="#2f7f62" opacity="0.55"/>
  264. </svg>
  265. </div>
  266. <h1 class="mt-5 text-[32px] font-extrabold tracking-[6px] text-brand">绿水青山</h1>
  267. <p class="mt-2 text-sm tracking-[3px] text-mist">与自然同行</p>
  268. </header>
  269. <!-- 登录卡片 -->
  270. <section class="glass-card mt-10 px-6 py-7">
  271. <h2 class="text-center text-lg font-bold text-brand">欢迎回来</h2>
  272. <form class="mt-6 space-y-4" id="loginForm" novalidate>
  273. <!-- 账号 -->
  274. <div class="field">
  275. <i class="fa fa-user mr-3"></i>
  276. <input type="text" id="account" name="account" placeholder="请输入账号" autocomplete="username" />
  277. </div>
  278. <!-- 密码 -->
  279. <div>
  280. <div class="field">
  281. <i class="fa fa-lock mr-3"></i>
  282. <input type="password" id="password" name="password" placeholder="请输入密码" autocomplete="current-password" />
  283. <i class="fa fa-eye toggle" id="togglePwd" aria-label="显示密码"></i>
  284. </div>
  285. <div class="mt-2 flex justify-end">
  286. <a class="link" href="javascript:void(0)" id="forgot">忘记密码?</a>
  287. </div>
  288. </div>
  289. <!-- 登录按钮 -->
  290. <button type="submit" class="login-btn mt-2" id="loginBtn">登 录</button>
  291. </form>
  292. </section>
  293. <!-- 协议 / 其他入口 -->
  294. <p class="mt-6 text-center text-xs text-mist/80">
  295. 登录即代表同意 <span class="link">服务协议</span> 与 <span class="link">隐私政策</span>
  296. </p>
  297. <div class="flex-1"></div>
  298. <p class="mt-6 text-center text-[11px] text-mist/60">v1.0.0 · 绿水青山环境服务</p>
  299. </div>
  300. <!-- ============ 底部山水(低透明度 + 浮动) ============ -->
  301. <svg class="scene scene-bottom float-slower" style="opacity:0.55" viewBox="0 0 400 140" preserveAspectRatio="none" aria-hidden="true">
  302. <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"/>
  303. <path d="M0,140 L0,98 Q80,64 160,100 Q250,58 400,100 L400,140 Z" fill="#368F6F" opacity="0.22"/>
  304. </svg>
  305. <!-- 底部溪流波纹 -->
  306. <svg class="scene scene-bottom float-slow" style="opacity:0.5" viewBox="0 0 400 70" preserveAspectRatio="none" aria-hidden="true">
  307. <path d="M0,26 C60,8 100,44 160,26 S280,8 400,30" fill="none" stroke="#5AB8D0" stroke-width="2" opacity="0.45"/>
  308. <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"/>
  309. <path d="M0,58 C80,46 120,66 200,56 S320,46 400,60" fill="none" stroke="#5AB8D0" stroke-width="1" opacity="0.22"/>
  310. </svg>
  311. <!-- 底部草叶纹理 / 绿叶点缀 -->
  312. <div class="scene-bottom" style="bottom:14px; z-index:0; opacity:0.8;">
  313. <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>
  314. <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>
  315. <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>
  316. </div>
  317. </main>
  318. <script>
  319. // 密码显隐切换
  320. const pwd = document.getElementById('password')
  321. const toggle = document.getElementById('togglePwd')
  322. toggle.addEventListener('click', () => {
  323. const show = pwd.type === 'password'
  324. pwd.type = show ? 'text' : 'password'
  325. toggle.classList.toggle('fa-eye', !show)
  326. toggle.classList.toggle('fa-eye-slash', show)
  327. })
  328. // 忘记密码
  329. document.getElementById('forgot').addEventListener('click', () => {
  330. alert('请联系管理员重置密码')
  331. })
  332. // 登录提交 + 按钮水波纹 + 加载态
  333. const form = document.getElementById('loginForm')
  334. const btn = document.getElementById('loginBtn')
  335. function spawnInk(x, y) {
  336. const ink = document.createElement('span')
  337. ink.className = 'ink'
  338. const size = 120
  339. ink.style.width = ink.style.height = size + 'px'
  340. ink.style.left = (x - size / 2) + 'px'
  341. ink.style.top = (y - size / 2) + 'px'
  342. btn.appendChild(ink)
  343. setTimeout(() => ink.remove(), 600)
  344. }
  345. form.addEventListener('submit', (e) => {
  346. e.preventDefault()
  347. const account = document.getElementById('account').value.trim()
  348. if (!account || !pwd.value) {
  349. // 简单校验反馈
  350. btn.animate(
  351. [{ transform: 'translateX(0)' }, { transform: 'translateX(-6px)' }, { transform: 'translateX(6px)' }, { transform: 'translateX(0)' }],
  352. { duration: 240, iterations: 2 }
  353. )
  354. return
  355. }
  356. const rect = btn.getBoundingClientRect()
  357. // 点击位置水波(若来自键盘则居中)
  358. const x = (e.clientX || 0) ? e.clientX - rect.left : rect.width / 2
  359. const y = (e.clientY || 0) ? e.clientY - rect.top : rect.height / 2
  360. spawnInk(x, y)
  361. btn.disabled = true
  362. const raw = btn.textContent
  363. btn.textContent = '登录中…'
  364. // 模拟登录请求
  365. setTimeout(() => {
  366. btn.disabled = false
  367. btn.textContent = raw
  368. alert('登录成功,欢迎回来,' + account + '!')
  369. }, 900)
  370. })
  371. </script>
  372. </body>
  373. </html>