feat(forum): rebuild forum home page

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Fable 5 committed 2026-11-11 14:41:02 +08:00
1 parent 51d5b8a2a1
commit 024ecddfed
16 files changed
+1061 -198

No files matched your search

+196
View File
@@ -0,0 +1,196 @@
<?php
namespace Jio\Forum;
defined('ABSPATH') || exit;
final class HomeContent {
public static function render(array $tab, int $index): string {
$page = self::page();
$html = match ($tab['type']) {
'plate' => self::plate_tab(),
'follow' => self::follow_tab($tab, $index, $page),
'pending' => self::pending_tab($tab, $index, $page),
default => self::posts_tab($tab, $index, $page),
};
if ($page === 1 && $tab['type'] !== 'plate' && jio_get_setting('jio_forum_home_quick_post', true) && $index === (int) jio_get_setting('jio_forum_home_quick_post_tab', 2)) {
$html = QuickPost::render() . $html;
}
return $html;
}
public static function page(): int {
$raw = $_GET['fpage'] ?? 1;
return max(1, is_scalar($raw) ? absint($raw) : 1);
}
private static function posts_tab(array $tab, int $index, int $page): string {
$args = self::query_args($tab, $page);
$html = self::post_list($args, $tab, $index, $page, $tab['type'] === 'synthesis');
if ($html !== '') return $html;
$html = jio_get_ajax_null('内容空空如也');
if ($page === 1) {
$html .= '<div class="text-center mb20 ajax-item">' . self::create_plate_button('ml10 mr10 mt10 but hollow c-blue padding-lg') . self::publish_button('ml10 mr10 mt10 but hollow c-green padding-lg') . '</div>';
}
return $html;
}
private static function follow_tab(array $tab, int $index, int $page): string {
$user_id = get_current_user_id();
if (!$user_id) {
return '<div class="ajax-item">' . jio_get_null('登录后继续查看', 20, 'null-user.svg', '', 0, 220)
. '<p class="text-center mb20"><a class="but jb-blue padding-lg" href="' . esc_url(jio_get_login_url(Core::home_url())) . '"><i class="fa fas fa-sign-in-alt mr6"></i>登录</a></p></div>';
}
$followed = View::user_marked_ids($user_id, '_jio_forum_follow_');
$html = '';
if ($page === 1 && jio_get_setting('jio_forum_home_recommend_plate', true)) {
$plates = self::plates(['posts_per_page' => 8, 'post__not_in' => $followed]);
$slide = Cards::plate_slide($plates);
if ($slide !== '') {
$html .= '<div class="ajax-item mb20"><div class="box-body notop"><div class="title-theme">热门推荐</div></div>' . $slide . '</div>';
}
}
$list = '';
if ($followed) {
$args = self::query_args($tab, $page);
$args['meta_query'][] = ['key' => '_jio_forum_plate_id', 'value' => $followed, 'compare' => 'IN'];
$list = self::post_list($args, $tab, $index, $page);
}
return $html . ($list !== '' ? $list : jio_get_ajax_null('暂无内容', 60, 'null.svg'));
}
private static function pending_tab(array $tab, int $index, int $page): string {
$html = '';
if ($page === 1) {
foreach (get_posts(['post_type' => Core::PLATE, 'post_status' => 'pending', 'posts_per_page' => 20]) as $plate) {
$html .= '<div class="forum-pending-plate jio-widget-box flex ac jsb ajax-item"><span class="text-ellipsis"><span class="badg badg-sm c-yellow mr6">待审核版块</span>' . esc_html($plate->post_title) . '</span>'
. '<button type="button" class="but c-blue p2-10 px12 flex0" data-forum-action="plate-audit" data-id="' . (int) $plate->ID . '">通过审核</button></div>';
}
}
$list = self::post_list(['post_type' => Core::POST, 'post_status' => 'pending', 'posts_per_page' => self::per_page(), 'paged' => $page], $tab, $index, $page);
$html .= $list;
return $html !== '' ? $html : jio_get_ajax_null('暂无待审核内容');
}
private static function plate_tab(): string {
$html = '';
$user_id = get_current_user_id();
if ($user_id && jio_get_setting('jio_forum_home_follow_plate', true)) {
$followed = View::user_marked_ids($user_id, '_jio_forum_follow_');
$slide = $followed ? Cards::plate_slide(self::plates(['post__in' => $followed, 'posts_per_page' => 20]), 'mini', false, true) : '';
if ($slide !== '') $html .= self::panel('我关注的版块', $slide);
}
if (jio_get_setting('jio_forum_home_hot_plate', true)) {
$slide = Cards::plate_slide(self::plates(['posts_per_page' => 8, 'meta_key' => '_jio_forum_follow_count', 'orderby' => 'meta_value_num']));
if ($slide !== '') $html .= self::panel('热门推荐', $slide);
}
$all = self::plates(['posts_per_page' => -1, 'orderby' => 'menu_order title', 'order' => 'ASC']);
$groups = [];
$loose = [];
$terms = wp_get_object_terms(wp_list_pluck($all, 'ID'), Core::CATEGORY, ['fields' => 'all_with_object_id']);
$by_plate = [];
foreach (is_array($terms) ? $terms : [] as $term) {
$by_plate[(int) $term->object_id][] = $term;
}
foreach ($all as $plate) {
if (empty($by_plate[$plate->ID])) {
$loose[] = $plate;
continue;
}
foreach ($by_plate[$plate->ID] as $term) {
$groups[$term->term_id]['term'] = $term;
$groups[$term->term_id]['plates'][] = $plate;
}
}
uasort($groups, static fn(array $a, array $b): int => count($b['plates']) <=> count($a['plates']));
$plate_name = (string) jio_get_setting('jio_forum_plate_name', '版块') ?: '版块';
$single_group = count($groups) === 0 && $loose;
if ($loose) $groups[0] = ['term' => null, 'plates' => $loose];
$i = 1;
foreach ($groups as $term_id => $group) {
$name = $group['term'] instanceof \WP_Term ? $group['term']->name : ($single_group ? '全部' . $plate_name : '其他' . $plate_name);
$html .= self::plate_group('home-plate-panel-' . $i++, $name, $group['plates'], (int) $term_id);
}
if ($html === '') {
$html = jio_get_null('暂时没有版块', 60, 'null.svg', '', 0, 170) . '<div class="text-center mt20">' . self::create_plate_button('but hollow c-blue padding-lg') . '</div>';
}
return '<div class="ajax-item">' . $html . '</div>';
}
/** @param \WP_Post[] $plates */
private static function plate_group(string $id, string $name, array $plates, int $term_id): string {
$add = self::create_plate_button('plate-add ml20 but hollow c-blue p2-10 px12 flex0', $term_id);
$items = '';
foreach ($plates as $plate) {
$items .= Cards::plate_item($plate);
}
$title = '<span class="title-theme" title="' . esc_attr($name) . '"><span class="text-ellipsis">' . esc_html($name) . '</span></span>';
$top = wp_is_mobile()
? '<h4 class="panel-title flex ac">' . $title . $add . '</h4>'
: '<h4 class="panel-title flex ac"><a class="flex ac jsb flex1" data-toggle="collapse" href="#' . esc_attr($id) . '">' . $title . '<span><i class="ml10 mr10 fas fa-angle-up em14 muted-2-color"></i></span></a>' . $add . '</h4>';
return '<div class="panel panel-plate">' . $top . '<div id="' . esc_attr($id) . '" class="panel-collapse collapse in"><div class="plate-lists">' . $items . '</div></div></div>';
}
private static function panel(string $title, string $body): string {
return '<div class="panel panel-plate"><h4 class="panel-title" title="' . esc_attr($title) . '"><span class="title-theme">' . esc_html($title) . '</span></h4>' . $body . '</div>';
}
/** @return \WP_Post[] */
private static function plates(array $args): array {
$plates = get_posts(array_merge(['post_type' => Core::PLATE, 'post_status' => 'publish', 'orderby' => 'modified', 'order' => 'DESC', 'no_found_rows' => true], $args));
PlateStats::prime(wp_list_pluck($plates, 'ID'));
return $plates;
}
private static function query_args(array $tab, int $page): array {
$args = [
'post_type' => Core::POST,
'post_status' => 'publish',
'posts_per_page' => self::per_page(),
'paged' => $page,
'jio_forum_list' => $tab['orderby'],
'meta_query' => [],
];
if ($tab['filter'] === 'essence') {
$args['meta_query'][] = ['key' => '_jio_forum_essence', 'value' => 1];
} elseif (in_array($tab['filter'], ['question', 'vote'], true)) {
$args['meta_query'][] = ['key' => '_jio_forum_type', 'value' => $tab['filter']];
}
return $args;
}
private static function post_list(array $args, array $tab, int $index, int $page, bool $show_topping = false): string {
$query = new \WP_Query($args);
if (!$query->have_posts()) return '';
Cards::prime($query->posts);
$html = '';
foreach ($query->posts as $post) {
$html .= $tab['style'] === 'mini' || $tab['type'] === 'pending'
? Cards::post_mini($post, 'alone ajax-item', $show_topping)
: Cards::post_detail($post, 'alone ajax-item', true, $show_topping);
}
if ($page < (int) $query->max_num_pages) {
$next = add_query_arg(['index' => $index, 'fpage' => $page + 1], remove_query_arg(['index', 'fpage']));
$html .= '<div class="text-center theme-pagination forum-more"><a class="but hollow c-blue" href="' . esc_url($next) . '" data-forum-more>加载更多</a></div>';
}
$html .= '<div class="post_ajax_loader">' . HomeTabs::placeholder('posts', 2) . '</div>';
return $html;
}
private static function per_page(): int {
return max(1, min(50, (int) jio_get_setting('jio_forum_page_size', 20)));
}
public static function create_plate_button(string $class, int $term_id = 0): string {
$user_id = get_current_user_id();
if (!$user_id || (!Access::is_admin($user_id) && !jio_get_setting('jio_forum_user_plates', true))) return '';
$name = (string) jio_get_setting('jio_forum_plate_name', '版块') ?: '版块';
return '<button type="button" class="' . esc_attr($class) . '" data-forum-open="plate" data-category="' . (int) $term_id . '"><i class="fa fas fa-plus mr3"></i>创建' . esc_html($name) . '</button>';
}
public static function publish_button(string $class): string {
$name = (string) jio_get_setting('jio_forum_post_name', '帖子') ?: '帖子';
$url = is_user_logged_in() ? Core::edit_url() : jio_get_login_url(Core::edit_url());
return '<a class="' . esc_attr($class) . '" href="' . esc_url($url) . '"><i class="fa fas fa-edit mr3"></i>发布' . esc_html($name) . '</a>';
}
}