Redesign the login and login-result pages within the existing dark glass palette: layered star-dot/grid/gradient background, gradient hairline panel borders, gradient headline text, primary/ghost button split, and tri-state status dots (ok/err/busy). Fix zero vertical spacing inside the login form (the form element had no gap between fields and the submit button).
This commit is contained in:
+232
-40
@@ -849,46 +849,207 @@ var loginTemplate = template.Must(template.New("login").Parse(`<!doctype html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="color-scheme" content="dark">
|
||||
<title>湛卢代理登录</title>
|
||||
<style>
|
||||
:root { color-scheme: light dark; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
|
||||
body { margin: 0; min-height: 100vh; display: grid; place-items: center; background: radial-gradient(circle at top left, #dde7ff, transparent 34rem), linear-gradient(135deg, #101828, #1f2937); color: #e5e7eb; }
|
||||
main { width: min(760px, calc(100vw - 32px)); display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: stretch; }
|
||||
.hero, form { border: 1px solid rgba(255,255,255,.14); background: rgba(15,23,42,.78); backdrop-filter: blur(18px); border-radius: 24px; box-shadow: 0 24px 80px rgba(0,0,0,.28); }
|
||||
.hero { padding: 30px; display: flex; flex-direction: column; justify-content: space-between; }
|
||||
h1 { margin: 0; font-size: clamp(28px, 4vw, 44px); letter-spacing: -0.04em; }
|
||||
p { color: #b6c2d9; line-height: 1.7; }
|
||||
code { color: #bfdbfe; word-break: break-all; }
|
||||
.login-card { padding: 28px; display: grid; gap: 16px; border: 1px solid rgba(255,255,255,.14); background: rgba(15,23,42,.78); backdrop-filter: blur(18px); border-radius: 24px; box-shadow: 0 24px 80px rgba(0,0,0,.28); }
|
||||
label { display: grid; gap: 8px; font-size: 14px; color: #cbd5e1; }
|
||||
input, textarea { width: 100%; box-sizing: border-box; border: 1px solid rgba(148,163,184,.35); border-radius: 14px; padding: 12px 14px; background: rgba(2,6,23,.55); color: #f8fafc; outline: none; font: inherit; }
|
||||
input:focus, textarea:focus { border-color: #60a5fa; box-shadow: 0 0 0 4px rgba(96,165,250,.16); }
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--bg: #0b1220;
|
||||
--panel: rgba(15, 23, 42, .82);
|
||||
--line: rgba(255, 255, 255, .12);
|
||||
--line-strong: rgba(255, 255, 255, .2);
|
||||
--accent-1: #3b82f6;
|
||||
--accent-2: #8b5cf6;
|
||||
--ink: #f8fafc;
|
||||
--body: #b6c2d9;
|
||||
--muted: #8ba0b8;
|
||||
--ok: #34d399;
|
||||
--err: #f87171;
|
||||
--radius-lg: 24px;
|
||||
--radius-md: 14px;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0; min-height: 100vh;
|
||||
display: grid; place-items: center;
|
||||
padding: 24px 16px;
|
||||
color: var(--ink);
|
||||
background:
|
||||
radial-gradient(1.5px 1.5px at 12% 20%, rgba(255,255,255,.22), transparent 55%),
|
||||
radial-gradient(1.5px 1.5px at 78% 14%, rgba(255,255,255,.18), transparent 55%),
|
||||
radial-gradient(1.5px 1.5px at 88% 68%, rgba(255,255,255,.16), transparent 55%),
|
||||
radial-gradient(1.5px 1.5px at 26% 82%, rgba(255,255,255,.14), transparent 55%),
|
||||
radial-gradient(1.5px 1.5px at 58% 92%, rgba(255,255,255,.12), transparent 55%),
|
||||
linear-gradient(rgba(255,255,255,.022) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(255,255,255,.022) 1px, transparent 1px),
|
||||
radial-gradient(60rem 42rem at 12% -8%, rgba(59,130,246,.16), transparent 60%),
|
||||
radial-gradient(50rem 36rem at 105% 110%, rgba(139,92,246,.14), transparent 60%),
|
||||
linear-gradient(160deg, #0b1220 0%, #111a2e 55%, #0e1626 100%);
|
||||
background-size: auto, auto, auto, auto, auto, 44px 44px, 44px 44px, auto, auto, auto;
|
||||
animation: fade-in .5s ease both;
|
||||
}
|
||||
@keyframes fade-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
|
||||
main {
|
||||
width: min(820px, 100%);
|
||||
display: grid; grid-template-columns: 1fr 1fr; gap: 28px; align-items: stretch;
|
||||
}
|
||||
.panel {
|
||||
position: relative;
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1px;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,.2), rgba(255,255,255,.05) 38%, rgba(255,255,255,.09));
|
||||
box-shadow: 0 24px 80px rgba(0,0,0,.42), inset 0 1px 0 rgba(255,255,255,.08);
|
||||
}
|
||||
.panel-inner {
|
||||
height: 100%;
|
||||
border-radius: calc(var(--radius-lg) - 1px);
|
||||
background: var(--panel);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
}
|
||||
.panel::before {
|
||||
content: "";
|
||||
position: absolute; inset: 0; border-radius: var(--radius-lg);
|
||||
padding: 1px;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,.14), transparent 30%);
|
||||
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
|
||||
-webkit-mask-composite: xor;
|
||||
mask-composite: exclude;
|
||||
pointer-events: none;
|
||||
}
|
||||
.hero { padding: 34px 30px; display: flex; flex-direction: column; justify-content: space-between; gap: 32px; }
|
||||
.eyebrow {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
font-size: 12px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
.eyebrow::before {
|
||||
content: ""; width: 22px; height: 1.5px;
|
||||
background: linear-gradient(90deg, var(--accent-1), var(--accent-2));
|
||||
}
|
||||
h1 {
|
||||
margin: 14px 0 0;
|
||||
font-size: clamp(30px, 4.4vw, 46px);
|
||||
font-weight: 800; letter-spacing: -0.035em; line-height: 1.08;
|
||||
background: linear-gradient(92deg, #f8fafc 20%, #bfdbfe 62%, #c4b5fd 100%);
|
||||
-webkit-background-clip: text; background-clip: text;
|
||||
-webkit-text-fill-color: transparent; color: transparent;
|
||||
}
|
||||
h2 { margin: 0; font-size: 20px; font-weight: 700; letter-spacing: -0.01em; }
|
||||
p { margin: 0; color: var(--body); line-height: 1.7; font-size: 15px; }
|
||||
.hero p { max-width: 34ch; }
|
||||
code {
|
||||
font-family: "SF Mono", ui-monospace, "Cascadia Code", Consolas, monospace;
|
||||
font-size: 12.5px; color: #bfdbfe; word-break: break-all;
|
||||
}
|
||||
.cred-path { padding: 12px 14px; border-radius: 12px; border: 1px solid var(--line); background: rgba(2,6,23,.5); }
|
||||
.cred-path .label { display: block; font-size: 11.5px; letter-spacing: .08em; color: var(--muted); margin-bottom: 6px; }
|
||||
.login-card { padding: 30px 28px; display: grid; gap: 16px; align-content: start; }
|
||||
.login-card form { display: grid; gap: 18px; }
|
||||
.login-card form .btn-primary { width: 100%; margin-top: 2px; }
|
||||
.login-card p { font-size: 13.5px; }
|
||||
label { display: grid; gap: 8px; font-size: 13.5px; font-weight: 500; color: #cbd5e1; }
|
||||
input, textarea {
|
||||
width: 100%;
|
||||
border: 1px solid rgba(148,163,184,.3);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 13px 14px;
|
||||
background: rgba(2,6,23,.55);
|
||||
color: var(--ink);
|
||||
outline: none; font: inherit;
|
||||
transition: border-color .18s ease, box-shadow .18s ease, background .18s ease;
|
||||
}
|
||||
input::placeholder { color: #64748b; }
|
||||
input:hover { border-color: rgba(148,163,184,.5); }
|
||||
input:focus, textarea:focus {
|
||||
border-color: var(--accent-1);
|
||||
box-shadow: 0 0 0 4px rgba(96,165,250,.16);
|
||||
background: rgba(2,6,23,.7);
|
||||
}
|
||||
.row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
||||
.login-button { display: block; text-align: center; text-decoration: none; border: 0; border-radius: 14px; padding: 14px 16px; background: linear-gradient(135deg, #3b82f6, #8b5cf6); color: white; font-weight: 700; cursor: pointer; font: inherit; }
|
||||
.login-button:hover { filter: brightness(1.08); }
|
||||
.status { min-height: 22px; color: #93c5fd; }
|
||||
.muted { font-size: 13px; color: #94a3b8; }
|
||||
@media (max-width: 760px) { main { grid-template-columns: 1fr; padding: 18px 0; } .row { grid-template-columns: 1fr; } }
|
||||
.btn {
|
||||
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
|
||||
border: 0; border-radius: var(--radius-md);
|
||||
padding: 14px 16px;
|
||||
font: inherit; font-weight: 700; font-size: 15px;
|
||||
cursor: pointer; text-decoration: none; color: #fff;
|
||||
transition: filter .15s ease, transform .06s ease, box-shadow .15s ease, background .15s ease, border-color .15s ease;
|
||||
}
|
||||
.btn:focus-visible { outline: 2px solid var(--accent-1); outline-offset: 2px; }
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, var(--accent-1), var(--accent-2));
|
||||
box-shadow: 0 10px 24px -10px rgba(99,102,241,.55);
|
||||
}
|
||||
.btn-primary:hover { filter: brightness(1.1); box-shadow: 0 12px 28px -10px rgba(99,102,241,.7); }
|
||||
.btn-primary:active { transform: translateY(1px); }
|
||||
.btn-primary:disabled { filter: saturate(.5) brightness(.8); cursor: not-allowed; box-shadow: none; }
|
||||
.btn-ghost {
|
||||
background: transparent;
|
||||
border: 1px solid rgba(148,163,184,.35);
|
||||
color: #bfdbfe; font-weight: 600;
|
||||
}
|
||||
.btn-ghost:hover { border-color: var(--accent-1); color: #dbeafe; background: rgba(96,165,250,.08); }
|
||||
.btn-ghost:active { transform: translateY(1px); }
|
||||
.btn-ghost:disabled { opacity: .55; cursor: not-allowed; }
|
||||
.status {
|
||||
min-height: 22px;
|
||||
font-size: 13.5px; line-height: 1.6;
|
||||
color: var(--muted);
|
||||
display: flex; align-items: flex-start; gap: 7px;
|
||||
}
|
||||
.status::before {
|
||||
content: ""; flex: none; width: 7px; height: 7px; border-radius: 50%;
|
||||
margin-top: 6px;
|
||||
background: currentColor;
|
||||
box-shadow: 0 0 0 3px transparent;
|
||||
}
|
||||
.status[data-state="ok"] { color: var(--ok); }
|
||||
.status[data-state="err"] { color: var(--err); }
|
||||
.status[data-state="busy"] { color: #93c5fd; }
|
||||
.status[data-state="busy"]::before { animation: pulse 1.1s ease-in-out infinite; }
|
||||
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
|
||||
.muted { font-size: 12.5px; color: var(--muted); line-height: 1.7; }
|
||||
.muted a, .link { color: #93c5fd; text-decoration: none; }
|
||||
.link:hover { text-decoration: underline; }
|
||||
.logout { border: 0; background: none; padding: 0; font: inherit; font-size: 12.5px; color: #93c5fd; cursor: pointer; }
|
||||
.logout:hover { text-decoration: underline; }
|
||||
.spacer { flex: 1; }
|
||||
@media (max-width: 760px) {
|
||||
body { place-items: start center; padding-top: 20px; }
|
||||
main { grid-template-columns: 1fr; gap: 18px; }
|
||||
.row { grid-template-columns: 1fr; }
|
||||
.hero { padding: 28px 24px; }
|
||||
.login-card { padding: 26px 22px; }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
body { animation: none; }
|
||||
.status[data-state="busy"]::before { animation: none; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<section class="hero">
|
||||
<section class="panel hero">
|
||||
<div>
|
||||
<div class="eyebrow">Zhanlu Proxy</div>
|
||||
<h1>湛卢代理登录</h1>
|
||||
<p>输入手机号获取验证码,按插件默认的移动云登录接口换取凭据和模型 API Key。服务会保存凭据,后续 OpenAI 兼容接口自动使用。</p>
|
||||
</div>
|
||||
<div class="muted">保存位置:<br><code>{{.CredentialsPath}}</code></div>
|
||||
<div class="cred-path">
|
||||
<span class="label">凭据保存位置</span>
|
||||
<code>{{.CredentialsPath}}</code>
|
||||
</div>
|
||||
</section>
|
||||
<section class="login-card">
|
||||
<section class="panel login-card">
|
||||
{{if not .AdminMode}}
|
||||
<h2>管理登录</h2>
|
||||
<p>请输入服务环境变量 <code>ZHANLU_LOGIN_PASSWORD</code> 配置的管理密码。</p>
|
||||
<form id="password-form">
|
||||
<label>登录密码<input name="password" type="password" autocomplete="current-password" placeholder="请输入服务访问密码" required></label>
|
||||
<button class="login-button" type="submit">进入登录管理</button>
|
||||
<button class="btn btn-primary" type="submit">进入登录管理</button>
|
||||
</form>
|
||||
<div class="status" id="status">需要登录后才能管理湛卢凭据。</div>
|
||||
<div class="status" id="status" data-state="busy">需要登录后才能管理湛卢凭据。</div>
|
||||
{{else}}
|
||||
<h2>手机号验证码登录</h2>
|
||||
<p>手机号和一次性 secret 会按插件逻辑用 RSA 加密后提交到移动云公网接口。</p>
|
||||
@@ -897,13 +1058,13 @@ var loginTemplate = template.Must(template.New("login").Parse(`<!doctype html>
|
||||
<label>验证码
|
||||
<div class="row">
|
||||
<input name="code" inputmode="numeric" autocomplete="one-time-code" placeholder="6 位验证码" required>
|
||||
<button class="login-button" id="code-button" type="button">获取验证码</button>
|
||||
<button class="btn btn-ghost" id="code-button" type="button">获取验证码</button>
|
||||
</div>
|
||||
</label>
|
||||
<button class="login-button" type="submit">登录并保存凭据</button>
|
||||
<button class="btn btn-primary" type="submit">登录并保存凭据</button>
|
||||
</form>
|
||||
<div class="status" id="status">正在检查登录状态...</div>
|
||||
<div class="muted">凭据保存到 JSON;验证码本身不会保存。{{if .PasswordEnabled}} <button id="logout-button" type="button">退出管理登录</button>{{end}}</div>
|
||||
<div class="status" id="status" data-state="busy">正在检查登录状态...</div>
|
||||
<div class="muted">凭据保存到 JSON;验证码本身不会保存。{{if .PasswordEnabled}} <button class="logout" id="logout-button" type="button">退出管理登录</button>{{end}}</div>
|
||||
{{end}}
|
||||
</section>
|
||||
</main>
|
||||
@@ -917,8 +1078,9 @@ var loginTemplate = template.Must(template.New("login").Parse(`<!doctype html>
|
||||
let countdown = 0;
|
||||
let countdownTimer = null;
|
||||
|
||||
function setStatus(text) {
|
||||
function setStatus(text, state) {
|
||||
statusEl.textContent = text;
|
||||
statusEl.dataset.state = state || '';
|
||||
}
|
||||
|
||||
function startCountdown() {
|
||||
@@ -942,10 +1104,10 @@ var loginTemplate = template.Must(template.New("login").Parse(`<!doctype html>
|
||||
event.preventDefault();
|
||||
const password = passwordForm.password.value;
|
||||
if (!password) {
|
||||
setStatus('请输入登录密码');
|
||||
setStatus('请输入登录密码', 'err');
|
||||
return;
|
||||
}
|
||||
setStatus('正在登录...');
|
||||
setStatus('正在登录...', 'busy');
|
||||
const res = await fetch('/api/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -953,7 +1115,7 @@ var loginTemplate = template.Must(template.New("login").Parse(`<!doctype html>
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok || !data.ok) {
|
||||
setStatus(data.error || '登录失败');
|
||||
setStatus(data.error || '登录失败', 'err');
|
||||
return;
|
||||
}
|
||||
window.location.href = '/admin/login';
|
||||
@@ -970,17 +1132,18 @@ var loginTemplate = template.Must(template.New("login").Parse(`<!doctype html>
|
||||
if (form) {
|
||||
fetch('/api/credentials').then(r => r.json()).then(data => {
|
||||
statusEl.textContent = data.configured ? ('已登录:' + (data.access_key || '')) : '当前未登录';
|
||||
statusEl.dataset.state = data.configured ? 'ok' : '';
|
||||
});
|
||||
}
|
||||
|
||||
codeButton && codeButton.addEventListener('click', async () => {
|
||||
const telephone = form.telephone.value.trim();
|
||||
if (!/^1[3-9]\d{9}$/.test(telephone)) {
|
||||
setStatus('请输入有效的 11 位手机号');
|
||||
setStatus('请输入有效的 11 位手机号', 'err');
|
||||
return;
|
||||
}
|
||||
codeButton.disabled = true;
|
||||
setStatus('正在发送验证码...');
|
||||
setStatus('正在发送验证码...', 'busy');
|
||||
const res = await fetch('/api/auth/code', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -989,11 +1152,11 @@ var loginTemplate = template.Must(template.New("login").Parse(`<!doctype html>
|
||||
const data = await res.json();
|
||||
if (!res.ok || !data.ok) {
|
||||
codeButton.disabled = false;
|
||||
setStatus(data.error || '验证码发送失败');
|
||||
setStatus(data.error || '验证码发送失败', 'err');
|
||||
return;
|
||||
}
|
||||
secret = data.secret;
|
||||
setStatus('验证码已发送');
|
||||
setStatus('验证码已发送', 'ok');
|
||||
startCountdown();
|
||||
});
|
||||
|
||||
@@ -1002,10 +1165,10 @@ var loginTemplate = template.Must(template.New("login").Parse(`<!doctype html>
|
||||
const telephone = form.telephone.value.trim();
|
||||
const code = form.code.value.trim();
|
||||
if (!secret) {
|
||||
setStatus('请先获取验证码');
|
||||
setStatus('请先获取验证码', 'err');
|
||||
return;
|
||||
}
|
||||
setStatus('正在登录并保存凭据...');
|
||||
setStatus('正在登录并保存凭据...', 'busy');
|
||||
const res = await fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -1013,10 +1176,10 @@ var loginTemplate = template.Must(template.New("login").Parse(`<!doctype html>
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok || !data.ok) {
|
||||
setStatus(data.error || '登录失败');
|
||||
setStatus(data.error || '登录失败', 'err');
|
||||
return;
|
||||
}
|
||||
setStatus('登录成功,已保存凭据:' + (data.access_key || '') + ';JSON:' + (data.path || ''));
|
||||
setStatus('登录成功,已保存凭据:' + (data.access_key || '') + ';JSON:' + (data.path || ''), 'ok');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
@@ -1024,5 +1187,34 @@ var loginTemplate = template.Must(template.New("login").Parse(`<!doctype html>
|
||||
|
||||
var loginResultTemplate = template.Must(template.New("login-result").Parse(`<!doctype html>
|
||||
<html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>湛卢登录结果</title><style>body{margin:0;min-height:100vh;display:grid;place-items:center;background:#0f172a;color:#e2e8f0;font-family:system-ui}.card{max-width:560px;margin:24px;padding:32px;border:1px solid #334155;border-radius:22px;background:#1e293b;text-align:center}a{color:#93c5fd}</style></head>
|
||||
<body><main class="card">{{if .Success}}<h1>登录成功</h1>{{else}}<h1>登录失败</h1>{{end}}<p>{{.Message}}</p><a href="/login">返回登录页</a></main></body></html>`))
|
||||
<meta name="color-scheme" content="dark">
|
||||
<title>湛卢登录结果</title><style>
|
||||
:root{color-scheme:dark;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;--ink:#f8fafc;--body:#b6c2d9;--ok:#34d399;--err:#f87171;--accent-1:#3b82f6;--accent-2:#8b5cf6}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;min-height:100vh;display:grid;place-items:center;padding:24px 16px;color:var(--ink);
|
||||
background:
|
||||
radial-gradient(60rem 42rem at 12% -8%,rgba(59,130,246,.16),transparent 60%),
|
||||
radial-gradient(50rem 36rem at 105% 110%,rgba(139,92,246,.14),transparent 60%),
|
||||
linear-gradient(160deg,#0b1220 0%,#111a2e 55%,#0e1626 100%);
|
||||
animation:fade-in .5s ease both}
|
||||
@keyframes fade-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
|
||||
@media(prefers-reduced-motion:reduce){body{animation:none}}
|
||||
.card{width:min(480px,100%);padding:44px 36px;border-radius:24px;text-align:center;position:relative;background:rgba(15,23,42,.82);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);box-shadow:0 24px 80px rgba(0,0,0,.42),inset 0 1px 0 rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.12)}
|
||||
.mark{width:64px;height:64px;margin:0 auto 22px;border-radius:50%;display:grid;place-items:center}
|
||||
.mark svg{width:30px;height:30px}
|
||||
.mark.ok{background:rgba(52,211,153,.12);border:1px solid rgba(52,211,153,.35)}
|
||||
.mark.err{background:rgba(248,113,113,.12);border:1px solid rgba(248,113,113,.35)}
|
||||
h1{margin:0;font-size:24px;font-weight:800;letter-spacing:-.02em}
|
||||
p{margin:14px 0 26px;color:var(--body);line-height:1.7;font-size:14.5px;word-break:break-word}
|
||||
.btn{display:inline-flex;align-items:center;justify-content:center;border:0;border-radius:14px;padding:13px 26px;font:inherit;font-weight:700;font-size:14.5px;text-decoration:none;color:#fff;cursor:pointer;background:linear-gradient(135deg,var(--accent-1),var(--accent-2));box-shadow:0 10px 24px -10px rgba(99,102,241,.55);transition:filter .15s ease}
|
||||
.btn:hover{filter:brightness(1.1)}
|
||||
.btn:focus-visible{outline:2px solid var(--accent-1);outline-offset:2px}
|
||||
</style></head>
|
||||
<body><main class="card">
|
||||
<div class="mark {{if .Success}}ok{{else}}err{{end}}">
|
||||
{{if .Success}}<svg viewBox="0 0 24 24" fill="none" stroke="#34d399" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>{{else}}<svg viewBox="0 0 24 24" fill="none" stroke="#f87171" stroke-width="2.4" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>{{end}}
|
||||
</div>
|
||||
{{if .Success}}<h1>登录成功</h1>{{else}}<h1>登录失败</h1>{{end}}
|
||||
<p>{{.Message}}</p>
|
||||
<a class="btn" href="/login">返回登录页</a>
|
||||
</main></body></html>`))
|
||||
|
||||
Reference in New Issue
Block a user