250 lines
12 KiB
HTML
250 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>后台管理 - 笔记小站</title>
|
|
<link rel="stylesheet" href="css/style.css">
|
|
</head>
|
|
<body>
|
|
<nav class="navbar">
|
|
<a class="navbar-brand" href="index.html"><span class="logo-icon">📝</span> 笔记小站</a>
|
|
<div class="navbar-actions" id="navbarActions"></div>
|
|
</nav>
|
|
|
|
<div class="container" id="app">
|
|
<div class="admin-header">
|
|
<h1>⚙️ 后台管理</h1>
|
|
</div>
|
|
<div class="admin-tabs">
|
|
<button class="admin-tab" data-tab="categories">📂 分类管理</button>
|
|
<button class="admin-tab" data-tab="notes">📝 笔记管理</button>
|
|
<button class="admin-tab" data-tab="comments">💬 评论管理</button>
|
|
<button class="admin-tab" data-tab="users">👥 用户管理</button>
|
|
</div>
|
|
<div id="tabContent"></div>
|
|
</div>
|
|
|
|
<div class="toast-container" id="toastContainer"></div>
|
|
|
|
<script src="js/data.js"></script>
|
|
<script src="js/common.js"></script>
|
|
<script>
|
|
(function () {
|
|
var activeTab = getUrlParam('tab') || 'categories';
|
|
|
|
var tabs = document.querySelectorAll('.admin-tab');
|
|
tabs.forEach(function (t) {
|
|
if (t.dataset.tab === activeTab) t.classList.add('active');
|
|
t.addEventListener('click', function () {
|
|
history.replaceState(null, '', '?tab=' + this.dataset.tab);
|
|
activeTab = this.dataset.tab;
|
|
tabs.forEach(function (x) { x.classList.remove('active'); });
|
|
this.classList.add('active');
|
|
renderTab();
|
|
});
|
|
});
|
|
renderTab();
|
|
|
|
function renderTab() {
|
|
var content = document.getElementById('tabContent');
|
|
if (activeTab === 'categories') content.innerHTML = renderCategories();
|
|
else if (activeTab === 'notes') content.innerHTML = renderNotes();
|
|
else if (activeTab === 'comments') content.innerHTML = renderComments();
|
|
else if (activeTab === 'users') content.innerHTML = renderUsers();
|
|
}
|
|
|
|
/* ============ Categories ============ */
|
|
function renderCategories() {
|
|
var h = '<div class="section-header">';
|
|
h += '<h2>分类列表</h2>';
|
|
h += '<button class="btn btn-primary" onclick="openCategoryModal()">+ 新增分类</button>';
|
|
h += '</div>';
|
|
h += '<div class="data-table">';
|
|
h += '<table><thead><tr><th>ID</th><th>图标</th><th>名称</th><th>颜色</th><th>操作</th></tr></thead><tbody>';
|
|
DATA.categories.forEach(function (c) {
|
|
h += '<tr><td>' + c.id + '</td><td>' + c.icon + '</td><td>' + c.name + '</td>';
|
|
h += '<td><span class="color-dot" style="background:' + c.color + '"></span> ' + c.color + '</td>';
|
|
h += '<td><button class="btn btn-sm" onclick="openCategoryModal(' + c.id + ')">编辑</button> ';
|
|
h += '<button class="btn btn-sm btn-danger" onclick="deleteCategory(' + c.id + ')">删除</button></td></tr>';
|
|
});
|
|
h += '</tbody></table></div>';
|
|
return h;
|
|
}
|
|
|
|
window.openCategoryModal = function (id) {
|
|
var cat = id ? findById(DATA.categories, id) : null;
|
|
var title = cat ? '编辑分类' : '新增分类';
|
|
var h = '<div class="form-group"><label>名称</label><input type="text" id="fname" value="' + (cat ? cat.name : '') + '"></div>';
|
|
h += '<div class="form-group"><label>图标</label><input type="text" id="ficon" value="' + (cat ? cat.icon : '📁') + '"></div>';
|
|
h += '<div class="form-group"><label>颜色</label><input type="color" id="fcolor" value="' + (cat ? cat.color : '#6366f1') + '"></div>';
|
|
h += '<div class="form-actions"><button class="btn" onclick="closeModal()">取消</button>';
|
|
h += '<button class="btn btn-primary" onclick="saveCategory(' + (cat ? cat.id : 0) + ')">保存</button></div>';
|
|
renderModal(title, h);
|
|
};
|
|
|
|
window.saveCategory = function (id) {
|
|
var name = document.getElementById('fname').value.trim();
|
|
var icon = document.getElementById('ficon').value.trim();
|
|
var color = document.getElementById('fcolor').value.trim();
|
|
if (!name) { showToast('请输入名称', 'error'); return; }
|
|
if (id) {
|
|
var c = findById(DATA.categories, id);
|
|
if (c) { c.name = name; c.icon = icon; c.color = color; }
|
|
} else {
|
|
DATA.categories.push({ id: DATA.categories.length + 1, name: name, icon: icon, color: color });
|
|
}
|
|
closeModal(); renderTab(); showToast('保存成功', 'success');
|
|
};
|
|
|
|
window.deleteCategory = function (id) {
|
|
DATA.categories = DATA.categories.filter(function (c) { return c.id !== id; });
|
|
renderTab(); showToast('已删除', 'info');
|
|
};
|
|
|
|
/* ============ Notes ============ */
|
|
function renderNotes() {
|
|
var h = '<div class="section-header">';
|
|
h += '<h2>笔记列表</h2>';
|
|
h += '<button class="btn btn-primary" onclick="openNoteModal()">+ 新增笔记</button>';
|
|
h += '</div><div class="data-table"><table><thead><tr><th>ID</th><th>标题</th><th>分类</th><th>作者</th><th>时间</th><th>操作</th></tr></thead><tbody>';
|
|
DATA.notes.forEach(function (n) {
|
|
var cat = findById(DATA.categories, n.categoryId);
|
|
var author = findById(DATA.users, n.userId);
|
|
h += '<tr><td>' + n.id + '</td><td>' + n.title + '</td>';
|
|
h += '<td>' + (cat ? cat.icon + ' ' + cat.name : '-') + '</td>';
|
|
h += '<td>' + (author ? author.nickname : '-') + '</td><td>' + n.createdAt + '</td>';
|
|
h += '<td><button class="btn btn-sm" onclick="openNoteModal(' + n.id + ')">编辑</button> ';
|
|
h += '<button class="btn btn-sm btn-danger" onclick="deleteNote(' + n.id + ')">删除</button></td></tr>';
|
|
});
|
|
h += '</tbody></table></div>';
|
|
return h;
|
|
}
|
|
|
|
window.openNoteModal = function (id) {
|
|
var note = id ? findById(DATA.notes, id) : null;
|
|
var title = note ? '编辑笔记' : '新增笔记';
|
|
var h = '<div class="form-group"><label>标题</label><input type="text" id="ftitle" value="' + (note ? note.title : '') + '"></div>';
|
|
h += '<div class="form-group"><label>分类</label><select id="fcat">';
|
|
DATA.categories.forEach(function (c) {
|
|
h += '<option value="' + c.id + '"' + (note && note.categoryId === c.id ? ' selected' : '') + '>' + c.icon + ' ' + c.name + '</option>';
|
|
});
|
|
h += '</select></div>';
|
|
h += '<div class="form-group"><label>内容</label><textarea id="fcontent" rows="6">' + (note ? note.content : '') + '</textarea></div>';
|
|
h += '<div class="form-actions"><button class="btn" onclick="closeModal()">取消</button>';
|
|
h += '<button class="btn btn-primary" onclick="saveNote(' + (note ? note.id : 0) + ')">保存</button></div>';
|
|
renderModal(title, h);
|
|
};
|
|
|
|
window.saveNote = function (id) {
|
|
var title = document.getElementById('ftitle').value.trim();
|
|
var catId = parseInt(document.getElementById('fcat').value);
|
|
var content = document.getElementById('fcontent').value.trim();
|
|
if (!title || !content) { showToast('请填写标题和内容', 'error'); return; }
|
|
if (id) {
|
|
var n = findById(DATA.notes, id);
|
|
if (n) { n.title = title; n.categoryId = catId; n.content = content; }
|
|
} else {
|
|
DATA.notes.push({
|
|
id: DATA.notes.length + 1,
|
|
title: title,
|
|
categoryId: catId,
|
|
content: content,
|
|
userId: 1,
|
|
createdAt: new Date().toISOString().slice(0, 10),
|
|
summary: content.slice(0, 100) + (content.length > 100 ? '...' : '')
|
|
});
|
|
}
|
|
closeModal(); renderTab(); showToast('保存成功', 'success');
|
|
};
|
|
|
|
window.deleteNote = function (id) {
|
|
DATA.notes = DATA.notes.filter(function (n) { return n.id !== id; });
|
|
DATA.comments = DATA.comments.filter(function (c) { return c.noteId !== id; });
|
|
renderTab(); showToast('已删除', 'info');
|
|
};
|
|
|
|
/* ============ Comments ============ */
|
|
function renderComments() {
|
|
var h = '<div class="section-header"><h2>评论列表</h2></div><div class="data-table"><table><thead><tr><th>ID</th><th>笔记</th><th>用户</th><th>内容</th><th>时间</th><th>操作</th></tr></thead><tbody>';
|
|
DATA.comments.forEach(function (c) {
|
|
var note = findById(DATA.notes, c.noteId);
|
|
var user = findById(DATA.users, c.userId);
|
|
h += '<tr><td>' + c.id + '</td><td>' + (note ? note.title : '已删除') + '</td>';
|
|
h += '<td>' + (user ? user.nickname : (c.author || '游客')) + '</td><td>' + c.content + '</td><td>' + c.createdAt + '</td>';
|
|
h += '<td><button class="btn btn-sm btn-danger" onclick="deleteComment(' + c.id + ')">删除</button></td></tr>';
|
|
});
|
|
h += '</tbody></table></div>';
|
|
return h;
|
|
}
|
|
|
|
window.deleteComment = function (id) {
|
|
DATA.comments = DATA.comments.filter(function (c) { return c.id !== id; });
|
|
renderTab(); showToast('已删除', 'info');
|
|
};
|
|
|
|
/* ============ Users ============ */
|
|
function renderUsers() {
|
|
var h = '<div class="section-header">';
|
|
h += '<h2>用户列表</h2>';
|
|
h += '<button class="btn btn-primary" onclick="openUserModal()">+ 新增用户</button>';
|
|
h += '</div><div class="data-table"><table><thead><tr><th>ID</th><th>头像</th><th>用户名</th><th>昵称</th><th>角色</th><th>时间</th><th>操作</th></tr></thead><tbody>';
|
|
DATA.users.forEach(function (u) {
|
|
h += '<tr><td>' + u.id + '</td><td>' + u.avatar + '</td><td>' + u.username + '</td><td>' + u.nickname + '</td>';
|
|
h += '<td><span class="badge badge-' + u.role + '">' + (u.role === 'admin' ? '管理员' : '普通用户') + '</span></td>';
|
|
h += '<td>' + u.createdAt + '</td>';
|
|
h += '<td><button class="btn btn-sm" onclick="openUserModal(' + u.id + ')">编辑</button> ';
|
|
h += '<button class="btn btn-sm btn-danger" onclick="deleteUser(' + u.id + ')">删除</button></td></tr>';
|
|
});
|
|
h += '</tbody></table></div>';
|
|
return h;
|
|
}
|
|
|
|
window.openUserModal = function (id) {
|
|
var u = id ? findById(DATA.users, id) : null;
|
|
var title = u ? '编辑用户' : '新增用户';
|
|
var h = '<div class="form-group"><label>用户名</label><input type="text" id="fname" value="' + (u ? u.username : '') + '"></div>';
|
|
h += '<div class="form-group"><label>昵称</label><input type="text" id="fnick" value="' + (u ? u.nickname : '') + '"></div>';
|
|
h += '<div class="form-group"><label>角色</label><select id="frole">';
|
|
h += '<option value="user"' + (u && u.role === 'user' ? ' selected' : '') + '>普通用户</option>';
|
|
h += '<option value="admin"' + (u && u.role === 'admin' ? ' selected' : '') + '>管理员</option>';
|
|
h += '</select></div>';
|
|
h += '<div class="form-actions"><button class="btn" onclick="closeModal()">取消</button>';
|
|
h += '<button class="btn btn-primary" onclick="saveUser(' + (u ? u.id : 0) + ')">保存</button></div>';
|
|
renderModal(title, h);
|
|
};
|
|
|
|
window.saveUser = function (id) {
|
|
var username = document.getElementById('fname').value.trim();
|
|
var nickname = document.getElementById('fnick').value.trim();
|
|
var role = document.getElementById('frole').value;
|
|
if (!username || !nickname) { showToast('请填写用户名和昵称', 'error'); return; }
|
|
if (id) {
|
|
var u = findById(DATA.users, id);
|
|
if (u) { u.username = username; u.nickname = nickname; u.role = role; }
|
|
} else {
|
|
DATA.users.push({
|
|
id: DATA.users.length + 1,
|
|
username: username, password: '123456',
|
|
nickname: nickname, avatar: '😊', role: role,
|
|
createdAt: new Date().toISOString().slice(0, 10)
|
|
});
|
|
}
|
|
closeModal(); renderTab(); showToast('保存成功', 'success');
|
|
};
|
|
|
|
window.deleteUser = function (id) {
|
|
DATA.users = DATA.users.filter(function (u) { return u.id !== id; });
|
|
renderTab(); showToast('已删除', 'info');
|
|
};
|
|
|
|
/* Helper */
|
|
function findById(arr, id) {
|
|
for (var i = 0; i < arr.length; i++) { if (arr[i].id === id) return arr[i]; }
|
|
return null;
|
|
}
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|