评论变为ajax加载 和提交

This commit is contained in:
2026-08-20 18:04:42 +08:00
parent d4d009f8b2
commit 6e88700f96
8 changed files with 345 additions and 217 deletions
+182
View File
@@ -0,0 +1,182 @@
/**
* 前台评论 AJAX 功能
*/
// 评论分页加载
async function loadComments(postSlug, page = 1) {
try {
const url = `/posts/${postSlug}/comments?page=${page}`;
const res = await fetch(url);
if (!res.ok) throw new Error('加载评论失败');
const data = await res.json();
renderComments(data.data || [], postSlug);
renderCommentPagination(data.page, data.total_pages, postSlug);
} catch (err) {
console.error('加载评论失败:', err);
document.querySelector('.comments-list').innerHTML = '<p class="no-comments">评论加载失败</p>';
}
}
// 渲染评论列表
function renderComments(comments, postSlug) {
const listEl = document.querySelector('.comments-list');
if (!listEl) return;
if (!comments || comments.length === 0) {
listEl.innerHTML = '<p class="no-comments">暂无评论,来发表第一条评论吧!</p>';
return;
}
listEl.innerHTML = comments.map(c => buildCommentHTML(c)).join('');
}
// 构建单条评论 HTML
function buildCommentHTML(comment) {
let html = `
<div class="comment-item" id="comment-${comment.id}">
<div class="comment-header">
<span class="comment-author">
${comment.website ? `<a href="${escapeHtml(comment.website)}" target="_blank">${escapeHtml(comment.author)}</a>` : escapeHtml(comment.author)}
</span>
<span class="comment-date">${formatDate(comment.created_at)}</span>
</div>
<div class="comment-content">${escapeHtml(comment.content)}</div>
`;
// 子评论
if (comment.children && comment.children.length > 0) {
html += '<div class="comment-children">';
comment.children.forEach(child => {
html += `
<div class="comment-item child" id="comment-${child.id}">
<div class="comment-header">
<span class="comment-author">${escapeHtml(child.author)}</span>
<span class="comment-date">${formatDate(child.created_at)}</span>
</div>
<div class="comment-content">${escapeHtml(child.content)}</div>
</div>
`;
});
html += '</div>';
}
html += '</div>';
return html;
}
// 渲染评论分页
function renderCommentPagination(currentPage, totalPages, postSlug) {
const paginationEl = document.querySelector('.comment-pagination');
if (!paginationEl) return;
if (totalPages <= 1) {
paginationEl.style.display = 'none';
return;
}
paginationEl.style.display = 'block';
let html = '';
if (currentPage > 1) {
html += `<a href="javascript:void(0)" onclick="loadComments('${postSlug}', ${currentPage - 1})" class="prev">← 上一页</a>`;
}
html += `<span class="page-info">${currentPage} / ${totalPages}</span>`;
if (currentPage < totalPages) {
html += `<a href="javascript:void(0)" onclick="loadComments('${postSlug}', ${currentPage + 1})" class="next">下一页 →</a>`;
}
paginationEl.innerHTML = html;
}
// 提交评论(AJAX)
async function submitComment(e) {
e.preventDefault();
const form = e.target;
const formData = new FormData(form);
const data = Object.fromEntries(formData.entries());
data.post_id = Number(data.post_id);
// 简单验证
if (!data.author || !data.email || !data.content) {
alert('请填写必填字段');
return;
}
// 邮箱格式验证
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(data.email)) {
alert('请输入有效的邮箱地址');
return;
}
const submitBtn = form.querySelector('button[type="submit"]');
submitBtn.disabled = true;
submitBtn.textContent = '提交中...';
try {
const res = await fetch(`/posts/${data.post_id}/comment`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const result = await res.json();
if (res.ok) {
alert('评论提交成功,等待审核');
form.reset();
// 重新加载评论(回到第一页)
const postSlug = window.location.pathname.split('/').pop();
loadComments(postSlug, 1);
} else {
alert(result.error || '提交失败');
}
} catch (err) {
console.error('提交评论失败:', err);
alert('网络错误,请稍后重试');
} finally {
submitBtn.disabled = false;
submitBtn.textContent = '发表评论';
}
}
// 工具函数:转义 HTML
function escapeHtml(text) {
if (!text) return '';
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 工具函数:格式化日期
function formatDate(dateStr) {
if (!dateStr) return '';
const date = new Date(dateStr);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hour = String(date.getHours()).padStart(2, '0');
const minute = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}`;
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
// 绑定评论表单提交事件
const commentForm = document.querySelector('.comment-form');
if (commentForm) {
commentForm.addEventListener('submit', submitComment);
}
// 如果当前是文章详情页,自动加载评论
const postSlug = window.location.pathname.match(/\/post\/([^\/]+)/);
if (postSlug && postSlug[1]) {
// 从 URL 获取初始页码
const urlParams = new URLSearchParams(window.location.search);
const cpage = parseInt(urlParams.get('cpage')) || 1;
loadComments(postSlug[1], cpage);
}
});