Files
ns2.0/templates/admin_wiki_categories.html

541 lines
20 KiB
HTML
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% 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()">&times;</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 %}