fix: 收斂帳號安全頁新版樣式
All checks were successful
CD Pipeline / deploy (push) Successful in 1m3s

This commit is contained in:
OoO
2026-05-17 22:34:09 +08:00
parent a9b5385615
commit de60792b7e
3 changed files with 196 additions and 21 deletions

View File

@@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
SYSTEM_VERSION = "V10.156"
SYSTEM_VERSION = "V10.157"
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
public_url = PUBLIC_URL # 用於模板顯示

View File

@@ -2,12 +2,125 @@
{% block title %}修改密碼 - EwoooC{% endblock %}
{% block extra_css %}
<style>
.change-password-page {
color: var(--momo-text-primary);
}
.change-password-card {
overflow: hidden;
background: var(--momo-bg-surface);
border: 1px solid var(--momo-border-light);
border-radius: var(--momo-radius-md);
box-shadow: none;
}
.change-password-card__head {
background: var(--momo-bg-paper);
border-bottom: 1px solid var(--momo-border-light);
color: var(--momo-text-primary);
}
.change-password-card__head h5 {
color: var(--momo-text-primary);
font-family: var(--momo-font-display);
font-size: var(--momo-text-title);
font-weight: 800;
letter-spacing: 0;
}
.password-rule-note {
padding: 12px 14px;
background: var(--momo-info-bg);
border: 1px solid var(--momo-info-border);
border-radius: var(--momo-radius-md);
color: var(--momo-info-text);
}
.password-rule-note h6 {
color: var(--momo-info-text);
font-family: var(--momo-font-display);
font-weight: 800;
}
.change-password-alert {
position: relative;
padding: 12px 42px 12px 14px;
margin-bottom: var(--momo-space-4);
border: 1px solid var(--momo-border-light);
border-radius: var(--momo-radius-md);
background: var(--momo-bg-paper);
color: var(--momo-text-primary);
font-weight: 700;
}
.change-password-alert--success {
background: var(--momo-success-bg);
border-color: var(--momo-success-border);
color: var(--momo-success-text);
}
.change-password-alert--danger {
background: var(--momo-danger-bg);
border-color: var(--momo-danger-border);
color: var(--momo-danger-text);
}
.change-password-alert--warning {
background: var(--momo-warning-bg);
border-color: var(--momo-warning-border);
color: var(--momo-warning-text);
}
.change-password-strength-bar {
border-radius: var(--momo-radius-pill);
}
.change-password-strength-bar--danger {
background: var(--momo-danger);
}
.change-password-strength-bar--warning {
background: var(--momo-warning);
}
.change-password-strength-bar--success {
background: var(--momo-success);
}
.change-password-feedback {
font-weight: 800;
}
.change-password-feedback.is-danger {
color: var(--momo-danger-text);
}
.change-password-feedback.is-warning {
color: var(--momo-warning-text);
}
.change-password-feedback.is-success {
color: var(--momo-success-text);
}
@media (max-width: 760px) {
.change-password-page .container {
padding-left: var(--momo-space-3);
padding-right: var(--momo-space-3);
}
}
</style>
{% endblock %}
{% block content %}
<div class="change-password-page">
<div class="container py-4">
<div class="row justify-content-center">
<div class="col-md-6 col-lg-5">
<div class="card shadow">
<div class="card-header bg-primary text-white">
<div class="card change-password-card">
<div class="card-header change-password-card__head">
<h5 class="mb-0">
<i class="fas fa-key me-2"></i>修改密碼
</h5>
@@ -49,7 +162,7 @@
</div>
<!-- 密碼要求提示 -->
<div class="alert alert-info">
<div class="password-rule-note">
<h6 class="alert-heading"><i class="fas fa-info-circle me-2"></i>密碼要求</h6>
<ul class="mb-0 ps-3">
{% for req in password_requirements %}
@@ -72,6 +185,7 @@
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
@@ -168,24 +282,24 @@
if (/[0-9]/.test(password)) score++;
if (/[!@#$%^&*]/.test(password)) score++;
let strengthText, strengthClass;
let strengthText, strengthTone;
if (score < 3) {
strengthText = '弱';
strengthClass = 'text-danger';
strengthTone = 'danger';
} else if (score < 5) {
strengthText = '中等';
strengthClass = 'text-warning';
strengthTone = 'warning';
} else {
strengthText = '強';
strengthClass = 'text-success';
strengthTone = 'success';
}
strengthDiv.innerHTML = `
<div class="progress" style="height: 5px;">
<div class="progress-bar bg-${strengthClass.replace('text-', '')}"
<div class="progress-bar change-password-strength-bar change-password-strength-bar--${strengthTone}"
style="width: ${(score / 6) * 100}%"></div>
</div>
<small class="${strengthClass}">密碼強度:${strengthText}</small>
<small class="change-password-feedback is-${strengthTone}">密碼強度:${strengthText}</small>
`;
}
@@ -200,16 +314,16 @@
}
if (newPassword === confirmPassword) {
matchDiv.innerHTML = '<small class="text-success"><i class="fas fa-check me-1"></i>密碼相符</small>';
matchDiv.innerHTML = '<small class="change-password-feedback is-success"><i class="fas fa-check me-1"></i>密碼相符</small>';
} else {
matchDiv.innerHTML = '<small class="text-danger"><i class="fas fa-times me-1"></i>密碼不相符</small>';
matchDiv.innerHTML = '<small class="change-password-feedback is-danger"><i class="fas fa-times me-1"></i>密碼不相符</small>';
}
}
function showAlert(message, type) {
const container = document.getElementById('alertContainer');
container.innerHTML = `
<div class="alert alert-${type} alert-dismissible fade show" role="alert">
<div class="change-password-alert change-password-alert--${type} alert-dismissible fade show" role="alert">
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>

View File

@@ -4,6 +4,67 @@
{% block extra_css %}
<style>
.login-history-page {
color: var(--momo-text-primary);
}
.login-history-table-head th {
background: var(--momo-bg-paper) !important;
color: var(--momo-text-secondary) !important;
font-family: var(--momo-font-mono, monospace);
font-size: var(--momo-text-label);
font-weight: 800;
letter-spacing: 0.04em;
}
.login-history-empty {
padding: var(--momo-space-5) !important;
color: var(--momo-text-secondary) !important;
text-align: center;
}
.login-history-empty.is-danger {
color: var(--momo-danger-text) !important;
}
.login-status-badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 22px;
padding: 3px 8px;
border: 1px solid var(--momo-border-light);
border-radius: var(--momo-radius-sm);
font-family: var(--momo-font-mono, monospace);
font-size: var(--momo-text-label);
font-weight: 800;
line-height: 1;
}
.login-status-badge.is-success {
background: var(--momo-success-bg);
border-color: var(--momo-success-border);
color: var(--momo-success-text);
}
.login-status-badge.is-danger {
background: var(--momo-danger-bg);
border-color: var(--momo-danger-border);
color: var(--momo-danger-text);
}
.login-status-badge.is-warning {
background: var(--momo-warning-bg);
border-color: var(--momo-warning-border);
color: var(--momo-warning-text);
}
.login-status-badge.is-muted {
background: var(--momo-tag-muted-bg);
border-color: var(--momo-tag-muted-border);
color: var(--momo-tag-muted-text);
}
@media (max-width: 760px) {
.login-history-table {
min-width: 0;
@@ -76,7 +137,7 @@
{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<div class="container-fluid py-4 login-history-page">
<div class="page-header">
<h1><i class="fas fa-clock-rotate-left me-2"></i>登入歷史</h1>
<p>系統登入記錄與異常嘗試追蹤</p>
@@ -94,7 +155,7 @@
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0 login-history-table">
<thead class="table-light">
<thead class="login-history-table-head">
<tr>
<th>時間</th>
<th>帳號</th>
@@ -121,9 +182,9 @@
{% block extra_js %}
<script>
const statusClass = {
success: 'text-bg-success',
failed: 'text-bg-danger',
locked: 'text-bg-warning'
success: 'is-success',
failed: 'is-danger',
locked: 'is-warning'
};
function escapeHtml(value) {
@@ -138,18 +199,18 @@ async function loadLoginHistory() {
const response = await fetch(`/api/login_history?limit=${limit}`);
const result = await response.json();
if (!result.success) {
tbody.innerHTML = `<tr><td colspan="6" class="text-center text-danger py-4">${escapeHtml(result.message)}</td></tr>`;
tbody.innerHTML = `<tr><td colspan="6" class="login-history-empty is-danger">${escapeHtml(result.message)}</td></tr>`;
return;
}
if (!result.data.length) {
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-muted py-4">尚無登入記錄</td></tr>';
tbody.innerHTML = '<tr><td colspan="6" class="login-history-empty">尚無登入記錄</td></tr>';
return;
}
tbody.innerHTML = result.data.map(item => `
<tr>
<td>${escapeHtml(item.login_time ? new Date(item.login_time).toLocaleString('zh-TW') : '')}</td>
<td>${escapeHtml(item.username_attempted || item.user_id || '')}</td>
<td><span class="badge ${statusClass[item.status] || 'text-bg-secondary'}">${escapeHtml(item.status)}</span></td>
<td><span class="login-status-badge ${statusClass[item.status] || 'is-muted'}">${escapeHtml(item.status)}</span></td>
<td>${escapeHtml(item.ip_address)}</td>
<td>${escapeHtml(item.failure_reason)}</td>
<td class="text-truncate" style="max-width: 360px;">${escapeHtml(item.user_agent)}</td>