541 lines
20 KiB
HTML
Executable File
541 lines
20 KiB
HTML
Executable File
{% extends "base.html" %}
|
||
|
||
{% block title %}Wiki 分类管理{% endblock %}
|
||
|
||
{% block head %}
|
||
<style>
|
||
.container {
|
||
margin-left: 250px;
|
||
padding: 20px;
|
||
min-height: 100vh;
|
||
background: #f8f9fa;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.container {
|
||
margin-left: 0;
|
||
padding: 15px;
|
||
}
|
||
}
|
||
|
||
.category-table {
|
||
width: 100%;
|
||
border-collapse: separate;
|
||
border-spacing: 0;
|
||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.category-table th,
|
||
.category-table td {
|
||
border: 1px solid #e0e0e0;
|
||
padding: 12px;
|
||
text-align: left;
|
||
transition: background-color 0.3s ease;
|
||
}
|
||
|
||
.category-table th {
|
||
background-color: #f1f3f5;
|
||
font-weight: 600;
|
||
color: #495057;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.category-table tr:nth-child(even) {
|
||
background-color: #f8f9fa;
|
||
}
|
||
|
||
.category-table tr:hover {
|
||
background-color: #e9ecef;
|
||
}
|
||
|
||
.category-actions {
|
||
display: flex;
|
||
gap: 10px;
|
||
justify-content: center;
|
||
}
|
||
|
||
.btn-edit,
|
||
.btn-delete {
|
||
padding: 6px 12px;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 0.9em;
|
||
transition: opacity 0.3s ease;
|
||
}
|
||
|
||
.btn-edit {
|
||
background-color: #28a745;
|
||
color: white;
|
||
}
|
||
|
||
.btn-delete {
|
||
background-color: #dc3545;
|
||
color: white;
|
||
}
|
||
|
||
.btn-edit:hover {
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.btn-delete:hover {
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.add-category-form {
|
||
margin-bottom: 20px;
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
background-color: #f8f9fa;
|
||
padding: 15px;
|
||
border-radius: 8px;
|
||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.add-category-form input,
|
||
.add-category-form select {
|
||
flex: 1;
|
||
padding: 8px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 4px;
|
||
min-width: 100px;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.add-category-form {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.category-table {
|
||
font-size: 0.9em;
|
||
}
|
||
|
||
.category-table td,
|
||
.category-table th {
|
||
padding: 8px;
|
||
}
|
||
}
|
||
|
||
.category-level-indicator {
|
||
display: inline-block;
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 50%;
|
||
margin-right: 8px;
|
||
}
|
||
|
||
.category-level-1 {
|
||
background-color: #007bff;
|
||
}
|
||
|
||
.category-level-2 {
|
||
background-color: #28a745;
|
||
}
|
||
|
||
.category-level-3 {
|
||
background-color: #dc3545;
|
||
}
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
{% include 'admin_nav.html' %}
|
||
<div class="container">
|
||
<h1>Wiki 分类管理</h1>
|
||
|
||
{% if error %}
|
||
<div class="alert alert-danger">
|
||
{{ error }}
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="add-category-section">
|
||
<form id="addCategoryForm" class="add-category-form">
|
||
<select id="first-level" required>
|
||
<option value="">选择一级分类</option>
|
||
<option value="版本号">版本号</option>
|
||
<option value="资讯">资讯</option>
|
||
</select>
|
||
<input type="text" id="second-level" placeholder="二级分类" required>
|
||
<input type="text" id="third-level" placeholder="三级分类" required>
|
||
<input type="text" id="description" placeholder="描述(可选)">
|
||
<input type="number" id="sort-order" placeholder="排序(默认999)" value="999">
|
||
<button type="submit" class="btn btn-primary">添加分类</button>
|
||
</form>
|
||
</div>
|
||
|
||
<table class="category-table">
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>一级分类</th>
|
||
<th>二级分类</th>
|
||
<th>三级分类</th>
|
||
<th>描述</th>
|
||
<th>排序</th>
|
||
<th>操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="categoriesTableBody">
|
||
{% for category in categories %}
|
||
<tr data-id="{{ category.id }}">
|
||
<td>{{ category.id }}</td>
|
||
<td>
|
||
<span class="category-level-indicator category-level-1"></span>
|
||
{{ category.first_level }}
|
||
</td>
|
||
<td>
|
||
<span class="category-level-indicator category-level-2"></span>
|
||
{{ category.second_level }}
|
||
</td>
|
||
<td>
|
||
<span class="category-level-indicator category-level-3"></span>
|
||
{{ category.third_level }}
|
||
</td>
|
||
<td>{{ category.description or '' }}</td>
|
||
<td>{{ category.sort_order }}</td>
|
||
<td class="category-actions">
|
||
<button class="btn-edit" onclick="editCategory({{ category.id }})">编辑</button>
|
||
<button class="btn-delete" onclick="deleteCategory({{ category.id }})">删除</button>
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 获取分类列表
|
||
async function fetchCategories() {
|
||
try {
|
||
const response = await fetch('/admin/wiki/categories', {
|
||
headers: {
|
||
'X-Requested-With': 'XMLHttpRequest'
|
||
}
|
||
});
|
||
|
||
if (!response.ok) {
|
||
console.error('获取分类失败:', response.statusText);
|
||
return [];
|
||
}
|
||
|
||
const categories = await response.json();
|
||
|
||
console.log('获取到的分类:', categories);
|
||
console.log('分类总数:', categories.length);
|
||
|
||
return categories;
|
||
} catch (error) {
|
||
console.error('获取分类时发生错误:', error);
|
||
|
||
// 显示错误消息
|
||
const errorDiv = document.createElement('div');
|
||
errorDiv.className = 'alert alert-danger';
|
||
errorDiv.textContent = `获取分类失败: ${error.message}`;
|
||
document.querySelector('.container').insertBefore(errorDiv, document.querySelector('.category-table'));
|
||
|
||
return [];
|
||
}
|
||
}
|
||
|
||
// 渲染分类列表
|
||
async function renderCategories() {
|
||
const categories = await fetchCategories();
|
||
const tableBody = document.getElementById('categoriesTableBody');
|
||
|
||
// 清空现有行
|
||
tableBody.innerHTML = '';
|
||
|
||
// 如果没有分类,显示提示
|
||
if (categories.length === 0) {
|
||
const noDataRow = `
|
||
<tr>
|
||
<td colspan="7" style="text-align: center; color: #888;">
|
||
暂无分类,请添加新分类
|
||
</td>
|
||
</tr>
|
||
`;
|
||
tableBody.innerHTML = noDataRow;
|
||
return;
|
||
}
|
||
|
||
// 按一级分类分组
|
||
const groupedCategories = {};
|
||
categories.forEach(category => {
|
||
if (!groupedCategories[category.first_level]) {
|
||
groupedCategories[category.first_level] = [];
|
||
}
|
||
groupedCategories[category.first_level].push(category);
|
||
});
|
||
|
||
// 渲染分类列表
|
||
Object.keys(groupedCategories).forEach(firstLevel => {
|
||
const firstLevelCategories = groupedCategories[firstLevel];
|
||
|
||
firstLevelCategories.forEach(category => {
|
||
const row = `
|
||
<tr data-id="${category.id}">
|
||
<td>${category.id}</td>
|
||
<td>
|
||
<span class="category-level-indicator category-level-1"></span>
|
||
${category.first_level}
|
||
</td>
|
||
<td>
|
||
<span class="category-level-indicator category-level-2"></span>
|
||
${category.second_level}
|
||
</td>
|
||
<td>
|
||
<span class="category-level-indicator category-level-3"></span>
|
||
${category.third_level}
|
||
</td>
|
||
<td>${category.description || ''}</td>
|
||
<td>${category.sort_order}</td>
|
||
<td class="category-actions">
|
||
<button class="btn-edit" onclick="editCategory(${category.id})">编辑</button>
|
||
<button class="btn-delete" onclick="deleteCategory(${category.id})">删除</button>
|
||
</td>
|
||
</tr>
|
||
`;
|
||
tableBody.innerHTML += row;
|
||
});
|
||
});
|
||
}
|
||
|
||
// 初始化页面
|
||
if (document.getElementById('categoriesTableBody')) {
|
||
renderCategories();
|
||
}
|
||
|
||
// 添加分类表单提交处理
|
||
document.getElementById('addCategoryForm').addEventListener('submit', async function(e) {
|
||
e.preventDefault();
|
||
|
||
const data = {
|
||
first_level: document.getElementById('first-level').value,
|
||
second_level: document.getElementById('second-level').value,
|
||
third_level: document.getElementById('third-level').value,
|
||
description: document.getElementById('description').value,
|
||
sort_order: document.getElementById('sort-order').value
|
||
};
|
||
|
||
try {
|
||
const response = await fetch('/admin/wiki/category/add', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(data)
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
// 重新加载分类列表
|
||
await renderCategories();
|
||
|
||
// 清空表单
|
||
document.getElementById('first-level').value = '';
|
||
document.getElementById('second-level').value = '';
|
||
document.getElementById('third-level').value = '';
|
||
document.getElementById('description').value = '';
|
||
document.getElementById('sort-order').value = '999';
|
||
|
||
// 显示成功消息
|
||
const successDiv = document.createElement('div');
|
||
successDiv.className = 'alert alert-success';
|
||
successDiv.textContent = '分类添加成功';
|
||
document.querySelector('.container').insertBefore(successDiv, document.querySelector('.category-table'));
|
||
|
||
// 3秒后移除成功消息
|
||
setTimeout(() => successDiv.remove(), 3000);
|
||
} else {
|
||
// 显示错误消息
|
||
const errorDiv = document.createElement('div');
|
||
errorDiv.className = 'alert alert-danger';
|
||
errorDiv.textContent = result.error || '添加分类失败';
|
||
document.querySelector('.container').insertBefore(errorDiv, document.querySelector('.category-table'));
|
||
}
|
||
} catch (error) {
|
||
console.error('添加分类时发生错误:', error);
|
||
|
||
// 显示错误消息
|
||
const errorDiv = document.createElement('div');
|
||
errorDiv.className = 'alert alert-danger';
|
||
errorDiv.textContent = `添加分类时发生错误: ${error.message}`;
|
||
document.querySelector('.container').insertBefore(errorDiv, document.querySelector('.category-table'));
|
||
}
|
||
});
|
||
|
||
// 删除分类
|
||
window.deleteCategory = async function(categoryId) {
|
||
if (!confirm('确定要删除这个分类吗?')) return;
|
||
|
||
try {
|
||
const response = await fetch(`/admin/wiki/category/delete/${categoryId}`, {
|
||
method: 'POST'
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
// 重新加载分类列表
|
||
await renderCategories();
|
||
|
||
// 显示成功消息
|
||
const successDiv = document.createElement('div');
|
||
successDiv.className = 'alert alert-success';
|
||
successDiv.textContent = '分类删除成功';
|
||
document.querySelector('.container').insertBefore(successDiv, document.querySelector('.category-table'));
|
||
|
||
// 3秒后移除成功消息
|
||
setTimeout(() => successDiv.remove(), 3000);
|
||
} else {
|
||
// 显示错误消息
|
||
const errorDiv = document.createElement('div');
|
||
errorDiv.className = 'alert alert-danger';
|
||
errorDiv.textContent = result.error || '删除分类失败';
|
||
document.querySelector('.container').insertBefore(errorDiv, document.querySelector('.category-table'));
|
||
}
|
||
} catch (error) {
|
||
console.error('删除分类时发生错误:', error);
|
||
|
||
// 显示错误消息
|
||
const errorDiv = document.createElement('div');
|
||
errorDiv.className = 'alert alert-danger';
|
||
errorDiv.textContent = `删除分类时发生错误: ${error.message}`;
|
||
document.querySelector('.container').insertBefore(errorDiv, document.querySelector('.category-table'));
|
||
}
|
||
};
|
||
|
||
// 编辑分类
|
||
window.editCategory = function(categoryId) {
|
||
// 获取当前行的数据
|
||
const row = document.querySelector(`tr[data-id="${categoryId}"]`);
|
||
const firstLevel = row.querySelector('td:nth-child(2)').textContent.trim();
|
||
const secondLevel = row.querySelector('td:nth-child(3)').textContent.trim();
|
||
const thirdLevel = row.querySelector('td:nth-child(4)').textContent.trim();
|
||
const description = row.querySelector('td:nth-child(5)').textContent.trim();
|
||
const sortOrder = row.querySelector('td:nth-child(6)').textContent.trim();
|
||
|
||
// 弹出编辑模态框
|
||
const editModal = `
|
||
<div class="modal" id="editCategoryModal">
|
||
<div class="modal-dialog">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">编辑分类</h5>
|
||
<button type="button" class="close" onclick="closeEditModal()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<form id="editCategoryForm">
|
||
<input type="hidden" id="edit-category-id" value="${categoryId}">
|
||
<div class="form-group">
|
||
<label>一级分类</label>
|
||
<select id="edit-first-level" class="form-control">
|
||
<option value="版本号" ${firstLevel === '版本号' ? 'selected' : ''}>版本号</option>
|
||
<option value="资讯" ${firstLevel === '资讯' ? 'selected' : ''}>资讯</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>二级分类</label>
|
||
<input type="text" id="edit-second-level" class="form-control" value="${secondLevel}">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>三级分类</label>
|
||
<input type="text" id="edit-third-level" class="form-control" value="${thirdLevel}">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>描述</label>
|
||
<input type="text" id="edit-description" class="form-control" value="${description}">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>排序</label>
|
||
<input type="number" id="edit-sort-order" class="form-control" value="${sortOrder}">
|
||
</div>
|
||
</form>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-secondary" onclick="closeEditModal()">取消</button>
|
||
<button type="button" class="btn btn-primary" onclick="saveEditCategory()">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// 插入模态框
|
||
document.body.insertAdjacentHTML('beforeend', editModal);
|
||
};
|
||
|
||
// 关闭编辑模态框
|
||
window.closeEditModal = function() {
|
||
const modal = document.getElementById('editCategoryModal');
|
||
if (modal) modal.remove();
|
||
};
|
||
|
||
// 保存编辑分类
|
||
window.saveEditCategory = async function() {
|
||
const categoryId = document.getElementById('edit-category-id').value;
|
||
const firstLevel = document.getElementById('edit-first-level').value;
|
||
const secondLevel = document.getElementById('edit-second-level').value;
|
||
const thirdLevel = document.getElementById('edit-third-level').value;
|
||
const description = document.getElementById('edit-description').value;
|
||
const sortOrder = document.getElementById('edit-sort-order').value;
|
||
|
||
try {
|
||
const response = await fetch('/admin/wiki/category/update', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
id: categoryId,
|
||
first_level: firstLevel,
|
||
second_level: secondLevel,
|
||
third_level: thirdLevel,
|
||
description: description,
|
||
sort_order: sortOrder
|
||
})
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
// 关闭模态框
|
||
closeEditModal();
|
||
|
||
// 重新加载分类列表
|
||
await renderCategories();
|
||
|
||
// 显示成功消息
|
||
const successDiv = document.createElement('div');
|
||
successDiv.className = 'alert alert-success';
|
||
successDiv.textContent = '分类更新成功';
|
||
document.querySelector('.container').insertBefore(successDiv, document.querySelector('.category-table'));
|
||
|
||
// 3秒后移除成功消息
|
||
setTimeout(() => successDiv.remove(), 3000);
|
||
} else {
|
||
// 显示错误消息
|
||
const errorDiv = document.createElement('div');
|
||
errorDiv.className = 'alert alert-danger';
|
||
errorDiv.textContent = result.error || '更新分类失败';
|
||
document.querySelector('.container').insertBefore(errorDiv, document.querySelector('.category-table'));
|
||
}
|
||
} catch (error) {
|
||
console.error('更新分类时发生错误:', error);
|
||
|
||
// 显示错误消息
|
||
const errorDiv = document.createElement('div');
|
||
errorDiv.className = 'alert alert-danger';
|
||
errorDiv.textContent = `更新分类时发生错误: ${error.message}`;
|
||
document.querySelector('.container').insertBefore(errorDiv, document.querySelector('.category-table'));
|
||
}
|
||
};
|
||
});
|
||
</script>
|
||
{% endblock %} |