Typecho文章列表无刷实现评论显示和回复功能
1年前
第一步在需要引入jquery库,这一步不能少,可以使用本地js或者下面的cdn或者其他的cdn(一般都放在主题中的footer.php)
<script src="https://s3.pstatp.com/cdn/expire-1-M/jquery/3.3.1/jquery.min.js"></script>
第二步把下面的html代码和js代码放到主题相关文件中(一般都放在主题中的footer.php)
html表单
<div class="hidden">
<input class="webSiteHomeUrl" value="<?php echo getWebsiteHomeUrl(); ?>" />
<input id="commentsRequireMail" value="<?php echo $this->options->commentsRequireMail; ?>" />
<input id="commentsRequireURL" value="<?php echo $this->options->commentsRequireURL; ?>" />
<li>
<div class="bg-white p-2 rounded-sm border-1 border-solid border-[#07c160]">
<div class="grid grid-cols-3 gap-2">
<input placeholder="昵称" class="border-0 outline-none bg-color-primary p-1 rounded-sm" />
<input placeholder="网址" class="border-0 outline-none bg-color-primary p-1 rounded-sm" />
<input placeholder="邮箱" class="border-0 outline-none bg-color-primary p-1 rounded-sm" />
</div>
<div class="mt-2">
<input placeholder="回复内容" class="border-0 outline-none w-full rounded-sm p-1" />
</div>
<div class="flex justify-end mt-2">
<div class="face mr-2 cursor-pointer"></div>
<button
class="btn-comment bg-[#07c160] border-0 outline-none text-white cursor-pointer rounded-sm">回复</button>
</div>
</div>
</li>
</div>
js代码
<script>
let globalData = {
webSiteHomeUrl: '',//请求的域名地址
};
window.onload = async () => {
// 网站接口请求地址前缀
globalData.webSiteHomeUrl = document.querySelector('.webSiteHomeUrl').value;
// 点击评论
clickComment();
};
/**
* 点击评论
*/
function clickComment() {
$(".comment-to").off('click');
$(".comment-to").on('click', function (e) {
removeAllCommentForm();
let cid = $(e.target).data('cid');
let coid = $(e.target).data('coid');
let name = $(e.target).data('name');
if (coid == undefined) {
// 如果没有coid,那么就在最下方显示评论框
document.querySelector('.comment-ul-cid-' + cid).insertAdjacentHTML('beforeend', getCommentFormHtml(cid));
} else {
//有coid,在对应评论处显示评论框
document.querySelector('.comment-li-coid-' + coid).insertAdjacentHTML('afterend', getCommentFormHtml(cid, coid, name));
}
// 点击评论回复按钮
$(".btn-comment").off('click');
$(".btn-comment").on('click', function (e) {
let cid = $(e.target).data('cid');
let coid = $(e.target).data('coid');
//邮件地址
let requiredMail = $("#commentsRequireMail").val();
//网站地址
let requiredURL = $("#commentsRequireURL").val();
let author = document.querySelector('.input-author').value;
let url = document.querySelector('.input-url').value;
let mail = document.querySelector('.input-mail').value;
let text = document.querySelector('.input-text').value;
let param = {
cid: cid,
parent: coid,
author: author,
mail: mail,
url: url,
text: text,
};
if (param.author === '') {
alert('昵称不能为空');
return;
}
if (requiredMail == 1 && param.mail === '') {
alert('邮件不能为空');
return;
}
if (requiredURL == 1 && param.url === '') {
alert('网址不能为空');
return;
}
if (param.text === '') {
alert('评论内容不能为空');
return;
}
// 记录信息到localStorage
window.localStorage.setItem('author', author);
window.localStorage.setItem('mail', mail);
window.localStorage.setItem('url', url);
$.ajax({
url: globalData.webSiteHomeUrl + '/api/comment',
type: 'POST',
data: param,
headers: {
'content-type': 'application/x-www-form-urlencoded'
},
success: function(response) {
response = $.parseJSON(response)
console.log(response);
if (response.status == 1) {
removeAllCommentForm();
let waiting = '';
// 把评论显示在对应位置
if (response.comment.status == 'waiting') {
// 显示待审核
waiting = '<span class="comment-waiting">待审核</span>';
}
if (param.parent > 0) {
//有coid,在对应评论处显示评论框
document.querySelector('.comment-li-coid-' + param.parent).insertAdjacentHTML('afterend', `
<li class="pos-rlt comment-li-coid-${response.comment.coid}">
<div class="comment-body">
<span class="text-[14px] text-color-link">
<a href="${response.comment.url}" target="_blank" class="cursor-pointer text-color-link no-underline">${response.comment.author}</a>
</span>
<span class="text-[14px]">回复</span>
<span class="text-[14px] text-color-link">${name}</span>
<span data-separator=":" class="before:content-[attr(data-separator)] text-[14px] cursor-help comment-to" data-coid="${response.comment.coid}" data-cid="${response.comment.cid}" data-name="${response.comment.author}">${param.text}</span>
${waiting}
</div>
</li>`);
} else {
// 如果没有coid,那么就在最下方显示评论框
document.querySelector('.comment-ul-cid-' + param.cid).insertAdjacentHTML('beforeend', `
<li class="pos-rlt comment-li-coid-${response.comment.coid}">
<div class="comment-body">
<span class="text-[14px] text-color-link">
<a href="${response.comment.url}" target="_blank" class="cursor-pointer text-color-link no-underline">${response.comment.author}</a>
</span>
<span data-separator=":" class="before:content-[attr(data-separator)] text-[14px] cursor-help comment-to" data-coid="${response.comment.coid}" data-cid="${response.comment.cid}" data-name="${response.comment.author}">${param.text}</span>
${waiting}
</div>
</li>
`);
}
} else {
// 评论异常,弹出进行提醒
alert('回复失败');
}
},
error: function(xhr, status, error) {
console.log(error);
// 处理错误
alert('系统异常,请稍候重试');
}
});
});
});
}
/**
* 获取评论框Html
*/
function getCommentFormHtml(cid, coid, name) {
let author = window.localStorage.getItem('author');
let mail = window.localStorage.getItem('mail');
let url = window.localStorage.getItem('url');
if (author == null) {
author = '';
}
if (mail == null) {
mail = '';
}
if (url == null) {
url = '';
}
let placeholder = '回复内容';
if (coid) {
placeholder = '回复@' + name;
}
return `
<li class="comment-form" data-cid="${cid}" data-coid="${coid}">
<div class="bg-white p-2 rounded-sm border-1 border-solid border-[#07c160]">
<div class="grid grid-cols-3 gap-2">
<input placeholder="昵称" class="border-0 outline-none bg-color-primary p-1 rounded-sm input-author" data-cid="${cid}" data-coid="${coid}" value="${author}" />
<input placeholder="网址" class="border-0 outline-none bg-color-primary p-1 rounded-sm input-url" data-cid="${cid}" data-coid="${coid}" value="${url}" />
<input placeholder="邮箱" class="border-0 outline-none bg-color-primary p-1 rounded-sm input-mail" data-cid="${cid}" data-coid="${coid}" value="${mail}" />
</div>
<div class="mt-2">
<input placeholder="${placeholder}" class="border-0 outline-none w-full rounded-sm p-1 input-text" data-cid="${cid}" data-coid="${coid}" />
</div>
<div class="flex justify-end mt-2">
<div class="face mr-2 cursor-pointer"></div>
<button class="btn-comment bg-[#07c160] border-0 outline-none text-white cursor-pointer rounded-sm" data-cid="${cid}" data-coid="${coid}">回复</button>
</div>
</div>
</li>
`;
}
/**
* 移除其他评论框
*/
function removeAllCommentForm() {
$(".comment-form").remove();
}
</script>
第三步在把下面的代码添加到相关文件中,(一般index.php),找到这段代码“<?php while ($this->next()):
?>//此次省略无数代码<?php endwhile; ?>”代码里面
<div class="index-comments">
<ul class="comment-ul-cid-<?php echo $this->cid; ?> comment-ul">
<?php foreach (listcomment($this->cid,6) as $comment): ?>
<li class="pos-rlt comment-li-coid-<?php echo $comment['coid'] ?>">
<div class="comment-body">
<span class="text-color-link">
<a href="<?php echo $comment['url'] ?>" target="_blank" class="text-color-link">
<?php echo $comment['author']; ?><?php if ($comment['authorId'] == $this->authorId) { ?><span class="zuozhe">作者</span><?php } ?>
</a><?php echo reply($comment['parent'],$this->authorId);?>:
</span>
<span data-separator=":" class="comment-to" data-coid="<?php echo $comment['coid'] ?>" data-cid="<?php echo $comment['cid'] ?>" data-name="<?php echo $comment['author'] ?>">
<?php echo strip_tags(preg_replace("/<br>|<p>|<\/p>/", ' ', $comment['text'])) ?>
</span>
</div>
</li>
<?php endforeach; ?>
</ul>
</div>
第四步把下面的代码添加到相关文件中(一般function.php)
// 主题初始化
function themeInit($self)
{
$route = $self->request->getPathInfo();
/* 主题开放API 路由规则 */
if ($self->request->isPost()) {
switch ($route) {
case '/api/comment':
addComment($self);
break;
}
}
}
/**
* 获取网站首页地址。
*/
function getWebsiteHomeUrl()
{
$rewrite = Helper::options()->rewrite;
$tmpUrl = Helper::options()->siteUrl;
if (!$rewrite) {
$tmpUrl = $tmpUrl . 'index.php';
}
return $tmpUrl;
}
/**
* 用于列表文章查询相关文章评论
* 通过文章cid查询评论数据函数
* 参数一 文章cid
* 参数二 要查询多少条数据
*/
function listcomment($archive,$limits = 3){
$db = Typecho_Db::get();
return $db->fetchAll($db->select()->from('table.comments')
->where("cid = ? AND status = 'approved' AND type = 'comment'", $archive)
->limit($limits)->order('created', Typecho_Db::SORT_DESC)
);
}
//评论加@或者回复
function reply($parent,$authorId) {
if ($parent == 0) {
return '';
}
$db = Typecho_Db::get();
$commentInfo = $db->fetchRow($db->select('authorId,author,status,mail')->from('table.comments')->where('coid = ?', $parent));
$zuozhe = "";
if ($commentInfo['authorId'] == $authorId) {
$zuozhe = "<span class='zuozhe'>作者</span>";
}
$link = ' 回复 <span class="huifu">'.$commentInfo['author'].$zuozhe.'</span>';
return $link;
}
/**
* 添加评论
*/
function addComment($self)
{
$self->response->setStatus(200);
$options = Helper::options();
$user = Typecho_Widget::widget('Widget_User');
$db = Typecho_Db::get();
// // Security 验证不通过时会直接跳转,所以需要自己进行判断
// // 需要开启反垃圾保护,此时将不验证来源
// if ($self->request->get('_') != Helper::security()->getToken($self->request->getReferer())) {
// $self->response->throwJson(array('status' => 0, 'msg' => _t('非法请求')));
// }
/** 检查ip评论间隔 */
if (!$user->pass('editor', true) && $self->authorId != $user->uid && $options->commentsPostIntervalEnable) {
$latestComment = $db->fetchRow($db->select('created')->from('table.comments')
->where('cid = ?', $self->request->cid)
->where('ip = ?', $self->request->getIp())
->order('created', Typecho_Db::SORT_DESC)
->limit(1));
if ($latestComment && ($options->gmtTime - $latestComment['created'] > 0 && $options->gmtTime - $latestComment['created'] < $options->commentsPostInterval)) {
$self->response->throwJson(array('status' => 0, 'msg' => _t('对不起, 您的发言过于频繁, 请稍侯再次发布')));
}
}
$comment = array(
'cid' => $self->request->cid,
'created' => $options->gmtTime,
'agent' => $self->request->getAgent(),
'ip' => $self->request->getIp(),
'ownerId' => $self->author->uid,
'type' => 'comment',
'status' => $options->commentsRequireModeration ? 'waiting' : 'approved'
);
/** 判断父节点 */
if ($parentId = $self->request->filter('int')->get('parent')) {
if ($options->commentsThreaded && ($parent = $db->fetchRow($db->select('coid', 'cid')->from('table.comments')->where('coid = ?', $parentId))) && $self->request->cid == $parent['cid']) {
$comment['parent'] = $parentId;
} else {
$self->response->throwJson(array('status' => 0, 'msg' => _t('父级评论不存在')));
}
}
$feedback = Typecho_Widget::widget('Widget_Feedback');
//检验格式
$validator = new Typecho_Validate();
$validator->addRule('author', 'required', _t('必须填写用户名'));
$validator->addRule('author', 'xssCheck', _t('请不要在用户名中使用特殊字符'));
$validator->addRule('author', array($feedback, 'requireUserLogin'), _t('您所使用的用户名已经被注册,请登录后再次提交'));
$validator->addRule('author', 'maxLength', _t('用户名最多包含200个字符'), 200);
if ($options->commentsRequireMail && !$user->hasLogin()) {
$validator->addRule('mail', 'required', _t('必须填写电子邮箱地址'));
}
$validator->addRule('mail', 'email', _t('邮箱地址不合法'));
$validator->addRule('mail', 'maxLength', _t('电子邮箱最多包含200个字符'), 200);
if ($options->commentsRequireURL && !$user->hasLogin()) {
$validator->addRule('url', 'required', _t('必须填写个人主页'));
}
$validator->addRule('url', 'url', _t('个人主页地址格式错误'));
$validator->addRule('url', 'maxLength', _t('个人主页地址最多包含200个字符'), 200);
$validator->addRule('text', 'required', _t('必须填写评论内容'));
$comment['text'] = $self->request->text;
/** 对一般匿名访问者,将用户数据保存一个月 */
if (!$user->hasLogin()) {
/** Anti-XSS */
$comment['author'] = $self->request->filter('trim')->author;
$comment['mail'] = $self->request->filter('trim')->mail;
$comment['url'] = $self->request->filter('trim')->url;
/** 修正用户提交的url */
if (!empty($comment['url'])) {
$urlParams = parse_url($comment['url']);
if (!isset($urlParams['scheme'])) {
$comment['url'] = 'http://' . $comment['url'];
}
}
$expire = $options->gmtTime + $options->timezone + 30 * 24 * 3600;
Typecho_Cookie::set('__typecho_remember_author', $comment['author'], $expire);
Typecho_Cookie::set('__typecho_remember_mail', $comment['mail'], $expire);
Typecho_Cookie::set('__typecho_remember_url', $comment['url'], $expire);
} else {
$comment['author'] = $user->screenName;
$comment['mail'] = $user->mail;
$comment['url'] = $user->url;
/** 记录登录用户的id */
$comment['authorId'] = $user->uid;
}
/** 如果系统设置为不需要审核,并且评论者之前须有评论通过了审核 */
if (!$options->commentsRequireModeration && $options->commentsWhitelist) {
if ($feedback->size($feedback->select()->where('author = ? AND mail = ? AND status = ?', $comment['author'], $comment['mail'], 'approved'))) {
$comment['status'] = 'approved'; // 评论者之前有评论通过了审核设置为审核通过
} else {
$comment['status'] = 'waiting'; // 没有的话评论设置为待审核
}
}
if ($error = $validator->run($comment)) {
$self->response->throwJson(array('status' => 0, 'msg' => implode(';', $error)));
}
/** 添加评论 */
$commentId = $feedback->insert($comment);
if (!$commentId) {
$self->response->throwJson(array('status' => 0, 'msg' => _t('评论失败')));
}
Typecho_Cookie::delete('__typecho_remember_text');
$db->fetchRow($feedback->select()->where('coid = ?', $commentId)
->limit(1), array($feedback, 'push'));
// 返回评论数据
$data = array(
'cid' => $feedback->cid,
'coid' => $feedback->coid,
'parent' => $feedback->parent,
'mail' => $feedback->mail,
'url' => $feedback->url,
'ip' => $feedback->ip,
'agent' => $feedback->agent,
'author' => $feedback->author,
'authorId' => $feedback->authorId,
'permalink' => $feedback->permalink,
'created' => $feedback->created,
'datetime' => $feedback->date->format('Y-m-d H:i:s'),
'status' => $feedback->status,
);
// 评论内容
ob_start();
$feedback->content();
$data['content'] = ob_get_clean();
$data['avatar'] = Typecho_Common::gravatarUrl($data['mail'], 48, Helper::options()->commentsAvatarRating, NULL, $self->request->isSecure());
$self->response->throwJson(array('status' => 1, 'comment' => $data));
}
此处代码取自icefox主题地址:https://github.com/xiaopanglian/icefox/
林风
11个月前 . LV.0
博主,新年好,我用了上述代码,但是为啥提交之后没有写入数据库,只在页面上有显示,控制台也不报错,就是没有插入数据库
寻梦xunm
11个月前 . LV.0
@林风
如何你网站开启了调试模式就会提交不成功,看一看根目录下面config.inc.php文件中是否有这段代码“define('__TYPECHO_DEBUG__', TRUE);”有的话注释掉或者删除。
小归客
1年前 . LV.1
真666
寻梦xunm
1年前 . LV.0
@小归客
用别人写的代码水的文章
kk
1年前 . LV.0
你都叫我王子了我能不来吗
寻梦xunm
1年前 . LV.0
@kk
滑稽
山猫日记
1年前 . LV.1
又是来大佬网站打卡的一天。
寻梦xunm
1年前 . LV.0
@山猫日记
感谢IP
Q
1年前 . LV.0
Hhhhh你好
寻梦xunm
1年前 . LV.0
@Q
你好
晨狐戏语
1年前 . LV.0
get一个新姿势
寻梦xunm
1年前 . LV.0
@晨狐戏语
嘿嘿