381 lines
16 KiB
Plaintext
381 lines
16 KiB
Plaintext
<div class="card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">文章管理</h5>
|
|
<div>
|
|
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addPostModal">
|
|
<i class="bi bi-plus"></i> 添加文章
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>标题</th>
|
|
<th>分类</th>
|
|
<th>作者</th>
|
|
<th>发布时间</th>
|
|
<th>操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<% posts.forEach(post => { %>
|
|
<tr>
|
|
<td>
|
|
<% if (post.isTop) { %>
|
|
<span class="badge bg-danger me-1">置顶</span>
|
|
<% } %>
|
|
<%= post.title %>
|
|
</td>
|
|
<td><%= post.category %></td>
|
|
<td><%= post.author %></td>
|
|
<td><%= new Date(post.createdAt).toLocaleString() %></td>
|
|
<td class="action-btns">
|
|
<button class="btn btn-sm btn-primary edit-post-btn" data-id="<%= post._id %>">编辑</button>
|
|
<button class="btn btn-sm btn-danger delete-post-btn" data-id="<%= post._id %>">删除
|
|
</button>
|
|
<% if (post.isTop) { %>
|
|
<button class="btn btn-sm btn-secondary cancel-post-top-btn" data-id="<%= post._id %>">
|
|
取消置顶
|
|
</button>
|
|
<% } else { %>
|
|
<button class="btn btn-sm btn-warning set-post-top-btn" data-id="<%= post._id %>">
|
|
设为置顶
|
|
</button>
|
|
<% } %>
|
|
<% if (post.isPublished) { %>
|
|
<button class="btn btn-sm btn-secondary cancel-publish-btn" data-id="<%= post._id %>">
|
|
取消发布
|
|
</button>
|
|
<% } else { %>
|
|
<button class="btn btn-sm btn-success publish-btn" data-id="<%= post._id %>">发布
|
|
</button>
|
|
<% } %>
|
|
</td>
|
|
</tr>
|
|
<% }); %>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<!-- 分页组件 -->
|
|
<%- include('../../components/pagination', {
|
|
currentPage: pagination.currentPage,
|
|
totalPages: pagination.totalPages,
|
|
baseUrl: baseUrl,
|
|
query: query
|
|
}) %>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 添加文章模态框 -->
|
|
<div class="modal fade" id="addPostModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">添加文章</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form id="addPostForm">
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">文章标题</label>
|
|
<input type="text" class="form-control" name="title" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">文章内容</label>
|
|
<textarea class="form-control" name="content" rows="10" required></textarea>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">分类</label>
|
|
<select class="form-control" name="category" required>
|
|
<option value="">请选择分类</option>
|
|
<option value="技术">技术</option>
|
|
<option value="随笔">随笔</option>
|
|
<option value="教程">教程</option>
|
|
<option value="数据库">数据库</option>
|
|
<option value="工具">工具</option>
|
|
<option value="未分类">未分类</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">作者</label>
|
|
<input type="text" class="form-control" name="author"
|
|
value="<%= user ? user.username : '' %>" required>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-check mb-3">
|
|
<input class="form-check-input" type="checkbox" name="isPublished" id="isPublished"
|
|
checked>
|
|
<label class="form-check-label" for="isPublished">立即发布</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-check mb-3">
|
|
<input class="form-check-input" type="checkbox" name="isTop" id="isTop">
|
|
<label class="form-check-label" for="isTop">置顶文章</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
|
<button type="submit" class="btn btn-primary">保存</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 编辑文章模态框 -->
|
|
<div class="modal fade" id="editPostModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">编辑文章</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form id="editPostForm">
|
|
<input type="hidden" name="postId" id="editPostId">
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">文章标题</label>
|
|
<input type="text" class="form-control" name="title" id="editTitle" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">文章内容</label>
|
|
<textarea class="form-control" name="content" id="editContent" rows="10" required></textarea>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">分类</label>
|
|
<select class="form-control" name="category" id="editCategory" required>
|
|
<option value="">请选择分类</option>
|
|
<option value="技术">技术</option>
|
|
<option value="随笔">随笔</option>
|
|
<option value="教程">教程</option>
|
|
<option value="数据库">数据库</option>
|
|
<option value="工具">工具</option>
|
|
<option value="未分类">未分类</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">作者</label>
|
|
<input type="text" class="form-control" name="author" id="editAuthor" required>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-check mb-3">
|
|
<input class="form-check-input" type="checkbox" name="isPublished" id="editIsPublished">
|
|
<label class="form-check-label" for="editIsPublished">发布文章</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-check mb-3">
|
|
<input class="form-check-input" type="checkbox" name="isTop" id="editIsTop">
|
|
<label class="form-check-label" for="editIsTop">置顶文章</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
|
<button type="submit" class="btn btn-primary">保存</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// 添加文章
|
|
document.getElementById('addPostForm').addEventListener('submit', function (e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(this);
|
|
const data = {
|
|
title: formData.get('title'),
|
|
content: formData.get('content'),
|
|
category: formData.get('category'),
|
|
author: formData.get('author'),
|
|
isPublished: formData.get('isPublished') === 'on',
|
|
isTop: formData.get('isTop') === 'on'
|
|
};
|
|
|
|
fetch('/admin/posts', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(data)
|
|
})
|
|
.then(response => response.json())
|
|
.then(result => {
|
|
if (result.success) {
|
|
alert('添加成功!');
|
|
location.reload();
|
|
} else {
|
|
alert('添加失败:' + result.message);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error(error);
|
|
alert('添加失败');
|
|
});
|
|
});
|
|
|
|
// 编辑文章
|
|
document.querySelectorAll('.edit-post-btn').forEach(btn => {
|
|
btn.addEventListener('click', function () {
|
|
const postId = this.getAttribute('data-id');
|
|
// 获取文章数据
|
|
fetch(`/admin/posts/${postId}`)
|
|
.then(response => response.json())
|
|
.then(post => {
|
|
// 填充表单
|
|
document.getElementById('editPostId').value = post._id;
|
|
document.getElementById('editTitle').value = post.title;
|
|
document.getElementById('editContent').value = post.content;
|
|
document.getElementById('editCategory').value = post.category;
|
|
document.getElementById('editAuthor').value = post.author;
|
|
document.getElementById('editIsPublished').checked = post.isPublished;
|
|
document.getElementById('editIsTop').checked = post.isTop;
|
|
// 显示模态框
|
|
const editModal = new bootstrap.Modal(document.getElementById('editPostModal'));
|
|
editModal.show();
|
|
})
|
|
.catch(error => {
|
|
console.error(error);
|
|
alert('获取文章信息失败');
|
|
});
|
|
});
|
|
});
|
|
|
|
// 提交编辑表单
|
|
document.getElementById('editPostForm').addEventListener('submit', function (e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(this);
|
|
const postId = formData.get('postId');
|
|
const data = {
|
|
title: formData.get('title'),
|
|
content: formData.get('content'),
|
|
category: formData.get('category'),
|
|
author: formData.get('author'),
|
|
isPublished: formData.get('isPublished') === 'on',
|
|
isTop: formData.get('isTop') === 'on'
|
|
};
|
|
|
|
fetch(`/admin/posts/${postId}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(data)
|
|
})
|
|
.then(response => response.json())
|
|
.then(result => {
|
|
if (result.success) {
|
|
alert('更新成功!');
|
|
location.reload();
|
|
} else {
|
|
alert('更新失败:' + result.message);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error(error);
|
|
alert('更新失败');
|
|
});
|
|
});
|
|
|
|
// 删除文章
|
|
document.querySelectorAll('.delete-post-btn').forEach(btn => {
|
|
btn.addEventListener('click', function () {
|
|
const postId = this.getAttribute('data-id');
|
|
if (confirm('确定要删除这篇文章吗?')) {
|
|
fetch(`/admin/posts/${postId}`, {
|
|
method: 'DELETE'
|
|
})
|
|
.then(response => response.json())
|
|
.then(result => {
|
|
if (result.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('删除失败');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error(error);
|
|
alert('删除失败');
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
// 设置/取消置顶
|
|
document.querySelectorAll('.set-post-top-btn, .cancel-post-top-btn').forEach(btn => {
|
|
btn.addEventListener('click', function () {
|
|
const postId = this.getAttribute('data-id');
|
|
const isTop = this.classList.contains('set-post-top-btn');
|
|
|
|
fetch(`/admin/posts/${postId}/top`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({isTop})
|
|
})
|
|
.then(response => response.json())
|
|
.then(result => {
|
|
if (result.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('操作失败');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error(error);
|
|
alert('操作失败');
|
|
});
|
|
});
|
|
});
|
|
|
|
// 发布/取消发布
|
|
document.querySelectorAll('.publish-btn, .cancel-publish-btn').forEach(btn => {
|
|
btn.addEventListener('click', function () {
|
|
const postId = this.getAttribute('data-id');
|
|
const isPublished = this.classList.contains('publish-btn');
|
|
|
|
fetch(`/admin/posts/${postId}/publish`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({isPublished})
|
|
})
|
|
.then(response => response.json())
|
|
.then(result => {
|
|
if (result.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('操作失败');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error(error);
|
|
alert('操作失败');
|
|
});
|
|
});
|
|
});
|
|
</script>
|