Files
ewoooc/templates/login.html
ogt 2144ef2102
All checks were successful
CD Pipeline / deploy (push) Successful in 1m11s
fix: lock pchome growth ui copy guardrails
2026-06-26 11:38:04 +08:00

364 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登入 - EwoooC</title>
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='images/logo_circle.svg') }}">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500;700&family=Noto+Sans+TC:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/ewoooc-tokens.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/ewoooc-tokens-v2-alias.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/ewoooc-shell.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/ewoooc-dotmatrix.css') }}">
<style>
* {
box-sizing: border-box;
}
body.login-body {
min-height: 100vh;
margin: 0;
color: var(--momo-text-primary);
font-family: var(--momo-font-family);
background:
radial-gradient(circle at 14px 14px, rgba(172, 92, 58, 0.14) 1.2px, transparent 1.4px) 0 0 / 18px 18px,
var(--momo-bg-page);
}
.login-page {
display: grid;
min-height: 100vh;
grid-template-columns: minmax(0, 1fr) minmax(360px, 440px);
}
.login-brand {
display: flex;
flex-direction: column;
justify-content: space-between;
padding: clamp(2rem, 5vw, 4.5rem);
border-right: 1px solid var(--momo-border-light);
background:
radial-gradient(circle at 18px 18px, rgba(42, 37, 32, 0.08) 1.1px, transparent 1.3px) 0 0 / 22px 22px,
rgba(255, 248, 239, 0.55);
}
.brand-mark {
display: inline-flex;
align-items: center;
gap: 12px;
color: var(--momo-text-primary);
font-family: var(--momo-font-display);
font-size: 1.15rem;
font-weight: 800;
text-decoration: none;
}
.brand-mark__icon {
display: inline-grid;
width: 34px;
height: 34px;
place-items: center;
border: 1px solid var(--momo-text-primary);
border-radius: var(--momo-radius-sm);
background: var(--momo-text-primary);
color: var(--momo-bg-paper);
}
.login-brand h1 {
max-width: 720px;
margin: clamp(4rem, 10vw, 8rem) 0 var(--momo-space-3);
color: var(--momo-text-primary);
font-family: var(--momo-font-display);
font-size: clamp(2.2rem, 5vw, 4.6rem);
font-weight: 850;
letter-spacing: 0;
line-height: 1.02;
}
.login-brand p {
max-width: 620px;
margin: 0;
color: var(--momo-text-secondary);
font-size: 1.02rem;
line-height: 1.8;
}
.login-meta {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: var(--momo-space-6);
}
.login-pill {
display: inline-flex;
align-items: center;
gap: 8px;
min-height: 32px;
padding: 5px 10px;
border: 1px solid var(--momo-border-light);
border-radius: var(--momo-radius-sm);
background: var(--momo-bg-elevated);
color: var(--momo-text-secondary);
font-family: var(--momo-font-mono);
font-size: var(--momo-text-label);
font-weight: 700;
}
.login-panel-wrap {
display: grid;
align-items: center;
padding: clamp(1rem, 4vw, 3rem);
background: var(--momo-bg-page);
}
.login-card {
width: 100%;
border: 1px solid var(--momo-border-light);
border-radius: var(--momo-radius-lg);
background: var(--momo-bg-surface);
overflow: hidden;
}
.login-card__head {
padding: var(--momo-space-5);
border-bottom: 1px solid var(--momo-border-light);
}
.login-card__head span {
display: inline-flex;
align-items: center;
gap: 8px;
margin-bottom: var(--momo-space-2);
color: var(--momo-page-accent-dark, var(--momo-accent-caramel));
font-family: var(--momo-font-mono);
font-size: var(--momo-text-label);
font-weight: 760;
letter-spacing: 0.04em;
}
.login-card__head h2 {
margin: 0;
color: var(--momo-text-primary);
font-family: var(--momo-font-display);
font-size: 1.55rem;
font-weight: 800;
letter-spacing: 0;
}
.login-card__body {
padding: var(--momo-space-5);
}
.form-label {
color: var(--momo-text-primary);
font-family: var(--momo-font-display);
font-weight: 700;
}
.form-control {
min-height: 46px;
border: 1px solid var(--momo-border-light);
border-radius: var(--momo-radius-md);
background: var(--momo-bg-elevated);
color: var(--momo-text-primary);
box-shadow: none;
}
.form-control:focus {
border-color: var(--momo-page-accent-line, rgba(172, 92, 58, 0.35));
box-shadow: 0 0 0 3px rgba(172, 92, 58, 0.12);
}
.password-wrapper {
position: relative;
}
.password-toggle {
position: absolute;
top: 50%;
right: 14px;
color: var(--momo-text-tertiary);
cursor: pointer;
transform: translateY(-50%);
}
.btn-login {
width: 100%;
min-height: 46px;
border: 1px solid var(--momo-text-primary);
border-radius: var(--momo-radius-md);
background: var(--momo-text-primary);
color: var(--momo-bg-paper);
font-family: var(--momo-font-display);
font-weight: 800;
}
.btn-login:hover,
.btn-login:focus {
border-color: var(--momo-page-accent-dark, #7a4a18);
background: var(--momo-page-accent-dark, #7a4a18);
color: var(--momo-bg-paper);
}
.alert-danger {
border: 1px solid var(--momo-danger-border);
border-radius: var(--momo-radius-md);
background: var(--momo-danger-bg);
color: var(--momo-danger-text);
}
.login-footer {
padding: 0 var(--momo-space-5) var(--momo-space-5);
color: var(--momo-text-tertiary);
font-family: var(--momo-font-mono);
font-size: var(--momo-text-label);
}
@media (max-width: 900px) {
.login-page {
grid-template-columns: 1fr;
}
.login-brand {
min-height: auto;
border-right: 0;
border-bottom: 1px solid var(--momo-border-light);
}
.login-brand h1 {
margin-top: var(--momo-space-6);
font-size: clamp(2rem, 12vw, 3.25rem);
}
.login-panel-wrap {
align-items: start;
}
}
@media (max-width: 520px) {
body.login-body {
min-height: 100svh;
}
.login-page {
min-height: 100svh;
}
.login-brand,
.login-panel-wrap {
padding: var(--momo-space-4);
}
.login-brand p {
font-size: 0.95rem;
line-height: 1.7;
}
.login-meta {
display: grid;
grid-template-columns: 1fr;
}
.login-card__head,
.login-card__body {
padding: var(--momo-space-4);
}
}
</style>
</head>
<body class="momo-v2-body login-body" data-page-group="system">
<main class="login-page">
<section class="login-brand" aria-label="EwoooC 登入品牌區">
<div>
<a class="brand-mark" href="/login">
<span class="brand-mark__icon"><i class="fas fa-grip"></i></span>
<span>EwoooC</span>
</a>
<h1>營運數據工作台</h1>
<p>登入後先看 PChome 業績、價差、缺貨與 AI 建議。</p>
<div class="login-meta">
<span class="login-pill"><i class="fas fa-shield-halved"></i>安全登入</span>
<span class="login-pill"><i class="fas fa-clock"></i>工作階段</span>
<span class="login-pill"><i class="fas fa-database"></i>資料服務</span>
</div>
</div>
<div class="login-footer d-none d-md-block">
EwoooC V{{ config.SYSTEM_VERSION if config is defined else '' }}
</div>
</section>
<section class="login-panel-wrap" aria-label="登入表單">
<div class="login-card">
<div class="login-card__head">
<span><i class="fas fa-lock"></i> Secure Access</span>
<h2>登入系統</h2>
</div>
<div class="login-card__body">
{% if error %}
<div class="alert alert-danger" role="alert">
<i class="fas fa-circle-exclamation me-2"></i>
{{ error }}
</div>
{% endif %}
<form method="POST" action="{{ url_for('login') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="mb-4">
<label for="password" class="form-label">密碼</label>
<div class="password-wrapper">
<input
type="password"
class="form-control"
id="password"
name="password"
placeholder="請輸入密碼"
required
autofocus
autocomplete="current-password"
>
<i class="fas fa-eye password-toggle" onclick="togglePassword()" aria-hidden="true"></i>
</div>
</div>
<button type="submit" class="btn btn-login">
<i class="fas fa-right-to-bracket me-2"></i>登入
</button>
</form>
</div>
<div class="login-footer d-md-none">
EwoooC V{{ config.SYSTEM_VERSION if config is defined else '' }}
</div>
</div>
</section>
</main>
<script>
function togglePassword() {
const passwordInput = document.getElementById('password');
const toggleIcon = document.querySelector('.password-toggle');
if (!passwordInput || !toggleIcon) return;
if (passwordInput.type === 'password') {
passwordInput.type = 'text';
toggleIcon.classList.remove('fa-eye');
toggleIcon.classList.add('fa-eye-slash');
} else {
passwordInput.type = 'password';
toggleIcon.classList.remove('fa-eye-slash');
toggleIcon.classList.add('fa-eye');
}
}
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('password')?.focus();
});
</script>
</body>
</html>