Files
ziwei-power/templates/login.html
mac 7999d7700b feat: 紫微·磁场管理打卡系统
- Flomo 风格左右布局(侧边栏+主内容区)
- 日历导航:月视图,三色状态圆点,点击日期快速切换
- 四层修为:立志/责善/改过/勤学
- 勤学 8 个固定预设项目,勾选后显示备注
- 编辑后 1.5 秒自动保存,切换日期静默保存
- Flask + SQLite,端口 5056
2026-06-01 23:11:00 +08:00

260 lines
7.0 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>紫微 · 登录</title>
<style>
:root {
--primary: #4A6CF7;
--primary-light: #EEF1FE;
--primary-dark: #3B5DE7;
--danger: #EF4444;
--bg: #F5F7FB;
--card: #FFFFFF;
--text: #1F2937;
--text-muted: #9CA3AF;
--border: #E5E7EB;
--radius: 12px;
--shadow: 0 4px 24px rgba(74,108,247,0.10);
}
* { margin:0; padding:0; box-sizing:border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', sans-serif;
background: var(--bg);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.login-card {
background: var(--card);
border-radius: 20px;
padding: 48px 40px 40px;
width: 100%;
max-width: 400px;
box-shadow: var(--shadow), 0 1px 3px rgba(0,0,0,0.06);
position: relative;
overflow: hidden;
}
.login-card::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0;
height: 4px;
background: linear-gradient(90deg, var(--primary), #818CF8, var(--primary));
background-size: 200% 100%;
animation: shimmer 3s ease-in-out infinite;
}
@keyframes shimmer {
0%,100% { background-position:0% 50%; }
50% { background-position:100% 50%; }
}
.login-card .logo-row {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
margin-bottom: 4px;
}
.login-card .logo-row .icon-logo { width:28px; height:28px; color:var(--primary); }
.login-card h1 {
font-size: 26px;
font-weight: 700;
color: var(--text);
letter-spacing: -0.5px;
}
.login-card .sub {
text-align: center;
font-size: 13px;
color: var(--text-muted);
margin-bottom: 36px;
line-height: 1.5;
}
.form-group { margin-bottom: 20px; }
.form-group label {
display: block;
font-size: 13px;
font-weight: 600;
color: var(--text);
margin-bottom: 8px;
}
.form-group .input-wrap {
position: relative;
}
.form-group .input-icon {
position: absolute;
left: 14px;
top: 50%;
transform: translateY(-50%);
width: 18px;
height: 18px;
color: var(--text-muted);
pointer-events: none;
}
.form-group input {
width: 100%;
padding: 12px 16px 12px 42px;
border: 1.5px solid var(--border);
border-radius: var(--radius);
font-size: 15px;
font-family: inherit;
background: var(--bg);
color: var(--text);
transition: all 0.2s;
}
.form-group input:focus {
outline: none;
border-color: var(--primary);
background: var(--card);
box-shadow: 0 0 0 3px rgba(74,108,247,0.10);
}
.form-group input::placeholder { color:var(--text-muted); }
.remember-row {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 28px;
font-size: 13px;
color: var(--text-muted);
}
.remember-row input[type="checkbox"] {
width: 16px; height: 16px;
accent-color: var(--primary);
flex-shrink: 0;
}
.btn-login {
width: 100%;
padding: 14px;
border: none;
background: var(--primary);
color: #FFF;
font-size: 16px;
font-weight: 600;
border-radius: var(--radius);
cursor: pointer;
transition: all 0.2s;
font-family: inherit;
letter-spacing: 0.5px;
}
.btn-login:hover {
background: var(--primary-dark);
transform: translateY(-1px);
box-shadow: 0 4px 16px rgba(74,108,247,0.30);
}
.btn-login:active { transform:translateY(0); }
.btn-login.loading { opacity:0.7; pointer-events:none; }
.error-msg {
text-align: center;
color: var(--danger);
font-size: 13px;
margin-top: 16px;
padding: 10px 16px;
background: #FEF2F2;
border-radius: 8px;
display: none;
line-height: 1.5;
}
.error-msg.show { display:block; }
.footer-text {
text-align: center;
font-size: 12px;
color: var(--text-muted);
margin-top: 28px;
}
.toast {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%) translateY(-20px);
padding: 12px 28px;
border-radius: 10px;
font-size: 14px;
font-weight: 600;
z-index: 9999;
opacity: 0;
pointer-events: none;
transition: all 0.3s;
white-space: nowrap;
box-shadow: 0 4px 20px rgba(0,0,0,0.12);
}
.toast.show { opacity:1; transform:translateX(-50%) translateY(0); pointer-events:auto; }
.toast.success { background:#10B981; color:#FFF; }
.toast.error { background:var(--danger); color:#FFF; }
.toast.info { background:var(--primary); color:#FFF; }
</style>
</head>
<body>
{% include "icons.html" %}
<div id="toast" class="toast"></div>
<div class="login-card">
<div class="logo-row">
<svg class="icon-logo"><use href="#icon-bolt"/></svg>
<h1>紫微 · 磁场管理</h1>
</div>
<p class="sub">立志不摇,责善不滥,改过不拖,勤学不辍</p>
<form id="login-form" onsubmit="doLogin(event)">
<div class="form-group">
<label for="username">账号</label>
<div class="input-wrap">
<svg class="input-icon"><use href="#icon-user"/></svg>
<input type="text" id="username" placeholder="请输入账号" autocomplete="username" required autofocus>
</div>
</div>
<div class="form-group">
<label for="password">密码</label>
<div class="input-wrap">
<svg class="input-icon"><use href="#icon-lock"/></svg>
<input type="password" id="password" placeholder="请输入密码" autocomplete="current-password" required>
</div>
</div>
<div class="remember-row">
<input type="checkbox" id="remember" checked>
<label for="remember">记住登录30 天有效)</label>
</div>
<button type="submit" class="btn-login" id="btn-login">登 录</button>
</form>
<div class="error-msg" id="error-msg"></div>
<p class="footer-text">紫微 · 天机阁磁场守护系统</p>
</div>
<script>
(function(){
'use strict';
window.showToast = function(msg, type){
var t = document.getElementById('toast');
t.textContent = msg;
t.className = 'toast ' + (type||'info');
setTimeout(function(){ t.classList.add('show'); }, 10);
setTimeout(function(){ t.classList.remove('show'); }, 2500);
};
window.doLogin = function(e){
e.preventDefault();
var btn = document.getElementById('btn-login');
var err = document.getElementById('error-msg');
btn.classList.add('loading');
btn.textContent = '登录中…';
err.classList.remove('show');
var formData = new FormData();
formData.append('username', document.getElementById('username').value);
formData.append('password', document.getElementById('password').value);
if (document.getElementById('remember').checked) formData.append('remember','on');
fetch('/login', { method:'POST', body:formData })
.then(function(r){ return r.json().then(function(d){ return {status:r.status, body:d}; }); })
.then(function(res){
if (res.body.ok) { window.location.href = res.body.next; }
else { err.textContent = res.body.error||'登录失败,请重试'; err.classList.add('show'); }
})
.catch(function(){ err.textContent='网络错误,请检查连接后重试'; err.classList.add('show'); })
.finally(function(){ btn.classList.remove('loading'); btn.textContent='登 录'; });
};
})();
</script>
</body>
</html>