多年前有人写了如何开发wordpress的碎碎念(链接),虽然已经10年前的代码了,但是依旧可以工作,我这边增加了行内评论显示和回复功能。让他更加像微博和朋友圈,具体的效果:https://jaketao.com/notes
具体代码分享如下:
第一步:制作碎碎念(原碎碎念制作方案,稍做改进)
1.1 新建post type
创建一个post-type.php文件放到你主题根目录(如果后台不显示,在functions里面引用下),内容是:
<?php
add_action('init', 'my_custom_init');
function my_custom_init()
{
$labels = array(
'name' => '碎语',
'singular_name' => 'Talk',
'add_new' => '发表新碎语',
'add_new_item' => '发表新碎语',
'edit_item' => '编辑碎语',
'new_item' => '新碎语',
'view_item' => '查看碎语',
'search_items' => '搜索碎语',
'not_found' => '暂无碎语',
'not_found_in_trash' => '没有已遗弃的碎语',
'parent_item_colon' => '',
'menu_name' => '碎语'
);
$args = array(
'labels' => $labels,
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => true,
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => null,
'supports' => array('title','editor','author','comment')
);
register_post_type('talk',$args);
}
?>
1.2 写一个前端页面
复制一个page.php,命名为page-talk.php然后放在目录内,头部改成
<?php /* Template Name: 碎碎念 */ ?>
然后在按照你的模板修改,以下是例子,记得增加对应的css在style.css
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
<?php $limit = get_option('posts_per_page');$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;query_posts('post_type=talk&post_status=publish&showposts=' . $limit=20 . '&paged=' . $paged);if (have_posts()) : while (have_posts()) : the_post(); ?>
<div class="talklist">
<span class="talkcontent">
<?php the_content(); ?>
</span>
<span>
<p class="talktime">
<?php _e('Published by','themejunkie'); ?> <?php the_author_posts_link(); ?>
<?php the_time('Y年n月j日G:i'); ?>
</p>
</span>
</div>
<?php endwhile;endif; ?>
<?php if (function_exists('wp_pagenavi')) wp_pagenavi();else { ?><div class="talkpagenavi"><?php previous_posts_link('【« 上一页】') ?> <?php next_posts_link('【下一页 »】') ?></div><?php } ?>
<?php wp_reset_query(); ?>
1.3 后台创建页面
在wordpress后台 – 页面 – 增加一个页面,使用你刚才写的模板(名字叫碎碎念),这样第一步就基本完成。
如果页面样式不符合你想要的,可以自行修改page-talk页面
第二步:增加页面评论、点赞功能
现在做的post-type其实还是一个文章,如果需要实现朋友圈、微博那样的页面看评论,需要做出以下修改:
2.1 修改functions.php
可以直接增加,或者创建一个新的functions-talk.php(记得引用)
// ==================================================
// AJAX:拉取评论
// ==================================================
add_action('wp_ajax_load_post_comments', 'load_post_comments_ajax');
add_action('wp_ajax_nopriv_load_post_comments', 'load_post_comments_ajax');
function load_post_comments_ajax() {
$post_id = intval($_POST['post_id'] ?? 0);
if (!$post_id) {
wp_send_json_error('post_id missing');
}
$comments = get_comments([
'post_id' => $post_id,
'status' => 'approve',
'orderby' => 'comment_date',
'order' => 'ASC',
]);
$data = [];
foreach ($comments as $c) {
$data[] = [
'id' => $c->comment_ID,
'parent' => $c->comment_parent,
'author' => $c->comment_author,
'content' => wpautop($c->comment_content),
'date' => $c->comment_date,
'post_id' => $c->comment_post_ID,
'user_id' => $c->user_id ?? 0,
'avatar' => get_avatar_url($c->user_id ?? 0, ['size' => 60]),
'user_url' => $c->comment_author_url ?: '',
'parent_author' => $c->comment_parent
? get_comment($c->comment_parent)->comment_author
: '',
'parent_user_id' => $c->comment_parent
? get_comment($c->comment_parent)->user_id
: 0,
'parent_url' => $c->comment_parent
? get_comment_link($c->comment_parent)
: '',
'children' => [],
];
}
// 构建嵌套评论树
$nest_comments = function (array $comments) {
$tree = [];
$map = [];
foreach ($comments as &$c) {
$map[$c['id']] = &$c;
$c['children'] = [];
}
foreach ($comments as &$c) {
if ($c['parent'] && isset($map[$c['parent']])) {
$map[$c['parent']]['children'][] = &$c;
} else {
$tree[] = &$c;
}
}
return $tree;
};
wp_send_json_success($nest_comments($data));
}
// ==================================================
// AJAX:加载评论表单
// ==================================================
add_action('wp_ajax_load_comment_form', 'load_comment_form_ajax');
add_action('wp_ajax_nopriv_load_comment_form', 'load_comment_form_ajax');
function load_comment_form_ajax() {
$post_id = intval($_POST['post_id'] ?? 0);
if (!$post_id) {
wp_send_json_error();
}
ob_start();
comment_form([
'title_reply' => '发表回复',
'comment_notes_before' => '',
'comment_notes_after' => '',
'logged_in_as' => '',
'label_submit' => '提交',
'id_form' => 'commentform',
'class_form' => 'comment-form',
'comment_field' => '
<div class="comment-form-comment">
<textarea
id="comment"
name="comment"
class="required"
rows="4"
placeholder="写下你的评论…"
required
></textarea>
<div class="comment-form-smile j-smilies" data-target="#comment">
<i class="wpcom-icon wi smile-icon">
<svg aria-hidden="true">
<use xlink:href="#wi-emotion"></use>
</svg>
</i>
</div>
</div>
',
], $post_id);
$form_html = ob_get_clean();
// 移除最外层 #respond(如存在)
$form_html = preg_replace(
'/<div id="respond" class="comment-respond">|<\/div>$/',
'',
$form_html
);
wp_send_json_success($form_html);
}
// ==================================================
// AJAX:提交评论
// ==================================================
add_action('wp_ajax_submit_comment', 'submit_comment_ajax');
add_action('wp_ajax_nopriv_submit_comment', 'submit_comment_ajax');
function submit_comment_ajax() {
if (!is_user_logged_in()) {
wp_send_json_error('need_login');
}
$user = wp_get_current_user();
$commentdata = [
'comment_post_ID' => intval($_POST['post_id']),
'comment_content' => trim($_POST['content']),
'comment_parent' => intval($_POST['parent']),
'user_id' => $user->ID,
'comment_author' => $user->display_name,
'comment_author_email' => $user->user_email,
'comment_author_url' => $user->user_url,
];
$comment_id = wp_new_comment($commentdata);
if (!$comment_id) {
wp_send_json_error('insert_failed');
}
wp_send_json_success([
'comment_id' => $comment_id,
]);
}
// ==================================================
// 前端脚本注册(仅碎碎念列表页)
// ==================================================
add_action('wp_enqueue_scripts', 'enqueue_post_comments_js');
function enqueue_post_comments_js() {
if (!is_page(9890)) {
return;
}
wp_enqueue_script(
'post-comments',
get_stylesheet_directory_uri() . '/js/post-comments.js',
['jquery'],
null,
true
);
wp_localize_script(
'post-comments',
'ajaxurl',
admin_url('admin-ajax.php')
);
}
// ==================================================
// 自定义提交按钮外观(仅碎碎念页)
// ==================================================
add_filter('comment_form_submit_field', function ($submit_field) {
if (!is_page(9890)) {
return $submit_field;
}
preg_match_all('/<input type="hidden".*?>/i', $submit_field, $matches);
$hidden = implode("\n", $matches[0]);
if (is_user_logged_in()) {
$user = wp_get_current_user();
$avatar = get_avatar($user->ID, 60);
$name = esc_html($user->display_name);
} else {
$avatar = '';
$name = '';
}
return '
<div class="form-submit">
<div class="pull-left form-submit-text">
' . $avatar . '
<span>' . $name . '</span>
</div>
<button
name="submit"
type="submit"
id="submit"
class="wpcom-btn btn-primary btn-xs submit"
>
提交
</button>
' . $hidden . '
</div>';
}, 20);
这里直接用AJAX重新弄了一个评论区域并且强制需要登陆才能回贴,如果想直接使用原生态的,可以自行修改。另外我是直接用了wpcom的css,如果需要自行修改可以改下class和css文件
2.2 增加post-comments.js文件
我的路径是js/post-commnents.js,需要修改自行修改
jQuery(function ($) {
/* ==================================================
* 展开 / 加载评论
* ================================================== */
$('.toggle-comments').on('click', function () {
const postId = $(this).data('post-id');
const box = $('#comment-box-' + postId);
const list = box.find('.comments-list');
// 已加载过:仅切换显示
if (box.data('loaded')) {
box.toggle();
return;
}
$.post(ajaxurl, {
action: 'load_post_comments',
post_id: postId
}, function (res) {
if (!res.success) return;
// 最新评论在前
const sortedComments = res.data.sort(
(a, b) => new Date(b.date) - new Date(a.date)
);
list.html(renderCommentsNested(sortedComments));
box.show().data('loaded', true);
// 更新评论数量
box.find('.comments-title')
.text(`评论列表(${res.data.length}条)`);
// 仅加载一次评论表单
loadCommentForm(postId, box);
});
});
/* ==================================================
* AJAX 加载评论表单
* ================================================== */
function loadCommentForm(postId, box) {
const respond = box.find('#respond');
if (respond.data('loaded')) return;
$.post(ajaxurl, {
action: 'load_comment_form',
post_id: postId
}, function (res) {
if (!res.success) return;
respond.html(res.data);
const form = respond.find('#commentform');
if (!form.length) return;
// comment_post_ID
if (!form.find('input[name="comment_post_ID"]').length) {
form.append(
`<input type="hidden" name="comment_post_ID" value="${postId}">`
);
} else {
form.find('input[name="comment_post_ID"]').val(postId);
}
// comment_parent
if (!form.find('input[name="comment_parent"]').length) {
form.append(
`<input type="hidden" name="comment_parent" value="0">`
);
} else {
form.find('input[name="comment_parent"]').val(0);
}
respond.data('loaded', true);
});
}
/* ==================================================
* 渲染嵌套评论(不滚动)
* ================================================== */
function renderCommentsNested(comments, parentId = 0, depth = 1) {
let html = '';
comments
.filter(c => c.parent == parentId)
.forEach((c, index) => {
const isEven = index % 2 === 0;
const classes = [
'comment',
depth > 1 ? '' : (isEven ? 'even' : 'odd'),
c.children && c.children.length ? 'parent' : '',
'depth-' + depth
].join(' ').trim();
html += `
<li class="${classes}" id="comment-${c.id}">
<div id="div-comment-${c.id}" class="comment-inner">
<div class="comment-author vcard">
<img
alt="${c.author}的头像"
src="${c.avatar}"
class="avatar avatar-60 photo"
height="60"
width="60"
>
</div>
<div class="comment-body">
<div class="nickname">
${
c.user_url
? `<a href="${c.user_url}" class="url j-user-card" data-user="${c.user_id}" target="_blank" rel="ugc">${c.author}</a>`
: c.author
}
<span class="comment-time">${c.date}</span>
</div>
<div class="comment-text">
${
c.parent_author
? `<span class="comment-text-reply">
<a class="j-user-card" data-user="${c.parent_user_id}" href="${c.parent_url}" target="_blank">
@${c.parent_author}
</a>:
</span>`
: ''
}
${c.content}
</div>
</div>
<div class="reply">
<a
rel="nofollow"
class="comment-reply-link"
href="javascript:;"
data-commentid="${c.id}"
data-postid="${c.post_id}"
data-belowelement="div-comment-${c.id}"
data-respondelement="respond"
data-replyto="回复给 ${c.author}"
aria-label="回复给 ${c.author}"
>
<i class="wpcom-icon wi">
<svg aria-hidden="true">
<use xlink:href="#wi-comment-fill"></use>
</svg>
</i>
<span>回复</span>
</a>
</div>
</div>
${
c.children && c.children.length
? `<ul class="comment-children">
${renderCommentsNested(c.children, c.id, depth + 1)}
</ul>`
: ''
}
</li>`;
});
return html;
}
/* ==================================================
* 提交评论(AJAX)
* ================================================== */
$(document).on('submit', '#commentform', function (e) {
e.preventDefault();
const form = $(this);
const postId = form.find('input[name="comment_post_ID"]').val();
const parent = form.find('input[name="comment_parent"]').val() || 0;
const content = form.find('#comment').val().trim();
if (!content) return;
const subscribe = form.find('#cren_subscribe_to_comment').is(':checked') ? 'on' : '';
$.post(ajaxurl, {
action: 'submit_comment',
post_id: postId,
parent: parent,
content: content,
author: form.find('#author').val(),
email: form.find('#email').val(),
cren_subscribe_to_comment: subscribe
}, function (res) {
if (!res.success) {
if (res === 'need_login') {
alert('请先登录后评论');
location.href = '/wp-login.php';
}
return;
}
// 重置表单
form[0].reset();
form.find('input[name="comment_parent"]').val(0);
// 重新加载评论
$.post(ajaxurl, {
action: 'load_post_comments',
post_id: postId
}, function (res2) {
if (!res2.success) return;
const sortedComments = res2.data.sort(
(a, b) => new Date(b.date) - new Date(a.date)
);
const box = $('#comment-box-' + postId);
box.find('.comments-list')
.html(renderCommentsNested(sortedComments));
box.find('.comments-title')
.text(`评论列表(${res2.data.length}条)`);
});
// 取消回复,回到顶部
$('#cancel-comment-reply-link').trigger('click');
});
});
/* ==================================================
* 点击“回复”
* ================================================== */
$(document).on('click', '.comment-reply-link', function (e) {
e.preventDefault();
const parentId = $(this).data('commentid');
const box = $(this).closest('.entry-comments');
const respond = box.find('#respond');
const target = $('#div-comment-' + parentId);
// 移动表单到目标评论下
respond.appendTo(target);
respond.find('input[name="comment_parent"]').val(parentId);
respond.find('#comment').focus();
$('#cancel-comment-reply-link').show();
});
/* ==================================================
* 取消回复(回到顶部)
* ================================================== */
$(document).on('click', '#cancel-comment-reply-link', function (e) {
e.preventDefault();
const respond = $('#respond');
const box = respond.closest('.entry-comments');
respond.find('input[name="comment_parent"]').val(0);
box.prepend(respond);
$(this).hide();
});
});
2.3 自动给碎语增加标题(可选)
因为碎语只有文章内容本体,标题都会是未命名,不是很雅观,这里代码直接取内容本体作为标题。
add_action('save_post_talk', 'talk_generate_title_after_save', 20, 3);
function talk_generate_title_after_save($post_id, $post, $update) {
// 1. 排除自动保存 / 修订版本
if (wp_is_post_autosave($post_id) || wp_is_post_revision($post_id)) {
return;
}
// 2. 只在 publish / future / draft 时处理(避免 auto-draft 干扰)
if (!in_array($post->post_status, ['publish', 'future', 'draft'], true)) {
return;
}
// 3. 如果已经有有效标题,则不处理
if (!empty($post->post_title) && $post->post_title !== '自动草稿') {
return;
}
// 4. 取内容生成标题
$content = trim(wp_strip_all_tags($post->post_content));
if (!empty($content)) {
$new_title = wp_trim_words($content, 10, '…');
} else {
$new_title = '碎语';
}
// 5. 防止递归触发
remove_action('save_post_talk', 'talk_generate_title_after_save', 20);
wp_update_post([
'ID' => $post_id,
'post_title' => $new_title,
]);
add_action('save_post_talk', 'talk_generate_title_after_save', 20, 3);
}
这样就大功告成,但是应该会有一些问题,前端格式问题直接改css和classname。效果如下(测试:https://jaketao.com/notes)

本站原创文章皆遵循“署名-非商业性使用-相同方式共享 3.0 (CC BY-NC-SA 3.0)”。转载请保留以下标注: