施工(HEL-226): 实现登录门户与本机免密切换账号
用设备 Cookie 和授权表记住本机已验证账号,登录页按确认样图做成门户,不再把密码写进浏览器。 Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Cursor
multica-agent
parent
8a5e78f022
commit
f0a1adf52f
@@ -1667,3 +1667,445 @@ button.account-role-badge:focus-visible {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
|
||||
.login-portal {
|
||||
min-height: 100vh;
|
||||
|
||||
display: flex;
|
||||
|
||||
background: var(--canvas);
|
||||
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.login-theme-toggle {
|
||||
position: fixed;
|
||||
|
||||
z-index: 2;
|
||||
|
||||
top: 16px;
|
||||
|
||||
right: 20px;
|
||||
|
||||
min-height: 32px;
|
||||
|
||||
padding: 0 12px;
|
||||
|
||||
border: 1px solid var(--border-strong);
|
||||
|
||||
border-radius: var(--size-radius-md);
|
||||
|
||||
background: var(--surface);
|
||||
|
||||
color: var(--text-secondary);
|
||||
|
||||
font-size: var(--font-size-label);
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.login-brand {
|
||||
width: clamp(420px, 34vw, 560px);
|
||||
|
||||
flex: 0 0 auto;
|
||||
|
||||
padding: 44px 48px 36px;
|
||||
|
||||
background: var(--login-brand-gradient);
|
||||
|
||||
color: #f4f7ff;
|
||||
}
|
||||
|
||||
.login-brand-mark {
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
|
||||
justify-content: center;
|
||||
|
||||
width: 56px;
|
||||
|
||||
height: 56px;
|
||||
|
||||
border-radius: 16px;
|
||||
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.login-brand-glyph {
|
||||
font-size: 24px;
|
||||
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
.login-brand-kicker {
|
||||
margin: 28px 0 8px;
|
||||
|
||||
font-size: var(--font-size-caption);
|
||||
|
||||
letter-spacing: 0.08em;
|
||||
|
||||
opacity: 0.78;
|
||||
}
|
||||
|
||||
.login-brand-title {
|
||||
margin: 0;
|
||||
|
||||
font-size: 36px;
|
||||
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
.login-brand-lead {
|
||||
margin: 12px 0 0;
|
||||
|
||||
max-width: 18em;
|
||||
|
||||
font-size: var(--font-size-body);
|
||||
|
||||
line-height: 1.7;
|
||||
|
||||
opacity: 0.86;
|
||||
}
|
||||
|
||||
.login-brand-stats {
|
||||
display: grid;
|
||||
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
||||
gap: 16px 20px;
|
||||
|
||||
margin: 40px 0 0;
|
||||
}
|
||||
|
||||
.login-stat {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.login-stat dt {
|
||||
color: rgba(244, 247, 255, 0.64);
|
||||
|
||||
font-size: var(--font-size-caption);
|
||||
}
|
||||
|
||||
.login-stat dd {
|
||||
margin: 4px 0 0;
|
||||
|
||||
font-size: 20px;
|
||||
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
.login-stat-tag {
|
||||
margin-left: 6px;
|
||||
|
||||
font-size: var(--font-size-caption);
|
||||
|
||||
font-weight: var(--font-weight-regular);
|
||||
}
|
||||
|
||||
.login-stage {
|
||||
flex: 1 1 auto;
|
||||
|
||||
display: grid;
|
||||
|
||||
place-items: center;
|
||||
|
||||
padding: 48px 24px;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
width: 400px;
|
||||
|
||||
max-width: calc(100vw - 48px);
|
||||
|
||||
padding: 32px;
|
||||
|
||||
border: 1px solid var(--border-strong);
|
||||
|
||||
border-radius: var(--size-radius-dialog);
|
||||
|
||||
background: var(--surface);
|
||||
|
||||
box-shadow: var(--shadow-raised);
|
||||
}
|
||||
|
||||
.login-card.is-loading .login-form {
|
||||
pointer-events: none;
|
||||
|
||||
opacity: 0.72;
|
||||
}
|
||||
|
||||
.login-card-title {
|
||||
margin: 0;
|
||||
|
||||
font-size: 22px;
|
||||
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
.login-card-lead {
|
||||
margin: 8px 0 0;
|
||||
|
||||
color: var(--text-secondary);
|
||||
|
||||
font-size: var(--font-size-label);
|
||||
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.login-tabs {
|
||||
display: grid;
|
||||
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
||||
margin-top: 24px;
|
||||
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.login-tab {
|
||||
min-height: 40px;
|
||||
|
||||
border: 0;
|
||||
|
||||
border-bottom: 2px solid transparent;
|
||||
|
||||
background: transparent;
|
||||
|
||||
color: var(--text-secondary);
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.login-tab.is-active {
|
||||
border-bottom-color: var(--action);
|
||||
|
||||
color: var(--action);
|
||||
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
.login-form {
|
||||
display: grid;
|
||||
|
||||
gap: 14px;
|
||||
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.login-portal .form-field input {
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
.login-submit {
|
||||
width: 100%;
|
||||
|
||||
height: 40px;
|
||||
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.login-spinner {
|
||||
width: 14px;
|
||||
|
||||
height: 14px;
|
||||
|
||||
border: 2px solid currentColor;
|
||||
|
||||
border-right-color: transparent;
|
||||
|
||||
border-radius: 50%;
|
||||
|
||||
animation: login-spin 0.7s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes login-spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.login-error {
|
||||
margin: 0;
|
||||
|
||||
color: var(--danger);
|
||||
|
||||
font-size: var(--font-size-caption);
|
||||
}
|
||||
|
||||
.login-error[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.login-hint,
|
||||
.login-privacy {
|
||||
margin: 16px 0 0;
|
||||
|
||||
color: var(--text-tertiary);
|
||||
|
||||
font-size: var(--font-size-caption);
|
||||
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.login-portal .login-privacy {
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.login-lock {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.login-back,
|
||||
.login-add,
|
||||
.login-manage,
|
||||
.login-account-enter,
|
||||
.login-account-remove {
|
||||
border: 0;
|
||||
|
||||
background: transparent;
|
||||
|
||||
color: var(--action);
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.login-portal .login-back {
|
||||
margin-bottom: 12px;
|
||||
|
||||
padding: 0;
|
||||
|
||||
font-size: var(--font-size-label);
|
||||
}
|
||||
|
||||
.login-portal .login-add {
|
||||
display: block;
|
||||
|
||||
width: 100%;
|
||||
|
||||
min-height: 40px;
|
||||
|
||||
margin-top: 8px;
|
||||
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.login-portal .login-manage {
|
||||
display: block;
|
||||
|
||||
width: 100%;
|
||||
|
||||
min-height: 40px;
|
||||
|
||||
margin-top: 8px;
|
||||
|
||||
text-align: left;
|
||||
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.login-account-list {
|
||||
display: grid;
|
||||
|
||||
gap: 8px;
|
||||
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.login-account-row {
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
|
||||
justify-content: space-between;
|
||||
|
||||
min-height: 58px;
|
||||
|
||||
padding: 0 14px;
|
||||
|
||||
border: 1px solid var(--border);
|
||||
|
||||
border-radius: var(--size-radius-md);
|
||||
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.login-account-row.is-current {
|
||||
border-color: var(--action);
|
||||
}
|
||||
|
||||
.login-account-row.is-confirming {
|
||||
display: grid;
|
||||
|
||||
gap: 10px;
|
||||
|
||||
padding: 12px 14px;
|
||||
}
|
||||
|
||||
.login-account-meta {
|
||||
display: grid;
|
||||
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.login-account-meta strong {
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
.login-account-meta span {
|
||||
color: var(--text-tertiary);
|
||||
|
||||
font-size: var(--font-size-caption);
|
||||
}
|
||||
|
||||
.login-account-check {
|
||||
color: var(--action);
|
||||
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.login-confirm-copy {
|
||||
margin: 0;
|
||||
|
||||
font-size: var(--font-size-label);
|
||||
}
|
||||
|
||||
.login-confirm-actions {
|
||||
display: flex;
|
||||
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.login-portal {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.login-brand {
|
||||
width: 100%;
|
||||
|
||||
padding: 20px 20px 16px;
|
||||
}
|
||||
|
||||
.login-brand-lead,
|
||||
.login-brand-stats {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.login-brand-title {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.login-brand-kicker {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.login-stage {
|
||||
padding: 28px 16px 40px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,12 +53,10 @@ async function applyAuthenticatedSession(session) {
|
||||
function showAuthGate(message = "") {
|
||||
state.user = null;
|
||||
state.csrfToken = "";
|
||||
const gate = document.querySelector("#authGate");
|
||||
gate.hidden = false;
|
||||
const errorElement = document.querySelector("#authError");
|
||||
errorElement.textContent = message;
|
||||
errorElement.hidden = !message;
|
||||
document.querySelector("#authUsername").focus();
|
||||
const params = new URLSearchParams();
|
||||
if (message) params.set("notice", message);
|
||||
const query = params.toString();
|
||||
window.location.replace("/login/" + (query ? `?${query}` : ""));
|
||||
}
|
||||
|
||||
async function logoutAccount() {
|
||||
@@ -197,16 +195,8 @@ async function changeAccountPassword(event) {
|
||||
}
|
||||
|
||||
async function switchAccount() {
|
||||
const button = document.querySelector("#switchAccountMenuButton");
|
||||
button.disabled = true;
|
||||
toggleAccountDropdown(false);
|
||||
try {
|
||||
await apiRequest("/api/auth/logout", "POST", {});
|
||||
window.location.reload();
|
||||
} catch (error) {
|
||||
showToast(error.message || "切换账号失败");
|
||||
button.disabled = false;
|
||||
}
|
||||
window.location.assign("/login/");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -146,6 +146,7 @@
|
||||
--shadow-float: var(--elevation-float);
|
||||
--duration-fast: 150ms;
|
||||
--duration-normal: 220ms;
|
||||
--login-brand-gradient: linear-gradient(165deg, #0c1e4a, #16307c, #2153cc);
|
||||
|
||||
--font-size-aux: 11.5px;
|
||||
--font-size-caption: 12.5px;
|
||||
@@ -509,6 +510,7 @@
|
||||
--warning-line: #6d5a38;
|
||||
--warning-line-strong: #66502d;
|
||||
--control-shadow: 0 1px 3px rgba(0, 0, 0, .3);
|
||||
--login-brand-gradient: linear-gradient(165deg, #080c18, #0e1730, #14224a);
|
||||
--dialog-backdrop: var(--backdrop);
|
||||
--ladder-level-1: #2d2426;
|
||||
--ladder-level-2: #2b2822;
|
||||
|
||||
Reference in New Issue
Block a user