feat(forum): detail/mini topic list item 1:1 structure
Expand render_topic_row into zibll-like detail cards (author card, cover, excerpt, multi-image grid, vote strip, plate + action-meta) and keep mini compact variant for dense lists.
This commit is contained in:
@@ -250,8 +250,8 @@ class Forum {
|
||||
$this->view_builder()->render_board_cards($boards);
|
||||
}
|
||||
|
||||
public function render_topic_row(array $topic): void {
|
||||
$this->view_builder()->render_topic_row($topic);
|
||||
public function render_topic_row(array $topic, string $style = 'detail'): void {
|
||||
$this->view_builder()->render_topic_row($topic, $style);
|
||||
}
|
||||
|
||||
public function get_board_view(int $board_id, array $args = []): array {
|
||||
|
||||
+181
-37
@@ -435,58 +435,97 @@ class ViewBuilder {
|
||||
|
||||
/**
|
||||
* Render a single topic row (reusable for AJAX and initial render).
|
||||
*
|
||||
* @param array $topic Topic payload.
|
||||
* @param string $style detail|mini (zibll forum-posts parity).
|
||||
*/
|
||||
public function render_topic_row(array $topic): void {
|
||||
public function render_topic_row(array $topic, string $style = 'detail'): void {
|
||||
$style = $style === 'mini' ? 'mini' : 'detail';
|
||||
$board = !empty($topic['boards'][0]) ? $topic['boards'][0] : null;
|
||||
$names = $this->forum->get_config()['names'] ?? [];
|
||||
$sticky_level = (int) ($topic['sticky_level'] ?? (!empty($topic['sticky']) ? 1 : 0));
|
||||
$row_class = 'aksrt-forum-topic-row';
|
||||
$permalink = (string) ($topic['permalink'] ?? '#');
|
||||
$cover = is_array($topic['cover'] ?? null) ? $topic['cover'] : null;
|
||||
$vote = is_array($topic['vote'] ?? null) ? $topic['vote'] : null;
|
||||
$excerpt = trim((string) ($topic['excerpt'] ?? ''));
|
||||
$topic_cat = is_array($topic['topic_cat'] ?? null) ? $topic['topic_cat'] : null;
|
||||
|
||||
$row_class = 'aksrt-forum-topic-row aksrt-forum-posts forum-posts ajax-item ' . $style;
|
||||
if ($sticky_level > 0) {
|
||||
$row_class .= ' is-sticky is-sticky-' . $sticky_level;
|
||||
}
|
||||
if (!empty($topic['featured'])) {
|
||||
$row_class .= ' is-featured';
|
||||
}
|
||||
$cover = $topic['cover'] ?? null;
|
||||
|
||||
$badges = '';
|
||||
if ($sticky_level > 0) {
|
||||
$badges .= '<span class="aksrt-forum-topic-row__badge aksrt-forum-topic-row__badge--sticky badge-topping">'
|
||||
. esc_html((string) ($topic['sticky_label'] ?? __('置顶', 'aksrt'))) . '</span>';
|
||||
}
|
||||
if (!empty($topic['featured'])) {
|
||||
$badges .= '<span class="aksrt-forum-topic-row__badge aksrt-forum-topic-row__badge--featured badge-essence">'
|
||||
. esc_html__('精华', 'aksrt') . '</span>';
|
||||
}
|
||||
if (($topic['bbs_type'] ?? '') === 'question') {
|
||||
$q_status = (int) ($topic['question_status'] ?? 0);
|
||||
$badges .= '<span class="aksrt-forum-topic-row__badge aksrt-forum-topic-row__badge--question'
|
||||
. ($q_status === 1 ? ' is-solved' : '') . '">'
|
||||
. esc_html($q_status === 1
|
||||
? (string) ($names['question_ok_badge'] ?? __('已解决', 'aksrt'))
|
||||
: (string) ($names['question_badge'] ?? __('提问', 'aksrt')))
|
||||
. '</span>';
|
||||
}
|
||||
|
||||
$title_html = $badges
|
||||
. '<h3 class="aksrt-forum-topic-row__title forum-title">'
|
||||
. '<a href="' . esc_url($permalink) . '">' . esc_html((string) ($topic['title'] ?? '')) . '</a></h3>';
|
||||
|
||||
if ($style === 'mini') {
|
||||
?>
|
||||
<article class="<?php echo esc_attr($row_class); ?>" data-topic-id="<?php echo esc_attr((string) ($topic['id'] ?? 0)); ?>">
|
||||
<div class="aksrt-forum-topic-row__avatar forum-user">
|
||||
<?php if (!empty($topic['author']['avatar_url'])) : ?>
|
||||
<img src="<?php echo esc_url((string) $topic['author']['avatar_url']); ?>" alt="" width="40" height="40" loading="lazy" class="forum-avatar">
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="aksrt-forum-topic-row__body entry-info">
|
||||
<div class="aksrt-forum-topic-row__title-wrap"><?php echo $title_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
|
||||
<div class="aksrt-forum-topic-row__meta item-meta flex ac jsb">
|
||||
<div class="meta-left">
|
||||
<span class="aksrt-forum-topic-row__author"><?php echo esc_html((string) ($topic['author']['display_name'] ?? '')); ?></span>
|
||||
<span class="aksrt-forum-topic-row__time icon-circle"><?php echo esc_html((string) ($topic['date'] ?? '')); ?></span>
|
||||
</div>
|
||||
<div class="meta-right action-meta">
|
||||
<span title="<?php esc_attr_e('浏览', 'aksrt'); ?>"><i class="fas fa-eye"></i> <?php echo esc_html(number_format_i18n((int) ($topic['views'] ?? 0))); ?></span>
|
||||
<span title="<?php esc_attr_e('回复', 'aksrt'); ?>"><i class="fas fa-comment"></i> <?php echo esc_html(number_format_i18n((int) ($topic['comment_count'] ?? 0))); ?></span>
|
||||
<span title="<?php esc_attr_e('评分', 'aksrt'); ?>"><i class="fas fa-thumbs-up"></i> <?php echo esc_html(number_format_i18n((int) ($topic['score'] ?? 0))); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<?php
|
||||
return;
|
||||
}
|
||||
|
||||
// detail
|
||||
$list_imgs = $this->extract_topic_list_images($topic, 4);
|
||||
?>
|
||||
<article class="<?php echo esc_attr($row_class); ?>">
|
||||
<div class="aksrt-forum-topic-row__avatar">
|
||||
<?php if (!empty($topic['author']['avatar_url'])) : ?>
|
||||
<img src="<?php echo esc_url((string) $topic['author']['avatar_url']); ?>" alt="" width="40" height="40" loading="lazy">
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="aksrt-forum-topic-row__body">
|
||||
<div class="aksrt-forum-topic-row__title-wrap">
|
||||
<?php if ($sticky_level > 0) : ?>
|
||||
<span class="aksrt-forum-topic-row__badge aksrt-forum-topic-row__badge--sticky"><?php echo esc_html((string) ($topic['sticky_label'] ?? __('置顶', 'aksrt'))); ?></span>
|
||||
<article class="<?php echo esc_attr($row_class); ?>" data-topic-id="<?php echo esc_attr((string) ($topic['id'] ?? 0)); ?>">
|
||||
<div class="aksrt-forum-topic-row__author-card mb10 flex ac">
|
||||
<div class="aksrt-forum-topic-row__avatar">
|
||||
<?php if (!empty($topic['author']['avatar_url'])) : ?>
|
||||
<img src="<?php echo esc_url((string) $topic['author']['avatar_url']); ?>" alt="" width="44" height="44" loading="lazy">
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($topic['featured'])) : ?>
|
||||
<span class="aksrt-forum-topic-row__badge aksrt-forum-topic-row__badge--featured"><?php esc_html_e('精华', 'aksrt'); ?></span>
|
||||
<?php endif; ?>
|
||||
<?php if (($topic['bbs_type'] ?? '') === 'question') : ?>
|
||||
<?php $q_status = (int) ($topic['question_status'] ?? 0); ?>
|
||||
<span class="aksrt-forum-topic-row__badge aksrt-forum-topic-row__badge--question<?php echo $q_status === 1 ? ' is-solved' : ''; ?>">
|
||||
<?php echo $q_status === 1
|
||||
? esc_html($names['question_ok_badge'] ?? __('已解决', 'aksrt'))
|
||||
: esc_html($names['question_badge'] ?? __('提问', 'aksrt')); ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<h3 class="aksrt-forum-topic-row__title">
|
||||
<a href="<?php echo esc_url((string) ($topic['permalink'] ?? '#')); ?>"><?php echo esc_html((string) ($topic['title'] ?? '')); ?></a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="aksrt-forum-topic-row__meta">
|
||||
<?php if ($board) : ?>
|
||||
<a class="aksrt-forum-topic-row__board" href="<?php echo esc_url((string) ($board['url'] ?? '#')); ?>"><?php echo esc_html((string) ($board['name'] ?? '')); ?></a>
|
||||
<?php endif; ?>
|
||||
<span class="aksrt-forum-topic-row__author"><?php echo esc_html((string) ($topic['author']['display_name'] ?? '')); ?></span>
|
||||
<span class="aksrt-forum-topic-row__time"><?php echo esc_html((string) ($topic['date'] ?? '')); ?></span>
|
||||
<span><i class="fas fa-eye"></i> <?php echo esc_html(number_format_i18n((int) ($topic['views'] ?? 0))); ?></span>
|
||||
<span><i class="fas fa-comment"></i> <?php echo esc_html(number_format_i18n((int) ($topic['comment_count'] ?? 0))); ?></span>
|
||||
<div class="aksrt-forum-topic-row__author-meta">
|
||||
<div class="aksrt-forum-topic-row__author-name"><?php echo esc_html((string) ($topic['author']['display_name'] ?? '')); ?></div>
|
||||
<div class="aksrt-forum-topic-row__time muted-2-color em09"><?php echo esc_html((string) ($topic['date'] ?? '')); ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (is_array($cover) && !empty($cover['type'])) : ?>
|
||||
<div class="aksrt-forum-topic-row__cover">
|
||||
<a class="aksrt-forum-topic-row__cover forum-lists-cover" href="<?php echo esc_url($permalink); ?>">
|
||||
<?php if (($cover['type'] ?? '') === 'video') : ?>
|
||||
<?php if (!empty($cover['poster'])) : ?>
|
||||
<img src="<?php echo esc_url((string) $cover['poster']); ?>" alt="" loading="lazy">
|
||||
@@ -498,12 +537,117 @@ class ViewBuilder {
|
||||
<?php elseif (!empty($cover['url'])) : ?>
|
||||
<img src="<?php echo esc_url((string) $cover['url']); ?>" alt="" loading="lazy">
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="aksrt-forum-topic-row__body">
|
||||
<div class="aksrt-forum-topic-row__title-wrap"><?php echo $title_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
|
||||
|
||||
<div class="content mt6">
|
||||
<?php if ($topic_cat && !empty($topic_cat['name'])) : ?>
|
||||
<a class="aksrt-forum-topic-row__topic-link focus-color mr3" href="<?php echo esc_url((string) ($topic_cat['url'] ?? '#')); ?>">
|
||||
<i class="fas fa-hashtag"></i><?php echo esc_html((string) $topic_cat['name']); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if ($excerpt !== '') : ?>
|
||||
<div class="excerpt"><?php echo esc_html($excerpt); ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($list_imgs) && empty($cover)) : ?>
|
||||
<div class="imgbox-container lists-imgs mt6 count-<?php echo esc_attr((string) count($list_imgs)); ?>">
|
||||
<?php foreach ($list_imgs as $img_url) : ?>
|
||||
<a class="lists-imgs__item cursor-zoom-in" href="<?php echo esc_url($permalink); ?>">
|
||||
<img src="<?php echo esc_url($img_url); ?>" alt="" loading="lazy">
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($vote['enabled']) && !empty($vote['options']) && is_array($vote['options'])) : ?>
|
||||
<div class="aksrt-forum-topic-row__vote mt10">
|
||||
<?php
|
||||
$total_votes = 0;
|
||||
foreach ($vote['options'] as $opt) {
|
||||
$total_votes += (int) ($opt['count'] ?? 0);
|
||||
}
|
||||
foreach (array_slice($vote['options'], 0, 4) as $opt) :
|
||||
$cnt = (int) ($opt['count'] ?? 0);
|
||||
$pct = $total_votes > 0 ? round($cnt * 100 / $total_votes) : 0;
|
||||
?>
|
||||
<div class="aksrt-forum-topic-row__vote-opt">
|
||||
<span class="aksrt-forum-topic-row__vote-label"><?php echo esc_html((string) ($opt['label'] ?? $opt['text'] ?? '')); ?></span>
|
||||
<span class="aksrt-forum-topic-row__vote-bar"><i style="width:<?php echo esc_attr((string) $pct); ?>%"></i></span>
|
||||
<span class="aksrt-forum-topic-row__vote-pct"><?php echo esc_html((string) $pct); ?>%</span>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="flex ac jsb mt10 aksrt-forum-topic-row__footer">
|
||||
<?php if ($board) : ?>
|
||||
<a class="but but-plate aksrt-forum-topic-row__board" href="<?php echo esc_url((string) ($board['url'] ?? '#')); ?>">
|
||||
<?php echo esc_html((string) ($board['name'] ?? '')); ?>
|
||||
<i class="fa fa-angle-right ml6"></i>
|
||||
</a>
|
||||
<?php else : ?>
|
||||
<span class="plate-null"></span>
|
||||
<?php endif; ?>
|
||||
<div class="action-meta flex0 flex jsb aksrt-forum-topic-row__actions">
|
||||
<span title="<?php esc_attr_e('评分', 'aksrt'); ?>"><i class="fas fa-thumbs-up"></i> <?php echo esc_html(number_format_i18n((int) ($topic['score'] ?? 0))); ?></span>
|
||||
<a href="<?php echo esc_url($permalink); ?>#comments" title="<?php esc_attr_e('回复', 'aksrt'); ?>"><i class="fas fa-comment"></i> <?php echo esc_html(number_format_i18n((int) ($topic['comment_count'] ?? 0))); ?></a>
|
||||
<span title="<?php esc_attr_e('浏览', 'aksrt'); ?>"><i class="fas fa-eye"></i> <?php echo esc_html(number_format_i18n((int) ($topic['views'] ?? 0))); ?></span>
|
||||
<span title="<?php esc_attr_e('收藏', 'aksrt'); ?>"><i class="fas fa-star"></i> <?php echo esc_html(number_format_i18n((int) ($topic['favorite_count'] ?? 0))); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull up to N image URLs from cover slides or content HTML for list grids.
|
||||
*
|
||||
* @return array<int,string>
|
||||
*/
|
||||
private function extract_topic_list_images(array $topic, int $max = 4): array {
|
||||
$max = max(1, min(6, $max));
|
||||
$out = [];
|
||||
$cover = is_array($topic['cover'] ?? null) ? $topic['cover'] : null;
|
||||
if ($cover) {
|
||||
if (($cover['type'] ?? '') === 'slide' && !empty($cover['images']) && is_array($cover['images'])) {
|
||||
foreach ($cover['images'] as $u) {
|
||||
$u = esc_url_raw((string) $u);
|
||||
if ($u !== '') {
|
||||
$out[] = $u;
|
||||
}
|
||||
if (count($out) >= $max) {
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
} elseif (!empty($cover['url'])) {
|
||||
$out[] = esc_url_raw((string) $cover['url']);
|
||||
}
|
||||
}
|
||||
if (count($out) >= $max) {
|
||||
return $out;
|
||||
}
|
||||
$content = (string) ($topic['content'] ?? '');
|
||||
if ($content !== '' && preg_match_all('/<img[^>]+src=["\']([^"\']+)["\']/i', $content, $m)) {
|
||||
foreach ($m[1] as $src) {
|
||||
$src = esc_url_raw((string) $src);
|
||||
if ($src === '' || in_array($src, $out, true)) {
|
||||
continue;
|
||||
}
|
||||
$out[] = $src;
|
||||
if (count($out) >= $max) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function get_user_forum_topics(int $user_id, array $args = []): array {
|
||||
$status = $args['status'] ?? 'publish';
|
||||
$page = max(1, (int) ($args['page'] ?? 1));
|
||||
|
||||
Reference in New Issue
Block a user