feat(widgets): category tabs lazy load + ranking extras
SSR first category panel; remaining tabs fetch grid HTML via /widgets/category-tabs. User ranking adds zibll-like desc modes.
This commit is contained in:
1 parent
d508a9aef8
commit
90d84af2f5
3 files changed
+175
-21
No files matched your search
@@ -39,6 +39,11 @@ class WidgetAjax {
|
|||||||
'callback' => [$this, 'rest_paid_posts'],
|
'callback' => [$this, 'rest_paid_posts'],
|
||||||
'permission_callback' => '__return_true',
|
'permission_callback' => '__return_true',
|
||||||
]);
|
]);
|
||||||
|
register_rest_route('aksrt/v1', '/widgets/category-tabs', [
|
||||||
|
'methods' => 'GET',
|
||||||
|
'callback' => [$this, 'rest_category_tabs'],
|
||||||
|
'permission_callback' => '__return_true',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rest_main_posts(\WP_REST_Request $request) {
|
public function rest_main_posts(\WP_REST_Request $request) {
|
||||||
@@ -116,4 +121,38 @@ class WidgetAjax {
|
|||||||
'total' => (int) ($payload['total'] ?? 0),
|
'total' => (int) ($payload['total'] ?? 0),
|
||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function rest_category_tabs(\WP_REST_Request $request) {
|
||||||
|
$cat = max(0, (int) ($request->get_param('cat') ?? 0));
|
||||||
|
$count = max(1, min(12, (int) ($request->get_param('count') ?? 6)));
|
||||||
|
$layout = sanitize_key((string) ($request->get_param('layout') ?? 'card'));
|
||||||
|
$columns = max(1, min(4, (int) ($request->get_param('columns') ?? 3)));
|
||||||
|
$context = sanitize_key((string) ($request->get_param('context') ?? 'homepage'));
|
||||||
|
if ($cat <= 0) {
|
||||||
|
return new \WP_REST_Response(['success' => true, 'html' => ''], 200);
|
||||||
|
}
|
||||||
|
$post_types = function_exists('aksrt_home_widget_post_types') ? aksrt_home_widget_post_types() : ['post'];
|
||||||
|
$query = new \WP_Query([
|
||||||
|
'post_type' => $post_types,
|
||||||
|
'post_status' => 'publish',
|
||||||
|
'posts_per_page' => $count,
|
||||||
|
'ignore_sticky_posts' => true,
|
||||||
|
'no_found_rows' => true,
|
||||||
|
'cat' => $cat,
|
||||||
|
]);
|
||||||
|
ob_start();
|
||||||
|
if (function_exists('aksrt_home_widget_render_post_grid')) {
|
||||||
|
aksrt_home_widget_render_post_grid($query, $layout, $columns, $context);
|
||||||
|
} elseif ($query->have_posts()) {
|
||||||
|
echo '<ul class="widget-tab-posts__list">';
|
||||||
|
while ($query->have_posts()) {
|
||||||
|
$query->the_post();
|
||||||
|
echo '<li><a href="' . esc_url(get_permalink()) . '">' . esc_html(get_the_title()) . '</a></li>';
|
||||||
|
}
|
||||||
|
echo '</ul>';
|
||||||
|
}
|
||||||
|
wp_reset_postdata();
|
||||||
|
$html = (string) ob_get_clean();
|
||||||
|
return new \WP_REST_Response(['success' => true, 'html' => $html], 200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@ class CategoryTabsWidget extends \WP_Widget {
|
|||||||
$tabs = $this->resolve_tabs($instance);
|
$tabs = $this->resolve_tabs($instance);
|
||||||
|
|
||||||
echo $args['before_widget'];
|
echo $args['before_widget'];
|
||||||
echo '<section class="aksrt-home-widget aksrt-home-widget--tabs widget-tab-posts">';
|
echo '<section class="aksrt-home-widget aksrt-home-widget--tabs widget-tab-posts" data-aksrt-home-cat-tabs>';
|
||||||
|
|
||||||
if (empty($tabs)) {
|
if (empty($tabs)) {
|
||||||
echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无可用分类', 'aksrt')) : '<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>';
|
||||||
@@ -69,33 +69,96 @@ class CategoryTabsWidget extends \WP_Widget {
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
foreach ($tabs as $index => $tab) {
|
foreach ($tabs as $index => $tab) {
|
||||||
$query = new \WP_Query([
|
|
||||||
'post_type' => aksrt_home_widget_post_types(),
|
|
||||||
'post_status' => 'publish',
|
|
||||||
'posts_per_page' => $count,
|
|
||||||
'ignore_sticky_posts' => true,
|
|
||||||
'no_found_rows' => true,
|
|
||||||
'cat' => (int) $tab['id'],
|
|
||||||
]);
|
|
||||||
?>
|
?>
|
||||||
<div class="widget-tab-posts__panel<?php echo 0 === $index ? ' active' : ''; ?>" data-tab-panel="cat-<?php echo (int) $tab['id']; ?>" role="tabpanel"<?php echo 0 === $index ? '' : ' hidden'; ?>>
|
<div class="widget-tab-posts__panel<?php echo 0 === $index ? ' active' : ''; ?>"
|
||||||
<?php aksrt_home_widget_render_post_grid($query, $layout, $columns, $context); ?>
|
data-tab-panel="cat-<?php echo (int) $tab['id']; ?>"
|
||||||
<?php if ($show_more) : ?>
|
data-loaded="<?php echo 0 === $index ? '1' : '0'; ?>"
|
||||||
<div class="aksrt-home-widget__footer">
|
data-cat="<?php echo (int) $tab['id']; ?>"
|
||||||
<a class="aksrt-home-widget__more" href="<?php echo esc_url($tab['link']); ?>">
|
data-more="<?php echo esc_url($tab['link']); ?>"
|
||||||
<?php esc_html_e('查看更多', 'aksrt'); ?>
|
role="tabpanel"<?php echo 0 === $index ? '' : ' hidden'; ?>>
|
||||||
</a>
|
<?php if (0 === $index) :
|
||||||
</div>
|
$query = new \WP_Query([
|
||||||
|
'post_type' => aksrt_home_widget_post_types(),
|
||||||
|
'post_status' => 'publish',
|
||||||
|
'posts_per_page' => $count,
|
||||||
|
'ignore_sticky_posts' => true,
|
||||||
|
'no_found_rows' => true,
|
||||||
|
'cat' => (int) $tab['id'],
|
||||||
|
]);
|
||||||
|
aksrt_home_widget_render_post_grid($query, $layout, $columns, $context);
|
||||||
|
wp_reset_postdata();
|
||||||
|
if ($show_more) : ?>
|
||||||
|
<div class="aksrt-home-widget__footer">
|
||||||
|
<a class="aksrt-home-widget__more" href="<?php echo esc_url($tab['link']); ?>">
|
||||||
|
<?php esc_html_e('查看更多', 'aksrt'); ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php endif;
|
||||||
|
else : ?>
|
||||||
|
<div class="post_ajax_loader" data-aksrt-ajax-loader></div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$rest = rest_url('aksrt/v1/widgets/category-tabs');
|
||||||
|
?>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
var section=document.currentScript?document.currentScript.parentElement:null;
|
||||||
|
if(!section||section._aksrtCatTabs)return; section._aksrtCatTabs=1;
|
||||||
|
var rest=<?php echo wp_json_encode($rest); ?>;
|
||||||
|
var count=<?php echo (int) $count; ?>;
|
||||||
|
var layout=<?php echo wp_json_encode($layout); ?>;
|
||||||
|
var columns=<?php echo (int) $columns; ?>;
|
||||||
|
var context=<?php echo wp_json_encode($context); ?>;
|
||||||
|
var showMore=<?php echo $show_more ? 'true' : 'false'; ?>;
|
||||||
|
var tabs=[].slice.call(section.querySelectorAll('.widget-tab-posts__tab'));
|
||||||
|
var panels=[].slice.call(section.querySelectorAll('.widget-tab-posts__panel'));
|
||||||
|
function load(panel){
|
||||||
|
if(!panel||panel.getAttribute('data-loaded')==='1')return;
|
||||||
|
var cat=panel.getAttribute('data-cat')||'0';
|
||||||
|
var more=panel.getAttribute('data-more')||'';
|
||||||
|
var qs=new URLSearchParams({cat:cat,count:String(count),layout:layout,columns:String(columns),context:context});
|
||||||
|
var url=rest+(rest.indexOf('?')>-1?'&':'?')+qs.toString();
|
||||||
|
panel.classList.add('is-ajax-loading');
|
||||||
|
var headers={Accept:'application/json'};
|
||||||
|
if(window.aksrtData&&aksrtData.restNonce)headers['X-WP-Nonce']=aksrtData.restNonce;
|
||||||
|
fetch(url,{credentials:'same-origin',headers:headers}).then(function(r){return r.json()}).then(function(j){
|
||||||
|
var d=(j&&j.data)||j||{};
|
||||||
|
var html=d.html||'';
|
||||||
|
if(showMore&&more){
|
||||||
|
html+='<div class="aksrt-home-widget__footer"><a class="aksrt-home-widget__more" href="'+more+'">查看更多</a></div>';
|
||||||
|
}
|
||||||
|
panel.innerHTML=html||'<p class="widget-empty">暂无文章</p>';
|
||||||
|
panel.setAttribute('data-loaded','1');
|
||||||
|
panel.classList.remove('is-ajax-loading');
|
||||||
|
}).catch(function(){panel.classList.remove('is-ajax-loading');});
|
||||||
|
}
|
||||||
|
tabs.forEach(function(tab){
|
||||||
|
tab.addEventListener('click',function(){
|
||||||
|
var target=tab.getAttribute('data-tab');
|
||||||
|
tabs.forEach(function(item){
|
||||||
|
var active=item===tab;
|
||||||
|
item.classList.toggle('active',active);
|
||||||
|
item.setAttribute('aria-selected',active?'true':'false');
|
||||||
|
});
|
||||||
|
panels.forEach(function(panel){
|
||||||
|
var active=panel.getAttribute('data-tab-panel')===target;
|
||||||
|
panel.classList.toggle('active',active);
|
||||||
|
if(active){panel.removeAttribute('hidden');load(panel);} else {panel.setAttribute('hidden','');}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
|
||||||
echo '</section>';
|
echo '</section>';
|
||||||
echo $args['after_widget'];
|
echo $args['after_widget'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function form($instance): void {
|
public function form($instance): void {
|
||||||
$title = (string) ($instance['title'] ?? '');
|
$title = (string) ($instance['title'] ?? '');
|
||||||
$category_ids = aksrt_home_widget_normalize_category_ids($instance['category_ids'] ?? [], 6);
|
$category_ids = aksrt_home_widget_normalize_category_ids($instance['category_ids'] ?? [], 6);
|
||||||
$count = (int) ($instance['count'] ?? 6);
|
$count = (int) ($instance['count'] ?? 6);
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ class WidgetUserRanking extends \WP_Widget {
|
|||||||
$count = max(1, min(20, (int) ($instance['count'] ?? 5)));
|
$count = max(1, min(20, (int) ($instance['count'] ?? 5)));
|
||||||
$show_avatar = !empty($instance['show_avatar']);
|
$show_avatar = !empty($instance['show_avatar']);
|
||||||
|
|
||||||
$users = $this->get_ranking($rank_by, $count);
|
$desc_mode = sanitize_key((string) ($instance['desc'] ?? 'desc'));
|
||||||
|
$users = $this->get_ranking($rank_by, $count, $desc_mode);
|
||||||
if (empty($users)) return;
|
if (empty($users)) return;
|
||||||
|
|
||||||
echo $args['before_widget'];
|
echo $args['before_widget'];
|
||||||
@@ -60,8 +61,13 @@ class WidgetUserRanking extends \WP_Widget {
|
|||||||
echo '</a>';
|
echo '</a>';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 昵称
|
// 昵称 + 可选签名/附加信息
|
||||||
|
echo '<div class="aksrt-user-ranking__meta">';
|
||||||
echo '<a href="' . esc_url($user_url) . '" class="aksrt-user-ranking__name">' . esc_html($user['name']) . '</a>';
|
echo '<a href="' . esc_url($user_url) . '" class="aksrt-user-ranking__name">' . esc_html($user['name']) . '</a>';
|
||||||
|
if (!empty($user['desc'])) {
|
||||||
|
echo '<div class="aksrt-user-ranking__desc em09 muted-2-color">' . esc_html((string) $user['desc']) . '</div>';
|
||||||
|
}
|
||||||
|
echo '</div>';
|
||||||
|
|
||||||
// 数值
|
// 数值
|
||||||
echo '<span class="aksrt-user-ranking__value">' . esc_html($user['value']) . '</span>';
|
echo '<span class="aksrt-user-ranking__value">' . esc_html($user['value']) . '</span>';
|
||||||
@@ -73,7 +79,7 @@ class WidgetUserRanking extends \WP_Widget {
|
|||||||
echo $args['after_widget'];
|
echo $args['after_widget'];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function get_ranking(string $rank_by, int $count): array {
|
private function get_ranking(string $rank_by, int $count, string $desc_mode = 'desc'): array {
|
||||||
$users = [];
|
$users = [];
|
||||||
|
|
||||||
switch ($rank_by) {
|
switch ($rank_by) {
|
||||||
@@ -93,6 +99,7 @@ class WidgetUserRanking extends \WP_Widget {
|
|||||||
'id' => (int) $row['user_id'],
|
'id' => (int) $row['user_id'],
|
||||||
'name' => $user->display_name,
|
'name' => $user->display_name,
|
||||||
'value' => number_format_i18n((int) $row['credits']) . ' 积分',
|
'value' => number_format_i18n((int) $row['credits']) . ' 积分',
|
||||||
|
'desc' => $this->user_extra_desc((int) $row['user_id'], $user, $desc_mode),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,6 +119,7 @@ class WidgetUserRanking extends \WP_Widget {
|
|||||||
'id' => $user->ID,
|
'id' => $user->ID,
|
||||||
'name' => $user->display_name,
|
'name' => $user->display_name,
|
||||||
'value' => number_format_i18n($post_count) . ' 篇',
|
'value' => number_format_i18n($post_count) . ' 篇',
|
||||||
|
'desc' => $this->user_extra_desc($user->ID, $user, $desc_mode),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,6 +145,7 @@ class WidgetUserRanking extends \WP_Widget {
|
|||||||
'id' => $uid,
|
'id' => $uid,
|
||||||
'name' => $user->display_name,
|
'name' => $user->display_name,
|
||||||
'value' => number_format_i18n((int) $row['val']) . ' 天',
|
'value' => number_format_i18n((int) $row['val']) . ' 天',
|
||||||
|
'desc' => $this->user_extra_desc($uid, $user, $desc_mode),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (count($users) >= $count) break;
|
if (count($users) >= $count) break;
|
||||||
@@ -163,6 +172,7 @@ class WidgetUserRanking extends \WP_Widget {
|
|||||||
'id' => $uid,
|
'id' => $uid,
|
||||||
'name' => $user->display_name,
|
'name' => $user->display_name,
|
||||||
'value' => number_format_i18n((int) $row['val']) . ' 天',
|
'value' => number_format_i18n((int) $row['val']) . ' 天',
|
||||||
|
'desc' => $this->user_extra_desc($uid, $user, $desc_mode),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (count($users) >= $count) break;
|
if (count($users) >= $count) break;
|
||||||
@@ -189,6 +199,7 @@ class WidgetUserRanking extends \WP_Widget {
|
|||||||
'id' => (int) $row['following_id'],
|
'id' => (int) $row['following_id'],
|
||||||
'name' => $user->display_name,
|
'name' => $user->display_name,
|
||||||
'value' => number_format_i18n((int) $row['cnt']) . ' 粉丝',
|
'value' => number_format_i18n((int) $row['cnt']) . ' 粉丝',
|
||||||
|
'desc' => $this->user_extra_desc((int) $row['following_id'], $user, $desc_mode),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,6 +211,37 @@ class WidgetUserRanking extends \WP_Widget {
|
|||||||
return $users;
|
return $users;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function user_extra_desc(int $user_id, $user, string $mode): string {
|
||||||
|
switch ($mode) {
|
||||||
|
case 'user_registered':
|
||||||
|
if ($user && !empty($user->user_registered)) {
|
||||||
|
$days = max(0, (int) floor((time() - strtotime($user->user_registered)) / DAY_IN_SECONDS));
|
||||||
|
return sprintf(__('加入 %d 天', 'aksrt'), $days);
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
case 'last_login':
|
||||||
|
$t = (string) get_user_meta($user_id, 'aksrt_last_login', true);
|
||||||
|
if ($t === '') {
|
||||||
|
$t = (string) get_user_meta($user_id, 'last_login', true);
|
||||||
|
}
|
||||||
|
return $t !== '' ? sprintf(__('最近登录 %s', 'aksrt'), $t) : '';
|
||||||
|
case 'last_checkin':
|
||||||
|
$t = (string) get_user_meta($user_id, 'aksrt_last_checkin', true);
|
||||||
|
if ($t === '') {
|
||||||
|
$t = (string) get_user_meta($user_id, 'last_checkin', true);
|
||||||
|
}
|
||||||
|
return $t !== '' ? sprintf(__('最近签到 %s', 'aksrt'), $t) : '';
|
||||||
|
case 'desc':
|
||||||
|
default:
|
||||||
|
$bio = get_user_meta($user_id, 'description', true);
|
||||||
|
if (!is_string($bio) || $bio === '') {
|
||||||
|
$bio = $user ? (string) $user->description : '';
|
||||||
|
}
|
||||||
|
return $bio !== '' ? wp_trim_words($bio, 12) : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function form($instance): void {
|
public function form($instance): void {
|
||||||
$title = !empty($instance['title']) ? $instance['title'] : __('排行榜', 'aksrt');
|
$title = !empty($instance['title']) ? $instance['title'] : __('排行榜', 'aksrt');
|
||||||
$rank_by = !empty($instance['rank_by']) ? $instance['rank_by'] : 'credits';
|
$rank_by = !empty($instance['rank_by']) ? $instance['rank_by'] : 'credits';
|
||||||
@@ -230,6 +272,15 @@ class WidgetUserRanking extends \WP_Widget {
|
|||||||
<?php esc_html_e('显示头像', 'aksrt'); ?>
|
<?php esc_html_e('显示头像', 'aksrt'); ?>
|
||||||
</label>
|
</label>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="<?php echo esc_attr($this->get_field_id('desc')); ?>"><?php esc_html_e('附加显示:', 'aksrt'); ?></label>
|
||||||
|
<select class="widefat" id="<?php echo esc_attr($this->get_field_id('desc')); ?>" name="<?php echo esc_attr($this->get_field_name('desc')); ?>">
|
||||||
|
<option value="desc" <?php selected(($instance['desc'] ?? 'desc'), 'desc'); ?>><?php esc_html_e('用户签名', 'aksrt'); ?></option>
|
||||||
|
<option value="user_registered" <?php selected(($instance['desc'] ?? ''), 'user_registered'); ?>><?php esc_html_e('加入天数', 'aksrt'); ?></option>
|
||||||
|
<option value="last_login" <?php selected(($instance['desc'] ?? ''), 'last_login'); ?>><?php esc_html_e('最后登录', 'aksrt'); ?></option>
|
||||||
|
<option value="last_checkin" <?php selected(($instance['desc'] ?? ''), 'last_checkin'); ?>><?php esc_html_e('最后签到', 'aksrt'); ?></option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="<?php echo esc_attr($this->get_field_name('checkin_btn')); ?>" value="1" <?php checked(!empty($instance['checkin_btn'])); ?>>
|
<input type="checkbox" name="<?php echo esc_attr($this->get_field_name('checkin_btn')); ?>" value="1" <?php checked(!empty($instance['checkin_btn'])); ?>>
|
||||||
@@ -246,6 +297,7 @@ class WidgetUserRanking extends \WP_Widget {
|
|||||||
$instance['count'] = max(1, min(20, (int) ($new_instance['count'] ?? 5)));
|
$instance['count'] = max(1, min(20, (int) ($new_instance['count'] ?? 5)));
|
||||||
$instance['show_avatar'] = !empty($new_instance['show_avatar']);
|
$instance['show_avatar'] = !empty($new_instance['show_avatar']);
|
||||||
$instance['checkin_btn'] = !empty($new_instance['checkin_btn']);
|
$instance['checkin_btn'] = !empty($new_instance['checkin_btn']);
|
||||||
|
$instance['desc'] = in_array($new_instance['desc'] ?? '', ['desc', 'user_registered', 'last_login', 'last_checkin'], true) ? $new_instance['desc'] : 'desc';
|
||||||
return $instance;
|
return $instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user