feat(admin): 支付中心列表页统一为 WP 原生分页
七个支付中心列表页(订单明细 / 售后服务 / 付费文章 / 优惠码 / 创作分成 /
提现审核 / 卡密管理)的分页收敛到共享助手 AdminListPagination,改用 WP 原生
paginate_links() 渲染。
- 基准 URL 显式以 page 参数 + 当前筛选参数拼装,不再让两个参数的
add_query_arg('paged', ...) 退回 $_SERVER['REQUEST_URI']:链接不再随当前
请求形态漂移,列条件也不再丢失
- 售后服务从写死的 limit => 100 改为 limit/offset 分页,总数接上原本零调用的
AfterSaleService::count();after_sale_status 先按状态常量归一,保证 count()
与 admin_list() 口径一致,分页条不说谎
- 每页 20 条,统一来自 AdminListPagination::DEFAULT_PER_PAGE
- 每页在查询前夹取页码,越界时回退到最后一页并重查,杜绝「空表且没有分页条」
的死胡同;页码夹取与早退也一并收敛到助手
- 提现审核的翻页链接排除 retry_id / retry_key 两个一次性动作参数
- 订单明细的标签页与翻页链接共用同一份筛选参数,不再各写一份
清理死代码:五个页面里近乎复制的 render_pagination()(其中订单明细与提现审核
两份从未被调用)、两页重复的内联 paginate_links 块、卡密管理的 render_pagenavi()
及其 docblock、PayChargeCards::DEFAULT_PER_PAGE 常量,以及只服务自定义禁用
按钮的 .tablenav-pages a.disabled 规则。
验证:tests/admin/ 76 项通过(新增 pay-pages-pagination.test.cjs,重写
charge-cards-pagination.test.cjs);用 git show HEAD 取出原文件对比,订单明细
与售后服务两页在新旧实现下渲染输出逐字节相同。
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
*
|
||||
* 后台列表页分页条(WP 原生 paginate_links)。
|
||||
*
|
||||
* 支付中心各页面(订单明细 / 售后服务 / 付费文章 / 优惠码 / 创作分成 / 提现审核 / 卡密管理)
|
||||
* 原先各写一份几乎相同的 paginate_links 调用,且基准 URL 都写成
|
||||
* add_query_arg('paged', '%#%') —— 只有两个参数时 WordPress 会退到
|
||||
* $_SERVER['REQUEST_URI'],链接绑在当前请求上,随请求形态漂移,列条件也容易丢。
|
||||
* 这里统一到一处:显式以「page 参数 + 当前筛选参数」为基准拼装,页码先夹到有效范围。
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class AdminListPagination {
|
||||
|
||||
/** 每页条数默认值。所有列表页一致,且不提供「每页显示」控件。 */
|
||||
public const DEFAULT_PER_PAGE = 20;
|
||||
|
||||
/**
|
||||
* 渲染分页条。
|
||||
*
|
||||
* 页码夹取到 [1, total_pages] 再决定渲染与否:历史写法在此处会留下「第 2 页 / 共 1 页」
|
||||
* 这类越界状态。真正的数据回退(数据也要跟着落到最后一页)由各页面在查询前完成,
|
||||
* 这里只负责把显示状态收敛到自洽。
|
||||
*
|
||||
* @param string $page_slug 当前 admin.php 的 page 参数。
|
||||
* @param int $total_count 当前筛选条件下的记录总数。
|
||||
* @param int $current_page 当前页码(未经夹取亦可)。
|
||||
* @param int $per_page 每页条数。
|
||||
* @param array<string, string|int> $query_args 当前筛选参数,不含 paged。
|
||||
*/
|
||||
public static function render(
|
||||
string $page_slug,
|
||||
int $total_count,
|
||||
int $current_page,
|
||||
int $per_page,
|
||||
array $query_args = []
|
||||
): void {
|
||||
if ($total_count <= 0 || $per_page <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$total_pages = max(1, (int) ceil($total_count / $per_page));
|
||||
$current_page = min(max(1, $current_page), $total_pages);
|
||||
|
||||
if ($total_pages <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 显式基准 URL:不再让 add_query_arg() 只有两个参数时退回 $_SERVER['REQUEST_URI']。
|
||||
$base = add_query_arg(array_merge(['page' => $page_slug], $query_args), admin_url('admin.php'));
|
||||
$base = add_query_arg('paged', '%#%', $base);
|
||||
|
||||
echo '<div class="tablenav"><div class="tablenav-pages">';
|
||||
echo '<span class="displaying-num">' . esc_html(sprintf(
|
||||
/* translators: %s: 记录总数 */
|
||||
__('共 %s 项', 'aksrt'),
|
||||
number_format_i18n($total_count)
|
||||
)) . '</span>';
|
||||
echo paginate_links([
|
||||
'base' => $base,
|
||||
'format' => '',
|
||||
'current' => $current_page,
|
||||
'total' => $total_pages,
|
||||
'prev_text' => '«',
|
||||
'next_text' => '»',
|
||||
]); // phpcs:ignore WordPress.Security.EscapeOutput -- paginate_links 自行转义。
|
||||
echo '</div></div>';
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,6 @@ class PayChargeCards {
|
||||
public const NONCE = 'aksrt_card_pass_page';
|
||||
public const EXPORT_AJAX_ACTION = 'aksrt_card_pass_export';
|
||||
|
||||
/** 每页条数默认值;列表页可由 ice_perpage 覆盖(无界面控件,仅供 URL / 程序化调用)。 */
|
||||
public const DEFAULT_PER_PAGE = 20;
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
/** @var string 当前页面 URL */
|
||||
@@ -119,7 +116,8 @@ class PayChargeCards {
|
||||
$orderby = isset($_GET['orderby']) ? sanitize_key((string) wp_unslash($_GET['orderby'])) : 'modified_time';
|
||||
$order = isset($_GET['desc']) ? (strtoupper((string) wp_unslash($_GET['desc'])) === 'ASC' ? 'ASC' : 'DESC') : 'DESC';
|
||||
$paged = max(1, (int) ($_GET['paged'] ?? 1));
|
||||
$per_page = max(1, min(100, (int) ($_REQUEST['ice_perpage'] ?? self::DEFAULT_PER_PAGE)));
|
||||
// 每页条数默认值取自共享助手;ice_perpage 仅作为 URL 参数被尊重,无界面控件。
|
||||
$per_page = max(1, min(100, (int) ($_REQUEST['ice_perpage'] ?? AdminListPagination::DEFAULT_PER_PAGE)));
|
||||
|
||||
// 参考主题:status=0 表示未使用;本主题规范状态为 active。
|
||||
if ($status === '0' || $status === 'active') {
|
||||
@@ -297,7 +295,7 @@ class PayChargeCards {
|
||||
echo '</form>';
|
||||
echo '<div class="clear"></div>';
|
||||
|
||||
$this->render_pagenavi($count_all, $per_page, $query_args);
|
||||
AdminListPagination::render(self::PAGE_SLUG, $count_all, $paged, $per_page, $query_args);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
@@ -1059,67 +1057,18 @@ class PayChargeCards {
|
||||
if ($order === 'ASC') {
|
||||
$args['desc'] = 'ASC';
|
||||
}
|
||||
if ($per_page !== self::DEFAULT_PER_PAGE) {
|
||||
if ($per_page !== AdminListPagination::DEFAULT_PER_PAGE) {
|
||||
$args['ice_perpage'] = (string) $per_page;
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页(参考主题 zibpay_admin_pagenavi 同款结构与禁用逻辑)。
|
||||
*
|
||||
* 与参考实现的两处差异,均为缺陷修复:
|
||||
* 1) 页码先夹到 [1, total_pages]。历史写法在此处会出现「第 2 页 / 共 1 页」这类越界状态,
|
||||
* 四个分支一个都不命中,上一页/下一页同时停在 '#',按钮既不禁用也点不动。
|
||||
* 2) 链接基于显式基准 URL(page_url + 当前筛选条件),不再依赖 REQUEST_URI。
|
||||
*
|
||||
* @param array<string,string> $query_args 当前筛选参数(不含 paged)。
|
||||
*/
|
||||
private function render_pagenavi(int $total_count, int $number_per_page, array $query_args = []): void {
|
||||
if ($total_count <= 0 || $number_per_page <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$total_pages = max(1, (int) ceil($total_count / $number_per_page));
|
||||
$current_page = min(max(1, (int) ($_GET['paged'] ?? 1)), $total_pages);
|
||||
|
||||
$base = $query_args ? add_query_arg($query_args, $this->page_url) : $this->page_url;
|
||||
$page_url = static function (int $page) use ($base): string {
|
||||
return add_query_arg('paged', $page, $base);
|
||||
};
|
||||
|
||||
$on_first = $current_page <= 1;
|
||||
$on_last = $current_page >= $total_pages;
|
||||
|
||||
$first_page_url = $on_first ? '#' : $page_url(1);
|
||||
$prev_page_url = $on_first ? '#' : $page_url($current_page - 1);
|
||||
$next_page_url = $on_last ? '#' : $page_url($current_page + 1);
|
||||
$last_page_url = $on_last ? '#' : $page_url($total_pages);
|
||||
?>
|
||||
<div class="tablenav bottom">
|
||||
<div class="tablenav-pages">
|
||||
<span class="displaying-num"><?php printf(esc_html__('每页 %1$s 共%2$s', 'aksrt'), esc_html((string) $number_per_page), esc_html((string) $total_count)); ?></span>
|
||||
<span class="pagination-links">
|
||||
<a class="first-page button<?php echo $on_first ? ' disabled' : ''; ?>" title="<?php esc_attr_e('前往第一页', 'aksrt'); ?>" href="<?php echo esc_url($first_page_url); ?>">«</a>
|
||||
<a class="prev-page button<?php echo $on_first ? ' disabled' : ''; ?>" title="<?php esc_attr_e('前往上一页', 'aksrt'); ?>" href="<?php echo esc_url($prev_page_url); ?>">‹</a>
|
||||
<span class="paging-input"><?php printf(esc_html__('第 %1$s 页,共 %2$s 页', 'aksrt'), esc_html((string) $current_page), '<span class="total-pages">' . esc_html((string) $total_pages) . '</span>'); ?></span>
|
||||
<a class="next-page button<?php echo $on_last ? ' disabled' : ''; ?>" title="<?php esc_attr_e('前往下一页', 'aksrt'); ?>" href="<?php echo esc_url($next_page_url); ?>">›</a>
|
||||
<a class="last-page button<?php echo $on_last ? ' disabled' : ''; ?>" title="<?php esc_attr_e('前往最后一页', 'aksrt'); ?>" href="<?php echo esc_url($last_page_url); ?>">»</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/** 行内样式与脚本:依赖显隐 + 点击复制。 */
|
||||
private function print_assets(): void {
|
||||
?>
|
||||
<style>
|
||||
.order-header { display: flex; align-items: flex-start; justify-content: space-between; flex-wrap: wrap; margin-top: 8px; }
|
||||
.order-header .subsubsub { margin: 6px 0; }
|
||||
/* 禁用的翻页按钮已经无法跳转,但 <a href="#"> 仍会把页面滚回顶部;
|
||||
这里直接让它们不可点击,避免「点了没反应还跳回页首」的错觉。 */
|
||||
.tablenav-pages a.disabled { pointer-events: none; }
|
||||
/* 排序箭头对齐:宿主做成零尺寸 inline-block —— 空 inline-block 的基线即其下边缘,
|
||||
会与文字基线对齐,于是箭头可用 bottom 精确锚定到「基线以上 N em」。
|
||||
箭头盒中心等于 bottom 值(translateY(50%) 抵消自身高度,与盒子高度无关):
|
||||
|
||||
@@ -171,7 +171,7 @@ class PayCoupons {
|
||||
$status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
|
||||
$search = isset($_GET['s']) ? sanitize_text_field((string) wp_unslash($_GET['s'])) : '';
|
||||
$page_num = max(1, (int) ($_GET['paged'] ?? 1));
|
||||
$per_page = 20;
|
||||
$per_page = AdminListPagination::DEFAULT_PER_PAGE;
|
||||
$type_labels = [
|
||||
'fixed' => __('固定金额', 'aksrt'),
|
||||
'percent' => __('折扣百分比', 'aksrt'),
|
||||
@@ -199,7 +199,21 @@ class PayCoupons {
|
||||
'offset' => ($page_num - 1) * $per_page,
|
||||
]);
|
||||
$items = $result['items'] ?? [];
|
||||
$pages = max(1, (int) ($result['total_pages'] ?? 1));
|
||||
$total = (int) ($result['total'] ?? 0);
|
||||
$pages = max(1, (int) ceil($total / $per_page));
|
||||
|
||||
// 页码越界(手工改 URL、或筛选后总数变小)会让 offset 落到数据之外:表格空掉
|
||||
// 且分页条不输出,只能手改 URL 退出。回退到最后一页重查一次。
|
||||
if ($page_num > $pages) {
|
||||
$page_num = $pages;
|
||||
$result = $svc->get_list([
|
||||
'status' => $status,
|
||||
'search' => $search,
|
||||
'limit' => $per_page,
|
||||
'offset' => ($page_num - 1) * $per_page,
|
||||
]);
|
||||
$items = $result['items'] ?? [];
|
||||
}
|
||||
|
||||
echo '<table class="wp-list-table widefat fixed striped"><thead><tr>';
|
||||
echo '<th>ID</th><th>' . esc_html__('代码', 'aksrt') . '</th><th>' . esc_html__('类型', 'aksrt') . '</th><th>' . esc_html__('面值', 'aksrt') . '</th><th>' . esc_html__('门槛/封顶', 'aksrt') . '</th><th>' . esc_html__('范围', 'aksrt') . '</th><th>' . esc_html__('已用/上限/每人', 'aksrt') . '</th><th>' . esc_html__('状态', 'aksrt') . '</th><th>' . esc_html__('过期', 'aksrt') . '</th><th>' . esc_html__('操作', 'aksrt') . '</th>';
|
||||
@@ -240,8 +254,16 @@ class PayCoupons {
|
||||
}
|
||||
echo '</td></tr>';
|
||||
}
|
||||
// 翻页链接显式带上当前筛选条件(不含 paged),口径与上方筛选表单一致。
|
||||
$query_args = array_filter([
|
||||
'status' => $status !== '' ? $status : null,
|
||||
's' => $search !== '' ? $search : null,
|
||||
], static function ($value): bool {
|
||||
return $value !== null;
|
||||
});
|
||||
|
||||
echo '</tbody></table>';
|
||||
$this->render_pagination($pages, $page_num);
|
||||
AdminListPagination::render(self::PAGE_SLUG, $total, $page_num, $per_page, $query_args);
|
||||
}
|
||||
|
||||
public function handle_create_coupon(): void {
|
||||
@@ -457,20 +479,4 @@ class PayCoupons {
|
||||
private function page_url(array $args = []): string {
|
||||
return add_query_arg(array_merge(['page' => self::PAGE_SLUG], $args), admin_url('admin.php'));
|
||||
}
|
||||
|
||||
private function render_pagination(int $pages, int $page_num): void {
|
||||
if ($pages <= 1) {
|
||||
return;
|
||||
}
|
||||
echo '<div class="tablenav"><div class="tablenav-pages">';
|
||||
echo paginate_links([
|
||||
'base' => add_query_arg('paged', '%#%'),
|
||||
'format' => '',
|
||||
'prev_text' => '«',
|
||||
'next_text' => '»',
|
||||
'total' => $pages,
|
||||
'current' => $page_num,
|
||||
]);
|
||||
echo '</div></div>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class PayIncome {
|
||||
$date_from = isset($_GET['date_from']) ? sanitize_text_field((string) wp_unslash($_GET['date_from'])) : '';
|
||||
$date_to = isset($_GET['date_to']) ? sanitize_text_field((string) wp_unslash($_GET['date_to'])) : '';
|
||||
$page_num = max(1, (int) ($_GET['paged'] ?? 1));
|
||||
$per_page = 20;
|
||||
$per_page = AdminListPagination::DEFAULT_PER_PAGE;
|
||||
$order_statuses = class_exists('\\AKSRT\\Payment\\Order')
|
||||
? \AKSRT\Payment\Order::get_valid_statuses()
|
||||
: [];
|
||||
@@ -124,7 +124,31 @@ class PayIncome {
|
||||
$result = $svc->get_list($list_args);
|
||||
$items = $result['items'] ?? [];
|
||||
$total = (int) ($result['total'] ?? 0);
|
||||
$pages = max(1, (int) ($result['max_pages'] ?? 1));
|
||||
$pages = max(1, (int) ceil($total / $per_page));
|
||||
|
||||
// 页码越界(手工改 URL、或筛选后总数变小)会让 offset 落到数据之外:表格空掉
|
||||
// 且分页条不输出,只能手改 URL 退出。回退到最后一页重查一次。
|
||||
if ($page_num > $pages) {
|
||||
$page_num = $pages;
|
||||
$list_args['offset'] = ($page_num - 1) * $per_page;
|
||||
$result = $svc->get_list($list_args);
|
||||
$items = $result['items'] ?? [];
|
||||
}
|
||||
|
||||
// 翻页链接显式带上当前筛选条件(不含 paged)。必须在下面的行循环之前取好:
|
||||
// 循环里 $user_id 会被每行的 author_id 覆盖。
|
||||
$query_args = array_filter([
|
||||
'status' => $status !== '' ? $status : null,
|
||||
'order_status' => $order_status !== '' ? $order_status : null,
|
||||
'user_id' => $user_id > 0 ? $user_id : null,
|
||||
'post_id' => $post_id > 0 ? $post_id : null,
|
||||
'currency' => $currency !== '' ? $currency : null,
|
||||
'order_number' => $order_number !== '' ? $order_number : null,
|
||||
'date_from' => $date_from !== '' ? $date_from : null,
|
||||
'date_to' => $date_to !== '' ? $date_to : null,
|
||||
], static function ($value): bool {
|
||||
return $value !== null;
|
||||
});
|
||||
|
||||
echo '<table class="wp-list-table widefat fixed striped"><thead><tr>';
|
||||
echo '<th><input type="checkbox" form="aksrt-pay-batch-settle-income" aria-label="' . esc_attr__('全选待结算记录', 'aksrt') . '" /></th><th>ID</th><th>' . esc_html__('用户', 'aksrt') . '</th><th>' . esc_html__('订单号', 'aksrt') . '</th><th>' . esc_html__('金额', 'aksrt') . '</th><th>' . esc_html__('状态', 'aksrt') . '</th><th>' . esc_html__('时间', 'aksrt') . '</th><th>' . esc_html__('操作', 'aksrt') . '</th>';
|
||||
@@ -164,7 +188,7 @@ class PayIncome {
|
||||
echo '</td></tr>';
|
||||
}
|
||||
echo '</tbody></table>';
|
||||
$this->render_pagination($pages, $page_num);
|
||||
AdminListPagination::render(self::PAGE_SLUG, $total, $page_num, $per_page, $query_args);
|
||||
$this->render_footer();
|
||||
}
|
||||
|
||||
@@ -256,19 +280,4 @@ class PayIncome {
|
||||
return add_query_arg(array_merge(['page' => $slug], $args), admin_url('admin.php'));
|
||||
}
|
||||
|
||||
private function render_pagination(int $pages, int $page_num): void {
|
||||
if ($pages <= 1) {
|
||||
return;
|
||||
}
|
||||
echo '<div class="tablenav"><div class="tablenav-pages">';
|
||||
echo paginate_links([
|
||||
'base' => add_query_arg('paged', '%#%'),
|
||||
'format' => '',
|
||||
'prev_text' => '«',
|
||||
'next_text' => '»',
|
||||
'total' => $pages,
|
||||
'current' => $page_num,
|
||||
]);
|
||||
echo '</div></div>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ class PayOrders {
|
||||
$date_from = isset($_GET['date_from']) ? sanitize_text_field((string) wp_unslash($_GET['date_from'])) : '';
|
||||
$date_to = isset($_GET['date_to']) ? sanitize_text_field((string) wp_unslash($_GET['date_to'])) : '';
|
||||
$page_num = max(1, (int) ($_GET['paged'] ?? 1));
|
||||
$per_page = 20;
|
||||
$per_page = AdminListPagination::DEFAULT_PER_PAGE;
|
||||
|
||||
if (!array_key_exists($product_type, $type_tabs)) {
|
||||
$product_type = '';
|
||||
@@ -118,20 +118,38 @@ class PayOrders {
|
||||
? \AKSRT\Core\DomainModel::order_status_labels()
|
||||
: [];
|
||||
|
||||
// 页码越界(手工改 URL、或筛选后总数变小)会让 offset 落到数据之外:表格空掉
|
||||
// 且分页条不输出,只能手改 URL 退出。回退到最后一页重查一次。
|
||||
if ($page_num > $pages) {
|
||||
$page_num = $pages;
|
||||
$args['offset'] = ($page_num - 1) * $per_page;
|
||||
$items = $repo->get_orders_admin($args, $search);
|
||||
}
|
||||
|
||||
// 当前筛选参数(不含 paged):标签页链接与翻页链接共用同一份,避免两处漂移。
|
||||
$query_args = array_filter([
|
||||
'product_type' => $product_type !== '' ? $product_type : null,
|
||||
'product_id' => $product_id > 0 ? $product_id : null,
|
||||
'user_id' => $user_id > 0 ? $user_id : null,
|
||||
'payment_method' => $pay_method !== '' ? $pay_method : null,
|
||||
'status' => ($status !== '' && $status !== 'all') ? $status : null,
|
||||
'date_from' => $date_from !== '' ? $date_from : null,
|
||||
'date_to' => $date_to !== '' ? $date_to : null,
|
||||
's' => $search !== '' ? $search : null,
|
||||
], static function ($value): bool {
|
||||
return $value !== null;
|
||||
});
|
||||
|
||||
// Type tabs.
|
||||
echo '<ul class="subsubsub" style="float:none;margin:8px 0 12px;">';
|
||||
$tab_bits = [];
|
||||
foreach ($type_tabs as $tkey => $tlabel) {
|
||||
$url = $this->page_url('aksrt_orders', array_filter([
|
||||
'product_type' => $tkey !== '' ? $tkey : null,
|
||||
'product_id' => $product_id > 0 ? $product_id : null,
|
||||
'user_id' => $user_id > 0 ? $user_id : null,
|
||||
'payment_method' => $pay_method !== '' ? $pay_method : null,
|
||||
'status' => ($status !== '' && $status !== 'all') ? $status : null,
|
||||
'date_from' => $date_from !== '' ? $date_from : null,
|
||||
'date_to' => $date_to !== '' ? $date_to : null,
|
||||
's' => $search !== '' ? $search : null,
|
||||
]));
|
||||
$url = $this->page_url('aksrt_orders', array_filter(
|
||||
['product_type' => $tkey !== '' ? $tkey : null] + $query_args,
|
||||
static function ($value): bool {
|
||||
return $value !== null;
|
||||
}
|
||||
));
|
||||
$cls = ($product_type === $tkey) ? 'current' : '';
|
||||
$tab_bits[] = '<li><a class="' . esc_attr($cls) . '" href="' . esc_url($url) . '">' . esc_html($tlabel) . '</a></li>';
|
||||
}
|
||||
@@ -142,16 +160,12 @@ class PayOrders {
|
||||
echo '<ul class="subsubsub" style="float:none;margin:0 0 12px;">';
|
||||
$m_bits = [];
|
||||
foreach ($method_tabs as $mkey => $mlabel) {
|
||||
$url = $this->page_url('aksrt_orders', array_filter([
|
||||
'product_type' => $product_type !== '' ? $product_type : null,
|
||||
'product_id' => $product_id > 0 ? $product_id : null,
|
||||
'user_id' => $user_id > 0 ? $user_id : null,
|
||||
'payment_method' => $mkey !== '' ? $mkey : null,
|
||||
'status' => ($status !== '' && $status !== 'all') ? $status : null,
|
||||
'date_from' => $date_from !== '' ? $date_from : null,
|
||||
'date_to' => $date_to !== '' ? $date_to : null,
|
||||
's' => $search !== '' ? $search : null,
|
||||
]));
|
||||
$url = $this->page_url('aksrt_orders', array_filter(
|
||||
['payment_method' => $mkey !== '' ? $mkey : null] + $query_args,
|
||||
static function ($value): bool {
|
||||
return $value !== null;
|
||||
}
|
||||
));
|
||||
$cls = ($pay_method === $mkey) ? 'current' : '';
|
||||
$m_bits[] = '<li><a class="' . esc_attr($cls) . '" href="' . esc_url($url) . '">' . esc_html($mlabel) . '</a></li>';
|
||||
}
|
||||
@@ -279,18 +293,7 @@ class PayOrders {
|
||||
|
||||
echo '</tbody></table>';
|
||||
|
||||
if ($pages > 1) {
|
||||
echo '<div class="tablenav"><div class="tablenav-pages">';
|
||||
echo paginate_links([
|
||||
'base' => add_query_arg('paged', '%#%'),
|
||||
'format' => '',
|
||||
'prev_text' => '«',
|
||||
'next_text' => '»',
|
||||
'total' => $pages,
|
||||
'current' => $page_num,
|
||||
]);
|
||||
echo '</div></div>';
|
||||
}
|
||||
AdminListPagination::render(self::PAGE_SLUG, $total, $page_num, $per_page, $query_args);
|
||||
|
||||
$this->render_footer();
|
||||
}
|
||||
@@ -400,12 +403,30 @@ class PayOrders {
|
||||
private function render_after_sales_page(): void {
|
||||
$service = \AKSRT\Payment\AfterSaleService::get_instance();
|
||||
$status = isset($_GET['after_sale_status']) ? sanitize_key((string) wp_unslash($_GET['after_sale_status'])) : '';
|
||||
// admin_list() 会按状态常量白名单过滤,count() 不会。这里先归一一次,
|
||||
// 否则手改 URL 传个野状态会变成「count 为 0 而 list 返回全部」,分页条直接说谎。
|
||||
if (!in_array($status, $this->after_sale_statuses(), true)) {
|
||||
$status = '';
|
||||
}
|
||||
$date_from = isset($_GET['date_from']) ? sanitize_text_field((string) wp_unslash($_GET['date_from'])) : '';
|
||||
$date_to = isset($_GET['date_to']) ? sanitize_text_field((string) wp_unslash($_GET['date_to'])) : '';
|
||||
$date_from = preg_match('/^\d{4}-\d{2}-\d{2}$/', $date_from) ? $date_from : '';
|
||||
$date_to = preg_match('/^\d{4}-\d{2}-\d{2}$/', $date_to) ? $date_to : '';
|
||||
$filter_args = ['status' => $status, 'date_from' => $date_from, 'date_to' => $date_to];
|
||||
$items = $service->admin_list($filter_args + ['limit' => 100]);
|
||||
$page_num = max(1, (int) ($_GET['paged'] ?? 1));
|
||||
$per_page = AdminListPagination::DEFAULT_PER_PAGE;
|
||||
|
||||
$items = $service->admin_list($filter_args + ['limit' => $per_page, 'offset' => ($page_num - 1) * $per_page]);
|
||||
$total = $service->count($filter_args);
|
||||
$pages = max(1, (int) ceil($total / $per_page));
|
||||
|
||||
// 页码越界(手工改 URL、或筛选后总数变小)会让 offset 落到数据之外:表格空掉
|
||||
// 且分页条不输出,只能手改 URL 退出。回退到最后一页重查一次。
|
||||
if ($page_num > $pages) {
|
||||
$page_num = $pages;
|
||||
$items = $service->admin_list($filter_args + ['limit' => $per_page, 'offset' => ($page_num - 1) * $per_page]);
|
||||
}
|
||||
|
||||
echo '<form method="get"><input type="hidden" name="page" value="' . esc_attr(self::PAGE_AFTER_SALES_SLUG) . '" />';
|
||||
echo '<select name="after_sale_status"><option value="">' . esc_html__('全部售后状态', 'aksrt') . '</option>';
|
||||
foreach ([\AKSRT\Payment\AfterSaleService::STATUS_PENDING => '待审核', \AKSRT\Payment\AfterSaleService::STATUS_PROCESSING => '处理中', \AKSRT\Payment\AfterSaleService::STATUS_COMPLETED => '已完成', \AKSRT\Payment\AfterSaleService::STATUS_REJECTED => '已拒绝', \AKSRT\Payment\AfterSaleService::STATUS_CANCELLED => '已取消'] as $key => $label) echo '<option value="' . esc_attr($key) . '"' . selected($status, $key, false) . '>' . esc_html($label) . '</option>';
|
||||
@@ -535,6 +556,16 @@ class PayOrders {
|
||||
echo '</div></td></tr>';
|
||||
}
|
||||
echo '</tbody></table>';
|
||||
// 翻页链接显式带上当前筛选条件(不含 paged),口径与上方筛选表单一致。
|
||||
$query_args = array_filter([
|
||||
'after_sale_status' => $status !== '' ? $status : null,
|
||||
'date_from' => $date_from !== '' ? $date_from : null,
|
||||
'date_to' => $date_to !== '' ? $date_to : null,
|
||||
], static function ($value): bool {
|
||||
return $value !== null;
|
||||
});
|
||||
AdminListPagination::render(self::PAGE_AFTER_SALES_SLUG, $total, $page_num, $per_page, $query_args);
|
||||
|
||||
// 事件委托:点击「操作」切换手风琴详情;「取消」仅收起面板;「确认」以 POST 提交选中的售后状态
|
||||
echo '<script>(function(){'
|
||||
. 'var postUrl=' . wp_json_encode(esc_url_raw(admin_url('admin-post.php'))) . ';'
|
||||
@@ -574,6 +605,17 @@ class PayOrders {
|
||||
. '})();</script>';
|
||||
}
|
||||
|
||||
/** 售后状态白名单:admin_list() 据此过滤,count() 不校验,故查询前先按它归一。 */
|
||||
private function after_sale_statuses(): array {
|
||||
return [
|
||||
\AKSRT\Payment\AfterSaleService::STATUS_PENDING,
|
||||
\AKSRT\Payment\AfterSaleService::STATUS_PROCESSING,
|
||||
\AKSRT\Payment\AfterSaleService::STATUS_COMPLETED,
|
||||
\AKSRT\Payment\AfterSaleService::STATUS_REJECTED,
|
||||
\AKSRT\Payment\AfterSaleService::STATUS_CANCELLED,
|
||||
];
|
||||
}
|
||||
|
||||
public function handle_after_sale_status(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
$id = isset($_POST['after_sale_id']) ? absint($_POST['after_sale_id']) : 0;
|
||||
@@ -859,20 +901,4 @@ class PayOrders {
|
||||
private function page_url(string $slug, array $args = []): string {
|
||||
return add_query_arg(array_merge(['page' => $slug], $args), admin_url('admin.php'));
|
||||
}
|
||||
|
||||
private function render_pagination(int $pages, int $page_num): void {
|
||||
if ($pages <= 1) {
|
||||
return;
|
||||
}
|
||||
echo '<div class="tablenav"><div class="tablenav-pages">';
|
||||
echo paginate_links([
|
||||
'base' => add_query_arg('paged', '%#%'),
|
||||
'format' => '',
|
||||
'prev_text' => '«',
|
||||
'next_text' => '»',
|
||||
'total' => $pages,
|
||||
'current' => $page_num,
|
||||
]);
|
||||
echo '</div></div>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class PayProducts {
|
||||
$pay_type = isset($_GET['pay_type']) ? sanitize_key((string) wp_unslash($_GET['pay_type'])) : '';
|
||||
$search = isset($_GET['s']) ? sanitize_text_field((string) wp_unslash($_GET['s'])) : '';
|
||||
$page_num = max(1, (int) ($_GET['paged'] ?? 1));
|
||||
$per_page = 20;
|
||||
$per_page = AdminListPagination::DEFAULT_PER_PAGE;
|
||||
|
||||
$type_labels = \AKSRT\Core\DomainModel::pay_type_labels();
|
||||
unset($type_labels[\AKSRT\Core\DomainModel::PAY_TYPE_NO]);
|
||||
@@ -51,9 +51,22 @@ class PayProducts {
|
||||
]);
|
||||
|
||||
$posts = $query['posts'];
|
||||
$total = $query['total'];
|
||||
$total = (int) $query['total'];
|
||||
$pages = max(1, (int) ceil($total / $per_page));
|
||||
|
||||
// 页码越界(手工改 URL、或筛选后总数变小)会让 WP_Query 停在空页上:表格空掉
|
||||
// 且分页条不输出,只能手改 URL 退出。回退到最后一页重查一次。
|
||||
if ($page_num > $pages) {
|
||||
$page_num = $pages;
|
||||
$query = $this->query_paid_posts([
|
||||
'pay_type' => $pay_type,
|
||||
'search' => $search,
|
||||
'page' => $page_num,
|
||||
'per_page' => $per_page,
|
||||
]);
|
||||
$posts = $query['posts'];
|
||||
}
|
||||
|
||||
$sales_map = [];
|
||||
if ($posts && class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
$ids = array_map(static function ($p) {
|
||||
@@ -146,8 +159,16 @@ class PayProducts {
|
||||
echo '</tr>';
|
||||
}
|
||||
|
||||
// 翻页链接显式带上当前筛选条件(不含 paged),口径与 render_filters() 的表单一致。
|
||||
$query_args = array_filter([
|
||||
'pay_type' => $pay_type !== '' ? $pay_type : null,
|
||||
's' => $search !== '' ? $search : null,
|
||||
], static function ($value): bool {
|
||||
return $value !== null;
|
||||
});
|
||||
|
||||
echo '</tbody></table>';
|
||||
$this->render_pagination($pages, $page_num);
|
||||
AdminListPagination::render(self::PAGE_SLUG, $total, $page_num, $per_page, $query_args);
|
||||
$this->render_footer();
|
||||
}
|
||||
|
||||
@@ -352,20 +373,4 @@ class PayProducts {
|
||||
private function page_url(string $slug, array $args = []): string {
|
||||
return add_query_arg(array_merge(['page' => $slug], $args), admin_url('admin.php'));
|
||||
}
|
||||
|
||||
private function render_pagination(int $pages, int $page_num): void {
|
||||
if ($pages <= 1) {
|
||||
return;
|
||||
}
|
||||
echo '<div class="tablenav"><div class="tablenav-pages">';
|
||||
echo paginate_links([
|
||||
'base' => add_query_arg('paged', '%#%'),
|
||||
'format' => '',
|
||||
'prev_text' => '«',
|
||||
'next_text' => '»',
|
||||
'total' => $pages,
|
||||
'current' => $page_num,
|
||||
]);
|
||||
echo '</div></div>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class PayWithdrawals {
|
||||
$date_from = isset($_GET['date_from']) ? $this->sanitize_date((string) wp_unslash($_GET['date_from'])) : '';
|
||||
$date_to = isset($_GET['date_to']) ? $this->sanitize_date((string) wp_unslash($_GET['date_to'])) : '';
|
||||
$page_num = max(1, (int) ($_GET['paged'] ?? 1));
|
||||
$per_page = 20;
|
||||
$per_page = AdminListPagination::DEFAULT_PER_PAGE;
|
||||
$svc = \AKSRT\Payment\Withdrawal::get_instance();
|
||||
$stats = $svc->get_stats();
|
||||
$retry_id = isset($_GET['retry_id']) ? max(0, (int) $_GET['retry_id']) : 0;
|
||||
@@ -111,6 +111,33 @@ class PayWithdrawals {
|
||||
]);
|
||||
$pages = max(1, (int) ceil($total / $per_page));
|
||||
|
||||
// 页码越界(手工改 URL、或筛选后总数变小)会让 offset 落到数据之外:表格空掉
|
||||
// 且分页条不输出,只能手改 URL 退出。回退到最后一页重查一次。
|
||||
if ($page_num > $pages) {
|
||||
$page_num = $pages;
|
||||
$items = $svc->get_all([
|
||||
'status' => $status,
|
||||
'keyword' => $keyword,
|
||||
'user_id' => $user_filter,
|
||||
'date_from' => $date_from,
|
||||
'date_to' => $date_to,
|
||||
'limit' => $per_page,
|
||||
'offset' => ($page_num - 1) * $per_page,
|
||||
]);
|
||||
}
|
||||
|
||||
// 翻页链接显式带上当前筛选条件(不含 paged),口径与上方筛选表单一致。
|
||||
// retry_id / retry_key 是一次性动作参数,绝不能进翻页链接。
|
||||
$query_args = array_filter([
|
||||
'status' => $status !== '' ? $status : null,
|
||||
's' => $keyword !== '' ? $keyword : null,
|
||||
'user_id' => $user_filter > 0 ? $user_filter : null,
|
||||
'date_from' => $date_from !== '' ? $date_from : null,
|
||||
'date_to' => $date_to !== '' ? $date_to : null,
|
||||
], static function ($value): bool {
|
||||
return $value !== null;
|
||||
});
|
||||
|
||||
echo '<table class="wp-list-table widefat fixed striped">';
|
||||
echo '<thead><tr>';
|
||||
echo '<th>' . esc_html__('订单号', 'aksrt') . '</th>';
|
||||
@@ -184,18 +211,7 @@ class PayWithdrawals {
|
||||
|
||||
echo '</tbody></table>';
|
||||
|
||||
if ($pages > 1) {
|
||||
echo '<div class="tablenav"><div class="tablenav-pages">';
|
||||
echo paginate_links([
|
||||
'base' => add_query_arg('paged', '%#%'),
|
||||
'format' => '',
|
||||
'prev_text' => '«',
|
||||
'next_text' => '»',
|
||||
'total' => $pages,
|
||||
'current' => $page_num,
|
||||
]);
|
||||
echo '</div></div>';
|
||||
}
|
||||
AdminListPagination::render(self::PAGE_SLUG, $total, $page_num, $per_page, $query_args);
|
||||
|
||||
$this->render_footer();
|
||||
}
|
||||
@@ -278,19 +294,4 @@ class PayWithdrawals {
|
||||
return preg_match('/^\d{4}-\d{2}-\d{2}$/', $date) === 1 ? $date : '';
|
||||
}
|
||||
|
||||
private function render_pagination(int $pages, int $page_num): void {
|
||||
if ($pages <= 1) {
|
||||
return;
|
||||
}
|
||||
echo '<div class="tablenav"><div class="tablenav-pages">';
|
||||
echo paginate_links([
|
||||
'base' => add_query_arg('paged', '%#%'),
|
||||
'format' => '',
|
||||
'prev_text' => '«',
|
||||
'next_text' => '»',
|
||||
'total' => $pages,
|
||||
'current' => $page_num,
|
||||
]);
|
||||
echo '</div></div>';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user