评论变为ajax加载 和提交
This commit is contained in:
@@ -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);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user