fix: 修复后台管理页面API路径和链接

- users/posts/topics页面API改为/admin/api/xxx
- 页面链接改为/admin/xxx
- 添加登录验证检查和退出登录按钮
This commit is contained in:
2026-04-12 17:43:17 +08:00
parent e6805d0b75
commit b585adefc9
3 changed files with 106 additions and 34 deletions

View File

@@ -17,19 +17,27 @@
</h1>
</div>
<nav class="mt-6">
<a href="/" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<a href="/admin" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<i class="ri-dashboard-line"></i><span>仪表盘</span>
</a>
<a href="/users" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<a href="/admin/users" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<i class="ri-user-line"></i><span>用户管理</span>
</a>
<a href="/posts" class="flex items-center gap-3 px-6 py-3 bg-slate-700 text-white">
<a href="/admin/posts" class="flex items-center gap-3 px-6 py-3 bg-slate-700 text-white">
<i class="ri-file-text-line"></i><span>帖子管理</span>
</a>
<a href="/topics" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<a href="/admin/topics" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<i class="ri-tools-line"></i><span>主题管理</span>
</a>
</nav>
<div class="absolute bottom-0 left-0 right-0 p-4 border-t border-slate-700">
<a href="/" target="_blank" class="text-slate-400 hover:text-white text-sm flex items-center gap-2">
<i class="ri-external-link-line"></i> 访问前台
</a>
<button onclick="logout()" class="mt-2 text-slate-400 hover:text-red-400 text-sm flex items-center gap-2">
<i class="ri-logout-box-line"></i> 退出登录
</button>
</div>
</aside>
<main class="ml-64 flex-1 p-8">
@@ -75,9 +83,25 @@
</div>
<script>
// 检查登录状态
async function checkAuth() {
const res = await fetch('/admin/api/check-auth');
const data = await res.json();
if (!data.logged_in) {
window.location.href = '/admin/login';
}
}
checkAuth();
// 退出登录
async function logout() {
await fetch('/admin/api/logout', { method: 'POST' });
window.location.href = '/admin/login';
}
async function loadPosts() {
const type = document.getElementById('typeFilter').value;
const url = type ? `/api/posts?type=${type}` : '/api/posts';
const url = type ? `/admin/api/posts?type=${type}` : '/admin/api/posts';
const res = await fetch(url);
const posts = await res.json();
@@ -125,7 +149,7 @@
}
async function viewPost(postId) {
const res = await fetch(`/api/posts/${postId}`);
const res = await fetch(`/admin/api/posts/${postId}`);
const post = await res.json();
document.getElementById('modalTitle').textContent = post.title;
@@ -149,11 +173,11 @@
<div>
<p class="text-sm text-gray-500 mb-2">内容</p>
<div class="p-4 bg-gray-50 rounded-lg text-sm text-gray-700 whitespace-pre-wrap max-h-60 overflow-auto">
${post.content}
${post.content || '无内容'}
</div>
</div>
${post.replies.length > 0 ? `
${post.replies && post.replies.length > 0 ? `
<div>
<p class="text-sm text-gray-500 mb-2">回复 (${post.replies.length})</p>
<div class="space-y-2 max-h-40 overflow-auto">
@@ -178,7 +202,7 @@
}
async function pinPost(postId) {
const res = await fetch(`/api/posts/${postId}/pin`, { method: 'POST' });
const res = await fetch(`/admin/api/posts/${postId}/pin`, { method: 'POST' });
const data = await res.json();
if (data.success) {
@@ -189,7 +213,7 @@
async function deletePost(postId) {
if (!confirm('确定要删除这个帖子吗?')) return;
const res = await fetch(`/api/posts/${postId}`, { method: 'DELETE' });
const res = await fetch(`/admin/api/posts/${postId}`, { method: 'DELETE' });
const data = await res.json();
if (data.success) {

View File

@@ -17,19 +17,27 @@
</h1>
</div>
<nav class="mt-6">
<a href="/" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<a href="/admin" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<i class="ri-dashboard-line"></i><span>仪表盘</span>
</a>
<a href="/users" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<a href="/admin/users" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<i class="ri-user-line"></i><span>用户管理</span>
</a>
<a href="/posts" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<a href="/admin/posts" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<i class="ri-file-text-line"></i><span>帖子管理</span>
</a>
<a href="/topics" class="flex items-center gap-3 px-6 py-3 bg-slate-700 text-white">
<a href="/admin/topics" class="flex items-center gap-3 px-6 py-3 bg-slate-700 text-white">
<i class="ri-tools-line"></i><span>主题管理</span>
</a>
</nav>
<div class="absolute bottom-0 left-0 right-0 p-4 border-t border-slate-700">
<a href="/" target="_blank" class="text-slate-400 hover:text-white text-sm flex items-center gap-2">
<i class="ri-external-link-line"></i> 访问前台
</a>
<button onclick="logout()" class="mt-2 text-slate-400 hover:text-red-400 text-sm flex items-center gap-2">
<i class="ri-logout-box-line"></i> 退出登录
</button>
</div>
</aside>
<main class="ml-64 flex-1 p-8">
@@ -68,8 +76,24 @@
</div>
<script>
// 检查登录状态
async function checkAuth() {
const res = await fetch('/admin/api/check-auth');
const data = await res.json();
if (!data.logged_in) {
window.location.href = '/admin/login';
}
}
checkAuth();
// 退出登录
async function logout() {
await fetch('/admin/api/logout', { method: 'POST' });
window.location.href = '/admin/login';
}
async function loadTopics() {
const res = await fetch('/api/topics');
const res = await fetch('/admin/api/topics');
const topics = await res.json();
const tbody = document.getElementById('topicTable');
@@ -83,22 +107,22 @@
<tr class="border-b border-gray-50 hover:bg-gray-50">
<td class="px-6 py-4">
<div class="flex items-center gap-2">
<span class="text-2xl">${t.icon}</span>
<span class="text-2xl">${t.icon || '🔧'}</span>
<span class="font-medium text-gray-800">${t.name}</span>
</div>
</td>
<td class="px-6 py-4 text-gray-600">${t.author}</td>
<td class="px-6 py-4">
<span class="px-2 py-1 bg-blue-100 text-blue-700 rounded text-xs">${t.sub_topics_count}</span>
<span class="px-2 py-1 bg-blue-100 text-blue-700 rounded text-xs">${t.sub_topics_count || 0}</span>
</td>
<td class="px-6 py-4">
<span class="px-2 py-1 bg-green-100 text-green-700 rounded text-xs">${t.questions_count}</span>
<span class="px-2 py-1 bg-green-100 text-green-700 rounded text-xs">${t.questions_count || 0}</span>
</td>
<td class="px-6 py-4">
<span class="px-2 py-1 bg-purple-100 text-purple-700 rounded text-xs">${t.followers_count}</span>
<span class="px-2 py-1 bg-purple-100 text-purple-700 rounded text-xs">${t.followers_count || 0}</span>
</td>
<td class="px-6 py-4 text-sm text-gray-500">
${new Date(t.created_at).toLocaleDateString()}
${t.created_at ? new Date(t.created_at).toLocaleDateString() : '-'}
</td>
<td class="px-6 py-4">
<button onclick="viewTopic('${t.id}')" class="text-blue-500 hover:text-blue-700 mr-3">
@@ -113,7 +137,7 @@
}
async function viewTopic(topicId) {
const res = await fetch(`/api/topics/${topicId}`);
const res = await fetch(`/admin/api/topics/${topicId}`);
const topic = await res.json();
document.getElementById('modalTitle').textContent = topic.name;
@@ -126,27 +150,27 @@
<div class="grid grid-cols-3 gap-4">
<div class="p-4 bg-blue-50 rounded-lg text-center">
<div class="text-2xl font-bold text-blue-600">${topic.sub_topics.length}</div>
<div class="text-2xl font-bold text-blue-600">${topic.sub_topics ? topic.sub_topics.length : 0}</div>
<div class="text-sm text-gray-500">子主题</div>
</div>
<div class="p-4 bg-green-50 rounded-lg text-center">
<div class="text-2xl font-bold text-green-600">${topic.questions.length}</div>
<div class="text-2xl font-bold text-green-600">${topic.questions ? topic.questions.length : 0}</div>
<div class="text-sm text-gray-500">问题</div>
</div>
<div class="p-4 bg-purple-50 rounded-lg text-center">
<div class="text-2xl font-bold text-purple-600">${topic.followers_count}</div>
<div class="text-2xl font-bold text-purple-600">${topic.followers_count || 0}</div>
<div class="text-sm text-gray-500">关注</div>
</div>
</div>
${topic.questions.length > 0 ? `
${topic.questions && topic.questions.length > 0 ? `
<div>
<p class="text-sm text-gray-500 mb-2">最新问题</p>
<div class="space-y-2 max-h-40 overflow-auto">
${topic.questions.slice(0, 5).map(q => `
<div class="p-2 bg-gray-50 rounded text-sm">
<p class="font-medium">${q.title}</p>
<p class="text-gray-500 text-xs">${q.answers.length} 回答</p>
<p class="text-gray-500 text-xs">${q.answers ? q.answers.length : 0} 回答</p>
</div>
`).join('')}
</div>
@@ -167,7 +191,7 @@
async function deleteTopic(topicId) {
if (!confirm('确定要删除这个主题吗?')) return;
const res = await fetch(`/api/topics/${topicId}`, { method: 'DELETE' });
const res = await fetch(`/admin/api/topics/${topicId}`, { method: 'DELETE' });
const data = await res.json();
if (data.success) {

View File

@@ -17,19 +17,27 @@
</h1>
</div>
<nav class="mt-6">
<a href="/" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<a href="/admin" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<i class="ri-dashboard-line"></i><span>仪表盘</span>
</a>
<a href="/users" class="flex items-center gap-3 px-6 py-3 bg-slate-700 text-white">
<a href="/admin/users" class="flex items-center gap-3 px-6 py-3 bg-slate-700 text-white">
<i class="ri-user-line"></i><span>用户管理</span>
</a>
<a href="/posts" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<a href="/admin/posts" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<i class="ri-file-text-line"></i><span>帖子管理</span>
</a>
<a href="/topics" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<a href="/admin/topics" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<i class="ri-tools-line"></i><span>主题管理</span>
</a>
</nav>
<div class="absolute bottom-0 left-0 right-0 p-4 border-t border-slate-700">
<a href="/" target="_blank" class="text-slate-400 hover:text-white text-sm flex items-center gap-2">
<i class="ri-external-link-line"></i> 访问前台
</a>
<button onclick="logout()" class="mt-2 text-slate-400 hover:text-red-400 text-sm flex items-center gap-2">
<i class="ri-logout-box-line"></i> 退出登录
</button>
</div>
</aside>
<main class="ml-64 flex-1 p-8">
@@ -57,8 +65,24 @@
</div>
<script>
// 检查登录状态
async function checkAuth() {
const res = await fetch('/admin/api/check-auth');
const data = await res.json();
if (!data.logged_in) {
window.location.href = '/admin/login';
}
}
checkAuth();
// 退出登录
async function logout() {
await fetch('/admin/api/logout', { method: 'POST' });
window.location.href = '/admin/login';
}
async function loadUsers() {
const res = await fetch('/api/users');
const res = await fetch('/admin/api/users');
const users = await res.json();
const tbody = document.getElementById('userTable');
@@ -94,13 +118,13 @@
async function deleteUser(userId) {
if (!confirm('确定要删除这个用户吗?这将同时删除该用户的所有帖子!')) return;
const res = await fetch(`/api/users/${userId}`, { method: 'DELETE' });
const res = await fetch(`/admin/api/users/${userId}`, { method: 'DELETE' });
const data = await res.json();
if (data.success) {
loadUsers();
} else {
alert('删除失败');
alert('删除失败: ' + (data.error || '未知错误'));
}
}