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:
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* 「卡密管理」分页契约。
|
||||
*
|
||||
* 背景(已修复的缺陷):
|
||||
* 背景(两次演进):
|
||||
* 1) 翻页链接此前写作 add_query_arg('paged', N) —— 只有两个参数时 WordPress 会落到
|
||||
* $_SERVER['REQUEST_URI'],链接绑在当前请求上,随请求形态漂移,列条件也容易丢。
|
||||
* 2) render_pagenavi() 的 URL 推导是一串 if/elseif。页码越界时(例如 per_page 缩小后
|
||||
@@ -12,14 +12,12 @@
|
||||
* 极端情况下 offset 落到数据之外,表格直接变空态并且连分页条都不输出,只能手改 URL 退出。
|
||||
* 3) ice_perpage 只在服务端被读取、默认每页条数偏大。条数小于默认值时永远只有一页,
|
||||
* 四个按钮恒为 disabled —— 看上去就是「分页是坏的」。
|
||||
* 处置:默认值定为 20;**不在工具栏增加「每页显示」控件**(用户明确要求),
|
||||
* ice_perpage 仅作为 URL 参数被尊重。
|
||||
*
|
||||
* 本契约固化四件事:
|
||||
* - 页码先夹到 [1, total_pages],禁用状态由夹取后的页码推导;
|
||||
* - 翻页链接基于显式基准 URL(page_url + 当前筛选参数),不再依赖 REQUEST_URI;
|
||||
* 现状:渲染已迁移到共享助手 AdminListPagination(WP 原生 paginate_links),
|
||||
* 基准 URL 与页码夹取都在那里做。本文件只固化卡密管理仍特有的部分:
|
||||
* - ice_perpage 仍作为 URL 参数被尊重,且工具栏不新增「每页显示」控件;
|
||||
* - 越界页码回退到最后一页并重查,不留空态死胡同;
|
||||
* - 每页条数默认 20,且工具栏不得新增「每页显示」控件。
|
||||
* - 筛选参数白名单化拼装,排序与每页条数随链接带走。
|
||||
*/
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
@@ -42,61 +40,58 @@ const slice = (from, to) => {
|
||||
};
|
||||
|
||||
const list = slice('private function render_list(', 'private function render_add_form(');
|
||||
const args = slice('private function list_query_args(', 'private function render_pagenavi(');
|
||||
const pager = slice('private function render_pagenavi(', 'private function print_assets(');
|
||||
const args = slice('private function list_query_args(', 'private function print_assets(');
|
||||
|
||||
test('翻页链接使用显式基准 URL,不再依赖 REQUEST_URI', () => {
|
||||
// 反面写法:只有两个参数的 add_query_arg —— 会退回到 $_SERVER['REQUEST_URI']。
|
||||
assert.doesNotMatch(page, /add_query_arg\('paged',\s*[^,)]+\)/);
|
||||
test('渲染已迁移到共享助手 AdminListPagination', () => {
|
||||
assert.match(
|
||||
list,
|
||||
/AdminListPagination::render\(self::PAGE_SLUG, \$count_all, \$paged, \$per_page, \$query_args\);/
|
||||
);
|
||||
|
||||
assert.match(pager, /\$base\s*=\s*\$query_args\s*\?\s*add_query_arg\(\$query_args,\s*\$this->page_url\)\s*:\s*\$this->page_url;/);
|
||||
assert.match(pager, /add_query_arg\('paged',\s*\$page,\s*\$base\)/);
|
||||
assert.equal(count(pager, /add_query_arg\('paged'/g), 1, '翻页 URL 只应有一个构造点');
|
||||
// 自定义标记与专属常量都应清空。
|
||||
assert.doesNotMatch(page, /render_pagenavi/);
|
||||
assert.doesNotMatch(page, /first-page button|prev-page button|next-page button|last-page button/);
|
||||
assert.doesNotMatch(page, /public const DEFAULT_PER_PAGE/);
|
||||
assert.doesNotMatch(page, /self::DEFAULT_PER_PAGE/);
|
||||
assert.match(page, /AdminListPagination::DEFAULT_PER_PAGE/);
|
||||
|
||||
// 只服务自定义禁用按钮的 CSS 规则一并删除。
|
||||
assert.doesNotMatch(page, /\.tablenav-pages a\.disabled \{ pointer-events: none; \}/);
|
||||
});
|
||||
|
||||
test('页码夹取到有效范围,禁用状态由夹取后的页码推导', () => {
|
||||
assert.match(pager, /\$total_pages\s*=\s*max\(1,\s*\(int\)\s*ceil\(\$total_count \/ \$number_per_page\)\)/);
|
||||
assert.match(pager, /\$current_page\s*=\s*min\(max\(1,\s*\(int\)\s*\(\$_GET\['paged'\] \?\? 1\)\),\s*\$total_pages\)/);
|
||||
test('ice_perpage 仍作为 URL 参数被尊重,默认每页 20', () => {
|
||||
assert.match(
|
||||
list,
|
||||
/\(int\) \(\$_REQUEST\['ice_perpage'\] \?\? AdminListPagination::DEFAULT_PER_PAGE\)/
|
||||
);
|
||||
|
||||
// 越界时 if/elseif 全不命中的分支结构不得回归。
|
||||
assert.match(pager, /\$on_first\s*=\s*\$current_page <= 1;/);
|
||||
assert.match(pager, /\$on_last\s*=\s*\$current_page >= \$total_pages;/);
|
||||
assert.equal(count(pager, /\$on_first \? '#' :/g), 2, '第一页/上一页都要按 on_first 落到 #');
|
||||
assert.equal(count(pager, /\$on_last \? '#' :/g), 2, '下一页/最后一页都要按 on_last 落到 #');
|
||||
assert.doesNotMatch(pager, /\$current_page == \$total_pages\b/);
|
||||
assert.doesNotMatch(pager, /\$prev_page_url\s*=\s*'#';/);
|
||||
});
|
||||
|
||||
test('越界页码回退到最后一页,不再出现无分页条的死胡同', () => {
|
||||
assert.match(list, /\$max_page\s*=\s*max\(1,\s*\(int\)\s*\(\$result\['max_pages'\] \?\? 1\)\)/);
|
||||
assert.match(list, /if \(!\$items && \$count_all > 0 && \$paged > \$max_page\)/);
|
||||
assert.match(list, /\$paged\s*=\s*\$max_page;/);
|
||||
// 回退之后必须重查,否则表格依旧是空的。
|
||||
assert.equal(count(list, /\$svc->get_list\(/g), 2);
|
||||
// 分页条必须在空态分支之后渲染,否则空态 return 会把分页条一起吞掉。
|
||||
assert.ok(list.indexOf('render_pagenavi(') > list.indexOf('未找到对应内容'), '分页渲染必须晚于空态处理');
|
||||
});
|
||||
|
||||
test('每页条数默认 20,工具栏不新增「每页显示」控件', () => {
|
||||
assert.match(page, /public const DEFAULT_PER_PAGE = 20;/);
|
||||
assert.match(list, /\(int\) \(\$_REQUEST\['ice_perpage'\] \?\? self::DEFAULT_PER_PAGE\)/);
|
||||
|
||||
// 明确约定:不在列表工具栏增加「每页显示」下拉/按钮。任何一处回来都视为回退。
|
||||
// 明确约定:不在列表工具栏增加「每页显示」下拉/按钮。
|
||||
assert.doesNotMatch(page, /per_page_selector/);
|
||||
assert.doesNotMatch(page, /PER_PAGE_CHOICES/);
|
||||
assert.doesNotMatch(page, /aksrt-perpage-form/);
|
||||
assert.doesNotMatch(page, /name="ice_perpage"/);
|
||||
assert.doesNotMatch(page, /order-header-right/);
|
||||
|
||||
// 但 ice_perpage 仍作为 URL 参数被尊重(无界面入口,供 URL / 程序化调用)。
|
||||
// 但 ice_perpage 仍随链接带走(无界面入口,供 URL / 程序化调用)。
|
||||
assert.match(args, /\$args\['ice_perpage'\] =/);
|
||||
|
||||
assert.match(list, /\$query_args = \$this->list_query_args\(/);
|
||||
assert.match(list, /render_pagenavi\(\$count_all, \$per_page, \$query_args\)/);
|
||||
// 工具栏恢复成「过滤链接 + 搜索表单」两段式。
|
||||
// 工具栏保持「过滤链接 + 搜索表单」两段式。
|
||||
assert.match(list, /echo '<div class="order-header"><ul class="subsubsub">' \. \$but_html \. '<\/ul>' \. \$search \. '<\/div>';/);
|
||||
});
|
||||
|
||||
test('越界页码回退到最后一页,不再出现无分页条的死胡同', () => {
|
||||
assert.match(list, /\$max_page\s*=\s*max\(1, \(int\) \(\$result\['max_pages'\] \?\? 1\)\)/);
|
||||
assert.match(list, /if \(!\$items && \$count_all > 0 && \$paged > \$max_page\)/);
|
||||
assert.match(list, /\$paged\s*=\s*\$max_page;/);
|
||||
// 回退之后必须重查,否则表格依旧是空的。
|
||||
assert.equal(count(list, /\$svc->get_list\(/g), 2);
|
||||
// 分页条必须在空态分支之后渲染,否则空态 return 会把分页条一起吞掉。
|
||||
assert.ok(
|
||||
list.indexOf('AdminListPagination::render(') > list.indexOf('未找到对应内容'),
|
||||
'分页渲染必须晚于空态处理'
|
||||
);
|
||||
});
|
||||
|
||||
test('筛选参数白名单化拼装,排序与每页条数随链接带走', () => {
|
||||
for (const key of ['status', 'type', 'other', 's', 'orderby', 'desc', 'ice_perpage']) {
|
||||
assert.match(args, new RegExp(`\\$args\\['${key}'\\] =`), `缺少 ${key}`);
|
||||
@@ -104,11 +99,7 @@ test('筛选参数白名单化拼装,排序与每页条数随链接带走', ()
|
||||
// 默认值不进 URL,避免链接里塞满无意义参数。
|
||||
assert.match(args, /\$orderby !== 'modified_time'/);
|
||||
assert.match(args, /\$order === 'ASC'/);
|
||||
assert.match(args, /\$per_page !== self::DEFAULT_PER_PAGE/);
|
||||
// paged 由 render_pagenavi 单独追加,不能混进基准参数里(否则会被自己覆盖)。
|
||||
assert.match(args, /\$per_page !== AdminListPagination::DEFAULT_PER_PAGE/);
|
||||
// paged 由 AdminListPagination 单独追加,不能混进基准参数里(否则会被自己覆盖)。
|
||||
assert.doesNotMatch(args, /\$args\['paged'\]/);
|
||||
});
|
||||
|
||||
test('禁用的翻页按钮不可点击,避免点空还滚回页首', () => {
|
||||
assert.match(page, /\.tablenav-pages a\.disabled \{ pointer-events: none; \}/);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* 支付中心列表页分页契约(WP 原生 paginate_links,统一到 AdminListPagination)。
|
||||
*
|
||||
* 背景(本次收敛的缺陷):
|
||||
* 1) 各页面的翻页基准 URL 写成 add_query_arg('paged', '%#%') —— 只有两个参数时
|
||||
* WordPress 会落到 $_SERVER['REQUEST_URI'],链接绑在当前请求上,随请求形态漂移,
|
||||
* 列条件也容易丢。现在统一由 AdminListPagination 以 page 参数 + 当前筛选参数显式拼装。
|
||||
* 2) 五个页面各复制一份几乎相同的 render_pagination(),其中「订单明细」与「提现审核」
|
||||
* 的那份从未被调用(死代码),两页各自又内联了一份 paginate_links。
|
||||
* 3) 页码越界(手工改 URL、或筛选后总数变小)会让 offset 落到数据之外:表格空掉
|
||||
* 且分页条不输出,只能手改 URL 退出。现在每页回退到最后一页重查一次。
|
||||
* 4) 「售后服务」原本写死 limit => 100,完全没有分页。
|
||||
*
|
||||
* 本契约固化:
|
||||
* - 七个页面全部改走 AdminListPagination::render(),不再有各自的翻页实现;
|
||||
* - 每页条数默认 20 且来自共享常量,不新增「每页显示」控件;
|
||||
* - 每页都在查询前夹取页码,越界时重查,杜绝「空表且没有分页条」的死胡同;
|
||||
* - 翻页链接显式带上当前筛选参数,不依赖 REQUEST_URI。
|
||||
*/
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const test = require('node:test');
|
||||
|
||||
const root = path.resolve(__dirname, '..', '..');
|
||||
const read = (rel) => fs.readFileSync(path.join(root, rel), 'utf8');
|
||||
|
||||
const helper = read('src/Admin/Native/AdminListPagination.php');
|
||||
const pages = {
|
||||
orders: read('src/Admin/Native/PayOrders.php'),
|
||||
products: read('src/Admin/Native/PayProducts.php'),
|
||||
coupons: read('src/Admin/Native/PayCoupons.php'),
|
||||
income: read('src/Admin/Native/PayIncome.php'),
|
||||
withdrawals: read('src/Admin/Native/PayWithdrawals.php'),
|
||||
chargeCards: read('src/Admin/Native/PayChargeCards.php'),
|
||||
};
|
||||
|
||||
const labels = {
|
||||
orders: '订单明细',
|
||||
products: '付费文章',
|
||||
coupons: '优惠码',
|
||||
income: '创作分成',
|
||||
withdrawals: '提现审核',
|
||||
chargeCards: '卡密管理',
|
||||
};
|
||||
|
||||
const count = (src, pattern) => (src.match(pattern) || []).length;
|
||||
|
||||
const emptyStates = {
|
||||
orders: '暂无订单',
|
||||
products: '暂无付费文章',
|
||||
coupons: '暂无优惠码',
|
||||
income: '暂无分成记录',
|
||||
withdrawals: '暂无提现记录',
|
||||
chargeCards: '未找到对应内容',
|
||||
};
|
||||
|
||||
test('七个页面全部改走共享分页助手', () => {
|
||||
for (const [key, src] of Object.entries(pages)) {
|
||||
assert.ok(
|
||||
src.includes('AdminListPagination::render('),
|
||||
`${labels[key]} 未使用 AdminListPagination::render()`
|
||||
);
|
||||
}
|
||||
|
||||
// 订单明细 + 售后服务在同一个文件里,各有一处。
|
||||
assert.equal(count(pages.orders, /AdminListPagination::render\(/g), 2, '订单明细与售后服务应各有一处');
|
||||
for (const key of Object.keys(pages)) {
|
||||
if (key === 'orders') continue;
|
||||
assert.equal(count(pages[key], /AdminListPagination::render\(/g), 1, `${labels[key]} 只应有一处`);
|
||||
}
|
||||
});
|
||||
|
||||
test('不再有各页面自己的翻页实现,也没有 REQUEST_URI 基准 URL', () => {
|
||||
// 只查六个页面:共享助手的 docblock 会引用这些反例,不能把它算进来。
|
||||
for (const [key, src] of Object.entries(pages)) {
|
||||
const label = labels[key];
|
||||
assert.doesNotMatch(src, /private function render_pagenavi\(/, `${label} 仍保留私有 render_pagenavi()`);
|
||||
assert.doesNotMatch(src, /private function render_pagination\(/, `${label} 仍保留私有 render_pagination()`);
|
||||
assert.doesNotMatch(src, /public const DEFAULT_PER_PAGE/, `${label} 仍保留自己的每页条数常量`);
|
||||
assert.doesNotMatch(src, /self::DEFAULT_PER_PAGE/, `${label} 仍引用自己的每页条数常量`);
|
||||
|
||||
// 两个参数的 add_query_arg('paged', ...) 会退回 $_SERVER['REQUEST_URI']。
|
||||
assert.doesNotMatch(src, /add_query_arg\('paged',\s*'[^']*'\s*\)/, `${label} 仍以 REQUEST_URI 为基准`);
|
||||
assert.doesNotMatch(src, /echo paginate_links\(/, `${label} 仍直接调用 paginate_links`);
|
||||
}
|
||||
});
|
||||
|
||||
test('每页条数默认 20 来自共享常量,且无「每页显示」控件', () => {
|
||||
assert.match(helper, /public const DEFAULT_PER_PAGE = 20;/);
|
||||
|
||||
for (const [key, src] of Object.entries(pages)) {
|
||||
assert.ok(
|
||||
src.includes('AdminListPagination::DEFAULT_PER_PAGE'),
|
||||
`${labels[key]} 未使用共享每页条数`
|
||||
);
|
||||
}
|
||||
|
||||
// 明确约定:任何页面都不新增「每页显示」下拉/按钮。
|
||||
for (const [key, src] of Object.entries(pages)) {
|
||||
assert.doesNotMatch(src, /per_page_selector/, `${labels[key]} 出现 per_page 选择器`);
|
||||
assert.doesNotMatch(src, /PER_PAGE_CHOICES/, `${labels[key]} 出现 PER_PAGE_CHOICES`);
|
||||
assert.doesNotMatch(src, /aksrt-perpage-form/, `${labels[key]} 出现每页条数表单`);
|
||||
assert.doesNotMatch(src, /name="ice_perpage"/, `${labels[key]} 出现 ice_perpage 输入框`);
|
||||
assert.doesNotMatch(src, /order-header-right/, `${labels[key]} 出现工具栏右侧每页控件`);
|
||||
}
|
||||
});
|
||||
|
||||
test('每页都在查询前夹取页码,越界时重查一次', () => {
|
||||
// 六个页面统一按 $page_num / $pages 夹取;卡密管理沿用自己既有的 $paged / $max_page 回退。
|
||||
for (const key of ['orders', 'products', 'coupons', 'income', 'withdrawals']) {
|
||||
assert.match(pages[key], /if \(\$page_num > \$pages\) \{/, `${labels[key]} 缺少页码夹取`);
|
||||
}
|
||||
assert.match(
|
||||
pages.chargeCards,
|
||||
/if \(!\$items && \$count_all > 0 && \$paged > \$max_page\)/,
|
||||
'卡密管理缺少越界回退'
|
||||
);
|
||||
|
||||
const requeries = {
|
||||
orders: [/\$items\s*=\s*\$repo->get_orders_admin\(/g, /\$items\s*=\s*\$service->admin_list\(/g],
|
||||
products: [/\$query\s*=\s*\$this->query_paid_posts\(/g],
|
||||
coupons: [/\$svc->get_list\(/g],
|
||||
income: [/\$svc->get_list\(/g],
|
||||
withdrawals: [/\$svc->get_all\(/g],
|
||||
chargeCards: [/\$svc->get_list\(/g],
|
||||
};
|
||||
|
||||
for (const [key, patterns] of Object.entries(requeries)) {
|
||||
for (const pattern of patterns) {
|
||||
assert.equal(count(pages[key], pattern), 2, `${labels[key]} 越界后必须重查(${pattern})`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('售后服务改为真正的 limit/offset 分页', () => {
|
||||
assert.doesNotMatch(pages.orders, /limit => 100/, '售后服务仍写死 limit => 100');
|
||||
assert.match(pages.orders, /\$total = \$service->count\(\$filter_args\);/, '售后服务未用 AfterSaleService::count() 取总数');
|
||||
|
||||
// count() 不像 admin_list() 那样校验状态,查询前必须先归一,否则分页条会说谎。
|
||||
assert.match(pages.orders, /if \(!in_array\(\$status, \$this->after_sale_statuses\(\), true\)\)/);
|
||||
assert.match(pages.orders, /private function after_sale_statuses\(\): array \{/);
|
||||
});
|
||||
|
||||
test('分页条晚于空态分支渲染', () => {
|
||||
for (const [key, src] of Object.entries(pages)) {
|
||||
const marker = emptyStates[key];
|
||||
assert.ok(src.includes(marker), `${labels[key]} 找不到空态标记 ${marker}`);
|
||||
|
||||
if (key === 'orders') {
|
||||
// 订单明细是第一处,售后服务是最后一处。
|
||||
assert.ok(
|
||||
src.indexOf('AdminListPagination::render(') > src.indexOf(marker),
|
||||
'订单明细的分页条必须晚于空态分支'
|
||||
);
|
||||
assert.ok(
|
||||
src.lastIndexOf('AdminListPagination::render(') > src.indexOf('暂无售后申请'),
|
||||
'售后服务的分页条必须晚于空态分支'
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
assert.ok(
|
||||
src.indexOf('AdminListPagination::render(') > src.indexOf(marker),
|
||||
`${labels[key]} 的分页条必须晚于空态分支`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('提现审核不把一次性动作参数带进翻页链接', () => {
|
||||
// 只取 $query_args 数组本体:上下文的注释与行循环里的 $retry_id 用法都不算。
|
||||
const start = pages.withdrawals.indexOf('$query_args = array_filter([');
|
||||
assert.ok(start !== -1, '提现审核缺少 $query_args 白名单');
|
||||
const slice = pages.withdrawals.slice(
|
||||
start,
|
||||
pages.withdrawals.indexOf('], static function', start)
|
||||
);
|
||||
assert.doesNotMatch(slice, /retry_id/, 'retry_id 不得进翻页链接');
|
||||
assert.doesNotMatch(slice, /retry_key/, 'retry_key 不得进翻页链接');
|
||||
for (const key of ['status', 's', 'user_id', 'date_from', 'date_to']) {
|
||||
assert.match(slice, new RegExp(`'${key}'\\s*=>`), `缺少筛选参数 ${key}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('共享助手自身:页码夹取、早退与显式基准 URL', () => {
|
||||
assert.match(helper, /namespace AKSRT\\Admin\\Native;/);
|
||||
assert.match(helper, /defined\('ABSPATH'\) \|\| exit;/);
|
||||
|
||||
// 空态与单页都不渲染。
|
||||
assert.match(helper, /if \(\$total_count <= 0 \|\| \$per_page <= 0\) \{\s*return;\s*\}/);
|
||||
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\);/);
|
||||
|
||||
// 原生 WP 分页 + 总数提示。
|
||||
assert.match(helper, /<div class="tablenav"><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 自行转义。/);
|
||||
});
|
||||
@@ -55,6 +55,7 @@ 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 ''; }
|
||||
}
|
||||
|
||||
@@ -83,6 +84,7 @@ namespace AKSRT\Payment {
|
||||
public static array $rows = [];
|
||||
public static function get_instance(): self { return self::$i ?: self::$i = new self(); }
|
||||
public function admin_list(array $args = []): array { return array_values(self::$rows); }
|
||||
public function count(array $args = []): int { return count(self::$rows); }
|
||||
public function get(int $id): ?array { return self::$rows[$id] ?? null; }
|
||||
public function can_finalize_order_refund(int $after_sale_id, string $order_number): bool {
|
||||
$row = self::$rows[$after_sale_id] ?? null;
|
||||
@@ -197,7 +199,8 @@ namespace AKSRT\Settings {
|
||||
}
|
||||
|
||||
namespace {
|
||||
require 'C:/Users/Xing/Desktop/AKSRT/AKSRT-theme/src/Admin/Native/PayOrders.php';
|
||||
require __DIR__ . '/../../src/Admin/Native/AdminListPagination.php';
|
||||
require 'C:/Users/Xing/Desktop/AKSRT/AKSRT-theme/src/Admin/Native/PayOrders.php';
|
||||
|
||||
$scenario = $argv[1] ?? '';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user