feat(shop): category filter bar + sort/load-more 1:1 UX
Port zibll hierarchical cat tabs, multi-metric sort with price toggle, skeleton/empty states, and REST archive payload (header/html/paginate) while reusing product-card template for AJAX HTML.
This commit is contained in:
1 parent
e2587ca3a0
commit
433b7cb3a6
9 files changed
+828
-273
No files matched your search
@@ -363,75 +363,92 @@ trait ShopControllerFrontendFlowOperations {
|
||||
$term_id = (int) ($request->get_param('term_id') ?? 0);
|
||||
$page = max(1, (int) ($request->get_param('page') ?? 1));
|
||||
$orderby = sanitize_key((string) ($request->get_param('orderby') ?? ''));
|
||||
$order = sanitize_key((string) ($request->get_param('order') ?? ''));
|
||||
|
||||
$view = \AKSRT\Shop\Shop::get_instance()->get_product_archive_view([
|
||||
'taxonomy' => $taxonomy,
|
||||
'term_id' => $term_id,
|
||||
'paged' => $page,
|
||||
'orderby' => $orderby,
|
||||
'order' => $order,
|
||||
]);
|
||||
|
||||
$sales_cfg = is_array($view['sales'] ?? null) ? $view['sales'] : [];
|
||||
$html = '';
|
||||
if (!empty($view['products']) && is_array($view['products'])) {
|
||||
ob_start();
|
||||
foreach ($view['products'] as $product) {
|
||||
$this->render_product_card_html($product, $view);
|
||||
if (function_exists('get_template_part')) {
|
||||
get_template_part('templates/parts/shop/product', 'card', [
|
||||
'product' => $product,
|
||||
'sales_cfg' => $sales_cfg,
|
||||
]);
|
||||
}
|
||||
}
|
||||
$html = (string) ob_get_clean();
|
||||
}
|
||||
|
||||
$empty_html = '';
|
||||
if ($html === '' && function_exists('aksrt_get_ajax_null')) {
|
||||
$empty_html = aksrt_get_ajax_null(__('内容空空如也', 'aksrt'));
|
||||
} elseif ($html === '') {
|
||||
$empty_html = '<div class="aksrt-product-archive__empty ajax-item"><p>' . esc_html__('内容空空如也', 'aksrt') . '</p></div>';
|
||||
}
|
||||
|
||||
// Header HTML for cat filter replace (when filter present).
|
||||
$header_html = '';
|
||||
if (!empty($view['filter']) && is_array($view['filter'])) {
|
||||
ob_start();
|
||||
$this->render_product_cat_filter_html($view);
|
||||
$header_html = (string) ob_get_clean();
|
||||
}
|
||||
|
||||
$paginate_html = '';
|
||||
$page_n = (int) ($view['page'] ?? 1);
|
||||
$max_pages = (int) ($view['max_pages'] ?? 1);
|
||||
$has_more = $page_n < $max_pages;
|
||||
if ($has_more && (string) ($view['paging'] ?? '') === 'ajax') {
|
||||
$next_url = add_query_arg([
|
||||
'taxonomy' => $taxonomy,
|
||||
'term_id' => $term_id,
|
||||
'page' => $page_n + 1,
|
||||
'orderby' => (string) ($view['orderby'] ?? 'date'),
|
||||
'order' => (string) ($view['order'] ?? ''),
|
||||
], rest_url('aksrt/v1/shop/frontend/products/archive'));
|
||||
if (function_exists('aksrt_get_ajax_next_paginate')) {
|
||||
$paginate_html = aksrt_get_ajax_next_paginate(
|
||||
(int) ($view['total'] ?? 0),
|
||||
$page_n,
|
||||
max(1, (int) ($view['per_page'] ?? 12)),
|
||||
$next_url
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'data' => array_merge($view, [
|
||||
'html' => $html,
|
||||
'has_more' => (int) ($view['page'] ?? 1) < (int) ($view['max_pages'] ?? 1),
|
||||
'html' => $html !== '' ? $html : $empty_html,
|
||||
'empty_html' => $empty_html,
|
||||
'header_html' => $header_html,
|
||||
'paginate_html' => $paginate_html,
|
||||
'has_more' => $has_more,
|
||||
'title' => !empty($view['term']['name'])
|
||||
? (string) $view['term']['name'] . ' - ' . wp_strip_all_tags(get_bloginfo('name'))
|
||||
: '',
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
private function render_product_card_html(array $product, array $view = []): void {
|
||||
$sales_cfg = is_array($view['sales'] ?? null) ? $view['sales'] : [];
|
||||
$mode = (string) ($sales_cfg['show'] ?? 'show');
|
||||
$min_sales = max(0, (int) ($sales_cfg['show_min'] ?? 0));
|
||||
$sales = (int) ($product['sales'] ?? 0);
|
||||
$can_show_sales = false;
|
||||
if ($mode === 'show') {
|
||||
$can_show_sales = $sales > 0;
|
||||
} elseif ($mode === 'threshold') {
|
||||
$can_show_sales = $sales >= $min_sales;
|
||||
}
|
||||
|
||||
$permalink = (string) ($product['permalink'] ?? '#');
|
||||
$title = (string) ($product['title'] ?? '');
|
||||
$image = (string) ($product['image'] ?? '');
|
||||
$price = (float) ($product['price'] ?? 0);
|
||||
$excerpt = (string) ($product['excerpt'] ?? '');
|
||||
?>
|
||||
<article class="aksrt-product-card">
|
||||
<a href="<?php echo esc_url($permalink); ?>" class="aksrt-product-card__link">
|
||||
<?php if ($image !== '') : ?>
|
||||
<div class="aksrt-product-card__image">
|
||||
<img src="<?php echo esc_url($image); ?>" alt="<?php echo esc_attr($title); ?>" loading="lazy">
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<div class="aksrt-product-card__image aksrt-product-card__image--placeholder">
|
||||
<i class="fas fa-image"></i>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="aksrt-product-card__body">
|
||||
<h3 class="aksrt-product-card__title"><?php echo esc_html($title); ?></h3>
|
||||
<?php if ($excerpt !== '') : ?>
|
||||
<p class="aksrt-product-card__excerpt"><?php echo esc_html(wp_trim_words($excerpt, 15)); ?></p>
|
||||
<?php endif; ?>
|
||||
<div class="aksrt-product-card__footer">
|
||||
<span class="aksrt-product-card__price">¥<?php echo esc_html(number_format($price, 2)); ?></span>
|
||||
<?php if ($can_show_sales) : ?>
|
||||
<span class="aksrt-product-card__sales"><?php printf(esc_html__('已售 %d', 'aksrt'), $sales); ?></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</article>
|
||||
<?php
|
||||
/**
|
||||
* Render cat filter bar HTML for AJAX header replace.
|
||||
*/
|
||||
private function render_product_cat_filter_html(array $view): void {
|
||||
$filter = is_array($view['filter'] ?? null) ? $view['filter'] : [];
|
||||
$orderby = (string) ($view['orderby'] ?? ($filter['orderby'] ?? 'date'));
|
||||
$order = strtoupper((string) ($view['order'] ?? ($filter['order'] ?? 'DESC')));
|
||||
$term = is_array($view['term'] ?? null) ? $view['term'] : [];
|
||||
$current_id = (int) ($filter['current_id'] ?? ($term['id'] ?? 0));
|
||||
include get_template_directory() . '/templates/parts/shop/product-cat-filter.php';
|
||||
}
|
||||
|
||||
public function get_frontend_revenue(\WP_REST_Request $request): \WP_REST_Response {
|
||||
|
||||
Reference in new issue
Block a user