Merge pull request 'Worktree single post zibll align' (#178) from worktree-single-post-zibll-align into main

Reviewed-on: CYL/wordpress-theme-aksrt#178
This commit is contained in:
2026-07-16 20:53:23 +08:00
26 changed files with 471 additions and 90 deletions
@@ -20,6 +20,7 @@ export const commentSchema = {
comment_img: true,
comment_upload_img: false,
comment_like_s: true,
comment_hot_threshold: 10,
comment_quick_s: true,
comment_city_s: true,
comment_author_tag: true,
@@ -78,6 +79,7 @@ export const commentSchema = {
title: '互动功能',
icon: 'fas fa-heart',
fields: [
{ key: 'comment_hot_threshold', label: '神评点赞阈值', type: 'number', default: 10, min: 1, max: 999, help: '点赞数达到该值后显示「神评」徽章', dependsOn: { key: 'comment_like_s', value: true } },
{ key: 'comment_like_s', label: '评论点赞', type: 'switch', default: true },
{ key: 'comment_quick_s', label: '快捷回复', type: 'switch', default: true },
{ key: 'comment_city_s', label: '评论城市', type: 'switch', default: true },
@@ -22,6 +22,10 @@ export const creditsSchema = {
points_free_bbs_posts_new: 5,
points_free_bbs_reply: 2,
points_free_bbs_essence: 10,
points_free_bbs_adopt: 20,
points_free_bbs_posts_hot: 5,
points_free_bbs_comment_hot: 5,
points_free_bbs_score_extra: 3,
points_earn_invite: 10,
points_spend_comment: 0,
points_spend_download: 0,
@@ -73,6 +77,10 @@ export const creditsSchema = {
{ key: 'points_free_bbs_posts_new', label: '论坛发帖', type: 'number', default: 5, min: 0, max: 1000 },
{ key: 'points_free_bbs_reply', label: '论坛回复', type: 'number', default: 2, min: 0, max: 1000 },
{ key: 'points_free_bbs_essence', label: '帖子加精', type: 'number', default: 10, min: 0, max: 1000 },
{ key: 'points_free_bbs_adopt', label: '回复被采纳', type: 'number', default: 20, min: 0, max: 1000 },
{ key: 'points_free_bbs_posts_hot', label: '热帖奖励', type: 'number', default: 5, min: 0, max: 1000 },
{ key: 'points_free_bbs_comment_hot', label: '神评奖励', type: 'number', default: 5, min: 0, max: 1000 },
{ key: 'points_free_bbs_score_extra', label: '帖子加分', type: 'number', default: 3, min: 0, max: 1000 },
{ key: 'points_earn_invite', label: '邀请注册奖励', type: 'number', default: 10, min: 0, max: 1000, help: '邀请不计入免费日上限' },
],
},
+31
View File
@@ -671,3 +671,34 @@ body.dark-theme .comment-login-prompt__inner {
margin-bottom: 0;
opacity: 0.9;
}
/* Identity chips + 神评 */
.aksrt-identity-chip {
display: inline-flex;
align-items: center;
gap: 3px;
vertical-align: middle;
max-width: 9em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.aksrt-identity-chip--comment {
font-size: 11px;
padding: 0 6px;
}
.aksrt-comment__badge--hot {
color: #ea580c;
background: rgba(234, 88, 12, 0.1);
border-radius: 999px;
padding: 0 6px;
margin-left: 4px;
font-size: 11px;
}
.aksrt-comment__header .author-box {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
}
@@ -278,6 +278,7 @@
.share-item[data-platform="twitter"]:hover { background: #f0f0f0; color: #000; }
.share-item[data-platform="wechat"]:hover { background: #e6f7ec; color: #07c160; }
.share-item[data-platform="copy"]:hover { background: #f0f2f5; color: #555; }
.share-item[data-platform="poster"]:hover { background: #fff4e8; color: #f59e0b; }
/* 微信二维码浮层 */
#share-qrcode-box {
@@ -332,6 +333,7 @@ html.dark .share-item {
.theme-dark .share-item[data-platform="twitter"]:hover { background: rgba(255,255,255,0.08); color: #fff; }
.theme-dark .share-item[data-platform="wechat"]:hover { background: rgba(7,193,96,0.12); color: #07c160; }
.theme-dark .share-item[data-platform="copy"]:hover { background: rgba(255,255,255,0.06); color: #ccc; }
.theme-dark .share-item[data-platform="poster"]:hover { background: rgba(245,158,11,0.12); color: #f59e0b; }
.theme-dark .post-drop-meta__menu,
html.dark .post-drop-meta__menu {
+12
View File
@@ -298,3 +298,15 @@
}
/* ============================================================
.aksrt-null--widget {
padding: 12px 0 !important;
}
.aksrt-null--widget .aksrt-null__img {
margin: 0 auto;
display: block;
}
.aksrt-null .separator {
border: 0;
}
+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="280" height="200" viewBox="0 0 280 200" fill="none" aria-hidden="true">
<rect width="280" height="200" fill="transparent"/>
<ellipse cx="140" cy="168" rx="72" ry="10" fill="#E8E8E8" opacity=".55"/>
<rect x="78" y="58" width="124" height="96" rx="14" fill="#F3F4F6" stroke="#D1D5DB" stroke-width="2"/>
<path d="M98 92h84M98 110h62M98 128h48" stroke="#C4C9D1" stroke-width="6" stroke-linecap="round"/>
<circle cx="188" cy="70" r="22" fill="#FFF7ED" stroke="#FDBA74" stroke-width="2"/>
<path d="M188 60v12M182 72h12" stroke="#F59E0B" stroke-width="3" stroke-linecap="round"/>
<path d="M104 48c8-14 28-18 40-8" stroke="#9CA3AF" stroke-width="3" stroke-linecap="round" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 746 B

+7
View File
@@ -40,6 +40,13 @@
}
btn.addEventListener('click', openReward);
document.querySelectorAll('[data-aksrt-reward-open]').forEach(function (el) {
if (el === btn) return;
el.addEventListener('click', function (e) {
e.preventDefault();
openReward();
});
});
if (closeBtn) closeBtn.addEventListener('click', closeReward);
if (overlay) overlay.addEventListener('click', closeReward);
+2
View File
@@ -43,6 +43,8 @@ require_once AKSRT_DIR . '/inc/helpers/aksrt-resource.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-pay.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-coupon-order.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-credits.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-null.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-identity.php';
require_once AKSRT_DIR . '/inc/helpers/menu-decoration.php';
require_once AKSRT_DIR . '/inc/helpers/class-aksrt-nav-walker.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-transactions.php';
@@ -250,6 +250,7 @@ class AKSRT_Admin_Schema_Post_List {
['key' => 'comment_upload_img', 'label' => '评论图片上传', 'type' => 'switch', 'default' => false],
]],
['key' => 'interaction', 'title' => '互动功能', 'fields' => [
['key' => 'comment_hot_threshold', 'label' => '神评点赞阈值', 'type' => 'number', 'default' => 10, 'min' => 1, 'max' => 999],
['key' => 'comment_like_s', 'label' => '评论点赞', 'type' => 'switch', 'default' => true],
['key' => 'comment_quick_s', 'label' => '快捷回复', 'type' => 'switch', 'default' => true],
['key' => 'comment_city_s', 'label' => '评论城市', 'type' => 'switch', 'default' => true],
@@ -428,6 +428,10 @@ class AKSRT_Admin_Schema_Users {
['key' => 'points_free_bbs_posts_new', 'label' => '论坛发帖', 'type' => 'number', 'default' => 5, 'min' => 0, 'max' => 1000],
['key' => 'points_free_bbs_reply', 'label' => '论坛回复', 'type' => 'number', 'default' => 2, 'min' => 0, 'max' => 1000],
['key' => 'points_free_bbs_essence', 'label' => '帖子加精', 'type' => 'number', 'default' => 10, 'min' => 0, 'max' => 1000],
['key' => 'points_free_bbs_adopt', 'label' => '回复被采纳', 'type' => 'number', 'default' => 20, 'min' => 0, 'max' => 1000],
['key' => 'points_free_bbs_posts_hot', 'label' => '热帖奖励', 'type' => 'number', 'default' => 5, 'min' => 0, 'max' => 1000],
['key' => 'points_free_bbs_comment_hot', 'label' => '神评奖励', 'type' => 'number', 'default' => 5, 'min' => 0, 'max' => 1000],
['key' => 'points_free_bbs_score_extra', 'label' => '帖子加分', 'type' => 'number', 'default' => 3, 'min' => 0, 'max' => 1000],
['key' => 'points_earn_invite', 'label' => '邀请注册奖励', 'type' => 'number', 'default' => 10, 'min' => 0, 'max' => 1000, 'help' => '邀请奖励不走免费日上限'],
]],
['key' => 'spend', 'title' => '积分消费', 'fields' => [
+9 -17
View File
@@ -234,22 +234,14 @@ class AKSRT_Credits_Earn {
}
}
public function on_forum_topic_featured(int $topic_id, int $operator_id): void {
unset($operator_id);
$post = get_post($topic_id);
if (!$post || $post->post_type !== 'aksrt_forum_topic') {
return;
}
$user_id = (int) $post->post_author;
if ($user_id <= 0 || !class_exists('AKSRT_Points_Free')) {
return;
}
if (get_post_meta($topic_id, 'aksrt_credit_essence_rewarded', true)) {
return;
}
$granted = AKSRT_Points_Free::grant($user_id, 'bbs_essence', 0, ['type' => 'post', 'id' => $topic_id]);
if (!is_wp_error($granted) && $granted > 0) {
update_post_meta($topic_id, 'aksrt_credit_essence_rewarded', 1);
}
/**
* Essence reward is owned by AKSRT_Forum::reward_essence() (free-day-cap).
* Keep hook for external integrations only — do not double-grant.
*
* @param int $topic_id
* @param int $author_or_operator Author id from forum (2nd arg) or operator.
*/
public function on_forum_topic_featured(int $topic_id, int $author_or_operator = 0): void {
unset($topic_id, $author_or_operator);
}
}
+34
View File
@@ -33,6 +33,10 @@ class AKSRT_Points_Free {
'bbs_posts_new' => [__('论坛发帖', 'aksrt'), __('发布论坛主题', 'aksrt'), 5],
'bbs_reply' => [__('论坛回复', 'aksrt'), __('回复论坛主题', 'aksrt'), 2],
'bbs_essence' => [__('帖子加精', 'aksrt'), __('主题被设为精华', 'aksrt'), 10],
'bbs_adopt' => [__('回复被采纳', 'aksrt'), __('问答帖回复被采纳', 'aksrt'), 20],
'bbs_posts_hot' => [__('热帖奖励', 'aksrt'), __('主题成为热帖', 'aksrt'), 5],
'bbs_comment_hot'=> [__('神评奖励', 'aksrt'), __('回复成为神评', 'aksrt'), 5],
'bbs_score_extra'=> [__('帖子加分', 'aksrt'), __('主题被加分时奖励', 'aksrt'), 3],
];
}
@@ -276,6 +280,36 @@ class AKSRT_Points_Free {
return max(0, (int) ($f['credits']['comment_new'] ?? $default));
}
return $default;
case 'bbs_essence':
if (class_exists('AKSRT_Settings') && method_exists('AKSRT_Settings', 'get_forum_settings')) {
$f = AKSRT_Settings::get_forum_settings();
return max(0, (int) ($f['credits']['essence'] ?? $default));
}
return $default;
case 'bbs_adopt':
if (class_exists('AKSRT_Settings') && method_exists('AKSRT_Settings', 'get_forum_settings')) {
$f = AKSRT_Settings::get_forum_settings();
return max(0, (int) ($f['credits']['adopted'] ?? $default));
}
return $default;
case 'bbs_posts_hot':
if (class_exists('AKSRT_Settings') && method_exists('AKSRT_Settings', 'get_forum_settings')) {
$f = AKSRT_Settings::get_forum_settings();
return max(0, (int) ($f['credits']['posts_hot'] ?? $default));
}
return $default;
case 'bbs_comment_hot':
if (class_exists('AKSRT_Settings') && method_exists('AKSRT_Settings', 'get_forum_settings')) {
$f = AKSRT_Settings::get_forum_settings();
return max(0, (int) ($f['credits']['comment_hot'] ?? $f['credits']['comment_like'] ?? $default));
}
return $default;
case 'bbs_score_extra':
if (class_exists('AKSRT_Settings') && method_exists('AKSRT_Settings', 'get_forum_settings')) {
$f = AKSRT_Settings::get_forum_settings();
return max(0, (int) ($f['credits']['score_extra'] ?? $default));
}
return $default;
default:
return $default;
}
+2 -2
View File
@@ -192,7 +192,7 @@ class AKSRT_Recent_Comments_Widget extends WP_Widget {
<?php endforeach; ?>
</div>
<?php else : ?>
<p class="widget-empty"><?php esc_html_e('暂无评论', 'aksrt'); ?></p>
<?php echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无评论', 'aksrt')) : '<p class="widget-empty">' . esc_html__('暂无评论', 'aksrt') . '</p>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif;
echo $args['after_widget'];
@@ -263,7 +263,7 @@ class AKSRT_Friend_Links_Widget extends WP_Widget {
<?php endforeach; ?>
</div>
<?php else : ?>
<p class="widget-empty"><?php esc_html_e('暂无友情链接', 'aksrt'); ?></p>
<?php echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无友情链接', 'aksrt')) : '<p class="widget-empty">' . esc_html__('暂无友情链接', 'aksrt') . '</p>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif;
echo $args['after_widget'];
+2 -2
View File
@@ -107,7 +107,7 @@ class AKSRT_Hot_Posts_Widget extends WP_Widget {
echo '</div>';
wp_reset_postdata();
} else {
echo '<p class="widget-empty">' . esc_html__('暂无热门文章', 'aksrt') . '</p>';
echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无热门文章', 'aksrt')) : '<p class="widget-empty">' . esc_html__('暂无热门文章', 'aksrt') . '</p>';
}
echo $args['after_widget'];
@@ -360,7 +360,7 @@ class AKSRT_Tab_Posts_Widget extends WP_Widget {
</ul>
<?php endif; ?>
<?php else : ?>
<p class="widget-empty"><?php esc_html_e('暂无文章', 'aksrt'); ?></p>
<?php echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无文章', 'aksrt')) : '<p class="widget-empty">' . esc_html__('暂无文章', 'aksrt') . '</p>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
</div>
<?php wp_reset_postdata(); $first = false; endforeach; ?>
+25 -1
View File
@@ -167,7 +167,31 @@ class AKSRT_Withdrawal {
}
$ok = $wpdb->update($this->table(), $update, ['id' => $withdrawal_id], $format, ['%d']) !== false;
return $ok ? true : new WP_Error('aksrt_withdraw_update', __('提现状态更新失败', 'aksrt'));
if (!$ok) {
return new WP_Error('aksrt_withdraw_update', __('提现状态更新失败', 'aksrt'));
}
// Notify user on terminal status (Zibll withdraw_reply parity).
if (class_exists('AKSRT_Message') && $user_id > 0 && in_array($new_status, [self::STATUS_COMPLETED, self::STATUS_REJECTED], true)) {
$gross = $this->resolve_gross_amount($row, $meta);
$amount_txt = class_exists('AKSRT_Balance') ? AKSRT_Balance::format($gross) : number_format($gross, 2);
if ($new_status === self::STATUS_REJECTED) {
$title = __('提现已拒绝', 'aksrt');
$body = sprintf(__('您申请提现 %s 元已被拒绝,冻结金额已退回钱包。', 'aksrt'), $amount_txt);
if ($note !== '') {
$body .= ' ' . sprintf(__('备注:%s', 'aksrt'), $note);
}
} else {
$title = __('提现已完成', 'aksrt');
$body = sprintf(__('您申请提现 %s 元已处理完成。', 'aksrt'), $amount_txt);
}
AKSRT_Message::get_instance()->send($user_id, 'withdraw', $body, [
'title' => $title,
'target_url' => function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url('credits') : '',
]);
}
return true;
}
/**
@@ -312,17 +312,27 @@ class AKSRT_Forum_Comment_Service {
update_post_meta($topic_id, '_aksrt_question_status', 1); // QUESTION_SOLVED
$adoptee_id = (int) $comment->user_id;
if ($adoptee_id > 0 && class_exists('AKSRT_Credits')) {
if ($adoptee_id > 0) {
$config = $this->forum->get_config();
$amount = (int) ($config['credits']['adopted'] ?? 20);
if ($amount > 0) {
$already = get_comment_meta($comment_id, '_aksrt_adopt_credited', true);
if (!$already) {
\AKSRT_Credits::add($adoptee_id, $amount, 'forum_adopted', [
'type' => 'comment',
'id' => $comment_id,
]);
update_comment_meta($comment_id, '_aksrt_adopt_credited', '1');
if (class_exists('AKSRT_Points_Free')) {
$granted = \AKSRT_Points_Free::grant($adoptee_id, 'bbs_adopt', $amount, [
'type' => 'comment',
'id' => $comment_id,
]);
if (!is_wp_error($granted) && (int) $granted > 0) {
update_comment_meta($comment_id, '_aksrt_adopt_credited', '1');
}
} elseif (class_exists('AKSRT_Credits')) {
\AKSRT_Credits::add($adoptee_id, $amount, 'forum_adopted', [
'type' => 'comment',
'id' => $comment_id,
]);
update_comment_meta($comment_id, '_aksrt_adopt_credited', '1');
}
}
}
@@ -498,15 +508,22 @@ class AKSRT_Forum_Comment_Service {
update_comment_meta($comment_id, '_aksrt_is_hot', '1');
// Credit the comment author
// Credit the comment author (free-task day cap).
$author_id = (int) $comment->user_id;
if ($author_id > 0 && class_exists('AKSRT_Credits')) {
$amount = (int) ($config['credits']['comment_like'] ?? 1);
if ($author_id > 0) {
$amount = (int) ($config['credits']['comment_hot'] ?? $config['credits']['comment_like'] ?? 5);
if ($amount > 0) {
\AKSRT_Credits::add($author_id, $amount, 'forum_hot_comment', [
'type' => 'comment',
'id' => $comment_id,
]);
if (class_exists('AKSRT_Points_Free')) {
\AKSRT_Points_Free::grant($author_id, 'bbs_comment_hot', $amount, [
'type' => 'comment',
'id' => $comment_id,
]);
} elseif (class_exists('AKSRT_Credits')) {
\AKSRT_Credits::add($author_id, $amount, 'forum_hot_comment', [
'type' => 'comment',
'id' => $comment_id,
]);
}
}
}
@@ -126,16 +126,23 @@ class AKSRT_Forum_Score_Service {
$reputation += $amount;
update_user_meta($author_id, 'aksrt_reputation', $reputation);
if ($amount > 0 && class_exists('AKSRT_Credits')) {
if ($amount > 0) {
$credits_amount = (int) ($config['credits']['score_extra'] ?? 3);
if ($credits_amount > 0) {
$daily = $this->today_credits_count($author_id, 'forum_score_extra');
$daily_max = (int) ($config['credits']['daily_max'] ?? 50);
if ($daily < $daily_max) {
AKSRT_Credits::add($author_id, $credits_amount, 'forum_score_extra', sprintf(
__('帖子「%s」被加分', 'aksrt'),
get_the_title($topic_id)
));
if (class_exists('AKSRT_Points_Free')) {
AKSRT_Points_Free::grant($author_id, 'bbs_score_extra', $credits_amount, [
'type' => 'post',
'id' => $topic_id,
]);
} elseif (class_exists('AKSRT_Credits')) {
$daily = $this->today_credits_count($author_id, 'forum_score_extra');
$daily_max = (int) ($config['credits']['daily_max'] ?? 50);
if ($daily < $daily_max) {
AKSRT_Credits::add($author_id, $credits_amount, 'forum_score_extra', sprintf(
__('帖子「%s」被加分', 'aksrt'),
get_the_title($topic_id)
));
}
}
}
}
@@ -232,10 +239,17 @@ class AKSRT_Forum_Score_Service {
update_post_meta($topic_id, '_aksrt_hot_rewarded', '1');
$config = $this->forum->get_config();
$amount = (int) ($config['credits']['posts_hot'] ?? 5);
if ($amount > 0 && class_exists('AKSRT_Credits')) {
AKSRT_Credits::add($author_id, $amount, 'forum_hot_topic', [
'type' => 'post', 'id' => $topic_id,
]);
if ($amount > 0) {
if (class_exists('AKSRT_Points_Free')) {
AKSRT_Points_Free::grant($author_id, 'bbs_posts_hot', $amount, [
'type' => 'post',
'id' => $topic_id,
]);
} elseif (class_exists('AKSRT_Credits')) {
AKSRT_Credits::add($author_id, $amount, 'forum_hot_topic', [
'type' => 'post', 'id' => $topic_id,
]);
}
}
$this->notify($author_id, 'hot', sprintf(
__('你的帖子「%s」成为热帖!', 'aksrt'), get_the_title($topic_id)
@@ -245,22 +245,32 @@ trait AKSRT_Forum_Topic_Service_Part_1 {
return;
}
// 积分奖励(§7.7):读 `credits.essence`,带每日上限。
if (class_exists('AKSRT_Credits') && class_exists('AKSRT_Settings')) {
// 积分奖励(§7.7):统一走免费积分日上限。
if (class_exists('AKSRT_Settings')) {
$cfg = AKSRT_Settings::get_forum_settings();
$amount = (int) ($cfg['credits']['essence'] ?? 0);
if ($amount > 0) {
$daily_max = (int) ($cfg['credits']['daily_max'] ?? 50);
if ($daily_max > 0 && $this->today_credits_count($author_id, 'forum_essence') >= $daily_max) {
$amount = 0;
}
if ($amount > 0) {
AKSRT_Credits::add($author_id, $amount, 'forum_essence', [
$granted = 0;
if (class_exists('AKSRT_Points_Free')) {
$result = AKSRT_Points_Free::grant($author_id, 'bbs_essence', $amount, [
'type' => 'post',
'id' => $topic_id,
], __('主题被设为精华', 'aksrt'));
'id' => $topic_id,
]);
$granted = is_wp_error($result) ? 0 : (int) $result;
} elseif (class_exists('AKSRT_Credits')) {
$daily_max = (int) ($cfg['credits']['daily_max'] ?? 50);
if ($daily_max > 0 && $this->today_credits_count($author_id, 'forum_essence') >= $daily_max) {
$amount = 0;
}
if ($amount > 0) {
AKSRT_Credits::add($author_id, $amount, 'forum_essence', [
'type' => 'post',
'id' => $topic_id,
], __('主题被设为精华', 'aksrt'));
$granted = $amount;
}
}
if ($granted > 0) {
update_post_meta($topic_id, '_aksrt_essence_rewarded', '1');
update_post_meta($topic_id, '_aksrt_essence_rewarded_at', current_time('mysql'));
}
@@ -189,7 +189,7 @@ if (!function_exists('aksrt_home_widget_render_post_grid')) {
*/
function aksrt_home_widget_render_post_grid(WP_Query $query, string $layout = 'card', int $columns = 3, string $context = 'homepage'): void {
if (!$query->have_posts()) {
echo '<p class="widget-empty">' . esc_html__('暂无内容', 'aksrt') . '</p>';
echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无内容', 'aksrt')) : '<p class="widget-empty">' . esc_html__('暂无内容', 'aksrt') . '</p>';
wp_reset_postdata();
return;
}
@@ -464,7 +464,7 @@ class AKSRT_Category_Tabs_Widget extends WP_Widget {
echo '<section class="aksrt-home-widget aksrt-home-widget--tabs widget-tab-posts">';
if (empty($tabs)) {
echo '<p class="widget-empty">' . esc_html__('暂无可用分类', 'aksrt') . '</p>';
echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无可用分类', 'aksrt')) : '<p class="widget-empty">' . esc_html__('暂无可用分类', 'aksrt') . '</p>';
echo '</section>';
echo $args['after_widget'];
return;
@@ -649,7 +649,7 @@ class AKSRT_Category_Recommend_Widget extends WP_Widget {
aksrt_home_widget_render_header($title);
if (empty($cards)) {
echo '<p class="widget-empty">' . esc_html__('暂无推荐分类', 'aksrt') . '</p>';
echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无推荐分类', 'aksrt')) : '<p class="widget-empty">' . esc_html__('暂无推荐分类', 'aksrt') . '</p>';
echo '</section>';
echo $args['after_widget'];
return;
+7 -1
View File
@@ -571,12 +571,18 @@ if (!function_exists('aksrt_comments_render_empty')) :
* @param array<string,mixed> $ctx Comments context.
*/
function aksrt_comments_render_empty(array $ctx): void {
$text = (string) ($ctx['empty_text'] ?? __('暂无评论内容', 'aksrt'));
if (function_exists('aksrt_get_null')) {
echo aksrt_get_null($text, 40, 'null.svg', 'comment comment-null comments-empty', 220); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<p class="comments-empty__hint muted-3-color em09 text-center" style="margin-top:-20px;">' . esc_html__('成为第一个评论者吧', 'aksrt') . '</p>';
return;
}
?>
<div class="comments-empty post_ajax_loader">
<div class="comments-empty__icon">
<i class="far fa-comment-dots"></i>
</div>
<p class="comments-empty__text"><?php echo esc_html((string) $ctx['empty_text']); ?></p>
<p class="comments-empty__text"><?php echo esc_html($text); ?></p>
<p class="comments-empty__hint"><?php esc_html_e('成为第一个评论者吧', 'aksrt'); ?></p>
</div>
<?php
+4
View File
@@ -61,6 +61,10 @@ if (!function_exists('aksrt_credits_label')) {
'free_bbs_posts_new'=> __('论坛发帖', 'aksrt'),
'free_bbs_reply'=> __('论坛回复', 'aksrt'),
'free_bbs_essence'=> __('帖子加精', 'aksrt'),
'free_bbs_adopt'=> __('回复被采纳', 'aksrt'),
'free_bbs_posts_hot'=> __('热帖奖励', 'aksrt'),
'free_bbs_comment_hot'=> __('神评奖励', 'aksrt'),
'free_bbs_score_extra'=> __('帖子加分', 'aksrt'),
];
if (isset($map[$reason])) {
return $map[$reason];
+9
View File
@@ -1055,9 +1055,18 @@ if (!function_exists('aksrt_comment_callback')) :
<div class="author-box">
<?php echo get_avatar($comment, $avatar_size, '', '', ['class' => 'avatar-img comt-avatar']); ?>
<span class="aksrt-comment__author"><?php comment_author_link(); ?></span>
<?php
$comment_user_id = (int) $comment->user_id;
if ($comment_user_id > 0 && function_exists('aksrt_render_user_identity_chips')) {
echo aksrt_render_user_identity_chips($comment_user_id, 'comment'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
?>
<?php if ($show_author_tag && $is_post_author) : ?>
<span class="aksrt-comment__badge"><?php esc_html_e('作者', 'aksrt'); ?></span>
<?php endif; ?>
<?php if (function_exists('aksrt_is_blog_comment_hot') && aksrt_is_blog_comment_hot($comment)) : ?>
<span class="aksrt-comment__badge aksrt-comment__badge--hot"><i class="fas fa-fire" aria-hidden="true"></i> <?php esc_html_e('神评', 'aksrt'); ?></span>
<?php endif; ?>
<?php if ($is_pinned) : ?>
<span class="aksrt-comment__badge aksrt-comment__badge--pinned"><i class="fas fa-thumbtack" aria-hidden="true"></i> <?php esc_html_e('置顶', 'aksrt'); ?></span>
<?php endif; ?>
+103
View File
@@ -0,0 +1,103 @@
<?php
/**
* Shared identity chrome: VIP / verify / level / badges for author bar & comments.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!function_exists('aksrt_get_user_identity_signals')) {
/**
* @return list<array{type:string,label:string,icon:string,tone:string,title?:string}>
*/
function aksrt_get_user_identity_signals(int $user_id): array {
if ($user_id <= 0) {
return [];
}
if (class_exists('AKSRT_User_Home')) {
$signals = AKSRT_User_Home::get_instance()->get_profile_signals($user_id);
return is_array($signals) ? array_values($signals) : [];
}
return [];
}
}
if (!function_exists('aksrt_render_user_identity_chips')) {
/**
* Render compact identity chips (verify / level / badges).
*
* @param int $user_id
* @param string $context author|comment|card
*/
function aksrt_render_user_identity_chips(int $user_id, string $context = 'author'): string {
if ($user_id <= 0) {
return '';
}
$html = '';
if (function_exists('aksrt_render_vip_badge')) {
$vip = aksrt_render_vip_badge($user_id, 'sm');
if (is_string($vip) && $vip !== '') {
$html .= $vip;
}
}
$signals = aksrt_get_user_identity_signals($user_id);
$limit = $context === 'comment' ? 3 : 6;
$n = 0;
foreach ($signals as $signal) {
if ($n >= $limit) {
break;
}
$label = trim((string) ($signal['label'] ?? ''));
if ($label === '') {
continue;
}
$tone = sanitize_html_class((string) ($signal['tone'] ?? 'info'));
$icon = sanitize_html_class((string) ($signal['icon'] ?? 'check'));
// Font Awesome: map short names to fa-* if needed.
if ($icon !== '' && !str_contains($icon, 'fa-')) {
$icon = 'fa-' . $icon;
}
$title = (string) ($signal['title'] ?? $label);
$html .= '<span class="badg badg-sm ml3 aksrt-identity-chip aksrt-identity-chip--' . esc_attr($tone) . ' aksrt-identity-chip--' . esc_attr($context) . '" title="' . esc_attr($title) . '">';
if ($icon !== '') {
$html .= '<i class="fa ' . esc_attr($icon) . '" aria-hidden="true"></i> ';
}
$html .= esc_html($label) . '</span>';
$n++;
}
return $html;
}
}
if (!function_exists('aksrt_is_blog_comment_hot')) {
/**
* "神评" for ordinary post comments when likes cross threshold.
*/
function aksrt_is_blog_comment_hot($comment): bool {
$comment_id = $comment instanceof WP_Comment ? (int) $comment->comment_ID : (int) $comment;
if ($comment_id <= 0) {
return false;
}
if ('1' === (string) get_comment_meta($comment_id, '_aksrt_comment_hot', true)) {
return true;
}
$threshold = 10;
if (class_exists('AKSRT_Settings') && method_exists('AKSRT_Settings', 'get_comment_display_settings')) {
$display = AKSRT_Settings::get_comment_display_settings();
if (isset($display['hot_threshold'])) {
$threshold = max(1, (int) $display['hot_threshold']);
}
}
$all = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_all() : [];
if (isset($all['comment_hot_threshold'])) {
$threshold = max(1, (int) $all['comment_hot_threshold']);
}
$likes = function_exists('aksrt_get_comment_likes') ? aksrt_get_comment_likes($comment_id) : (int) get_comment_meta($comment_id, 'aksrt_comment_likes', true);
if ($likes < $threshold) {
return false;
}
update_comment_meta($comment_id, '_aksrt_comment_hot', '1');
return true;
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
/**
* Illustrated empty / null state helpers (Zibll zib_get_null parity).
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!function_exists('aksrt_null_image_url')) {
/**
* Prefer theme null illustration; fall back to placeholder.
*/
function aksrt_null_image_url(string $file = 'null.svg'): string {
$file = sanitize_file_name($file);
if ($file === '') {
$file = 'null.svg';
}
$candidates = [
'/assets/img/' . $file,
'/img/' . $file,
'/assets/img/placeholder.svg',
];
foreach ($candidates as $rel) {
$path = get_template_directory() . $rel;
if (is_readable($path)) {
return get_template_directory_uri() . $rel;
}
}
return get_template_directory_uri() . '/assets/img/placeholder.svg';
}
}
if (!function_exists('aksrt_get_null')) {
/**
* Zibll-like empty illustration block.
*
* @param string|null $text Caption (null → 暂无内容).
* @param int|string $margin Vertical padding / top margin.
* @param string $img Image file under assets/img.
* @param string $class Extra class on wrapper.
* @param int $width Image max width.
* @param int $height Optional fixed height.
*/
function aksrt_get_null($text = null, $margin = 60, string $img = 'null.svg', string $class = '', int $width = 280, int $height = 0): string {
if (null === $text) {
$text = __('暂无内容', 'aksrt');
}
$margin = is_numeric($margin) ? (string) $margin : '60';
$caption = $text !== ''
? '<p style="margin-top:' . esc_attr($margin) . 'px;" class="em09 muted-3-color separator">' . esc_html((string) $text) . '</p>'
: '';
if ($height > 0) {
$style = $width > 0 ? 'max-width:' . (int) $width . 'px;' : '';
$style .= 'height:' . (int) $height . 'px;';
} else {
$style = $width > 0 ? 'width:' . (int) $width . 'px;' : '';
}
$style .= 'opacity:.7;';
$src = aksrt_null_image_url($img);
return '<div class="text-center aksrt-null ' . esc_attr($class) . '" style="padding:' . esc_attr($margin) . 'px 0;">'
. '<img class="no-imgbox aksrt-null__img" style="' . esc_attr($style) . '" src="' . esc_url($src) . '" alt="" loading="lazy" decoding="async">'
. $caption
. '</div>';
}
}
if (!function_exists('aksrt_get_widget_null')) {
/**
* Compact null for sidebar widgets.
*/
function aksrt_get_widget_null(string $text = '', int $margin = 24, int $width = 140): string {
if ($text === '') {
$text = __('暂无内容', 'aksrt');
}
return aksrt_get_null($text, $margin, 'null.svg', 'widget-empty aksrt-null--widget', $width);
}
}
+18 -12
View File
@@ -236,6 +236,7 @@ if (!function_exists('aksrt_single_render_actions')) :
<button type="button" class="share-item" data-platform="twitter"><i class="fab fa-x-twitter"></i><span>X</span></button>
<button type="button" class="share-item" data-platform="wechat"><i class="fab fa-weixin"></i><span>微信</span></button>
<button type="button" class="share-item" data-platform="copy"><i class="fas fa-link"></i><span><?php esc_html_e('复制链接', 'aksrt'); ?></span></button>
<button type="button" class="share-item" data-platform="poster"><i class="fas fa-image"></i><span><?php esc_html_e('海报分享', 'aksrt'); ?></span></button>
</div>
<div id="share-qrcode-box" aria-hidden="true"><img id="share-qrcode-image" src="" alt="QR Code"></div>
</div>
@@ -305,11 +306,9 @@ if (!function_exists('aksrt_single_render_author_card')) :
$author_url = get_author_posts_url($author_id);
$author_name = get_the_author();
$bio = trim((string) get_the_author_meta('description'));
$vip_badge = function_exists('aksrt_render_vip_badge') ? aksrt_render_vip_badge($author_id, 'sm') : '';
$signals = [];
if (class_exists('AKSRT_User_Home')) {
$signals = AKSRT_User_Home::get_instance()->get_profile_signals($author_id);
}
$identity_chips = function_exists('aksrt_render_user_identity_chips')
? aksrt_render_user_identity_chips($author_id, 'card')
: '';
$avatar = function_exists('aksrt_get_avatar_html')
? aksrt_get_avatar_html($author_id, 80, ['class' => 'avatar avatar-lg'])
: get_avatar($author_id, 80, '', esc_attr($author_name), ['class' => 'avatar avatar-lg']);
@@ -330,13 +329,9 @@ if (!function_exists('aksrt_single_render_author_card')) :
<div class="user-info mt20 mb10">
<div class="user-name flex jc">
<a class="display-name" href="<?php echo esc_url($author_url); ?>"><?php echo esc_html($author_name); ?></a>
<?php echo $vip_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php foreach ($signals as $signal) : ?>
<span class="badg badg-sm ml3 aksrt-identity-chip aksrt-identity-chip--<?php echo esc_attr((string) ($signal['tone'] ?? 'info')); ?>">
<i class="fa fa-<?php echo esc_attr((string) ($signal['icon'] ?? 'check')); ?>" aria-hidden="true"></i>
<?php echo esc_html((string) ($signal['label'] ?? '')); ?>
</span>
<?php endforeach; ?>
<?php if ($identity_chips !== '') : ?>
<?php echo $identity_chips; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
</div>
<?php if ($bio !== '') : ?>
<div class="user-desc mt10 muted-2-color em09"><?php echo esc_html($bio); ?></div>
@@ -554,6 +549,17 @@ if (!function_exists('aksrt_single_render_related')) :
$query = new WP_Query($args);
if (!$query->have_posts()) {
wp_reset_postdata();
$empty_wrap = 'theme-box relates' . ($style === 'img' ? ' relates-thumb' : '');
?>
<div class="<?php echo esc_attr($empty_wrap); ?>">
<div class="box-body notop">
<div class="title-theme"><?php echo esc_html($title); ?></div>
</div>
<div class="zib-widget">
<?php echo function_exists('aksrt_get_null') ? aksrt_get_null(__('暂无相关文章', 'aksrt'), 40, 'null.svg', 'related-null', 220) : ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
</div>
<?php
return;
}
+21 -15
View File
@@ -48,12 +48,10 @@ $aksrt_msg_url = function_exists('aksrt_get_messages_page_url') ? aksrt_get_mess
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink());
$is_self = is_user_logged_in() && get_current_user_id() === $author_id;
// Identity chips: VIP + verify/level/badges.
$vip_badge = function_exists('aksrt_render_vip_badge') ? aksrt_render_vip_badge($author_id, 'sm') : '';
$signals = [];
if (class_exists('AKSRT_User_Home')) {
$signals = AKSRT_User_Home::get_instance()->get_profile_signals($author_id);
}
// Identity chips: VIP + verify/level/badges (shared helper).
$identity_chips = function_exists('aksrt_render_user_identity_chips')
? aksrt_render_user_identity_chips($author_id, 'author')
: '';
?>
<div class="<?php echo esc_attr($classes); ?>">
@@ -71,15 +69,9 @@ if (class_exists('AKSRT_User_Home')) {
<div class="flex1">
<span class="name flex ac flex1">
<a class="display-name text-ellipsis" href="<?php echo esc_url($author_url); ?>"><?php echo esc_html($author_name); ?></a>
<?php if ($vip_badge !== '') : ?>
<?php echo $vip_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php if ($identity_chips !== '') : ?>
<?php echo $identity_chips; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
<?php foreach ($signals as $signal) : ?>
<span class="badg badg-sm ml3 aksrt-identity-chip aksrt-identity-chip--<?php echo esc_attr((string) ($signal['tone'] ?? 'info')); ?>">
<i class="fa fa-<?php echo esc_attr((string) ($signal['icon'] ?? 'check')); ?>" aria-hidden="true"></i>
<?php echo esc_html((string) ($signal['label'] ?? '')); ?>
</span>
<?php endforeach; ?>
</span>
<div class="px12-sm muted-2-color text-ellipsis">
<?php if ($show_time) : ?>
@@ -100,10 +92,24 @@ if (class_exists('AKSRT_User_Home')) {
<button type="button" class="px12-sm ml10 follow but c-red" data-aksrt-follow="<?php echo (int) $author_id; ?>">
<i class="fa fa-heart em09" aria-hidden="true"></i> <?php esc_html_e('关注', 'aksrt'); ?>
</button>
<?php if ($aksrt_pm_on && $aksrt_msg_url !== '') : ?>
<?php if ($aksrt_pm_on && $aksrt_msg_url !== '') : ?>
<a href="<?php echo esc_url(add_query_arg('msg_type', 'conversation', $aksrt_msg_url)); ?>" class="ml6 but c-blue px12-sm">
<i class="fa fa-comment em09" aria-hidden="true"></i> <?php esc_html_e('私信', 'aksrt'); ?>
</a>
<?php else : ?>
<?php
$aksrt_post_settings = class_exists('AKSRT_Settings') && method_exists('AKSRT_Settings', 'get_post_page_settings')
? AKSRT_Settings::get_post_page_settings()
: [];
$rewards_on = !empty($aksrt_post_settings['rewards_enabled'])
|| !empty($aksrt_post_settings['post_rewards_enabled'])
|| !empty($aksrt_post_settings['post_rewards_s']);
?>
<?php if ($rewards_on) : ?>
<button type="button" class="ml6 but c-yellow px12-sm" data-aksrt-reward-open>
<i class="fa fa-gift em09" aria-hidden="true"></i> <?php esc_html_e('赞赏', 'aksrt'); ?>
</button>
<?php endif; ?>
<?php endif; ?>
<?php else : ?>
<a href="<?php echo esc_url($login_url); ?>" class="px12-sm ml10 follow but c-red">