fix(admin): 支付中心分页条改回四按钮式 #39
@@ -2,13 +2,22 @@
|
||||
/**
|
||||
* @package AKSRT
|
||||
*
|
||||
* 后台列表页分页条(WP 原生 paginate_links)。
|
||||
* 后台列表页分页条 —— 1:1 复刻「卡密管理」页的 zibpay_admin_pagenavi 结构。
|
||||
*
|
||||
* 支付中心各页面(订单明细 / 售后服务 / 付费文章 / 优惠码 / 创作分成 / 提现审核 / 卡密管理)
|
||||
* 原先各写一份几乎相同的 paginate_links 调用,且基准 URL 都写成
|
||||
* add_query_arg('paged', '%#%') —— 只有两个参数时 WordPress 会退到
|
||||
* $_SERVER['REQUEST_URI'],链接绑在当前请求上,随请求形态漂移,列条件也容易丢。
|
||||
* 这里统一到一处:显式以「page 参数 + 当前筛选参数」为基准拼装,页码先夹到有效范围。
|
||||
* 原先各写一份几乎相同的翻页代码。这里统一到一处,标记沿用卡密管理那一套:
|
||||
* `« ‹ 第 X 页,共 Y 页 › »` 四枚按钮 + 「每页 X 共Y」计数,没有数字页码链接。
|
||||
*
|
||||
* 与参考实现的两处差异,均为缺陷修复:
|
||||
* 1) 基准 URL 显式以「page 参数 + 当前筛选参数」拼装。历史写法里 add_query_arg('paged', N)
|
||||
* 只有两个参数时会落到 $_SERVER['REQUEST_URI'],链接绑在当前请求上,随请求形态
|
||||
* 漂移,列条件也容易丢。
|
||||
* 2) 页码先夹到 [1, total_pages],禁用状态由夹取后的页码推导。历史写法在此处会留下
|
||||
* 「第 2 页 / 共 1 页」这类越界状态,四个分支一个都不命中,上一页/下一页同时停在
|
||||
* '#':按钮既不禁用也点不动。
|
||||
*
|
||||
* 单页也渲染分页条(只要总数 > 0):与参考实现一致,运营能直接看到「每页 20 共 5」
|
||||
* 这样的口径,四枚按钮全 disabled 也明确了「没有更多页」。
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
@@ -20,12 +29,14 @@ class AdminListPagination {
|
||||
/** 每页条数默认值。所有列表页一致,且不提供「每页显示」控件。 */
|
||||
public const DEFAULT_PER_PAGE = 20;
|
||||
|
||||
/** @var bool 内联样式只输出一次 */
|
||||
private static $assets_printed = false;
|
||||
|
||||
/**
|
||||
* 渲染分页条。
|
||||
*
|
||||
* 页码夹取到 [1, total_pages] 再决定渲染与否:历史写法在此处会留下「第 2 页 / 共 1 页」
|
||||
* 这类越界状态。真正的数据回退(数据也要跟着落到最后一页)由各页面在查询前完成,
|
||||
* 这里只负责把显示状态收敛到自洽。
|
||||
* 真正的数据回退(数据也要跟着落到最后一页)由各页面在查询前完成,这里只把页码
|
||||
* 夹到有效范围,保证显示状态自洽。
|
||||
*
|
||||
* @param string $page_slug 当前 admin.php 的 page 参数。
|
||||
* @param int $total_count 当前筛选条件下的记录总数。
|
||||
@@ -44,31 +55,57 @@ class AdminListPagination {
|
||||
return;
|
||||
}
|
||||
|
||||
self::print_assets();
|
||||
|
||||
$total_pages = max(1, (int) ceil($total_count / $per_page));
|
||||
$current_page = min(max(1, $current_page), $total_pages);
|
||||
|
||||
if ($total_pages <= 1) {
|
||||
// 显式基准 URL:不再让 add_query_arg() 只有两个参数时退回 $_SERVER['REQUEST_URI']。
|
||||
$base = add_query_arg(array_merge(['page' => $page_slug], $query_args), admin_url('admin.php'));
|
||||
$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) $per_page), esc_html((string) $total_count)); ?></span>
|
||||
<span class="pagination-links">
|
||||
<a class="first-page button<?php echo $on_first ? ' disabled' : ''; ?>" title="<?php echo esc_attr__('前往第一页', 'aksrt'); ?>" href="<?php echo esc_url($first_page_url); ?>">«</a>
|
||||
<a class="prev-page button<?php echo $on_first ? ' disabled' : ''; ?>" title="<?php echo esc_attr__('前往上一页', '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 echo esc_attr__('前往下一页', 'aksrt'); ?>" href="<?php echo esc_url($next_page_url); ?>">›</a>
|
||||
<a class="last-page button<?php echo $on_last ? ' disabled' : ''; ?>" title="<?php echo esc_attr__('前往最后一页', 'aksrt'); ?>" href="<?php echo esc_url($last_page_url); ?>">»</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* 行内样式。
|
||||
*
|
||||
* 这些原生后台页面不加载主题 CSS(admin-main.css 只在 edit.php 文章列表入队),
|
||||
* 所以分页条依赖的规则随标记一起内联输出。
|
||||
*/
|
||||
private static function print_assets(): void {
|
||||
if (self::$assets_printed) {
|
||||
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>';
|
||||
self::$assets_printed = true;
|
||||
?>
|
||||
<style>
|
||||
/* 禁用的翻页按钮已经无法跳转,但 <a href="#"> 仍会把页面滚回顶部;
|
||||
这里直接让它们不可点击,避免「点了没反应还跳回页首」的错觉。 */
|
||||
.tablenav-pages a.disabled { pointer-events: none; }
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,9 @@
|
||||
* 3) ice_perpage 只在服务端被读取、默认每页条数偏大。条数小于默认值时永远只有一页,
|
||||
* 四个按钮恒为 disabled —— 看上去就是「分页是坏的」。
|
||||
*
|
||||
* 现状:渲染已迁移到共享助手 AdminListPagination(WP 原生 paginate_links),
|
||||
* 基准 URL 与页码夹取都在那里做。本文件只固化卡密管理仍特有的部分:
|
||||
* 现状:渲染已迁移到共享助手 AdminListPagination,四按钮标记由助手统一输出
|
||||
* (与这里原来那套逐行一致),基准 URL 与页码夹取也都在那里做。
|
||||
* 本文件只固化卡密管理仍特有的部分:
|
||||
* - ice_perpage 仍作为 URL 参数被尊重,且工具栏不新增「每页显示」控件;
|
||||
* - 越界页码回退到最后一页并重查,不留空态死胡同;
|
||||
* - 筛选参数白名单化拼装,排序与每页条数随链接带走。
|
||||
@@ -55,7 +56,7 @@ test('渲染已迁移到共享助手 AdminListPagination', () => {
|
||||
assert.doesNotMatch(page, /self::DEFAULT_PER_PAGE/);
|
||||
assert.match(page, /AdminListPagination::DEFAULT_PER_PAGE/);
|
||||
|
||||
// 只服务自定义禁用按钮的 CSS 规则一并删除。
|
||||
// 只服务自定义禁用按钮的 CSS 规则已随标记一起搬进助手。
|
||||
assert.doesNotMatch(page, /\.tablenav-pages a\.disabled \{ pointer-events: none; \}/);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* 支付中心列表页分页契约(WP 原生 paginate_links,统一到 AdminListPagination)。
|
||||
* 支付中心列表页分页契约(四按钮式,统一到 AdminListPagination)。
|
||||
*
|
||||
* 背景(本次收敛的缺陷):
|
||||
* 1) 各页面的翻页基准 URL 写成 add_query_arg('paged', '%#%') —— 只有两个参数时
|
||||
@@ -15,6 +15,8 @@
|
||||
*
|
||||
* 本契约固化:
|
||||
* - 七个页面全部改走 AdminListPagination::render(),不再有各自的翻页实现;
|
||||
* - 渲染标记是卡密管理那一套:« ‹ 第 X 页,共 Y 页 › » 四枚按钮 + 「每页 X 共Y」计数,
|
||||
* 没有数字页码链接;只要总数 > 0 就渲染,单页时四枚按钮全 disabled;
|
||||
* - 每页条数默认 20 且来自共享常量,不新增「每页显示」控件;
|
||||
* - 每页都在查询前夹取页码,越界时重查,杜绝「空表且没有分页条」的死胡同;
|
||||
* - 翻页链接显式带上当前筛选参数,不依赖 REQUEST_URI。
|
||||
@@ -185,26 +187,44 @@ test('提现审核不把一次性动作参数带进翻页链接', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('共享助手自身:页码夹取、早退与显式基准 URL', () => {
|
||||
test('共享助手自身:页码夹取、四按钮标记与显式基准 URL', () => {
|
||||
assert.match(helper, /namespace AKSRT\\Admin\\Native;/);
|
||||
assert.match(helper, /defined\('ABSPATH'\) \|\| exit;/);
|
||||
|
||||
// 空态与单页都不渲染。
|
||||
// 总数为 0 或每页条数非法时不渲染;单页照样渲染(与原版 render_pagenavi 一致)。
|
||||
assert.match(helper, /if \(\$total_count <= 0 \|\| \$per_page <= 0\) \{\s*return;\s*\}/);
|
||||
assert.doesNotMatch(helper, /\$total_pages <= 1/, '单页也必须渲染分页条');
|
||||
assert.match(helper, /\$total_pages\s*=\s*max\(1, \(int\) ceil\(\$total_count \/ \$per_page\)\);/);
|
||||
assert.match(helper, /\$current_page = min\(max\(1, \$current_page\), \$total_pages\);/);
|
||||
assert.match(helper, /if \(\$total_pages <= 1\) \{\s*return;\s*\}/);
|
||||
|
||||
// 基准 URL 显式拼装:add_query_arg 必带第三个参数(那正是两参数写法退回 REQUEST_URI 的根治点)。
|
||||
assert.match(helper, /\$base = add_query_arg\(array_merge\(\['page' => \$page_slug\], \$query_args\), admin_url\('admin\.php'\)\);/);
|
||||
assert.match(helper, /\$base = add_query_arg\('paged', '%#%', \$base\);/);
|
||||
// 基准 URL 显式拼装:add_query_arg 必带第三个参数(两参数写法会退回 REQUEST_URI)。
|
||||
assert.match(helper, /\$base\s*=\s*add_query_arg\(array_merge\(\['page' => \$page_slug\], \$query_args\), admin_url\('admin\.php'\)\);/);
|
||||
assert.match(helper, /\$page_url = static function \(int \$page\) use \(\$base\): string \{/);
|
||||
assert.match(helper, /return add_query_arg\('paged', \$page, \$base\);/);
|
||||
assert.doesNotMatch(helper, /add_query_arg\('paged',\s*'[^']*'\s*\)/);
|
||||
|
||||
// 原生 WP 分页 + 总数提示。
|
||||
assert.match(helper, /<div class="tablenav"><div class="tablenav-pages">/);
|
||||
// 卡密管理式四按钮标记。
|
||||
assert.match(helper, /<div class="tablenav bottom">/);
|
||||
assert.match(helper, /<div class="tablenav-pages">/);
|
||||
assert.match(helper, /<span class="displaying-num">/);
|
||||
assert.match(helper, /number_format_i18n\(\$total_count\)/);
|
||||
assert.match(helper, /echo paginate_links\(\[/);
|
||||
assert.match(helper, /'prev_text' => '«',/);
|
||||
assert.match(helper, /'next_text' => '»',/);
|
||||
assert.match(helper, /phpcs:ignore WordPress\.Security\.EscapeOutput -- paginate_links 自行转义。/);
|
||||
assert.match(helper, /每页 %1\$s 共%2\$s/);
|
||||
assert.match(helper, /<span class="pagination-links">/);
|
||||
assert.match(helper, /<span class="paging-input">/);
|
||||
assert.match(helper, /第 %1\$s 页,共 %2\$s 页/);
|
||||
assert.match(helper, /<span class="total-pages">/);
|
||||
for (const cls of ['first-page', 'prev-page', 'next-page', 'last-page']) {
|
||||
assert.match(helper, new RegExp(`class="${cls} button`), `缺少 ${cls} 按钮`);
|
||||
}
|
||||
assert.match(helper, /\$on_first = \$current_page <= 1;/);
|
||||
assert.match(helper, /\$on_last\s*=\s*\$current_page >= \$total_pages;/);
|
||||
assert.equal(count(helper, /\$on_first \? '#' :/g), 2, '第一页/上一页都要按 on_first 落到 #');
|
||||
assert.equal(count(helper, /\$on_last \? '#' :/g), 2, '下一页/最后一页都要按 on_last 落到 #');
|
||||
|
||||
// 禁用的翻页按钮不可点击,否则 <a href="#"> 会把页面滚回顶部。
|
||||
assert.match(helper, /\.tablenav-pages a\.disabled \{ pointer-events: none; \}/);
|
||||
assert.match(helper, /private static \$assets_printed = false;/);
|
||||
assert.match(helper, /private static function print_assets\(\): void \{/);
|
||||
|
||||
// 不再使用 WP 原生 paginate_links。
|
||||
assert.doesNotMatch(helper, /echo paginate_links\(/);
|
||||
});
|
||||
|
||||
@@ -55,8 +55,6 @@ function wp_die($m) { throw new Exception('wp_die: ' . $m); }
|
||||
function get_userdata($id) { return $id > 0 ? (object) ['display_name' => 'User' . $id] : null; }
|
||||
function get_the_title($id) { return 'Product' . $id; }
|
||||
function is_admin() { return true; }
|
||||
function number_format_i18n($n, $d = 0) { return number_format((float) $n, $d); }
|
||||
function paginate_links($args = []) { return ''; }
|
||||
}
|
||||
|
||||
namespace AKSRT\Ledger {
|
||||
|
||||
Reference in New Issue
Block a user