diff --git a/assets/css/frontend/widget-batch5.css b/assets/css/frontend/widget-batch5.css
index 027d6884..20d5c359 100644
--- a/assets/css/frontend/widget-batch5.css
+++ b/assets/css/frontend/widget-batch5.css
@@ -2296,3 +2296,86 @@ a.main-color > .icon-cover-card:hover,
}
.tag-cloud.aksrt-tag-cloud a { transition: transform .15s ease; }
.tag-cloud.aksrt-tag-cloud a:hover { transform: translateY(-1px); }
+
+
+/* batch22-24 forum/user residual */
+.forum-posts.mini.aksrt-ftopic-mini,
+.forum-posts.detail.aksrt-ftopic-detail {
+ display: block;
+ position: relative;
+ padding: 12px 0;
+ border-bottom: 1px solid var(--color-line-soft, #f0f1f3);
+}
+.forum-posts.mini.aksrt-ftopic-mini:last-child,
+.forum-posts.detail.aksrt-ftopic-detail:last-child { border-bottom: 0; }
+.forum-posts .forum-posts-header .avatar-img img,
+.forum-posts .avatar-img img {
+ width: 28px; height: 28px; border-radius: 50%; object-fit: cover; display: block;
+}
+.forum-posts.detail .avatar-img img { width: 40px; height: 40px; }
+.forum-posts .forum-title { margin: 0; font-size: 14px; font-weight: 600; line-height: 1.4; }
+.forum-posts .forum-title a { color: inherit; text-decoration: none; }
+.forum-posts .forum-title a:hover { color: var(--theme-color, #f04494); }
+.forum-posts .item-meta { gap: 2px; }
+.forum-posts .forum-excerpt { line-height: 1.5; }
+.forum-posts badge.img-badge { z-index: 2; }
+
+.plate-lists .plate-item,
+.aksrt-plate-lists .plate-item {
+ display: flex;
+ gap: 12px;
+ align-items: flex-start;
+ padding: 10px 0;
+ border-bottom: 1px solid var(--color-line-soft, #f0f1f3);
+ color: inherit;
+ text-decoration: none;
+}
+.plate-lists .plate-item:last-child { border-bottom: 0; }
+.plate-thumb,
+.aksrt-plate-item__thumb {
+ width: 48px; height: 48px; border-radius: 10px; overflow: hidden;
+ flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
+ background: var(--surface-2, #f3f4f6); color: #999;
+}
+.plate-thumb img, .aksrt-plate-item__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
+.panel-plate.scroll-plate,
+.aksrt-plate-cards {
+ display: flex; gap: 12px; overflow-x: auto; padding-bottom: 4px; scrollbar-width: thin;
+}
+.plate-card, .aksrt-plate-card {
+ flex: 0 0 160px; width: 160px; text-decoration: none; color: inherit;
+}
+.plate-card .plate-thumb, .aksrt-plate-card__cover {
+ width: 100%; aspect-ratio: 1; border-radius: 12px; overflow: hidden;
+ background: var(--surface-2, #f3f4f6); display: flex; align-items: center; justify-content: center;
+}
+.plate-card .plate-thumb img, .aksrt-plate-card__cover img { width: 100%; height: 100%; object-fit: cover; }
+
+.moderator-box.aksrt-plate-mods__list,
+.aksrt-plate-mods__list { display: flex; flex-direction: column; gap: 12px; }
+.aksrt-plate-mods.style-grid .aksrt-plate-mods__list {
+ display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 12px;
+}
+.aksrt-plate-mods__item { gap: 4px; }
+.aksrt-plate-mods__avatar { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; display: block; }
+.aksrt-plate-mods.style-grid .aksrt-plate-mods__item { text-align: center; }
+.aksrt-plate-mods.style-grid .aksrt-plate-mods__avatar { width: 48px; height: 48px; margin: 0 auto 6px; }
+
+.user-ranking-box .user-ranking-item,
+.aksrt-user-ranking-box .user-ranking-item { position: relative; }
+.user-ranking-item .avatar-img img { width: 36px; height: 36px; border-radius: 50%; object-fit: cover; display: block; }
+.user-ranking-item .badg { white-space: nowrap; }
+
+.aksrt-category-recommend__card[data-aksrt-animate] { will-change: transform, opacity; }
+.aksrt-category-recommend__posts.posts-mini-wrap { margin-top: 8px; }
+
+.aksrt-plate-info.plate-info-sidebar { overflow: hidden; }
+.aksrt-plate-info__cover.plate-thumb {
+ width: 100%; height: 110px; border-radius: 12px; overflow: hidden;
+ background: var(--surface-2, #f3f4f6); display: flex; align-items: center; justify-content: center;
+}
+.aksrt-plate-info__cover.plate-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
+.aksrt-plate-info__stats.count-mates { gap: 8px; flex-wrap: wrap; }
+
+.aksrt-ftag-list { display: flex; flex-wrap: wrap; gap: 8px; }
+.aksrt-ftag-item.is-card { min-width: 120px; justify-content: space-between; }
diff --git a/inc/bootstrap.php b/inc/bootstrap.php
index e8ec4665..2828ee90 100644
--- a/inc/bootstrap.php
+++ b/inc/bootstrap.php
@@ -32,6 +32,7 @@ require_once AKSRT_DIR . '/inc/helpers/aksrt-archive.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-archive-filters.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-comments.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-forum.php';
+require_once AKSRT_DIR . '/inc/helpers/aksrt-forum-widgets.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-slider.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-vip.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-auth-ui.php';
diff --git a/inc/helpers/aksrt-forum-widgets.php b/inc/helpers/aksrt-forum-widgets.php
new file mode 100644
index 00000000..49ff9e22
--- /dev/null
+++ b/inc/helpers/aksrt-forum-widgets.php
@@ -0,0 +1,363 @@
+ (string) get_post_meta($post_id, '_aksrt_topping', true) === '1'
+ || (string) get_post_meta($post_id, '_aksrt_is_top', true) === '1',
+ 'essence' => (string) get_post_meta($post_id, '_aksrt_essence', true) === '1',
+ 'views' => (int) get_post_meta($post_id, '_aksrt_views', true),
+ 'replies' => (int) get_comments_number($post_id),
+ 'plate_id' => (int) get_post_meta($post_id, '_aksrt_plate_id', true),
+ ];
+ }
+endif;
+
+if (!function_exists('aksrt_forum_topic_badges_html')) :
+ /**
+ * Topping / essence badges for forum topic rows.
+ */
+ function aksrt_forum_topic_badges_html(int $post_id, bool $show_topping = true): string {
+ $flags = aksrt_forum_topic_meta_flags($post_id);
+ $html = '';
+ if ($show_topping && !empty($flags['topping'])) {
+ $html .= '' . esc_html__('置顶', 'aksrt') . '';
+ }
+ if (!empty($flags['essence'])) {
+ $html .= '' . esc_html__('精华', 'aksrt') . '';
+ }
+ return $html;
+ }
+endif;
+
+if (!function_exists('aksrt_render_forum_topic_mini')) :
+ /**
+ * Mini forum topic row (zibll zib_bbs_get_posts_mini_list parity).
+ *
+ * @param int|null $post_id Null = current post in loop.
+ * @param string $class Extra classes on outer element.
+ * @param bool $show_topping Whether to show topping badge.
+ */
+ function aksrt_render_forum_topic_mini(?int $post_id = null, string $class = 'ajax-item', bool $show_topping = true): string {
+ $post_id = $post_id ?: (int) get_the_ID();
+ if ($post_id <= 0) {
+ return '';
+ }
+ $post = get_post($post_id);
+ if (!$post || $post->post_status !== 'publish') {
+ return '';
+ }
+
+ $author_id = (int) $post->post_author;
+ $flags = aksrt_forum_topic_meta_flags($post_id);
+ $permalink = get_permalink($post_id);
+ $title = get_the_title($post_id);
+
+ $avatar_url = function_exists('aksrt_get_avatar_url')
+ ? aksrt_get_avatar_url($author_id, ['size' => 48])
+ : get_avatar_url($author_id, ['size' => 48]);
+ $home = function_exists('aksrt_get_user_home_url')
+ ? aksrt_get_user_home_url($author_id)
+ : get_author_posts_url($author_id);
+
+ $name = function_exists('aksrt_get_user_name')
+ ? aksrt_get_user_name([
+ 'id' => $author_id,
+ 'class' => 'flex ac flex1',
+ 'follow' => false,
+ 'medal' => false,
+ 'level' => false,
+ ])
+ : '' . esc_html(get_the_author_meta('display_name', $author_id)) . '';
+
+ // Prefer last reply time when available.
+ $time_raw = (string) get_post_meta($post_id, '_aksrt_last_reply_time', true);
+ if ($time_raw === '' || !strtotime($time_raw)) {
+ $time_raw = (string) $post->post_date;
+ }
+ $time_txt = function_exists('aksrt_time_ago')
+ ? aksrt_time_ago($time_raw)
+ : human_time_diff(strtotime($time_raw), current_time('timestamp')) . __('前', 'aksrt');
+
+ $views_txt = function_exists('aksrt_format_number')
+ ? aksrt_format_number($flags['views'])
+ : (string) $flags['views'];
+
+ $cls = trim('forum-posts mini aksrt-ftopic-mini relative ' . $class);
+ $html = '';
+ $html .= aksrt_forum_topic_badges_html($post_id, $show_topping);
+ $html .= '';
+
+ return $html;
+ }
+endif;
+
+if (!function_exists('aksrt_render_forum_topic_detail')) :
+ /**
+ * Detail forum topic row (richer entry meta).
+ *
+ * @param int|null $post_id
+ * @param string $class
+ * @param bool $show_topping
+ */
+ function aksrt_render_forum_topic_detail(?int $post_id = null, string $class = 'ajax-item', bool $show_topping = true): string {
+ $post_id = $post_id ?: (int) get_the_ID();
+ if ($post_id <= 0) {
+ return '';
+ }
+ $post = get_post($post_id);
+ if (!$post || $post->post_status !== 'publish') {
+ return '';
+ }
+
+ $author_id = (int) $post->post_author;
+ $flags = aksrt_forum_topic_meta_flags($post_id);
+ $permalink = get_permalink($post_id);
+ $title = get_the_title($post_id);
+ $excerpt = wp_trim_words(wp_strip_all_tags((string) $post->post_content), 36, '…');
+
+ $avatar_url = function_exists('aksrt_get_avatar_url')
+ ? aksrt_get_avatar_url($author_id, ['size' => 64])
+ : get_avatar_url($author_id, ['size' => 64]);
+ $home = function_exists('aksrt_get_user_home_url')
+ ? aksrt_get_user_home_url($author_id)
+ : get_author_posts_url($author_id);
+
+ $name = function_exists('aksrt_get_user_name')
+ ? aksrt_get_user_name([
+ 'id' => $author_id,
+ 'class' => 'flex ac',
+ 'follow' => false,
+ 'medal' => true,
+ 'level' => true,
+ ])
+ : '' . esc_html(get_the_author_meta('display_name', $author_id)) . '';
+
+ $time_raw = (string) get_post_meta($post_id, '_aksrt_last_reply_time', true);
+ if ($time_raw === '' || !strtotime($time_raw)) {
+ $time_raw = (string) $post->post_date;
+ }
+ $time_txt = function_exists('aksrt_time_ago')
+ ? aksrt_time_ago($time_raw)
+ : human_time_diff(strtotime($time_raw), current_time('timestamp')) . __('前', 'aksrt');
+
+ $views_txt = function_exists('aksrt_format_number')
+ ? aksrt_format_number($flags['views'])
+ : (string) $flags['views'];
+
+ // Optional plate name chip.
+ $plate_chip = '';
+ if ($flags['plate_id'] > 0) {
+ $plate = get_post($flags['plate_id']);
+ if ($plate instanceof WP_Post) {
+ $plate_chip = '' . esc_html(get_the_title($plate)) . '';
+ }
+ }
+
+ $cls = trim('forum-posts detail aksrt-ftopic-detail relative ' . $class);
+ $html = '';
+ $html .= aksrt_forum_topic_badges_html($post_id, $show_topping);
+ $html .= '';
+ $html .= '';
+ if ($excerpt !== '') {
+ $html .= '' . esc_html($excerpt) . '
';
+ }
+ $html .= '';
+ $html .= $plate_chip;
+ $html .= ' ' . esc_html((string) $views_txt) . '';
+ $html .= ' ' . esc_html((string) $flags['replies']) . '';
+ $html .= '
';
+
+ return $html;
+ }
+endif;
+
+if (!function_exists('aksrt_render_plate_thumb_html')) :
+ /**
+ * Shared plate thumb with lazy pattern.
+ */
+ function aksrt_render_plate_thumb_html(int $plate_id, string $size = 'thumbnail'): string {
+ $thumb = get_the_post_thumbnail_url($plate_id, $size);
+ $icon = (string) get_post_meta($plate_id, '_aksrt_icon', true);
+ $ph = get_template_directory_uri() . '/assets/img/placeholder.svg';
+ if ($thumb) {
+ return '
';
+ }
+ if ($icon !== '') {
+ return '';
+ }
+ return '';
+ }
+endif;
+
+if (!function_exists('aksrt_render_plate_row')) :
+ /**
+ * Plate list row (zibll plate-item flex).
+ *
+ * @param WP_Post|int $plate
+ */
+ function aksrt_render_plate_row($plate): string {
+ $post = $plate instanceof WP_Post ? $plate : get_post((int) $plate);
+ if (!$post instanceof WP_Post) {
+ return '';
+ }
+ $id = (int) $post->ID;
+ $url = get_permalink($id);
+ $count = (int) get_post_meta($id, '_aksrt_posts_count', true);
+ $follow = (int) get_post_meta($id, '_aksrt_follow_count', true);
+ $desc = $post->post_excerpt !== '' ? $post->post_excerpt : wp_trim_words(wp_strip_all_tags((string) $post->post_content), 18);
+
+ $html = '';
+ $html .= aksrt_render_plate_thumb_html($id, 'thumbnail');
+ $html .= '';
+ $html .= '';
+ if ($desc !== '') {
+ $html .= '
' . esc_html($desc) . '
';
+ }
+ $html .= '
';
+ $html .= '' . esc_html(sprintf(/* translators: %d topic count */ __('%d 帖', 'aksrt'), $count)) . '';
+ if ($follow > 0) {
+ $html .= '' . esc_html(sprintf(/* translators: %d follow count */ __('%d 关注', 'aksrt'), $follow)) . '';
+ }
+ $html .= '
';
+ return $html;
+ }
+endif;
+
+if (!function_exists('aksrt_render_plate_card')) :
+ /**
+ * Plate card slide item.
+ *
+ * @param WP_Post|int $plate
+ */
+ function aksrt_render_plate_card($plate): string {
+ $post = $plate instanceof WP_Post ? $plate : get_post((int) $plate);
+ if (!$post instanceof WP_Post) {
+ return '';
+ }
+ $id = (int) $post->ID;
+ $url = get_permalink($id);
+ $thumb = get_the_post_thumbnail_url($id, 'medium');
+ $count = (int) get_post_meta($id, '_aksrt_posts_count', true);
+ $follow = (int) get_post_meta($id, '_aksrt_follow_count', true);
+ $ph = get_template_directory_uri() . '/assets/img/placeholder.svg';
+
+ $html = '';
+ $html .= '';
+ if ($thumb) {
+ $html .= '
 . ')
';
+ } else {
+ $html .= '
';
+ }
+ $html .= '
';
+ $html .= '' . esc_html(get_the_title($post)) . '
';
+ $html .= '';
+ $html .= '' . esc_html(sprintf(__('%d 帖', 'aksrt'), $count)) . '';
+ $html .= '' . esc_html(sprintf(__('%d 关注', 'aksrt'), $follow)) . '';
+ $html .= '
';
+ return $html;
+ }
+endif;
+
+if (!function_exists('aksrt_render_moderator_row')) :
+ /**
+ * Moderator identity row with optional follow.
+ *
+ * @param int $user_id
+ * @param string $role author|super|moderator
+ * @param bool $show_avatar
+ * @param string $style list|grid
+ */
+ function aksrt_render_moderator_row(int $user_id, string $role = 'moderator', bool $show_avatar = true, string $style = 'list'): string {
+ if ($user_id <= 0) {
+ return '';
+ }
+ $user = get_userdata($user_id);
+ if (!$user) {
+ return '';
+ }
+ $avatar = function_exists('aksrt_get_avatar_url')
+ ? aksrt_get_avatar_url($user_id, ['size' => 64])
+ : get_avatar_url($user_id, ['size' => 64]);
+ $home = function_exists('aksrt_get_user_home_url')
+ ? aksrt_get_user_home_url($user_id)
+ : get_author_posts_url($user_id);
+ $ph = get_template_directory_uri() . '/assets/img/placeholder.svg';
+
+ if ($role === 'author' || $role === 'super') {
+ $role_label = __('超级版主', 'aksrt');
+ } else {
+ $role_label = __('版主', 'aksrt');
+ }
+
+ $name = function_exists('aksrt_get_user_name')
+ ? aksrt_get_user_name([
+ 'id' => $user_id,
+ 'class' => 'flex ac flex1',
+ 'follow' => false,
+ 'medal' => true,
+ 'level' => true,
+ ])
+ : '' . esc_html($user->display_name) . '';
+
+ // Follow control (not for self).
+ $follow_html = '';
+ $cuid = get_current_user_id();
+ if ($cuid > 0 && $cuid !== $user_id && class_exists('\AKSRT\User\Follow')) {
+ $following = \AKSRT\User\Follow::get_instance()->is_following($cuid, $user_id);
+ $label = $following ? __('已关注', 'aksrt') : __('关注', 'aksrt');
+ $follow_html = '';
+ }
+
+ if ($style === 'grid') {
+ $html = '
';
+ if ($show_avatar) {
+ $html .= '
 . ')
';
+ }
+ $html .= '
' . $name . '
';
+ $html .= '
' . esc_html($role_label) . '
';
+ $html .= $follow_html;
+ $html .= '
';
+ return $html;
+ }
+
+ $html = '';
+ if ($show_avatar) {
+ $html .= '
 . ')
';
+ }
+ $html .= '
';
+ $html .= '
' . $name . '
';
+ $html .= '
' . esc_html($role_label) . '
';
+ $html .= '
';
+ $html .= $follow_html;
+ $html .= '
';
+ return $html;
+ }
+endif;
diff --git a/src/AKSRT/Widgets/CategoryBlockWidget.php b/src/AKSRT/Widgets/CategoryBlockWidget.php
index 767328f9..80f72769 100644
--- a/src/AKSRT/Widgets/CategoryBlockWidget.php
+++ b/src/AKSRT/Widgets/CategoryBlockWidget.php
@@ -215,25 +215,26 @@ if (!function_exists('aksrt_home_widget_render_post_grid')) {
return;
}
- // Fallback: simple sidebar list
+ // Fallback: shared mini list (lazy thumbs + meta).
+ if (function_exists('aksrt_render_mini_list')) {
+ echo '';
+ return;
+ }
echo '';
wp_reset_postdata();
diff --git a/src/AKSRT/Widgets/CategoryRecommendWidget.php b/src/AKSRT/Widgets/CategoryRecommendWidget.php
index 7d002742..32720184 100644
--- a/src/AKSRT/Widgets/CategoryRecommendWidget.php
+++ b/src/AKSRT/Widgets/CategoryRecommendWidget.php
@@ -48,56 +48,82 @@ class CategoryRecommendWidget extends \WP_Widget {
ob_start();
printf('', (int) $columns);
foreach ($cards as $card) {
- ?>
-
-
-
-
- 0) {
+ $v = function_exists('aksrt_format_number') ? aksrt_format_number((int) $card['views']) : number_format_i18n((int) $card['views']);
+ $more = '
' . esc_html((string) $v) . '';
+ }
+ echo '
';
+ if (function_exists('aksrt_graphic_card') && $cover !== '') {
+ echo aksrt_graphic_card([ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ 'type' => 'style-2',
+ 'class' => 'aksrt-category-recommend__hero mb10',
+ 'img' => $cover,
+ 'alt' => $name,
+ 'link' => ['url' => $link],
+ 'text1' => $name,
+ 'text2' => $show_description ? (string) $card['description'] : '',
+ 'text3' => $badge,
+ 'more' => $more,
+ 'lazy' => true,
+ ]);
+ } else {
+ echo '';
+ echo '';
+ }
+ echo '';
+ // Prefer shared mini list when post IDs available.
+ if (!empty($card['post_ids']) && function_exists('aksrt_render_mini_list')) {
+ $q = new \WP_Query([
+ 'post_type' => 'any',
+ 'post__in' => array_map('intval', (array) $card['post_ids']),
+ 'orderby' => 'post__in',
+ 'posts_per_page' => count((array) $card['post_ids']),
+ 'ignore_sticky_posts' => true,
+ 'no_found_rows' => true,
+ ]);
+ echo '
';
+ aksrt_render_mini_list($q, ['context' => 'widget', 'show_thumb' => true, 'show_meta' => $show_dates]);
+ echo '
';
+ wp_reset_postdata();
+ } else {
+ echo '
';
+ foreach ($card['posts'] as $post_item) {
+ echo '- ';
+ echo '' . esc_html($post_item['title']) . '';
+ if ($show_dates && $post_item['date'] !== '') {
+ echo '' . esc_html($post_item['date']) . '';
+ }
+ echo '
';
+ }
+ echo '
';
+ }
+ if ($show_more) {
+ echo '';
+ }
+ echo '
';
}
echo '
';
return (string) ob_get_clean();
diff --git a/src/AKSRT/Widgets/CategoryTabsWidget.php b/src/AKSRT/Widgets/CategoryTabsWidget.php
index aa738b19..6552eacc 100644
--- a/src/AKSRT/Widgets/CategoryTabsWidget.php
+++ b/src/AKSRT/Widgets/CategoryTabsWidget.php
@@ -130,7 +130,7 @@ class CategoryTabsWidget extends \WP_Widget {
if(showMore&&more){
html+='';
}
- panel.innerHTML=html||'暂无文章
';
+ panel.innerHTML=html||'暂无文章
';
panel.setAttribute('data-loaded','1');
panel.classList.remove('is-ajax-loading');
}).catch(function(){panel.classList.remove('is-ajax-loading');});
diff --git a/src/AKSRT/Widgets/WidgetForumBoardList.php b/src/AKSRT/Widgets/WidgetForumBoardList.php
index 6127f973..c66869fa 100644
--- a/src/AKSRT/Widgets/WidgetForumBoardList.php
+++ b/src/AKSRT/Widgets/WidgetForumBoardList.php
@@ -57,27 +57,44 @@ class WidgetForumBoardList extends \WP_Widget {
$plates = get_posts($query_args);
if ($plates) {
ob_start();
- echo '';
return (string) ob_get_clean();
}
}
+
// Taxonomy fallback.
$term_args = [
'taxonomy' => 'aksrt_forum_board',
diff --git a/src/AKSRT/Widgets/WidgetForumPlateInfo.php b/src/AKSRT/Widgets/WidgetForumPlateInfo.php
index 0a1a2957..f8f78952 100644
--- a/src/AKSRT/Widgets/WidgetForumPlateInfo.php
+++ b/src/AKSRT/Widgets/WidgetForumPlateInfo.php
@@ -54,11 +54,12 @@ class WidgetForumPlateInfo extends \WP_Widget {
: home_url('/');
ob_start();
- echo '';
+ echo '