handlers区分backend和frontend,需要确认。
This commit is contained in:
+63
-16
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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}}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user