handlers区分backend和frontend,需要确认。

This commit is contained in:
2026-08-12 17:57:31 +08:00
parent 60869ec214
commit 42c6f2450d
20 changed files with 242 additions and 137 deletions
+63 -16
View File
@@ -4,6 +4,32 @@ const API_BASE = '/api';
// 当前登录用户
let currentUser = null;
// ============ Hash 路由 ============
// 从 URL hash 解析当前页面名(支持 #/posts、#/posts/5 等形式)
function currentHashPage() {
const hash = location.hash.replace(/^#\/?/, '').split('/')[0];
return hash || 'dashboard';
}
// 由 showPage 主动触发的 hash 变更(hashchange 事件中据此跳过,避免重复加载)
let internalHashChange = false;
// 切换页面时同步 URL hash
function setHashPage(pageName) {
if (location.hash !== '#/' + pageName) {
internalHashChange = true;
location.hash = '#/' + pageName;
}
}
// 同步侧边栏菜单高亮
function syncMenuActive(pageName) {
document.querySelectorAll('.menu-item').forEach(i => {
i.classList.toggle('active', i.dataset.page === pageName);
});
}
// 跳转到登录页
function goLogin() {
currentUser = null;
@@ -27,17 +53,38 @@ document.addEventListener('DOMContentLoaded', () => {
// 向服务端校验登录态与角色(不信任本地缓存,防止残留会话导致权限误判)
refreshCurrentUser();
showPage('dashboard');
// 按 URL hash 显示初始页面(默认仪表盘;无权限/不存在时静默回退)
const initialPage = currentHashPage();
if (document.getElementById(initialPage + '-page')) {
showPage(initialPage);
} else {
showPage('dashboard');
}
// Hash 路由:浏览器前进/后退或手动改 hash 时切换页面
window.addEventListener('hashchange', () => {
if (internalHashChange) {
// 由 showPage 主动设置,页面已切换,跳过
internalHashChange = false;
return;
}
const page = currentHashPage();
if (!document.getElementById(page + '-page')) {
// 无权限或不存在时回退仪表盘,避免空白/报错
setHashPage('dashboard');
showPage('dashboard');
return;
}
showPage(page);
});
// 绑定菜单点击事件(菜单由服务端模板渲染,只包含有权限的页面)
document.querySelectorAll('.menu-item').forEach(item => {
item.addEventListener('click', (e) => {
e.preventDefault();
const page = item.dataset.page;
setHashPage(page);
showPage(page);
document.querySelectorAll('.menu-item').forEach(i => i.classList.remove('active'));
item.classList.add('active');
});
});
@@ -111,6 +158,9 @@ async function refreshCurrentUser() {
// 切换页面(页面框架由服务端模板渲染,未渲染的页面不存在也不加载数据)
function showPage(pageName) {
setHashPage(pageName);
syncMenuActive(pageName);
document.querySelectorAll('.page').forEach(p => p.classList.add('hidden'));
const targetPage = document.getElementById(pageName + '-page');
if (!targetPage) {
@@ -174,20 +224,17 @@ function getHeaders() {
};
}
// 加载仪表盘
// 加载仪表盘(专用统计接口,只返回数字)
async function loadDashboard() {
try {
const [posts, categories, tags, comments] = await Promise.all([
fetch(`${API_BASE}/posts?page_size=1`).then(r => r.json()),
fetch(`${API_BASE}/categories`).then(r => r.json()),
fetch(`${API_BASE}/tags`).then(r => r.json()),
fetch(`${commentsAPIBase()}/comments?status=pending`).then(r => r.json())
]);
document.getElementById('stat-posts').textContent = posts.total || 0;
document.getElementById('stat-categories').textContent = categories.data?.length || 0;
document.getElementById('stat-tags').textContent = tags.data?.length || 0;
document.getElementById('stat-comments').textContent = comments.data?.length || 0;
const res = await fetch(`${API_BASE}/dashboard/stats`, { headers: getHeaders() });
if (!res.ok) return;
const data = await res.json();
document.getElementById('stat-posts').textContent = data.posts || 0;
document.getElementById('stat-categories').textContent = data.categories || 0;
document.getElementById('stat-tags').textContent = data.tags || 0;
document.getElementById('stat-comments').textContent = data.pending_comments || 0;
} catch (err) {
console.error('加载仪表盘失败:', err);
}
+9 -9
View File
@@ -51,7 +51,7 @@
{{if index .PageSet "themes"}}{{template "admin_modal_theme" .}}{{end}}
<script>window.__currentUser = {{.CurrentUser}};</script>
<script src="/static/admin/app.js?v=20"></script>
<script src="/static/admin/app.js?v=21"></script>
</body>
</html>
{{end}}
@@ -61,22 +61,22 @@
<div id="dashboard-page" class="page">
<h1>仪表盘</h1>
<div class="stats-grid">
<div class="stat-card">
<a href="#posts" class="stat-card" data-page="posts" onclick="showPage('posts')">
<h3>文章总数</h3>
<p class="stat-number" id="stat-posts">0</p>
</div>
<div class="stat-card">
</a>
<a href="#categories" class="stat-card" data-page="categories" onclick="showPage('categories')">
<h3>分类总数</h3>
<p class="stat-number" id="stat-categories">0</p>
</div>
<div class="stat-card">
</a>
<a href="#tags" class="stat-card" data-page="tags" onclick="showPage('tags')">
<h3>标签总数</h3>
<p class="stat-number" id="stat-tags">0</p>
</div>
<div class="stat-card">
</a>
<a href="#comments" class="stat-card" data-page="comments" onclick="showPage('comments')">
<h3>待审核评论</h3>
<p class="stat-number" id="stat-comments">0</p>
</div>
</a>
</div>
</div>
{{end}}
+10
View File
@@ -199,6 +199,16 @@ body {
gap: 24px;
}
a.stat-card {
text-decoration: none;
color: inherit;
cursor: pointer;
transition: box-shadow 0.2s, transform 0.15s;
}
a.stat-card:hover {
box-shadow: 0 4px 16px rgba(0,0,0,0.13);
transform: translateY(-2px);
}
.stat-card {
background: #fff;
padding: 24px;