129 lines
6.0 KiB
HTML
129 lines
6.0 KiB
HTML
{% extends "admin_base.html" %}
|
|
|
|
{% block title %}帖子管理 - 智联青云管理后台{% endblock %}
|
|
|
|
{% block admin_content %}
|
|
<div class="space-y-6">
|
|
<h1 class="text-3xl font-extrabold bg-gradient-to-r from-blue-400 via-purple-400 to-pink-400 bg-clip-text text-transparent">帖子管理</h1>
|
|
|
|
<div class="flex gap-4">
|
|
<input id="search-input" type="text" placeholder="搜索标题/内容..." class="input-futuristic flex-1">
|
|
<select id="tag-filter" class="input-futuristic">
|
|
<option value="">全部标签</option>
|
|
<option value="讨论">讨论</option>
|
|
<option value="求助">求助</option>
|
|
<option value="分享">分享</option>
|
|
<option value="公告">公告</option>
|
|
</select>
|
|
<button onclick="loadPosts()" class="btn-futuristic px-6">搜索</button>
|
|
</div>
|
|
|
|
<div class="futuristic-card-dark overflow-hidden">
|
|
<div class="table-responsive">
|
|
<table class="table-futuristic" style="min-width: 1000px;">
|
|
<thead>
|
|
<tr>
|
|
<th style="min-width: 60px;">ID</th>
|
|
<th style="min-width: 250px;">标题</th>
|
|
<th style="min-width: 120px;">作者</th>
|
|
<th style="min-width: 100px;">标签</th>
|
|
<th style="min-width: 100px;">置顶</th>
|
|
<th style="min-width: 150px;">发布时间</th>
|
|
<th style="min-width: 200px;">操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="posts-tbody"></tbody>
|
|
</table>
|
|
</div>
|
|
<div id="loading-spinner" class="text-center py-8 text-slate-500 hidden">
|
|
<svg class="animate-spin h-6 w-6 mx-auto text-cyan-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
</svg>
|
|
<span class="text-sm">加载中...</span>
|
|
</div>
|
|
<div id="empty-msg" class="text-center py-12 text-slate-500 hidden">暂无帖子</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
{% block scripts %}
|
|
<script>
|
|
const tagClassMap = {
|
|
'讨论': 'bg-blue-500/20 text-blue-400 border-blue-500/30',
|
|
'求助': 'bg-yellow-500/20 text-yellow-400 border-yellow-500/30',
|
|
'分享': 'bg-green-500/20 text-green-400 border-green-500/30',
|
|
'公告': 'bg-red-500/20 text-red-400 border-red-500/30'
|
|
};
|
|
|
|
async function loadPosts() {
|
|
const q = document.getElementById('search-input').value;
|
|
const tag = document.getElementById('tag-filter').value;
|
|
const tbody = document.getElementById('posts-tbody');
|
|
const spinner = document.getElementById('loading-spinner');
|
|
const empty = document.getElementById('empty-msg');
|
|
|
|
tbody.innerHTML = '';
|
|
spinner.classList.remove('hidden');
|
|
empty.classList.add('hidden');
|
|
|
|
try {
|
|
const params = new URLSearchParams();
|
|
if (q) params.set('q', q);
|
|
if (tag) params.set('tag', tag);
|
|
const res = await fetch('/api/admin/posts?' + params);
|
|
const data = await res.json();
|
|
spinner.classList.add('hidden');
|
|
|
|
if (!data.success || !data.posts.length) {
|
|
empty.classList.remove('hidden');
|
|
return;
|
|
}
|
|
|
|
let html = '';
|
|
data.posts.forEach(p => {
|
|
const tagClass = tagClassMap[p.tag] || 'bg-slate-500/20 text-slate-400 border-slate-500/30';
|
|
html += `<tr>
|
|
<td>${p.id}</td>
|
|
<td class="font-medium text-slate-200" style="max-width: 250px; white-space: normal; word-wrap: break-word;">${p.title}</td>
|
|
<td class="text-slate-400">${p.author}</td>
|
|
<td><span class="badge-futuristic ${tagClass}">${p.tag || '-'}</span></td>
|
|
<td>
|
|
<span class="badge-futuristic ${p.pinned ? 'bg-orange-500/20 text-orange-400 border-orange-500/30' : 'bg-slate-500/20 text-slate-400 border-slate-500/30'}">${p.pinned ? '已置顶' : '未置顶'}</span>
|
|
</td>
|
|
<td class="text-slate-400">${p.created_at}</td>
|
|
<td class="space-x-2">
|
|
<button onclick="togglePin(${p.id}, ${p.pinned})" class="${p.pinned ? 'btn-outline-futuristic' : 'btn-futuristic'} px-3 py-1 text-xs">${p.pinned ? '取消置顶' : '置顶'}</button>
|
|
<button onclick="deletePost(${p.id}, '${p.title.replace(/'/g, "\\'")}')" class="btn-outline-futuristic px-3 py-1 text-xs border-red-500/30 text-red-400 hover:bg-red-500/20 hover:border-red-500/50">删除</button>
|
|
</td>
|
|
</tr>`;
|
|
});
|
|
tbody.innerHTML = html;
|
|
} catch(e) {
|
|
spinner.classList.add('hidden');
|
|
empty.textContent = '加载失败,请稍后重试';
|
|
empty.classList.remove('hidden');
|
|
}
|
|
}
|
|
async function togglePin(id, currentlyPinned) {
|
|
if (!confirm(currentlyPinned ? '确定取消置顶?' : '确定置顶该帖子?')) return;
|
|
try {
|
|
const res = await fetch(`/api/admin/posts/${id}/pin`, {method: 'PUT'});
|
|
const data = await res.json();
|
|
if (data.success) { loadPosts(); } else { alert(data.message || '操作失败'); }
|
|
} catch(e) { alert('网络错误'); }
|
|
}
|
|
|
|
async function deletePost(id, title) {
|
|
if (!confirm(`确定删除帖子「${title}」?此操作不可恢复!`)) return;
|
|
try {
|
|
const res = await fetch(`/api/admin/posts/${id}`, {method: 'DELETE'});
|
|
const data = await res.json();
|
|
if (data.success) { loadPosts(); } else { alert(data.message || '操作失败'); }
|
|
} catch(e) { alert('网络错误'); }
|
|
}
|
|
|
|
document.getElementById('search-input').addEventListener('keydown', e => { if (e.key === 'Enter') loadPosts(); });
|
|
document.addEventListener('DOMContentLoaded', loadPosts);
|
|
</script>
|
|
{% endblock %}
|