diff --git a/assets/admin/src/schema/sections/payment/recharge.js b/assets/admin/src/schema/sections/payment/recharge.js
index 3f3035dc..bac9c1c8 100644
--- a/assets/admin/src/schema/sections/payment/recharge.js
+++ b/assets/admin/src/schema/sections/payment/recharge.js
@@ -1,12 +1,13 @@
/**
* 余额充值 — 参考主题结构:用户余额(含余额提现)、转账、充值商品选项、
- * 卡密充值、充值卡(原「充值卡」页并入)与余额兑换积分。
+ * 卡密充值与余额兑换积分。卡密的生成/管理在后台「卡密管理」页完成,
+ * 此处不再保留「充值卡」配置项。
*/
export const rechargeSchema = {
key: 'payment.recharge',
title: '余额充值',
icon: 'fa fas fa-bolt',
- description: '配置余额功能、充值商品选项、卡密充值、充值卡规则与余额兑换积分。',
+ description: '配置余额功能、充值商品选项、卡密充值与余额兑换积分。',
defaults: {
pay_balance_s: true,
pay_balance_withdraw_s: false,
@@ -24,20 +25,6 @@ export const rechargeSchema = {
pay_balance_pass_charge_s: false,
pay_balance_pass_charge_desc: '',
pay_balance_pass_charge_only_password: false,
- card_pass_enabled: false,
- card_pass_desc: '',
- card_pass_type: 'balance',
- card_pass_prefix: 'AK',
- card_pass_length: 16,
- card_pass_batch_size: 50,
- card_pass_max_redeem: 1,
- card_pass_expiry: 0,
- card_pass_amount: '10.00',
- card_pass_vip_days: 30,
- card_pass_credits: 0,
- card_pass_redeem_page: '',
- card_pass_success_msg: '兑换成功!',
- card_pass_fail_msg: '卡号无效或已过期',
pay_balance_points_exchange_s: false,
pay_balance_points_exchange_rate: 100,
pay_balance_points_exchange_min_points: 100,
@@ -116,38 +103,6 @@ export const rechargeSchema = {
{ key: 'pay_balance_pass_charge_only_password', label: '单密码模式', type: 'switch', default: false, dependsOn: { key: 'pay_balance_pass_charge_s', value: true }, help: '仅需输入密码,不需要卡号' },
],
},
- {
- key: 'charge-card',
- title: '充值卡',
- icon: 'fa fas fa-id-card',
- fields: [
- { key: 'card_pass_enabled', label: '启用充值卡', type: 'switch', default: false, help: '充值卡规则与批量发卡配置;卡密兑换入口由上方「卡密充值」控制' },
- { key: 'card_pass_desc', label: '充值卡说明', type: 'textarea', default: '', dependsOn: { key: 'card_pass_enabled', value: true } },
- {
- key: 'card_pass_type',
- label: '充值卡类型',
- type: 'select',
- default: 'balance',
- dependsOn: { key: 'card_pass_enabled', value: true },
- options: [
- { label: '余额卡', value: 'balance' },
- { label: 'VIP卡', value: 'vip' },
- { label: '积分卡', value: 'credits' },
- ],
- },
- { key: 'card_pass_prefix', label: '卡号前缀', type: 'input', default: 'AK', dependsOn: { key: 'card_pass_enabled', value: true } },
- { key: 'card_pass_length', label: '卡号长度', type: 'number', default: 16, min: 8, max: 32, dependsOn: { key: 'card_pass_enabled', value: true } },
- { key: 'card_pass_batch_size', label: '批量生成数量', type: 'number', default: 50, min: 1, max: 500, dependsOn: { key: 'card_pass_enabled', value: true } },
- { key: 'card_pass_max_redeem', label: '单卡最大兑换次数', type: 'number', default: 1, min: 1, max: 100, dependsOn: { key: 'card_pass_enabled', value: true } },
- { key: 'card_pass_expiry', label: '有效期(天)', type: 'number', default: 0, min: 0, help: '0为永久有效', dependsOn: { key: 'card_pass_enabled', value: true } },
- { key: 'card_pass_amount', label: '面额(元)', type: 'input', sanitize_type: 'money_decimal', default: '10.00', min: 1, max: 9999, dependsOn: { key: 'card_pass_enabled', value: true } },
- { key: 'card_pass_vip_days', label: 'VIP天数', type: 'number', default: 30, min: 1, max: 3650, dependsOn: { key: 'card_pass_enabled', value: true } },
- { key: 'card_pass_credits', label: '赠送积分', type: 'number', default: 0, min: 0, max: 99999, dependsOn: { key: 'card_pass_enabled', value: true } },
- { key: 'card_pass_redeem_page', label: '兑换页面', type: 'input', default: '', dependsOn: { key: 'card_pass_enabled', value: true } },
- { key: 'card_pass_success_msg', label: '兑换成功提示', type: 'input', default: '兑换成功!', dependsOn: { key: 'card_pass_enabled', value: true } },
- { key: 'card_pass_fail_msg', label: '兑换失败提示', type: 'input', default: '卡号无效或已过期', dependsOn: { key: 'card_pass_enabled', value: true } },
- ],
- },
{
key: 'points-exchange',
title: '余额兑换积分',
diff --git a/src/Admin/Native/PayChargeCards.php b/src/Admin/Native/PayChargeCards.php
index cb443d7e..1fb3c59e 100644
--- a/src/Admin/Native/PayChargeCards.php
+++ b/src/Admin/Native/PayChargeCards.php
@@ -1,6 +1,13 @@
ensure_cap();
- $this->render_header(__('卡密管理', 'aksrt'));
+ $this->page_url = admin_url('admin.php?page=' . self::PAGE_SLUG);
if (!class_exists('\AKSRT\Payment\ChargeCard')) {
- echo '
' . esc_html__('卡密模块未初始化。', 'aksrt') . '
';
- $this->render_footer();
+ echo '' . esc_html__('卡密管理', 'aksrt') . '
';
+ echo '
' . esc_html__('卡密模块未初始化。', 'aksrt') . '
';
return;
}
- $status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
- $type = isset($_GET['type']) ? sanitize_key((string) wp_unslash($_GET['type'])) : '';
- $search = isset($_GET['s']) ? sanitize_text_field((string) wp_unslash($_GET['s'])) : '';
- $page_num = max(1, (int) ($_GET['paged'] ?? 1));
- $per_page = 20;
- $svc = \AKSRT\Payment\ChargeCard::get_instance();
- $stats = $svc->get_stats();
- $type_labels = [
- 'balance' => __('余额', 'aksrt'),
- 'vip' => __('VIP', 'aksrt'),
- 'credits' => __('积分', 'aksrt'),
+ $tab = isset($_GET['tab']) ? sanitize_key((string) wp_unslash($_GET['tab'])) : '';
+ $page_title = __('卡密管理', 'aksrt');
+ $head_but = '' . esc_html__('添加卡密', 'aksrt') . '';
+
+ // 页面级动作(参考主题在渲染前处理 add / delete)。
+ $action = isset($_REQUEST['action']) ? sanitize_key((string) wp_unslash($_REQUEST['action'])) : '';
+ if ($action === 'add') {
+ $this->handle_add();
+ } elseif ($action === 'delete') {
+ $this->handle_delete();
+ }
+
+ switch ($tab) {
+ case 'add':
+ $page_title = __('添加卡密', 'aksrt');
+ $head_but = '' . esc_html__('返回列表', 'aksrt') . '';
+ break;
+
+ case 'export':
+ $page_title = __('导出卡密', 'aksrt');
+ $head_but = '' . esc_html__('返回列表', 'aksrt') . '';
+ break;
+ }
+
+ echo '';
+ $this->print_assets();
+ echo '
' . esc_html($page_title) . '
';
+ echo $head_but; // phpcs:ignore WordPress.Security.EscapeOutput -- 内部构造,已转义。
+
+ if ($tab === 'add') {
+ $this->render_add_form();
+ } elseif ($tab === 'export') {
+ $this->render_export_form();
+ } else {
+ $this->render_list();
+ }
+
+ echo '';
+ }
+
+ /* ------------------------------------------------------------------
+ * 列表
+ * ------------------------------------------------------------------ */
+
+ private function render_list(): void {
+ $svc = \AKSRT\Payment\ChargeCard::get_instance();
+
+ $s = isset($_REQUEST['s']) ? sanitize_text_field((string) wp_unslash($_REQUEST['s'])) : '';
+ $status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
+ $type = isset($_GET['type']) ? sanitize_key((string) wp_unslash($_GET['type'])) : '';
+ $other = isset($_GET['other']) ? sanitize_text_field((string) wp_unslash($_GET['other'])) : '';
+ $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'] ?? 30)));
+
+ // 参考主题:status=0 表示未使用;本主题规范状态为 active。
+ if ($status === '0' || $status === 'active') {
+ $status = 'active';
+ } elseif (!in_array($status, ['used', 'expired', 'disabled'], true)) {
+ $status = '';
+ }
+ if (!in_array($type, ['balance', 'vip', 'credits', 'custom'], true)) {
+ $type = '';
+ }
+
+ // 标题右侧按钮与过滤链接。
+ $head_but = '';
+ if ($s !== '') {
+ $head_but .= '' . sprintf(esc_html__('搜索 “%s” 的内容', 'aksrt'), esc_html($s)) . '
';
+ } else {
+ $head_but .= '' . esc_html__('添加卡密', 'aksrt') . '';
+ $head_but .= '' . esc_html__('导出卡密', 'aksrt') . '';
+ }
+
+ $sub_but = [
+ ['name' => __('全部', 'aksrt'), 'href' => $this->page_url],
];
- $status_labels = [
- 'active' => __('可用', 'aksrt'),
- 'used' => __('已用', 'aksrt'),
- 'disabled' => __('已禁用', 'aksrt'),
- 'expired' => __('已过期', 'aksrt'),
- ];
-
- echo '' . esc_html(sprintf(
- /* translators: 1: total 2: active 3: used */
- __('共 %1$d 张 · 可用 %2$d · 已用 %3$d', 'aksrt'),
- (int) ($stats['total'] ?? 0),
- (int) ($stats['active'] ?? 0),
- (int) ($stats['used'] ?? 0)
- )) . '
';
-
- echo '' . esc_html__('批量生成卡密', 'aksrt') . '
';
- echo '';
-
- if (!empty($_GET['aksrt_generated'])) {
- $generated = (int) $_GET['aksrt_generated'];
- echo '' . esc_html(sprintf(
- /* translators: %d count */
- __('成功生成 %d 张卡密。', 'aksrt'),
- $generated
- )) . '
';
+ if ($s === '') {
+ $sub_but[] = ['name' => __('未使用', 'aksrt'), 'href' => add_query_arg('status', '0', $this->page_url)];
+ $sub_but[] = ['name' => __('已使用', 'aksrt'), 'href' => add_query_arg('status', 'used', $this->page_url)];
+ foreach ($this->type_labels() as $_k => $_v) {
+ $sub_but[] = ['name' => $_v, 'href' => add_query_arg('type', $_k, $this->page_url)];
+ }
}
+ $but_html = '';
+ foreach ($sub_but as $but) {
+ $but_html .= '' . esc_html($but['name']) . ' | ';
+ }
+ $but_html = substr($but_html, 0, -2);
- $generated_codes = get_transient('aksrt_pay_card_codes_' . get_current_user_id());
- if (is_array($generated_codes) && $generated_codes) {
- delete_transient('aksrt_pay_card_codes_' . get_current_user_id());
- echo '' . esc_html__('本次生成卡密(请及时复制保存):', 'aksrt') . '
';
- echo '
';
- echo '
';
- }
+ $search = '';
- echo '';
+ echo $head_but; // phpcs:ignore WordPress.Security.EscapeOutput -- 已转义。
+ echo '';
$result = $svc->get_list([
- 'status' => $status,
- 'type' => $type,
- 'search' => $search,
- 'limit' => $per_page,
- 'offset' => ($page_num - 1) * $per_page,
+ 'status' => $status,
+ 'type' => $type,
+ 'other' => $other,
+ 'search' => $s,
+ 'orderby' => $orderby,
+ 'order' => $order,
+ 'limit' => $per_page,
+ 'offset' => ($paged - 1) * $per_page,
]);
- $items = $result['items'] ?? [];
- $total = (int) ($result['total'] ?? 0);
- $pages = max(1, (int) ($result['max_pages'] ?? 1));
-
- echo '';
- echo '';
- echo '| ID | ';
- echo '' . esc_html__('类型', 'aksrt') . ' | ';
- echo '' . esc_html__('密码', 'aksrt') . ' | ';
- echo '' . esc_html__('面额', 'aksrt') . ' | ';
- echo '' . esc_html__('状态', 'aksrt') . ' | ';
- echo '' . esc_html__('过期', 'aksrt') . ' | ';
- echo '' . esc_html__('使用者', 'aksrt') . ' | ';
- echo '' . esc_html__('创建时间', 'aksrt') . ' | ';
- echo '' . esc_html__('操作', 'aksrt') . ' | ';
- echo '
';
+ $items = (array) ($result['items'] ?? []);
+ $count_all = (int) ($result['total'] ?? 0);
if (!$items) {
- echo '| ' . esc_html__('暂无卡密', 'aksrt') . ' |
';
+ echo '';
+ echo '';
+ echo '| ' . esc_html__('未找到对应内容,或暂无内容', 'aksrt') . ' |
';
+ echo '
';
+ return;
}
- foreach ($items as $row) {
- $id = (int) ($row['id'] ?? 0);
- $st = (string) ($row['status'] ?? '');
- $row_type = (string) ($row['type'] ?? '');
- $user_id = (int) ($row['used_by'] ?? $row['user_id'] ?? 0);
- $user = $user_id > 0 ? get_userdata($user_id) : null;
+ // 表头(与参考主题同列、同排序链接)。
+ $theads = [
+ ['width' => '6%', 'orderby' => 'card', 'name' => __('卡号', 'aksrt')],
+ ['width' => '10%', 'orderby' => 'password', 'name' => __('密码', 'aksrt')],
+ ['width' => '5%', 'orderby' => 'type', 'name' => __('类型', 'aksrt')],
+ ['width' => '5%', 'orderby' => 'create_time', 'name' => __('创建时间', 'aksrt')],
+ ['width' => '5%', 'orderby' => 'modified_time', 'name' => __('更新时间', 'aksrt')],
+ ['width' => '5%', 'orderby' => 'status', 'name' => __('状态', 'aksrt')],
+ ['width' => '8%', 'orderby' => 'other', 'name' => __('标识', 'aksrt')],
+ ];
- echo '';
- echo '| ' . esc_html((string) $id) . ' | ';
- echo '' . esc_html($type_labels[$row_type] ?? $row_type) . ' | ';
- echo '' . esc_html((string) ($row['password'] ?? '')) . ' | ';
- echo '' . esc_html((string) ($row['amount'] ?? '')) . ' | ';
- echo '' . esc_html($status_labels[$st] ?? $st) . ' | ';
- echo '' . esc_html((string) ($row['expires_at'] ?? '—')) . ' | ';
- echo '' . ($user ? esc_html($user->display_name) : '—') . ' | ';
- echo '' . esc_html((string) ($row['created_at'] ?? '')) . ' | ';
- echo '';
- if ($st === 'active') {
- echo '';
+ $thead_th = ' | | ';
+ foreach ($theads as $thead) {
+ $orderby_links = '';
+ if ($thead['orderby']) {
+ $orderby_url = add_query_arg('orderby', $thead['orderby']);
+ $orderby_links .= '';
+ $orderby_links .= '';
+ $orderby_links = '' . $orderby_links . '';
+ }
+ $thead_th .= '' . esc_html($thead['name']) . $orderby_links . ' | ';
+ }
+
+ $tbody = '';
+ foreach ($items as $msg) {
+ $meta = [];
+ if (!empty($msg['meta'])) {
+ $decoded = json_decode((string) $msg['meta'], true);
+ $meta = is_array($decoded) ? $decoded : [];
+ }
+
+ $status_html = '';
+ if ($msg['type'] === 'custom') {
+ $type_html = '' . esc_html__('自定义卡密', 'aksrt') . '';
} else {
- echo '—';
+ $status_html = '' . esc_html__('未使用', 'aksrt') . '';
+ if ($msg['status'] === 'used') {
+ $status_html = '' . esc_html__('已使用', 'aksrt') . '';
+ if (!empty($msg['order_number'])) {
+ $order_link_url = admin_url('admin.php?page=aksrt_orders&s=' . urlencode((string) $msg['order_number']));
+ $status_html .= ' [' . esc_html__('查看', 'aksrt') . ']';
+ }
+ } elseif ($msg['status'] === 'expired') {
+ $status_html = '' . esc_html__('已过期', 'aksrt') . '';
+ } elseif ($msg['status'] === 'disabled') {
+ $status_html = '' . esc_html__('已禁用', 'aksrt') . '';
+ }
}
- echo '';
- echo '
';
+
+ $other_a = '';
+ if (!empty($msg['note'])) {
+ $other_a = '' . esc_html($msg['note']) . '';
+ }
+
+ if ($msg['type'] === 'vip') {
+ $type_html = '' . esc_html__('会员兑换', 'aksrt') . '';
+ $level = (int) ($meta['level'] ?? 1);
+ $type_html .= '
' . esc_html($this->vip_level_name($level)) . ':' . esc_html($this->vip_time_text($meta));
+ } elseif ($msg['type'] === 'credits') {
+ $type_html = '' . esc_html__('积分兑换', 'aksrt') . '';
+ $type_html .= '
' . esc_html__('积分:', 'aksrt') . esc_html((string) (int) ($meta['points'] ?? 0));
+ } elseif ($msg['type'] === 'balance') {
+ $type_html = '' . esc_html__('余额充值', 'aksrt') . '';
+ $type_html .= '
' . esc_html__('金额:', 'aksrt') . esc_html($this->balance_amount_text($meta));
+ } else {
+ $type_html = '' . esc_html__('自定义卡密', 'aksrt') . '';
+ }
+
+ $card_name = (string) ($meta['custom_name']['card_name'] ?? '');
+ $password_name = (string) ($meta['custom_name']['password_name'] ?? '');
+
+ $tbody .= '';
+ $tbody .= '|
+
+ | ';
+ $tbody .= "" . esc_html($card_name) . ' ' . esc_html((string) $msg['card']) . ' | ';
+ $tbody .= "" . esc_html($password_name) . ' ' . esc_html((string) $msg['password']) . ' | ';
+ $tbody .= "$type_html | ";
+ $tbody .= '' . esc_html((string) $msg['created_at']) . ' | ';
+ $tbody .= '' . esc_html((string) ($msg['used_at'] ?: $msg['created_at'])) . ' | ';
+ $tbody .= "$status_html | ";
+ $tbody .= "$other_a | ";
+ $tbody .= '
';
}
- echo '
';
+ echo '';
+ echo '