refactor: rename the theme brand and every identifier from AKSRT to Jio
Full-project rename. `git grep -i aksrt` is now empty apart from the
DBMigrator 3.3.0 changelog note that documents the rename itself.
Code identifiers
- PHP namespace AKSRT\ -> Jio\ (composer PSR-4 + 378 declarations)
- global constants AKSRT_* -> JIO_* (VERSION/DIR/URI, PAY_TYPE_*, CAPTCHA_*)
- procedural functions, hooks, options, meta keys, table names,
cookies, query vars, ajax actions and nonces: aksrt_ -> jio_
- text domain aksrt -> jio; Theme Name -> Jio
- JS globals: aksrtData/aksrtAdminData/... -> jioData/jioAdminData/...,
window.AKSRTCaptcha & friends -> window.JioCaptcha, Aksrt* -> Jio*
- Vue components Aks*.vue -> Jio*.vue (15 files renamed + imports)
- CSS classes/data-attrs/custom properties: aksrt- -> jio-
- REST namespace aksrt/v1 -> jio/v1 (incl. payment gateway callbacks)
Storage layer
- DB tables {prefix}aksrt_* -> {prefix}jio_*; SCHEMA_VERSION 3.2.0 -> 3.3.0
so dbDelta creates the new names on install
- widget id_base aksrt_widget_* -> jio_widget_*
- uninstall.php drop list rebuilt from the actual migrator tables
(dropped 3 stale entries, added the 17 missing ones)
Removed
- hardcoded https://api.aksrt.com/version.json default update endpoint;
get_endpoint() now reads the 「更新端点」 setting and do_check() no-ops
when it is empty
Files renamed: inc/helpers/aksrt-*.php -> jio-*.php (54),
assets/admin/src/components/form/Aks*.vue -> Jio*.vue (15).
Build artifacts regenerated: assets/css/build, assets/js/runtime,
assets/admin/build.
Verified: audit-psr4-paths errors=0, audit-admin-contracts errors=0
(warnings=56, unchanged baseline), 813/813 widget-system tests,
44/44 phpunit, php -l clean across all PHP.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
5b16bc6e15
commit
35bb96e82f
997 files changed
+28216
-28188
No files matched your search
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*
|
||||
* 后台列表页分页条 —— 1:1 复刻「卡密管理」页的 zibpay_admin_pagenavi 结构。
|
||||
*
|
||||
@@ -20,7 +20,7 @@
|
||||
* 这样的口径,四枚按钮全 disabled 也明确了「没有更多页」。
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -78,13 +78,13 @@ class AdminListPagination {
|
||||
?>
|
||||
<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="displaying-num"><?php printf(esc_html__('每页 %1$s 共%2$s', 'jio'), 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>
|
||||
<a class="first-page button<?php echo $on_first ? ' disabled' : ''; ?>" title="<?php echo esc_attr__('前往第一页', 'jio'); ?>" href="<?php echo esc_url($first_page_url); ?>">«</a>
|
||||
<a class="prev-page button<?php echo $on_first ? ' disabled' : ''; ?>" title="<?php echo esc_attr__('前往上一页', 'jio'); ?>" href="<?php echo esc_url($prev_page_url); ?>">‹</a>
|
||||
<span class="paging-input"><?php printf(esc_html__('第 %1$s 页,共 %2$s 页', 'jio'), 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__('前往下一页', 'jio'); ?>" href="<?php echo esc_url($next_page_url); ?>">›</a>
|
||||
<a class="last-page button<?php echo $on_last ? ' disabled' : ''; ?>" title="<?php echo esc_attr__('前往最后一页', 'jio'); ?>" href="<?php echo esc_url($last_page_url); ?>">»</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
/**
|
||||
* 原生后台运营页共享 UI。
|
||||
*
|
||||
* 支付中心(aksrt_pay 菜单)与用户子菜单(users.php)的运营页统一使用这里的
|
||||
* 支付中心(jio_pay 菜单)与用户子菜单(users.php)的运营页统一使用这里的
|
||||
* 页面外壳、页头、统计卡、卡片、徽标与空状态。样式在 assets/css/admin/native-ui.css,
|
||||
* 只在 SLUGS 列出的页面入队,规则全部限定在 .aksrt-native 下。
|
||||
* 只在 SLUGS 列出的页面入队,规则全部限定在 .jio-native 下。
|
||||
*
|
||||
* 输出方法直接 echo,参数在内部转义;$html 形参约定为调用方已转义的片段。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -19,22 +19,22 @@ final class NativeUi {
|
||||
|
||||
/** 使用共享 UI 的 admin page 参数。 */
|
||||
public const SLUGS = [
|
||||
'aksrt_pay',
|
||||
'aksrt_orders',
|
||||
'aksrt_pay_products',
|
||||
'aksrt_coupons',
|
||||
'aksrt_charge_cards',
|
||||
'aksrt_income',
|
||||
'aksrt_withdraw',
|
||||
'aksrt_invite_codes',
|
||||
'aksrt_messages',
|
||||
'aksrt_user_vip',
|
||||
'aksrt_user_auth',
|
||||
'aksrt_user_ban',
|
||||
'aksrt_tickets',
|
||||
'jio_pay',
|
||||
'jio_orders',
|
||||
'jio_pay_products',
|
||||
'jio_coupons',
|
||||
'jio_charge_cards',
|
||||
'jio_income',
|
||||
'jio_withdraw',
|
||||
'jio_invite_codes',
|
||||
'jio_messages',
|
||||
'jio_user_vip',
|
||||
'jio_user_auth',
|
||||
'jio_user_ban',
|
||||
'jio_tickets',
|
||||
];
|
||||
|
||||
public const STYLE_HANDLE = 'aksrt-admin-native-ui';
|
||||
public const STYLE_HANDLE = 'jio-admin-native-ui';
|
||||
|
||||
private const TONES = ['success', 'info', 'warning', 'danger', 'neutral', 'primary'];
|
||||
|
||||
@@ -59,8 +59,8 @@ final class NativeUi {
|
||||
return;
|
||||
}
|
||||
$css = '/assets/css/admin/native-ui.css';
|
||||
if (is_file(AKSRT_DIR . $css)) {
|
||||
wp_enqueue_style(self::STYLE_HANDLE, AKSRT_URI . $css, [], (string) filemtime(AKSRT_DIR . $css));
|
||||
if (is_file(JIO_DIR . $css)) {
|
||||
wp_enqueue_style(self::STYLE_HANDLE, JIO_URI . $css, [], (string) filemtime(JIO_DIR . $css));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ final class NativeUi {
|
||||
|
||||
/** 打开 .wrap 外壳。$modifier 追加到 class(如页面专属根类)。 */
|
||||
public static function page_open(string $modifier = ''): void {
|
||||
$class = trim('wrap aksrt-native ' . $modifier);
|
||||
$class = trim('wrap jio-native ' . $modifier);
|
||||
echo '<div class="' . esc_attr($class) . '">';
|
||||
}
|
||||
|
||||
@@ -86,19 +86,19 @@ final class NativeUi {
|
||||
* @param array<int, array{label:string,url?:string,dialog?:string,primary?:bool,icon?:string}> $actions
|
||||
*/
|
||||
public static function header(string $title, string $description = '', string $icon = '', array $actions = []): void {
|
||||
echo '<div class="aksrt-native-header"><div class="aksrt-native-header__main">';
|
||||
echo '<div class="jio-native-header"><div class="jio-native-header__main">';
|
||||
// h1 让 WP 把 admin_notices 挪到标题之后。
|
||||
echo '<h1 class="aksrt-native-header__title">';
|
||||
echo '<h1 class="jio-native-header__title">';
|
||||
if ($icon !== '') {
|
||||
echo '<span class="aksrt-native-header__icon dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>';
|
||||
echo '<span class="jio-native-header__icon dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>';
|
||||
}
|
||||
echo esc_html($title) . '</h1>';
|
||||
if ($description !== '') {
|
||||
echo '<p class="aksrt-native-header__desc">' . esc_html($description) . '</p>';
|
||||
echo '<p class="jio-native-header__desc">' . esc_html($description) . '</p>';
|
||||
}
|
||||
echo '</div>';
|
||||
if ($actions !== []) {
|
||||
echo '<div class="aksrt-native-header__actions">';
|
||||
echo '<div class="jio-native-header__actions">';
|
||||
foreach ($actions as $action) {
|
||||
echo isset($action['dialog'])
|
||||
? self::button_dialog($action['label'], (string) $action['dialog'], !empty($action['primary']), (string) ($action['icon'] ?? ''))
|
||||
@@ -117,11 +117,11 @@ final class NativeUi {
|
||||
return '<a class="' . esc_attr($class) . '" href="' . esc_url($url) . '">' . $icon_html . esc_html($label) . '</a>';
|
||||
}
|
||||
|
||||
/** 打开页面内 <dialog id="$dialog_id"> 的按钮;点击行为由页面脚本按 data-aksrt-dialog-open 绑定。 */
|
||||
/** 打开页面内 <dialog id="$dialog_id"> 的按钮;点击行为由页面脚本按 data-jio-dialog-open 绑定。 */
|
||||
public static function button_dialog(string $label, string $dialog_id, bool $primary = false, string $icon = ''): string {
|
||||
$class = $primary ? 'button button-primary' : 'button';
|
||||
$icon_html = $icon !== '' ? '<span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>' : '';
|
||||
return '<button type="button" class="' . esc_attr($class) . '" data-aksrt-dialog-open="' . esc_attr($dialog_id) . '" aria-haspopup="dialog" aria-controls="' . esc_attr($dialog_id) . '">'
|
||||
return '<button type="button" class="' . esc_attr($class) . '" data-jio-dialog-open="' . esc_attr($dialog_id) . '" aria-haspopup="dialog" aria-controls="' . esc_attr($dialog_id) . '">'
|
||||
. $icon_html . esc_html($label) . '</button>';
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ final class NativeUi {
|
||||
if ($items === []) {
|
||||
return;
|
||||
}
|
||||
echo '<div class="aksrt-native-stats">';
|
||||
echo '<div class="jio-native-stats">';
|
||||
foreach ($items as $item) {
|
||||
self::stat($item);
|
||||
}
|
||||
@@ -150,19 +150,19 @@ final class NativeUi {
|
||||
$tag = $url !== '' ? 'a' : 'div';
|
||||
$attr = $url !== '' ? ' href="' . esc_url($url) . '"' : '';
|
||||
|
||||
echo '<' . $tag . ' class="aksrt-native-stat aksrt-native-stat--' . esc_attr($tone) . '"' . $attr . '>';
|
||||
echo '<span class="aksrt-native-stat__label">';
|
||||
echo '<' . $tag . ' class="jio-native-stat jio-native-stat--' . esc_attr($tone) . '"' . $attr . '>';
|
||||
echo '<span class="jio-native-stat__label">';
|
||||
if (!empty($item['icon'])) {
|
||||
echo '<span class="dashicons ' . esc_attr((string) $item['icon']) . '" aria-hidden="true"></span>';
|
||||
}
|
||||
echo esc_html($item['label']) . '</span>';
|
||||
echo '<span class="aksrt-native-stat__value">';
|
||||
echo '<span class="jio-native-stat__value">';
|
||||
if (!empty($item['unit'])) {
|
||||
echo '<span class="aksrt-native-stat__unit">' . esc_html((string) $item['unit']) . '</span>';
|
||||
echo '<span class="jio-native-stat__unit">' . esc_html((string) $item['unit']) . '</span>';
|
||||
}
|
||||
echo esc_html((string) $item['value']) . '</span>';
|
||||
if (!empty($item['hint'])) {
|
||||
echo '<span class="aksrt-native-stat__hint">' . esc_html((string) $item['hint']) . '</span>';
|
||||
echo '<span class="jio-native-stat__hint">' . esc_html((string) $item['hint']) . '</span>';
|
||||
}
|
||||
echo '</' . $tag . '>';
|
||||
}
|
||||
@@ -176,19 +176,19 @@ final class NativeUi {
|
||||
* $flush=true 时正文无内边距(承载整表)。
|
||||
*/
|
||||
public static function card_open(string $title = '', string $actions_html = '', bool $flush = false, string $modifier = ''): void {
|
||||
$class = 'aksrt-native-card' . ($flush ? ' aksrt-native-card--flush' : '') . ($modifier !== '' ? ' ' . $modifier : '');
|
||||
$class = 'jio-native-card' . ($flush ? ' jio-native-card--flush' : '') . ($modifier !== '' ? ' ' . $modifier : '');
|
||||
echo '<section class="' . esc_attr($class) . '">';
|
||||
if ($title !== '' || $actions_html !== '') {
|
||||
echo '<div class="aksrt-native-card__head">';
|
||||
echo '<div class="jio-native-card__head">';
|
||||
if ($title !== '') {
|
||||
echo '<h2 class="aksrt-native-card__title">' . esc_html($title) . '</h2>';
|
||||
echo '<h2 class="jio-native-card__title">' . esc_html($title) . '</h2>';
|
||||
}
|
||||
if ($actions_html !== '') {
|
||||
echo '<div class="aksrt-native-card__actions">' . $actions_html . '</div>';
|
||||
echo '<div class="jio-native-card__actions">' . $actions_html . '</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
echo '<div class="aksrt-native-card__body">';
|
||||
echo '<div class="jio-native-card__body">';
|
||||
}
|
||||
|
||||
public static function card_close(): void {
|
||||
@@ -198,7 +198,7 @@ final class NativeUi {
|
||||
/** 表格卡:整表铺满卡片,带横向滚动容器。 */
|
||||
public static function table_card_open(string $title = '', string $actions_html = ''): void {
|
||||
self::card_open($title, $actions_html, true);
|
||||
echo '<div class="aksrt-native-table-wrap">';
|
||||
echo '<div class="jio-native-table-wrap">';
|
||||
}
|
||||
|
||||
/** 关闭表格卡;分页条应在 table-wrap 关闭后、卡片关闭前输出,故拆成两步。 */
|
||||
@@ -211,7 +211,7 @@ final class NativeUi {
|
||||
* ----------------------------------------------------------------*/
|
||||
|
||||
public static function badge(string $label, string $tone = 'neutral', bool $dot = true): string {
|
||||
$class = 'aksrt-native-badge aksrt-native-badge--' . self::tone($tone) . ($dot ? ' has-dot' : '');
|
||||
$class = 'jio-native-badge jio-native-badge--' . self::tone($tone) . ($dot ? ' has-dot' : '');
|
||||
return '<span class="' . esc_attr($class) . '">' . esc_html($label) . '</span>';
|
||||
}
|
||||
|
||||
@@ -228,9 +228,9 @@ final class NativeUi {
|
||||
}
|
||||
|
||||
public static function empty_state(string $title, string $hint = '', string $icon = 'dashicons-portfolio'): string {
|
||||
$html = '<div class="aksrt-native-empty">';
|
||||
$html = '<div class="jio-native-empty">';
|
||||
$html .= '<span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>';
|
||||
$html .= '<span class="aksrt-native-empty__title">' . esc_html($title) . '</span>';
|
||||
$html .= '<span class="jio-native-empty__title">' . esc_html($title) . '</span>';
|
||||
if ($hint !== '') {
|
||||
$html .= '<span>' . esc_html($hint) . '</span>';
|
||||
}
|
||||
@@ -239,7 +239,7 @@ final class NativeUi {
|
||||
|
||||
/** 表格内的空状态整行。 */
|
||||
public static function empty_row(int $colspan, string $title, string $hint = ''): string {
|
||||
return '<tr class="no-items"><td class="aksrt-native-empty-cell" colspan="' . esc_attr((string) max(1, $colspan)) . '">'
|
||||
return '<tr class="no-items"><td class="jio-native-empty-cell" colspan="' . esc_attr((string) max(1, $colspan)) . '">'
|
||||
. self::empty_state($title, $hint)
|
||||
. '</td></tr>';
|
||||
}
|
||||
@@ -248,13 +248,13 @@ final class NativeUi {
|
||||
public static function user_cell(int $user_id, string $fallback = ''): string {
|
||||
$user = $user_id > 0 ? get_userdata($user_id) : false;
|
||||
if (!$user) {
|
||||
return '<span class="aksrt-native-muted">' . esc_html($fallback !== '' ? $fallback : ($user_id > 0 ? '#' . $user_id : '—')) . '</span>';
|
||||
return '<span class="jio-native-muted">' . esc_html($fallback !== '' ? $fallback : ($user_id > 0 ? '#' . $user_id : '—')) . '</span>';
|
||||
}
|
||||
$avatar = function_exists('aksrt_get_avatar_url') ? (string) aksrt_get_avatar_url($user_id, 28) : (string) get_avatar_url($user_id, ['size' => 28]);
|
||||
return '<a class="aksrt-native-user" href="' . esc_url(get_edit_user_link($user_id)) . '">'
|
||||
$avatar = function_exists('jio_get_avatar_url') ? (string) jio_get_avatar_url($user_id, 28) : (string) get_avatar_url($user_id, ['size' => 28]);
|
||||
return '<a class="jio-native-user" href="' . esc_url(get_edit_user_link($user_id)) . '">'
|
||||
. ($avatar !== '' ? '<img src="' . esc_url($avatar) . '" width="28" height="28" alt="">' : '')
|
||||
. '<span><span class="aksrt-native-user__name">' . esc_html($user->display_name) . '</span>'
|
||||
. '<span class="aksrt-native-user__meta">#' . esc_html((string) $user_id) . '</span></span></a>';
|
||||
. '<span><span class="jio-native-user__name">' . esc_html($user->display_name) . '</span>'
|
||||
. '<span class="jio-native-user__meta">#' . esc_html((string) $user_id) . '</span></span></a>';
|
||||
}
|
||||
|
||||
private static function tone(string $tone): string {
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*
|
||||
* 后台「卡密管理」页面 — 1:1 复刻参考主题 zibll 的 zibpay/page/charge-card.php。
|
||||
*
|
||||
* 结构:列表(过滤/搜索/排序/批量删除/分页) + 添加卡密(自动生成 / 导入) + 导出卡密(text / xls)。
|
||||
* 卡密类型与参考主题一致:余额充值 / 会员兑换 / 积分兑换 / 自定义卡密;
|
||||
* 本主题内部类型映射:balance / vip / credits / custom。
|
||||
* 差异点:AKSRT 无商城发货(shipped)子系统,故不含「已售」标记与链接;删除取代参考主题的批量操作语义一致。
|
||||
* 差异点:Jio 无商城发货(shipped)子系统,故不含「已售」标记与链接;删除取代参考主题的批量操作语义一致。
|
||||
*
|
||||
* 外壳与配色走 NativeUi(.aksrt-native),页面专属交互样式仍由 print_assets() 输出。
|
||||
* 外壳与配色走 NativeUi(.jio-native),页面专属交互样式仍由 print_assets() 输出。
|
||||
* 类体按关注点拆成 flat sibling traits(PSR-4 要求 trait 与宿主类同目录,不能放子目录):
|
||||
* - PayChargeCardsList 列表渲染 + 翻页参数
|
||||
* - PayChargeCardsAssets 页面专属内联样式/脚本
|
||||
* - PayChargeCardsForms 添加/导出表单 + 依赖显隐助手
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -27,9 +27,9 @@ class PayChargeCards {
|
||||
use PayChargeCardsForms;
|
||||
|
||||
public const CAPABILITY = 'manage_options';
|
||||
public const PAGE_SLUG = 'aksrt_charge_cards';
|
||||
public const NONCE = 'aksrt_card_pass_page';
|
||||
public const EXPORT_AJAX_ACTION = 'aksrt_card_pass_export';
|
||||
public const PAGE_SLUG = 'jio_charge_cards';
|
||||
public const NONCE = 'jio_card_pass_page';
|
||||
public const EXPORT_AJAX_ACTION = 'jio_card_pass_export';
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
@@ -54,17 +54,17 @@ class PayChargeCards {
|
||||
$this->ensure_cap();
|
||||
$this->page_url = admin_url('admin.php?page=' . self::PAGE_SLUG);
|
||||
|
||||
if (!class_exists('\AKSRT\Payment\ChargeCard')) {
|
||||
if (!class_exists('\Jio\Payment\ChargeCard')) {
|
||||
NativeUi::page_open();
|
||||
echo '<h1 class="wp-heading-inline">' . esc_html__('卡密管理', 'aksrt') . '</h1>';
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('卡密模块未初始化。', 'aksrt') . '</p></div>';
|
||||
echo '<h1 class="wp-heading-inline">' . esc_html__('卡密管理', 'jio') . '</h1>';
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('卡密模块未初始化。', 'jio') . '</p></div>';
|
||||
NativeUi::page_close();
|
||||
return;
|
||||
}
|
||||
|
||||
$tab = isset($_GET['tab']) ? sanitize_key((string) wp_unslash($_GET['tab'])) : '';
|
||||
$s = isset($_REQUEST['s']) ? sanitize_text_field((string) wp_unslash($_REQUEST['s'])) : '';
|
||||
$page_title = __('卡密管理', 'aksrt');
|
||||
$page_title = __('卡密管理', 'jio');
|
||||
$head_but = '';
|
||||
|
||||
// 页面级动作(参考主题在渲染前处理 add / delete)。
|
||||
@@ -78,20 +78,20 @@ class PayChargeCards {
|
||||
// 标题右侧按钮是页面头部的一部分,只在此处输出一次(列表页同样如此,由 render_list 负责列表本体)。
|
||||
switch ($tab) {
|
||||
case 'add':
|
||||
$page_title = __('添加卡密', 'aksrt');
|
||||
$head_but = '<a href="' . esc_url($this->page_url) . '" class="page-title-action">' . esc_html__('返回列表', 'aksrt') . '</a>';
|
||||
$page_title = __('添加卡密', 'jio');
|
||||
$head_but = '<a href="' . esc_url($this->page_url) . '" class="page-title-action">' . esc_html__('返回列表', 'jio') . '</a>';
|
||||
break;
|
||||
|
||||
case 'export':
|
||||
$page_title = __('导出卡密', 'aksrt');
|
||||
$head_but = '<a href="' . esc_url($this->page_url) . '" class="page-title-action">' . esc_html__('返回列表', 'aksrt') . '</a>';
|
||||
$page_title = __('导出卡密', 'jio');
|
||||
$head_but = '<a href="' . esc_url($this->page_url) . '" class="page-title-action">' . esc_html__('返回列表', 'jio') . '</a>';
|
||||
break;
|
||||
|
||||
default:
|
||||
$head_but = '<a href="' . esc_url(add_query_arg('tab', 'add', $this->page_url)) . '" class="page-title-action">' . esc_html__('添加卡密', 'aksrt') . '</a>';
|
||||
$head_but .= '<a href="' . esc_url(add_query_arg('tab', 'export', $this->page_url)) . '" class="page-title-action">' . esc_html__('导出卡密', 'aksrt') . '</a>';
|
||||
$head_but = '<a href="' . esc_url(add_query_arg('tab', 'add', $this->page_url)) . '" class="page-title-action">' . esc_html__('添加卡密', 'jio') . '</a>';
|
||||
$head_but .= '<a href="' . esc_url(add_query_arg('tab', 'export', $this->page_url)) . '" class="page-title-action">' . esc_html__('导出卡密', 'jio') . '</a>';
|
||||
if ($s !== '') {
|
||||
$head_but .= '<div><div class="update-nag" style="margin: 10px 0 0;">' . sprintf(esc_html__('搜索 “%s” 的内容', 'aksrt'), esc_html($s)) . ' </div></div>';
|
||||
$head_but .= '<div><div class="update-nag" style="margin: 10px 0 0;">' . sprintf(esc_html__('搜索 “%s” 的内容', 'jio'), esc_html($s)) . ' </div></div>';
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -118,7 +118,7 @@ class PayChargeCards {
|
||||
*/
|
||||
public function handle_export(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('暂无此权限', 'aksrt'));
|
||||
wp_die(esc_html__('暂无此权限', 'jio'));
|
||||
}
|
||||
|
||||
@set_time_limit(0);
|
||||
@@ -133,9 +133,9 @@ class PayChargeCards {
|
||||
$status = $status_raw === '0' ? 'active' : $status_raw;
|
||||
}
|
||||
|
||||
$rows = \AKSRT\Payment\ChargeCard::get_instance()->get_export_rows($type, $status);
|
||||
$rows = \Jio\Payment\ChargeCard::get_instance()->get_export_rows($type, $status);
|
||||
if (!$rows) {
|
||||
wp_die(esc_html__('暂无可导出的内容', 'aksrt'));
|
||||
wp_die(esc_html__('暂无可导出的内容', 'jio'));
|
||||
}
|
||||
|
||||
$filename = $type . '_' . gmdate('d_m_Y');
|
||||
@@ -150,24 +150,24 @@ class PayChargeCards {
|
||||
$meta = is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
$status_text = $row['status'] === 'used'
|
||||
? __('已使用', 'aksrt')
|
||||
: ($row['status'] === 'expired' ? __('已过期', 'aksrt') : ($row['status'] === 'disabled' ? __('已禁用', 'aksrt') : __('未使用', 'aksrt')));
|
||||
? __('已使用', 'jio')
|
||||
: ($row['status'] === 'expired' ? __('已过期', 'jio') : ($row['status'] === 'disabled' ? __('已禁用', 'jio') : __('未使用', 'jio')));
|
||||
|
||||
switch ($type) {
|
||||
case 'credits': // 积分兑换
|
||||
$title = [__('卡号', 'aksrt'), __('密码', 'aksrt'), __('面额', 'aksrt'), __('备注', 'aksrt'), __('状态', 'aksrt')];
|
||||
$title = [__('卡号', 'jio'), __('密码', 'jio'), __('面额', 'jio'), __('备注', 'jio'), __('状态', 'jio')];
|
||||
$data[] = [(string) $row['card'], (string) $row['password'], (string) (int) ($meta['points'] ?? 0), (string) $row['note'], $status_text];
|
||||
break;
|
||||
case 'vip': // 会员兑换
|
||||
$title = [__('卡号', 'aksrt'), __('密码', 'aksrt'), __('兑换会员', 'aksrt'), __('备注', 'aksrt'), __('状态', 'aksrt')];
|
||||
$title = [__('卡号', 'jio'), __('密码', 'jio'), __('兑换会员', 'jio'), __('备注', 'jio'), __('状态', 'jio')];
|
||||
$data[] = [(string) $row['card'], (string) $row['password'], $this->vip_meta_query($meta), (string) $row['note'], $status_text];
|
||||
break;
|
||||
case 'custom': // 自定义
|
||||
$title = [__('卡号', 'aksrt'), __('密码', 'aksrt'), __('备注', 'aksrt'), __('状态', 'aksrt')];
|
||||
$title = [__('卡号', 'jio'), __('密码', 'jio'), __('备注', 'jio'), __('状态', 'jio')];
|
||||
$data[] = [(string) $row['card'], (string) $row['password'], (string) $row['note'], ''];
|
||||
break;
|
||||
default: // 余额充值
|
||||
$title = [__('卡号', 'aksrt'), __('密码', 'aksrt'), __('面额', 'aksrt'), __('备注', 'aksrt'), __('状态', 'aksrt')];
|
||||
$title = [__('卡号', 'jio'), __('密码', 'jio'), __('面额', 'jio'), __('备注', 'jio'), __('状态', 'jio')];
|
||||
$data[] = [(string) $row['card'], (string) $row['password'], $this->balance_amount_text($meta), (string) $row['note'], $status_text];
|
||||
}
|
||||
}
|
||||
@@ -237,7 +237,7 @@ class PayChargeCards {
|
||||
$add_type = isset($_REQUEST['add_type']) ? sanitize_key((string) wp_unslash($_REQUEST['add_type'])) : 'auto';
|
||||
$pass_type = isset($_REQUEST['type']) ? sanitize_key((string) wp_unslash($_REQUEST['type'])) : '';
|
||||
if (!in_array($pass_type, ['balance', 'vip', 'credits', 'custom'], true)) {
|
||||
$this->page_notice(__('错误!', 'aksrt'), __('参数传入错误', 'aksrt'), 'error');
|
||||
$this->page_notice(__('错误!', 'jio'), __('参数传入错误', 'jio'), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ class PayChargeCards {
|
||||
// 一并落在区间外,因此用一个区间判定即可覆盖全部非法输入,不另设分支。
|
||||
$auto_num = isset($_REQUEST['auto_num']) ? (int) $_REQUEST['auto_num'] : 0;
|
||||
if ($auto_num < 1 || $auto_num > 1000) {
|
||||
$this->page_notice(__('错误!', 'aksrt'), __('生成数量需为 1 - 1000 的整数', 'aksrt'), 'error');
|
||||
$this->page_notice(__('错误!', 'jio'), __('生成数量需为 1 - 1000 的整数', 'jio'), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ class PayChargeCards {
|
||||
// 阻止表单提交),因此下限由服务端兜底。
|
||||
$auto_price = max(0.0, $auto_price);
|
||||
if (!$auto_price) {
|
||||
$this->page_notice(__('错误!', 'aksrt'), __('请输入卡密的面额', 'aksrt'), 'error');
|
||||
$this->page_notice(__('错误!', 'jio'), __('请输入卡密的面额', 'jio'), 'error');
|
||||
return;
|
||||
}
|
||||
$pass_meta = $pass_type === 'credits'
|
||||
@@ -273,7 +273,7 @@ class PayChargeCards {
|
||||
case 'vip':
|
||||
$vip = isset($_REQUEST['vip_exchange']) && is_array($_REQUEST['vip_exchange']) ? wp_unslash($_REQUEST['vip_exchange']) : [];
|
||||
if (empty($vip['time'])) {
|
||||
$this->page_notice(__('错误!', 'aksrt'), __('请填写兑换会员参数', 'aksrt'), 'error');
|
||||
$this->page_notice(__('错误!', 'jio'), __('请填写兑换会员参数', 'jio'), 'error');
|
||||
return;
|
||||
}
|
||||
$pass_meta = $this->vip_meta($vip);
|
||||
@@ -298,15 +298,15 @@ class PayChargeCards {
|
||||
|
||||
$remarks = isset($_REQUEST['auto_remarks']) ? sanitize_text_field((string) wp_unslash($_REQUEST['auto_remarks'])) : '';
|
||||
|
||||
$created = \AKSRT\Payment\ChargeCard::get_instance()->generate_cards($pass_type, $auto_num, $pass_meta, $rand_number, $rand_password, $remarks);
|
||||
$created = \Jio\Payment\ChargeCard::get_instance()->generate_cards($pass_type, $auto_num, $pass_meta, $rand_number, $rand_password, $remarks);
|
||||
|
||||
$this->page_notice(__('完成!', 'aksrt'), sprintf(__('已自动生成%s个卡密', 'aksrt'), (string) $created));
|
||||
$this->page_notice(__('完成!', 'jio'), sprintf(__('已自动生成%s个卡密', 'jio'), (string) $created));
|
||||
}
|
||||
|
||||
private function handle_import(string $pass_type): void {
|
||||
$import_data = isset($_REQUEST['import_data']) ? (string) wp_unslash($_REQUEST['import_data']) : '';
|
||||
if (!$import_data) {
|
||||
$this->page_notice(__('错误!', 'aksrt'), __('请粘贴您需要导入的数据', 'aksrt'), 'error');
|
||||
$this->page_notice(__('错误!', 'jio'), __('请粘贴您需要导入的数据', 'jio'), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ class PayChargeCards {
|
||||
|
||||
$success_i = 0;
|
||||
$error_i = 0;
|
||||
$svc = \AKSRT\Payment\ChargeCard::get_instance();
|
||||
$svc = \Jio\Payment\ChargeCard::get_instance();
|
||||
|
||||
foreach ($import_data_array as $line) {
|
||||
$v = explode($import_division, $line);
|
||||
@@ -395,11 +395,11 @@ class PayChargeCards {
|
||||
|
||||
if ($success_i) {
|
||||
$this->page_notice(
|
||||
__('导入完成', 'aksrt'),
|
||||
sprintf(__('成功导入%s个卡密%s', 'aksrt'), (string) $success_i, $error_i ? sprintf(__(',%s个导入失败', 'aksrt'), (string) $error_i) : '')
|
||||
__('导入完成', 'jio'),
|
||||
sprintf(__('成功导入%s个卡密%s', 'jio'), (string) $success_i, $error_i ? sprintf(__(',%s个导入失败', 'jio'), (string) $error_i) : '')
|
||||
);
|
||||
} else {
|
||||
$this->page_notice(__('导入失败', 'aksrt'), sprintf(__('%s个导入失败,数据格式错误', 'aksrt'), (string) $error_i), 'error');
|
||||
$this->page_notice(__('导入失败', 'jio'), sprintf(__('%s个导入失败,数据格式错误', 'jio'), (string) $error_i), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,13 +409,13 @@ class PayChargeCards {
|
||||
$delete_ids = isset($_REQUEST['action_id']) ? array_map('absint', (array) wp_unslash($_REQUEST['action_id'])) : [];
|
||||
$delete_ids = array_filter($delete_ids);
|
||||
if (!$delete_ids) {
|
||||
$this->page_notice(__('错误!', 'aksrt'), __('未选择需要删除的内容', 'aksrt'), 'error');
|
||||
$this->page_notice(__('错误!', 'jio'), __('未选择需要删除的内容', 'jio'), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
$delete_i = \AKSRT\Payment\ChargeCard::get_instance()->delete_by_ids($delete_ids);
|
||||
$delete_i = \Jio\Payment\ChargeCard::get_instance()->delete_by_ids($delete_ids);
|
||||
|
||||
$this->page_notice(__('删除完成', 'aksrt'), sprintf(__('已删除%s个卡密', 'aksrt'), (string) $delete_i));
|
||||
$this->page_notice(__('删除完成', 'jio'), sprintf(__('已删除%s个卡密', 'jio'), (string) $delete_i));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
@@ -429,20 +429,20 @@ class PayChargeCards {
|
||||
*/
|
||||
private function type_labels(): array {
|
||||
return [
|
||||
'balance' => __('余额充值', 'aksrt'),
|
||||
'vip' => __('会员兑换', 'aksrt'),
|
||||
'credits' => __('积分兑换', 'aksrt'),
|
||||
'custom' => __('自定义卡密', 'aksrt'),
|
||||
'balance' => __('余额充值', 'jio'),
|
||||
'vip' => __('会员兑换', 'jio'),
|
||||
'credits' => __('积分兑换', 'jio'),
|
||||
'custom' => __('自定义卡密', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
private function vip_level_name(int $level): string {
|
||||
$s = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
|
||||
$s = class_exists('\Jio\Settings\Settings') ? \Jio\Settings\Settings::get_all() : [];
|
||||
$name = trim((string) ($s['pay_user_vip_' . max(1, min(2, $level)) . '_name'] ?? ''));
|
||||
if ($name !== '') {
|
||||
return $name;
|
||||
}
|
||||
return $level === 2 ? __('二级会员', 'aksrt') : __('一级会员', 'aksrt');
|
||||
return $level === 2 ? __('二级会员', 'jio') : __('一级会员', 'jio');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -454,13 +454,13 @@ class PayChargeCards {
|
||||
$time = array_key_exists('time', $meta) ? (string) $meta['time'] : null;
|
||||
if ($time !== null && $time !== '') {
|
||||
if ($time === 'Permanent') {
|
||||
return __('永久', 'aksrt');
|
||||
return __('永久', 'jio');
|
||||
}
|
||||
$unit = (isset($meta['unit']) && $meta['unit'] === 'month') ? __('个月', 'aksrt') : __('天', 'aksrt');
|
||||
$unit = (isset($meta['unit']) && $meta['unit'] === 'month') ? __('个月', 'jio') : __('天', 'jio');
|
||||
return (string) ((int) $time) . $unit;
|
||||
}
|
||||
$days = (int) ($meta['days'] ?? 0);
|
||||
return $days > 0 ? $days . __('天', 'aksrt') : __('永久', 'aksrt');
|
||||
return $days > 0 ? $days . __('天', 'jio') : __('永久', 'jio');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -524,13 +524,13 @@ class PayChargeCards {
|
||||
}
|
||||
|
||||
private function currency_unit(): string {
|
||||
$s = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
|
||||
return trim((string) ($s['pay_currency_unit'] ?? '')) !== '' ? trim((string) $s['pay_currency_unit']) : __('元', 'aksrt');
|
||||
$s = class_exists('\Jio\Settings\Settings') ? \Jio\Settings\Settings::get_all() : [];
|
||||
return trim((string) ($s['pay_currency_unit'] ?? '')) !== '' ? trim((string) $s['pay_currency_unit']) : __('元', 'jio');
|
||||
}
|
||||
|
||||
private function ensure_cap(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*
|
||||
* PayChargeCards 页面专属内联样式与脚本:条件依赖显隐 + 点击复制。
|
||||
* 这些是卡密管理页特有的表现(排序箭头基线锚定、按钮组、导入提示块、复制提示),
|
||||
@@ -9,7 +9,7 @@
|
||||
* 作为 flat sibling trait 被 PayChargeCards 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -32,23 +32,23 @@ trait PayChargeCardsAssets {
|
||||
.orderby-but > a + a { bottom: .075em; }
|
||||
.orderby-but:hover a { opacity: .6; }
|
||||
.orderby-but > a:hover { opacity: 1; }
|
||||
.aksrt-cardpass-bulk { margin: 0 0 12px; }
|
||||
.jio-cardpass-bulk { margin: 0 0 12px; }
|
||||
/* 卡密表列多(≥10 列),窄视口下须保持最小宽度并由表格卡片横向滚动,
|
||||
而非让各列挤扁。复刻原先列表表格的内联 style="min-width: 1000px;"。 */
|
||||
.aksrt-cardpass-table { min-width: 1000px; }
|
||||
.aksrt-btnset { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||
.aksrt-btnset-item { display: inline-block; cursor: pointer; }
|
||||
.aksrt-btnset-item input { position: absolute; opacity: 0; pointer-events: none; }
|
||||
.aksrt-btnset-item span { display: inline-block; padding: 3px 12px; border: 1px solid #c3c4c7; border-radius: 3px; background: #fff; }
|
||||
.aksrt-btnset-item input:checked + span { background: #2271b1; border-color: #2271b1; color: #fff; }
|
||||
.jio-cardpass-table { min-width: 1000px; }
|
||||
.jio-btnset { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||
.jio-btnset-item { display: inline-block; cursor: pointer; }
|
||||
.jio-btnset-item input { position: absolute; opacity: 0; pointer-events: none; }
|
||||
.jio-btnset-item span { display: inline-block; padding: 3px 12px; border: 1px solid #c3c4c7; border-radius: 3px; background: #fff; }
|
||||
.jio-btnset-item input:checked + span { background: #2271b1; border-color: #2271b1; color: #fff; }
|
||||
/* 导入提示:复刻后台 notice-warning 的外观,但**不用** .notice 类 —— 见 submessage_row()
|
||||
的说明,带 .notice 会被 wp-admin/js/common.js 搬到页面顶部。改这里请连带改那里。 */
|
||||
.aksrt-submsg { margin: 6px 0; padding: 6px 12px; background: #fef8ee; border-left: 4px solid #f0b849; }
|
||||
.jio-submsg { margin: 6px 0; padding: 6px 12px; background: #fef8ee; border-left: 4px solid #f0b849; }
|
||||
/* 提示内的条目列表必须有 <ul> 承载:list-style-position 默认 outside,
|
||||
圆点落在内容盒左侧的 padding 区里;若 <li> 没有列表容器提供缩进,
|
||||
圆点就会溢出到提示块的文字边界之外。这里显式给出缩进,避免依赖 UA/后台的 ul 默认值。 */
|
||||
.aksrt-submsg-list { margin: 6px 0; padding-left: 20px; list-style: disc; }
|
||||
.aksrt-copy-tip { margin-left: 6px; color: #00a32a; font-size: 12px; white-space: nowrap; }
|
||||
.jio-submsg-list { margin: 6px 0; padding-left: 20px; list-style: disc; }
|
||||
.jio-copy-tip { margin-left: 6px; color: #00a32a; font-size: 12px; white-space: nowrap; }
|
||||
</style>
|
||||
<script>
|
||||
(function () {
|
||||
@@ -56,7 +56,7 @@ trait PayChargeCardsAssets {
|
||||
// 含方括号的字段名(如 vip_exchange[time])会让选择器抛异常并被 catch 吞掉,
|
||||
// 从而把依赖行永久留在初始状态。
|
||||
function fieldsByName(key) {
|
||||
var all = document.querySelectorAll('.aksrt-cardpass-form [name]');
|
||||
var all = document.querySelectorAll('.jio-cardpass-form [name]');
|
||||
var out = [];
|
||||
for (var i = 0; i < all.length; i++) {
|
||||
if (all[i].getAttribute('name') === key) out.push(all[i]);
|
||||
@@ -77,7 +77,7 @@ trait PayChargeCardsAssets {
|
||||
}
|
||||
function evalDeps() {
|
||||
// 全文档查找 data-dep 节点:行与提示块各自带状态、各自落 display。
|
||||
// 刻意不做限域(不写 .aksrt-cardpass-form 前缀):提示块一旦被 HTML 解析
|
||||
// 刻意不做限域(不写 .jio-cardpass-form 前缀):提示块一旦被 HTML 解析
|
||||
// 移出表格甚至移出表单子树,限域查找会让它停在初始隐藏状态,表现为「提示不见了」。
|
||||
document.querySelectorAll('[data-dep]').forEach(function (el) {
|
||||
try {
|
||||
@@ -115,9 +115,9 @@ trait PayChargeCardsAssets {
|
||||
if (!text) return;
|
||||
var done = function () {
|
||||
var tip = document.createElement('span');
|
||||
tip.className = 'aksrt-copy-tip';
|
||||
tip.className = 'jio-copy-tip';
|
||||
var tag = el.getAttribute('data-clipboard-tag') || '';
|
||||
tip.textContent = (tag ? tag + ' ' : '') + '<?php echo esc_js(__('已复制', 'aksrt')); ?>';
|
||||
tip.textContent = (tag ? tag + ' ' : '') + '<?php echo esc_js(__('已复制', 'jio')); ?>';
|
||||
el.appendChild(tip);
|
||||
setTimeout(function () { tip.remove(); }, 1200);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*
|
||||
* PayChargeCards 的「添加卡密 / 导出卡密」表单渲染与依赖显隐求值。
|
||||
* 表单结构、字段依赖(field_row / submessage_row / deps_visible)、按钮组与数字输入
|
||||
@@ -9,7 +9,7 @@
|
||||
* 作为 flat sibling trait 被 PayChargeCards 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -18,55 +18,55 @@ trait PayChargeCardsForms {
|
||||
private function render_add_form(): void {
|
||||
$type_labels = $this->type_labels();
|
||||
?>
|
||||
<p><b><?php esc_html_e('在此添加卡密', 'aksrt'); ?></b></p>
|
||||
<p><b><?php esc_html_e('在此添加卡密', 'jio'); ?></b></p>
|
||||
<ul style="list-style:disc;margin:0 0 12px 18px;line-height:1.9">
|
||||
<li><?php esc_html_e('如果您已经准备好了卡密资料,请选择导入的方式添加', 'aksrt'); ?></li>
|
||||
<li><?php esc_html_e('您也可以采用系统生成的方式,自动批量添加卡密', 'aksrt'); ?></li>
|
||||
<li><?php esc_html_e('如果您已经准备好了卡密资料,请选择导入的方式添加', 'jio'); ?></li>
|
||||
<li><?php esc_html_e('您也可以采用系统生成的方式,自动批量添加卡密', 'jio'); ?></li>
|
||||
</ul>
|
||||
<form class="aksrt-cardpass-form" method="post">
|
||||
<form class="jio-cardpass-form" method="post">
|
||||
<?php wp_nonce_field(self::NONCE); ?>
|
||||
<input type="hidden" name="action" value="add" />
|
||||
<table class="form-table" role="presentation">
|
||||
<?php
|
||||
$this->field_row(
|
||||
__('卡密类型', 'aksrt'),
|
||||
__('卡密类型', 'jio'),
|
||||
$this->button_set('type', $type_labels, 'balance'),
|
||||
[]
|
||||
);
|
||||
$this->field_row(
|
||||
__('添加方式', 'aksrt'),
|
||||
__('添加方式', 'jio'),
|
||||
$this->button_set('add_type', [
|
||||
'auto' => __('系统自动生成', 'aksrt'),
|
||||
'import' => __('导入卡密', 'aksrt'),
|
||||
'auto' => __('系统自动生成', 'jio'),
|
||||
'import' => __('导入卡密', 'jio'),
|
||||
], 'auto'),
|
||||
[]
|
||||
);
|
||||
|
||||
// 自动生成
|
||||
$this->field_row(
|
||||
__('生成数量', 'aksrt'),
|
||||
$this->spinner('auto_num', 20, __('张', 'aksrt')),
|
||||
__('生成数量', 'jio'),
|
||||
$this->spinner('auto_num', 20, __('张', 'jio')),
|
||||
[['key' => 'add_type', 'op' => '==', 'val' => 'auto']],
|
||||
__('需要生成多少张卡密(1 - 1000 的整数,单次生成数量太多可能会对服务器性能造成影响)', 'aksrt')
|
||||
__('需要生成多少张卡密(1 - 1000 的整数,单次生成数量太多可能会对服务器性能造成影响)', 'jio')
|
||||
);
|
||||
$this->field_row(
|
||||
__('标识', 'aksrt'),
|
||||
__('标识', 'jio'),
|
||||
'<input type="text" class="regular-text" name="auto_remarks" value="' . esc_attr('cardpass_' . current_time('YmdHis')) . '" />',
|
||||
[['key' => 'add_type', 'op' => '==', 'val' => 'auto']],
|
||||
__('对生成的卡密做标记标识,方便后期查找管理', 'aksrt') . '<br>' . sprintf(esc_html__('注意:标识中不能有%1$s字符,系统已占用', 'aksrt'), '<code>shipped</code>')
|
||||
__('对生成的卡密做标记标识,方便后期查找管理', 'jio') . '<br>' . sprintf(esc_html__('注意:标识中不能有%1$s字符,系统已占用', 'jio'), '<code>shipped</code>')
|
||||
);
|
||||
$this->field_row(
|
||||
__('面额', 'aksrt'),
|
||||
__('面额', 'jio'),
|
||||
'<input type="number" step="any" name="auto_price" value="0" />',
|
||||
[
|
||||
['key' => 'add_type', 'op' => '==', 'val' => 'auto'],
|
||||
['key' => 'type', 'op' => 'any', 'val' => 'balance,credits'],
|
||||
],
|
||||
__('单张卡密的面额', 'aksrt')
|
||||
__('单张卡密的面额', 'jio')
|
||||
);
|
||||
$this->field_row(
|
||||
__('兑换会员', 'aksrt'),
|
||||
__('会员等级', 'aksrt') . ' ' . $this->button_set('vip_exchange[level]', [
|
||||
__('兑换会员', 'jio'),
|
||||
__('会员等级', 'jio') . ' ' . $this->button_set('vip_exchange[level]', [
|
||||
'1' => $this->vip_level_name(1),
|
||||
'2' => $this->vip_level_name(2),
|
||||
], '1'),
|
||||
@@ -77,18 +77,18 @@ trait PayChargeCardsForms {
|
||||
);
|
||||
$this->field_row(
|
||||
' ',
|
||||
__('兑换会员时长', 'aksrt') . '<br><input type="text" name="vip_exchange[time]" value="" />',
|
||||
__('兑换会员时长', 'jio') . '<br><input type="text" name="vip_exchange[time]" value="" />',
|
||||
[
|
||||
['key' => 'add_type', 'op' => '==', 'val' => 'auto'],
|
||||
['key' => 'type', 'op' => '==', 'val' => 'vip'],
|
||||
],
|
||||
__('填', 'aksrt') . '<code>Permanent</code>' . __('为永久', 'aksrt')
|
||||
__('填', 'jio') . '<code>Permanent</code>' . __('为永久', 'jio')
|
||||
);
|
||||
$this->field_row(
|
||||
' ',
|
||||
__('时长单位', 'aksrt') . ' ' . $this->button_set('vip_exchange[unit]', [
|
||||
'day' => __('天', 'aksrt'),
|
||||
'month' => __('个月', 'aksrt'),
|
||||
__('时长单位', 'jio') . ' ' . $this->button_set('vip_exchange[unit]', [
|
||||
'day' => __('天', 'jio'),
|
||||
'month' => __('个月', 'jio'),
|
||||
], 'month'),
|
||||
[
|
||||
['key' => 'add_type', 'op' => '==', 'val' => 'auto'],
|
||||
@@ -97,41 +97,41 @@ trait PayChargeCardsForms {
|
||||
]
|
||||
);
|
||||
$this->field_row(
|
||||
__('自定义显示名称', 'aksrt'),
|
||||
__('卡号显示名称', 'aksrt') . '<br><input type="text" class="regular-text" name="custom_name[card_name]" value="" /><br>' .
|
||||
__('卡密显示名称', 'aksrt') . '<br><input type="text" class="regular-text" name="custom_name[password_name]" value="" />',
|
||||
__('自定义显示名称', 'jio'),
|
||||
__('卡号显示名称', 'jio') . '<br><input type="text" class="regular-text" name="custom_name[card_name]" value="" /><br>' .
|
||||
__('卡密显示名称', 'jio') . '<br><input type="text" class="regular-text" name="custom_name[password_name]" value="" />',
|
||||
[
|
||||
['key' => 'add_type', 'op' => '==', 'val' => 'auto'],
|
||||
['key' => 'type', 'op' => '==', 'val' => 'custom'],
|
||||
]
|
||||
);
|
||||
$this->field_row(
|
||||
__('高级选项', 'aksrt'),
|
||||
__('高级选项', 'jio'),
|
||||
'<input type="checkbox" name="auto_top_s" value="1" />',
|
||||
[['key' => 'add_type', 'op' => '==', 'val' => 'auto']]
|
||||
);
|
||||
$this->field_row(
|
||||
__('自定义卡号位数', 'aksrt'),
|
||||
$this->spinner('auto_rand_number_limit', 20, __('位数', 'aksrt')),
|
||||
__('自定义卡号位数', 'jio'),
|
||||
$this->spinner('auto_rand_number_limit', 20, __('位数', 'jio')),
|
||||
[
|
||||
['key' => 'add_type', 'op' => '==', 'val' => 'auto'],
|
||||
['key' => 'auto_top_s', 'op' => 'checked'],
|
||||
]
|
||||
);
|
||||
$this->field_row(
|
||||
__('自定义密码位数', 'aksrt'),
|
||||
$this->spinner('auto_rand_password_limit', 35, __('位数', 'aksrt')),
|
||||
__('自定义密码位数', 'jio'),
|
||||
$this->spinner('auto_rand_password_limit', 35, __('位数', 'jio')),
|
||||
[
|
||||
['key' => 'add_type', 'op' => '==', 'val' => 'auto'],
|
||||
['key' => 'auto_top_s', 'op' => 'checked'],
|
||||
],
|
||||
__('自定义自动生成的长度(不能太短,太短可能会出现重复)如果启用了单密码模式,可以将卡号位数设置为0', 'aksrt')
|
||||
__('自定义自动生成的长度(不能太短,太短可能会出现重复)如果启用了单密码模式,可以将卡号位数设置为0', 'jio')
|
||||
);
|
||||
|
||||
// 导入提示统一放在「卡密数据」正上方:切换卡密类型/添加方式时,
|
||||
// 命中的那一条紧邻示例数据与输入框,便于对照填写。
|
||||
$this->field_row(
|
||||
__('单密码模式', 'aksrt'),
|
||||
__('单密码模式', 'jio'),
|
||||
'<input type="checkbox" name="only_pass_s" value="1" />',
|
||||
[
|
||||
['key' => 'add_type', 'op' => '==', 'val' => 'import'],
|
||||
@@ -143,32 +143,32 @@ trait PayChargeCardsForms {
|
||||
['key' => 'add_type', 'op' => '!=', 'val' => 'auto'],
|
||||
['key' => 'type', 'op' => 'any', 'val' => 'balance,credits'],
|
||||
],
|
||||
'<p><b>' . esc_html__('导入卡密', 'aksrt') . '</b></p>
|
||||
<ul class="aksrt-submsg-list">
|
||||
<li>' . esc_html__('一行一个卡密,单行格式为:', 'aksrt') . '<code>' . esc_html__('卡号 密码 面额 标识', 'aksrt') . '</code></li>
|
||||
<li>' . esc_html__('卡号、密码、面额、标识默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'aksrt') . '</li>
|
||||
<li>' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'aksrt') . '</li><li>' . esc_html__('如果使用的是单密码模式,卡号部分随便填什么都行,但不能留空', 'aksrt') . '</li>
|
||||
'<p><b>' . esc_html__('导入卡密', 'jio') . '</b></p>
|
||||
<ul class="jio-submsg-list">
|
||||
<li>' . esc_html__('一行一个卡密,单行格式为:', 'jio') . '<code>' . esc_html__('卡号 密码 面额 标识', 'jio') . '</code></li>
|
||||
<li>' . esc_html__('卡号、密码、面额、标识默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'jio') . '</li>
|
||||
<li>' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'jio') . '</li><li>' . esc_html__('如果使用的是单密码模式,卡号部分随便填什么都行,但不能留空', 'jio') . '</li>
|
||||
</ul>
|
||||
<p><b>' . esc_html__('数据示例', 'aksrt') . '</b></p>
|
||||
<div>4849977843617 3w20V4Qe7dRhAIoPYum6KSA 50 ' . sprintf(esc_html__('%1$s余额', 'aksrt'), esc_html('50' . $this->currency_unit())) . '</div>
|
||||
<div>6484027668705 SkdJZ5vWcpqPxrbs3pqPcpq 100 ' . sprintf(esc_html__('%1$s余额', 'aksrt'), esc_html('100' . $this->currency_unit())) . '</div>'
|
||||
<p><b>' . esc_html__('数据示例', 'jio') . '</b></p>
|
||||
<div>4849977843617 3w20V4Qe7dRhAIoPYum6KSA 50 ' . sprintf(esc_html__('%1$s余额', 'jio'), esc_html('50' . $this->currency_unit())) . '</div>
|
||||
<div>6484027668705 SkdJZ5vWcpqPxrbs3pqPcpq 100 ' . sprintf(esc_html__('%1$s余额', 'jio'), esc_html('100' . $this->currency_unit())) . '</div>'
|
||||
);
|
||||
$this->submessage_row(
|
||||
[
|
||||
['key' => 'add_type', 'op' => '!=', 'val' => 'auto'],
|
||||
['key' => 'type', 'op' => '==', 'val' => 'vip'],
|
||||
],
|
||||
'<p><b>' . esc_html__('导入会员兑换卡密', 'aksrt') . '</b></p>
|
||||
<ul class="aksrt-submsg-list">
|
||||
<li>' . esc_html__('一行一个卡密,单行格式为:', 'aksrt') . '<code>' . esc_html__('卡号 密码 会员参数 标识', 'aksrt') . '</code></li>
|
||||
<li>' . esc_html__('卡号、密码、面额、标识默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'aksrt') . '</li>
|
||||
<li>' . esc_html__('会员参数格式为:', 'aksrt') . '<code>level=1&time=5&unit=day</code></li>
|
||||
<li>' . esc_html__('参数说明:level为等级,只能为1或2。time为时长,填Permanent或数字。unit为时长单位,只能填day(天)或者month(月)', 'aksrt') . '</li>
|
||||
<li>' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'aksrt') . '</li><li>' . esc_html__('如果使用的是单密码模式,卡号部分随便填什么都行,但不能留空', 'aksrt') . '</li>
|
||||
'<p><b>' . esc_html__('导入会员兑换卡密', 'jio') . '</b></p>
|
||||
<ul class="jio-submsg-list">
|
||||
<li>' . esc_html__('一行一个卡密,单行格式为:', 'jio') . '<code>' . esc_html__('卡号 密码 会员参数 标识', 'jio') . '</code></li>
|
||||
<li>' . esc_html__('卡号、密码、面额、标识默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'jio') . '</li>
|
||||
<li>' . esc_html__('会员参数格式为:', 'jio') . '<code>level=1&time=5&unit=day</code></li>
|
||||
<li>' . esc_html__('参数说明:level为等级,只能为1或2。time为时长,填Permanent或数字。unit为时长单位,只能填day(天)或者month(月)', 'jio') . '</li>
|
||||
<li>' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'jio') . '</li><li>' . esc_html__('如果使用的是单密码模式,卡号部分随便填什么都行,但不能留空', 'jio') . '</li>
|
||||
</ul>
|
||||
<p><b>' . esc_html__('数据示例', 'aksrt') . '</b></p>
|
||||
<div>4849977843617 3w20V4Qe7dRhAIoPYum6KSA level=1&time=5&unit=day ' . esc_html__('5天一级会员', 'aksrt') . '</div>
|
||||
<div>6484027668705 SkdJZ5vWcpqPxrbs3pqPcpq level=2&time=Permanent ' . esc_html__('永久2级会员', 'aksrt') . '</div>
|
||||
<p><b>' . esc_html__('数据示例', 'jio') . '</b></p>
|
||||
<div>4849977843617 3w20V4Qe7dRhAIoPYum6KSA level=1&time=5&unit=day ' . esc_html__('5天一级会员', 'jio') . '</div>
|
||||
<div>6484027668705 SkdJZ5vWcpqPxrbs3pqPcpq level=2&time=Permanent ' . esc_html__('永久2级会员', 'jio') . '</div>
|
||||
'
|
||||
);
|
||||
$this->submessage_row(
|
||||
@@ -177,17 +177,17 @@ trait PayChargeCardsForms {
|
||||
['key' => 'type', 'op' => '==', 'val' => 'custom'],
|
||||
['key' => 'only_pass_s', 'op' => 'unchecked'],
|
||||
],
|
||||
'<p><b>' . esc_html__('自定义卡密导入', 'aksrt') . '</b></p>
|
||||
<ul class="aksrt-submsg-list">
|
||||
<li>' . esc_html__('一行一个卡密,单行格式为:', 'aksrt') . '<code>' . esc_html__('卡号 密码 标识 自定义参数', 'aksrt') . '</code></li>
|
||||
<li>' . esc_html__('默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'aksrt') . '</li>
|
||||
<li>' . esc_html__('自定义参数格式为:', 'aksrt') . '<code>card_name=授权号&password_name=授权密码</code>,' . esc_html__('card_name为卡号的显示名称,password_name为卡密的显示名称,可以留空', 'aksrt') . '</li>
|
||||
<li>' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'aksrt') . '</li>
|
||||
'<p><b>' . esc_html__('自定义卡密导入', 'jio') . '</b></p>
|
||||
<ul class="jio-submsg-list">
|
||||
<li>' . esc_html__('一行一个卡密,单行格式为:', 'jio') . '<code>' . esc_html__('卡号 密码 标识 自定义参数', 'jio') . '</code></li>
|
||||
<li>' . esc_html__('默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'jio') . '</li>
|
||||
<li>' . esc_html__('自定义参数格式为:', 'jio') . '<code>card_name=授权号&password_name=授权密码</code>,' . esc_html__('card_name为卡号的显示名称,password_name为卡密的显示名称,可以留空', 'jio') . '</li>
|
||||
<li>' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'jio') . '</li>
|
||||
</ul>
|
||||
<p><b>' . esc_html__('数据示例', 'aksrt') . '</b></p>
|
||||
<div>4849977843617 3w20V4Qe7dRhAIoPYum6KSA ' . esc_html__('平台卡密01', 'aksrt') . ' card_name=' . esc_html__('授权号', 'aksrt') . '&password_name=' . esc_html__('授权码', 'aksrt') . '</div>
|
||||
<div>6484027668705 SkdJZ5vWcpqPxrbs3pqPcpq ' . esc_html__('平台卡密01', 'aksrt') . ' card_name=' . esc_html__('授权号', 'aksrt') . '&password_name=' . esc_html__('授权码', 'aksrt') . '</div>
|
||||
<div>111111@qq.com 11assssddsds ' . esc_html__('某某账号', 'aksrt') . ' card_name=' . esc_html__('账号', 'aksrt') . '&password_name=' . esc_html__('密码', 'aksrt') . '</div>'
|
||||
<p><b>' . esc_html__('数据示例', 'jio') . '</b></p>
|
||||
<div>4849977843617 3w20V4Qe7dRhAIoPYum6KSA ' . esc_html__('平台卡密01', 'jio') . ' card_name=' . esc_html__('授权号', 'jio') . '&password_name=' . esc_html__('授权码', 'jio') . '</div>
|
||||
<div>6484027668705 SkdJZ5vWcpqPxrbs3pqPcpq ' . esc_html__('平台卡密01', 'jio') . ' card_name=' . esc_html__('授权号', 'jio') . '&password_name=' . esc_html__('授权码', 'jio') . '</div>
|
||||
<div>111111@qq.com 11assssddsds ' . esc_html__('某某账号', 'jio') . ' card_name=' . esc_html__('账号', 'jio') . '&password_name=' . esc_html__('密码', 'jio') . '</div>'
|
||||
);
|
||||
$this->submessage_row(
|
||||
[
|
||||
@@ -195,72 +195,72 @@ trait PayChargeCardsForms {
|
||||
['key' => 'type', 'op' => '==', 'val' => 'custom'],
|
||||
['key' => 'only_pass_s', 'op' => 'checked'],
|
||||
],
|
||||
'<p><b>' . esc_html__('自定义卡密导入(单密码模式)', 'aksrt') . '</b></p>
|
||||
<ul class="aksrt-submsg-list">
|
||||
<li>' . esc_html__('一行一个卡密,单行格式为:', 'aksrt') . '<code>' . esc_html__('密码 标识 自定义参数', 'aksrt') . '</code></li>
|
||||
<li>' . esc_html__('默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'aksrt') . '</li>
|
||||
<li>' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'aksrt') . '</li>
|
||||
<li>' . esc_html__('自定义参数格式为:', 'aksrt') . '<code>password_name=授权密码</code>,' . esc_html__('password_name为卡密的显示名称,可以留空', 'aksrt') . '</li>
|
||||
'<p><b>' . esc_html__('自定义卡密导入(单密码模式)', 'jio') . '</b></p>
|
||||
<ul class="jio-submsg-list">
|
||||
<li>' . esc_html__('一行一个卡密,单行格式为:', 'jio') . '<code>' . esc_html__('密码 标识 自定义参数', 'jio') . '</code></li>
|
||||
<li>' . esc_html__('默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'jio') . '</li>
|
||||
<li>' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'jio') . '</li>
|
||||
<li>' . esc_html__('自定义参数格式为:', 'jio') . '<code>password_name=授权密码</code>,' . esc_html__('password_name为卡密的显示名称,可以留空', 'jio') . '</li>
|
||||
</ul>
|
||||
<p><b>' . esc_html__('数据示例', 'aksrt') . '</b></p>
|
||||
<div>3w20V4Qe7dRhAIoPYum6KSA ' . esc_html__('平台卡密01', 'aksrt') . ' password_name=' . esc_html__('授权码', 'aksrt') . '</div>
|
||||
<div>SkdJZ5vWcpqPxrbs3pqPcpq ' . esc_html__('平台卡密01', 'aksrt') . ' password_name=' . esc_html__('授权码', 'aksrt') . '</div>'
|
||||
<p><b>' . esc_html__('数据示例', 'jio') . '</b></p>
|
||||
<div>3w20V4Qe7dRhAIoPYum6KSA ' . esc_html__('平台卡密01', 'jio') . ' password_name=' . esc_html__('授权码', 'jio') . '</div>
|
||||
<div>SkdJZ5vWcpqPxrbs3pqPcpq ' . esc_html__('平台卡密01', 'jio') . ' password_name=' . esc_html__('授权码', 'jio') . '</div>'
|
||||
);
|
||||
$this->field_row(
|
||||
__('卡密数据', 'aksrt'),
|
||||
__('卡密数据', 'jio'),
|
||||
'<textarea name="import_data" rows="10" class="large-text code" style="resize: both;max-width: none;"></textarea>',
|
||||
[['key' => 'add_type', 'op' => '==', 'val' => 'import']]
|
||||
);
|
||||
$this->field_row(
|
||||
__('自定义分隔符号', 'aksrt'),
|
||||
__('自定义分隔符号', 'jio'),
|
||||
'<input type="text" class="mini-input" style="width:80px;" name="import_division" value=" " />',
|
||||
[['key' => 'add_type', 'op' => '==', 'val' => 'import']],
|
||||
__('卡号和密码之间分割符号(默认为空格分割)', 'aksrt')
|
||||
__('卡号和密码之间分割符号(默认为空格分割)', 'jio')
|
||||
);
|
||||
?>
|
||||
</table>
|
||||
<?php submit_button(__('确认提交', 'aksrt'), 'primary', 'submit', true); ?>
|
||||
<?php submit_button(__('确认提交', 'jio'), 'primary', 'submit', true); ?>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function render_export_form(): void {
|
||||
?>
|
||||
<form class="aksrt-cardpass-form" method="post" action="<?php echo esc_url(admin_url('admin-ajax.php')); ?>">
|
||||
<form class="jio-cardpass-form" method="post" action="<?php echo esc_url(admin_url('admin-ajax.php')); ?>">
|
||||
<input type="hidden" name="action" value="<?php echo esc_attr(self::EXPORT_AJAX_ACTION); ?>" />
|
||||
<table class="form-table" role="presentation">
|
||||
<?php
|
||||
$this->field_row(
|
||||
__('卡密类型', 'aksrt'),
|
||||
__('卡密类型', 'jio'),
|
||||
$this->button_set('type', $this->type_labels(), 'balance'),
|
||||
[]
|
||||
);
|
||||
$this->field_row(
|
||||
__('选择状态', 'aksrt'),
|
||||
__('选择状态', 'jio'),
|
||||
$this->button_set('status', [
|
||||
'all' => __('全部', 'aksrt'),
|
||||
'0' => __('未使用', 'aksrt'),
|
||||
'used' => __('已使用', 'aksrt'),
|
||||
'all' => __('全部', 'jio'),
|
||||
'0' => __('未使用', 'jio'),
|
||||
'used' => __('已使用', 'jio'),
|
||||
], 'all'),
|
||||
[['key' => 'type', 'op' => '!=', 'val' => 'custom']]
|
||||
);
|
||||
$this->field_row(
|
||||
__('导出格式', 'aksrt'),
|
||||
__('导出格式', 'jio'),
|
||||
$this->button_set('export_format', [
|
||||
'text' => __('文本文档', 'aksrt'),
|
||||
'xls' => __('Excel表格', 'aksrt'),
|
||||
'text' => __('文本文档', 'jio'),
|
||||
'xls' => __('Excel表格', 'jio'),
|
||||
], 'xls'),
|
||||
[]
|
||||
);
|
||||
$this->field_row(
|
||||
' ',
|
||||
__('分隔符号', 'aksrt') . '<br><input type="text" class="mini-input" style="width:80px;" name="text_division" value=" " />',
|
||||
__('分隔符号', 'jio') . '<br><input type="text" class="mini-input" style="width:80px;" name="text_division" value=" " />',
|
||||
[['key' => 'export_format', 'op' => '==', 'val' => 'text']],
|
||||
__('卡号和密码之间分割符号(默认为空格分割)', 'aksrt')
|
||||
__('卡号和密码之间分割符号(默认为空格分割)', 'jio')
|
||||
);
|
||||
?>
|
||||
</table>
|
||||
<?php submit_button(__('确认提交', 'aksrt'), 'primary', 'submit', true); ?>
|
||||
<?php submit_button(__('确认提交', 'jio'), 'primary', 'submit', true); ?>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
@@ -294,9 +294,9 @@ trait PayChargeCardsForms {
|
||||
// 把页面上所有「非 inline」的 notice 整块搬到 .wp-header-end(不存在时取 .wrap 内首个
|
||||
// h1/h2)之后。提示块一旦带上 notice,就会被搬到页面顶部、脱离「添加方式—卡密数据」
|
||||
// 之间的字段区,而且搬迁发生在求值之后,行内的显隐逻辑无从纠正。
|
||||
// 外观改由 .aksrt-submsg 自带(见 print_assets),与参考主题 CSF content 字段一致。
|
||||
// 外观改由 .jio-submsg 自带(见 print_assets),与参考主题 CSF content 字段一致。
|
||||
// data-dep 与初始隐藏状态同时挂在 <tr> 和内层提示块上:提示块若脱离所在行,仍能自行显隐。
|
||||
echo '<tr' . $dep_attr . $hidden . '><td colspan="2"><div' . $dep_attr . ' class="aksrt-submsg"' . $hidden . '>' . $content . '</div></td></tr>'; // phpcs:ignore WordPress.Security.EscapeOutput -- 文案已转义。
|
||||
echo '<tr' . $dep_attr . $hidden . '><td colspan="2"><div' . $dep_attr . ' class="jio-submsg"' . $hidden . '>' . $content . '</div></td></tr>'; // phpcs:ignore WordPress.Security.EscapeOutput -- 文案已转义。
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -398,9 +398,9 @@ trait PayChargeCardsForms {
|
||||
* @param array<string,string> $options
|
||||
*/
|
||||
private function button_set(string $name, array $options, string $current): string {
|
||||
$html = '<div class="aksrt-btnset">';
|
||||
$html = '<div class="jio-btnset">';
|
||||
foreach ($options as $value => $label) {
|
||||
$html .= '<label class="aksrt-btnset-item"><input type="radio" name="' . esc_attr($name) . '" value="' . esc_attr((string) $value) . '"' . checked((string) $current, (string) $value, false) . '><span>' . esc_html((string) $label) . '</span></label>';
|
||||
$html .= '<label class="jio-btnset-item"><input type="radio" name="' . esc_attr($name) . '" value="' . esc_attr((string) $value) . '"' . checked((string) $current, (string) $value, false) . '><span>' . esc_html((string) $label) . '</span></label>';
|
||||
}
|
||||
return $html . '</div>';
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*
|
||||
* PayChargeCards 列表渲染(过滤 / 搜索 / 排序 / 批量删除 / 分页)与翻页参数拼装。
|
||||
* 作为 flat sibling trait 被 PayChargeCards 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -16,7 +16,7 @@ trait PayChargeCardsList {
|
||||
* @param string $s 搜索词(由页面头部统一解析后传入,用于提示、过滤链接与查询)。
|
||||
*/
|
||||
private function render_list(string $s): void {
|
||||
$svc = \AKSRT\Payment\ChargeCard::get_instance();
|
||||
$svc = \Jio\Payment\ChargeCard::get_instance();
|
||||
|
||||
$status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
|
||||
$type = isset($_GET['type']) ? sanitize_key((string) wp_unslash($_GET['type'])) : '';
|
||||
@@ -39,11 +39,11 @@ trait PayChargeCardsList {
|
||||
|
||||
// 过滤链接(标题右侧按钮与搜索提示由页面头部统一输出)。
|
||||
$sub_but = [
|
||||
['name' => __('全部', 'aksrt'), 'href' => $this->page_url],
|
||||
['name' => __('全部', 'jio'), 'href' => $this->page_url],
|
||||
];
|
||||
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)];
|
||||
$sub_but[] = ['name' => __('未使用', 'jio'), 'href' => add_query_arg('status', '0', $this->page_url)];
|
||||
$sub_but[] = ['name' => __('已使用', 'jio'), '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)];
|
||||
}
|
||||
@@ -56,8 +56,8 @@ trait PayChargeCardsList {
|
||||
|
||||
$search = '<form class="form-inline form-order" method="post">
|
||||
<div class="form-group" style="float: right;">
|
||||
<input type="text" class="form-control" name="s" placeholder="' . esc_attr__('搜索卡密', 'aksrt') . '">
|
||||
<button type="submit" class="button">' . esc_html__('提交', 'aksrt') . '</button>
|
||||
<input type="text" class="form-control" name="s" placeholder="' . esc_attr__('搜索卡密', 'jio') . '">
|
||||
<button type="submit" class="button">' . esc_html__('提交', 'jio') . '</button>
|
||||
</div>
|
||||
</form>';
|
||||
|
||||
@@ -93,8 +93,8 @@ trait PayChargeCardsList {
|
||||
|
||||
if (!$items) {
|
||||
NativeUi::table_card_open();
|
||||
echo '<table class="widefat fixed striped posts table table-bordered aksrt-cardpass-table">';
|
||||
echo '<tbody>' . NativeUi::empty_row(1, esc_html__('未找到对应内容,或暂无内容', 'aksrt')) . '</tbody>';
|
||||
echo '<table class="widefat fixed striped posts table table-bordered jio-cardpass-table">';
|
||||
echo '<tbody>' . NativeUi::empty_row(1, esc_html__('未找到对应内容,或暂无内容', 'jio')) . '</tbody>';
|
||||
echo '</table>';
|
||||
NativeUi::table_wrap_close();
|
||||
NativeUi::card_close();
|
||||
@@ -103,22 +103,22 @@ trait PayChargeCardsList {
|
||||
|
||||
// 表头(与参考主题同列、同排序链接)。
|
||||
$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')],
|
||||
['width' => '6%', 'orderby' => 'card', 'name' => __('卡号', 'jio')],
|
||||
['width' => '10%', 'orderby' => 'password', 'name' => __('密码', 'jio')],
|
||||
['width' => '5%', 'orderby' => 'type', 'name' => __('类型', 'jio')],
|
||||
['width' => '5%', 'orderby' => 'create_time', 'name' => __('创建时间', 'jio')],
|
||||
['width' => '5%', 'orderby' => 'modified_time', 'name' => __('更新时间', 'jio')],
|
||||
['width' => '5%', 'orderby' => 'status', 'name' => __('状态', 'jio')],
|
||||
['width' => '8%', 'orderby' => 'other', 'name' => __('标识', 'jio')],
|
||||
];
|
||||
|
||||
$thead_th = '<td id="cb" class="manage-column column-cb check-column" style="width: 2%;"><label class="screen-reader-text" for="cb-select-all-1">' . esc_html__('全选', 'aksrt') . '</label><input id="cb-select-all-1" type="checkbox"></td>';
|
||||
$thead_th = '<td id="cb" class="manage-column column-cb check-column" style="width: 2%;"><label class="screen-reader-text" for="cb-select-all-1">' . esc_html__('全选', 'jio') . '</label><input id="cb-select-all-1" type="checkbox"></td>';
|
||||
foreach ($theads as $thead) {
|
||||
$orderby_links = '';
|
||||
if ($thead['orderby']) {
|
||||
$orderby_url = add_query_arg('orderby', $thead['orderby']);
|
||||
$orderby_links .= '<a title="' . esc_attr__('降序', 'aksrt') . '" href="' . esc_url(add_query_arg('desc', 'ASC', $orderby_url)) . '"><span class="dashicons dashicons-arrow-up"></span></a>';
|
||||
$orderby_links .= '<a title="' . esc_attr__('升序', 'aksrt') . '" href="' . esc_url(add_query_arg('desc', 'DESC', $orderby_url)) . '"><span class="dashicons dashicons-arrow-down"></span></a>';
|
||||
$orderby_links .= '<a title="' . esc_attr__('降序', 'jio') . '" href="' . esc_url(add_query_arg('desc', 'ASC', $orderby_url)) . '"><span class="dashicons dashicons-arrow-up"></span></a>';
|
||||
$orderby_links .= '<a title="' . esc_attr__('升序', 'jio') . '" href="' . esc_url(add_query_arg('desc', 'DESC', $orderby_url)) . '"><span class="dashicons dashicons-arrow-down"></span></a>';
|
||||
$orderby_links = '<span class="orderby-but">' . $orderby_links . '</span>';
|
||||
}
|
||||
$thead_th .= '<th class="" width="' . esc_attr($thead['width']) . '">' . esc_html($thead['name']) . $orderby_links . '</th>';
|
||||
@@ -135,17 +135,17 @@ trait PayChargeCardsList {
|
||||
// 自定义卡密不展示状态;其余类型按状态着色(类型链接统一在下方链表中生成)。
|
||||
$status_html = '';
|
||||
if ($msg['type'] !== 'custom') {
|
||||
$status_html = '<span style="color: #3d7ffd;">' . esc_html__('未使用', 'aksrt') . '</span>';
|
||||
$status_html = '<span style="color: #3d7ffd;">' . esc_html__('未使用', 'jio') . '</span>';
|
||||
if ($msg['status'] === 'used') {
|
||||
$status_html = '<span style="color: #f93b3b;">' . esc_html__('已使用', 'aksrt') . '</span>';
|
||||
$status_html = '<span style="color: #f93b3b;">' . esc_html__('已使用', 'jio') . '</span>';
|
||||
if (!empty($msg['order_number'])) {
|
||||
$order_link_url = admin_url('admin.php?page=aksrt_orders&s=' . urlencode((string) $msg['order_number']));
|
||||
$status_html .= '<a style="color: #ff6215;" target="_blank" href="' . esc_url($order_link_url) . '"> [' . esc_html__('查看', 'aksrt') . ']</a>';
|
||||
$order_link_url = admin_url('admin.php?page=jio_orders&s=' . urlencode((string) $msg['order_number']));
|
||||
$status_html .= '<a style="color: #ff6215;" target="_blank" href="' . esc_url($order_link_url) . '"> [' . esc_html__('查看', 'jio') . ']</a>';
|
||||
}
|
||||
} elseif ($msg['status'] === 'expired') {
|
||||
$status_html = '<span style="color: #8c8f94;">' . esc_html__('已过期', 'aksrt') . '</span>';
|
||||
$status_html = '<span style="color: #8c8f94;">' . esc_html__('已过期', 'jio') . '</span>';
|
||||
} elseif ($msg['status'] === 'disabled') {
|
||||
$status_html = '<span style="color: #8c8f94;">' . esc_html__('已禁用', 'aksrt') . '</span>';
|
||||
$status_html = '<span style="color: #8c8f94;">' . esc_html__('已禁用', 'jio') . '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,28 +155,28 @@ trait PayChargeCardsList {
|
||||
}
|
||||
|
||||
if ($msg['type'] === 'vip') {
|
||||
$type_html = '<a style="color:#975106" href="' . esc_url(add_query_arg('type', 'vip', $this->page_url)) . '">' . esc_html__('会员兑换', 'aksrt') . '</a>';
|
||||
$type_html = '<a style="color:#975106" href="' . esc_url(add_query_arg('type', 'vip', $this->page_url)) . '">' . esc_html__('会员兑换', 'jio') . '</a>';
|
||||
$level = (int) ($meta['level'] ?? 1);
|
||||
$type_html .= '<br>' . esc_html($this->vip_level_name($level)) . ':' . esc_html($this->vip_time_text($meta));
|
||||
} elseif ($msg['type'] === 'credits') {
|
||||
$type_html = '<a style="color:#289d0f" href="' . esc_url(add_query_arg('type', 'credits', $this->page_url)) . '">' . esc_html__('积分兑换', 'aksrt') . '</a>';
|
||||
$type_html .= '<br>' . esc_html__('积分:', 'aksrt') . esc_html((string) (int) ($meta['points'] ?? 0));
|
||||
$type_html = '<a style="color:#289d0f" href="' . esc_url(add_query_arg('type', 'credits', $this->page_url)) . '">' . esc_html__('积分兑换', 'jio') . '</a>';
|
||||
$type_html .= '<br>' . esc_html__('积分:', 'jio') . esc_html((string) (int) ($meta['points'] ?? 0));
|
||||
} elseif ($msg['type'] === 'balance') {
|
||||
$type_html = '<a style="color:#0c76e6" href="' . esc_url(add_query_arg('type', 'balance', $this->page_url)) . '">' . esc_html__('余额充值', 'aksrt') . '</a>';
|
||||
$type_html .= '<br>' . esc_html__('金额:', 'aksrt') . esc_html($this->balance_amount_text($meta));
|
||||
$type_html = '<a style="color:#0c76e6" href="' . esc_url(add_query_arg('type', 'balance', $this->page_url)) . '">' . esc_html__('余额充值', 'jio') . '</a>';
|
||||
$type_html .= '<br>' . esc_html__('金额:', 'jio') . esc_html($this->balance_amount_text($meta));
|
||||
} else {
|
||||
$type_html = '<a style="color:#a42ade" href="' . esc_url(add_query_arg('type', 'custom', $this->page_url)) . '">' . esc_html__('自定义卡密', 'aksrt') . '</a>';
|
||||
$type_html = '<a style="color:#a42ade" href="' . esc_url(add_query_arg('type', 'custom', $this->page_url)) . '">' . esc_html__('自定义卡密', 'jio') . '</a>';
|
||||
}
|
||||
|
||||
$card_name = (string) ($meta['custom_name']['card_name'] ?? '');
|
||||
$password_name = (string) ($meta['custom_name']['password_name'] ?? '');
|
||||
|
||||
$tbody .= '<tr>';
|
||||
$tbody .= '<th scope="row" class="check-column"><label class="screen-reader-text" for="cb-select-' . (int) $msg['id'] . '">' . esc_html__('选择', 'aksrt') . '</label>
|
||||
$tbody .= '<th scope="row" class="check-column"><label class="screen-reader-text" for="cb-select-' . (int) $msg['id'] . '">' . esc_html__('选择', 'jio') . '</label>
|
||||
<input id="cb-select-' . (int) $msg['id'] . '" type="checkbox" name="action_id[]" value="' . (int) $msg['id'] . '">
|
||||
</th>';
|
||||
$tbody .= "<td><span data-clipboard-text='" . esc_attr((string) $msg['card']) . "' data-clipboard-tag=\"" . esc_attr($card_name !== '' ? $card_name : __('卡号', 'aksrt')) . "\">" . esc_html($card_name) . ' ' . esc_html((string) $msg['card']) . '</span></td>';
|
||||
$tbody .= "<td><span data-clipboard-text='" . esc_attr((string) $msg['password']) . "' data-clipboard-tag=\"" . esc_attr($password_name !== '' ? $password_name : __('密码', 'aksrt')) . "\">" . esc_html($password_name) . ' ' . esc_html((string) $msg['password']) . '</span></td>';
|
||||
$tbody .= "<td><span data-clipboard-text='" . esc_attr((string) $msg['card']) . "' data-clipboard-tag=\"" . esc_attr($card_name !== '' ? $card_name : __('卡号', 'jio')) . "\">" . esc_html($card_name) . ' ' . esc_html((string) $msg['card']) . '</span></td>';
|
||||
$tbody .= "<td><span data-clipboard-text='" . esc_attr((string) $msg['password']) . "' data-clipboard-tag=\"" . esc_attr($password_name !== '' ? $password_name : __('密码', 'jio')) . "\">" . esc_html($password_name) . ' ' . esc_html((string) $msg['password']) . '</span></td>';
|
||||
$tbody .= "<td>$type_html</td>";
|
||||
$tbody .= '<td>' . esc_html((string) $msg['created_at']) . '</td>';
|
||||
$tbody .= '<td>' . esc_html((string) ($msg['used_at'] ?: $msg['created_at'])) . '</td>';
|
||||
@@ -185,18 +185,18 @@ trait PayChargeCardsList {
|
||||
$tbody .= '</tr>';
|
||||
}
|
||||
|
||||
echo '<form class="aksrt-cardpass-list-form" method="post">';
|
||||
echo '<form class="jio-cardpass-list-form" method="post">';
|
||||
wp_nonce_field(self::NONCE);
|
||||
echo '<div class="bulkactions aksrt-native-bulk aksrt-cardpass-bulk">
|
||||
<label for="bulk-action-selector-top" class="screen-reader-text">' . esc_html__('选择批量操作', 'aksrt') . '</label><select name="action" id="bulk-action-selector-top">
|
||||
<option value="-1">' . esc_html__('批量操作', 'aksrt') . '</option>
|
||||
<option value="delete">' . esc_html__('删除', 'aksrt') . '</option>
|
||||
echo '<div class="bulkactions jio-native-bulk jio-cardpass-bulk">
|
||||
<label for="bulk-action-selector-top" class="screen-reader-text">' . esc_html__('选择批量操作', 'jio') . '</label><select name="action" id="bulk-action-selector-top">
|
||||
<option value="-1">' . esc_html__('批量操作', 'jio') . '</option>
|
||||
<option value="delete">' . esc_html__('删除', 'jio') . '</option>
|
||||
</select>
|
||||
<input type="submit" class="button action" value="' . esc_attr__('应用', 'aksrt') . '">
|
||||
<input type="submit" class="button action" value="' . esc_attr__('应用', 'jio') . '">
|
||||
</div>';
|
||||
|
||||
NativeUi::table_card_open();
|
||||
echo '<table class="widefat fixed striped posts table table-bordered aksrt-cardpass-table">';
|
||||
echo '<table class="widefat fixed striped posts table table-bordered jio-cardpass-table">';
|
||||
echo '<thead><tr>' . $thead_th . '</tr></thead>'; // phpcs:ignore WordPress.Security.EscapeOutput -- 已转义。
|
||||
echo '<tbody>' . $tbody . '</tbody>'; // phpcs:ignore WordPress.Security.EscapeOutput -- 已转义。
|
||||
echo '</table>';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -12,9 +12,9 @@ class PayCoupons {
|
||||
use PayCouponsForms;
|
||||
|
||||
public const CAPABILITY = 'manage_options';
|
||||
public const PAGE_SLUG = 'aksrt_coupons';
|
||||
public const CREATE_DIALOG = 'aksrt-coupon-create';
|
||||
public const BATCH_DIALOG = 'aksrt-coupon-batch';
|
||||
public const PAGE_SLUG = 'jio_coupons';
|
||||
public const CREATE_DIALOG = 'jio-coupon-create';
|
||||
public const BATCH_DIALOG = 'jio-coupon-batch';
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
@@ -30,11 +30,11 @@ class PayCoupons {
|
||||
return;
|
||||
}
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']);
|
||||
add_action('admin_post_aksrt_pay_create_coupon', [$this, 'handle_create_coupon']);
|
||||
add_action('admin_post_aksrt_pay_batch_coupons', [$this, 'handle_batch_coupons']);
|
||||
add_action('admin_post_aksrt_pay_update_coupon', [$this, 'handle_update_coupon']);
|
||||
add_action('admin_post_aksrt_pay_disable_coupon', [$this, 'handle_disable_coupon']);
|
||||
add_action('admin_post_aksrt_pay_enable_coupon', [$this, 'handle_enable_coupon']);
|
||||
add_action('admin_post_jio_pay_create_coupon', [$this, 'handle_create_coupon']);
|
||||
add_action('admin_post_jio_pay_batch_coupons', [$this, 'handle_batch_coupons']);
|
||||
add_action('admin_post_jio_pay_update_coupon', [$this, 'handle_update_coupon']);
|
||||
add_action('admin_post_jio_pay_disable_coupon', [$this, 'handle_disable_coupon']);
|
||||
add_action('admin_post_jio_pay_enable_coupon', [$this, 'handle_enable_coupon']);
|
||||
}
|
||||
|
||||
public function enqueue_assets(): void {
|
||||
@@ -44,56 +44,56 @@ class PayCoupons {
|
||||
}
|
||||
$css = '/assets/css/admin/coupons.css';
|
||||
$js = '/assets/js/admin-coupons.js';
|
||||
wp_enqueue_style('aksrt-admin-coupons', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-coupons', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-coupons', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-coupons', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
public function render_page(): void {
|
||||
$this->ensure_cap();
|
||||
|
||||
$ready = class_exists('\AKSRT\Payment\Coupon');
|
||||
NativeUi::page_open('aksrt-coupons-page');
|
||||
$ready = class_exists('\Jio\Payment\Coupon');
|
||||
NativeUi::page_open('jio-coupons-page');
|
||||
NativeUi::header(
|
||||
__('优惠码', 'aksrt'),
|
||||
__('创建、批量生成与管理结算优惠码。配置项仍在 AKSRT 主题设置 SPA。', 'aksrt'),
|
||||
__('优惠码', 'jio'),
|
||||
__('创建、批量生成与管理结算优惠码。配置项仍在 Jio 主题设置 SPA。', 'jio'),
|
||||
'dashicons-tickets-alt',
|
||||
$ready ? [
|
||||
['label' => __('新建优惠码', 'aksrt'), 'dialog' => self::CREATE_DIALOG, 'primary' => true, 'icon' => 'dashicons-plus-alt2'],
|
||||
['label' => __('批量生成', 'aksrt'), 'dialog' => self::BATCH_DIALOG, 'icon' => 'dashicons-randomize'],
|
||||
['label' => __('新建优惠码', 'jio'), 'dialog' => self::CREATE_DIALOG, 'primary' => true, 'icon' => 'dashicons-plus-alt2'],
|
||||
['label' => __('批量生成', 'jio'), 'dialog' => self::BATCH_DIALOG, 'icon' => 'dashicons-randomize'],
|
||||
] : []
|
||||
);
|
||||
$this->render_flash();
|
||||
|
||||
if (!$ready) {
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('优惠码模块未初始化。', 'aksrt') . '</p></div>';
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('优惠码模块未初始化。', 'jio') . '</p></div>';
|
||||
NativeUi::page_close();
|
||||
return;
|
||||
}
|
||||
|
||||
$svc = \AKSRT\Payment\Coupon::get_instance();
|
||||
$svc = \Jio\Payment\Coupon::get_instance();
|
||||
$edit_id = isset($_GET['edit_id']) ? (int) $_GET['edit_id'] : 0;
|
||||
$edit_row = $edit_id > 0 ? $svc->get($edit_id) : null;
|
||||
|
||||
if ($edit_id > 0 && $edit_row) {
|
||||
$this->render_edit_form($edit_row);
|
||||
} elseif ($edit_id > 0) {
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('优惠码不存在。', 'aksrt') . '</p></div>';
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('优惠码不存在。', 'jio') . '</p></div>';
|
||||
}
|
||||
|
||||
$this->render_generated_codes();
|
||||
$this->render_list($svc);
|
||||
$this->render_create_dialog();
|
||||
$this->render_batch_dialog();
|
||||
echo '<div class="aksrt-coupon-toast" role="status" aria-live="polite" data-aksrt-coupon-toast'
|
||||
. ' data-copied="' . esc_attr__('已复制到剪贴板', 'aksrt') . '"'
|
||||
. ' data-copy-failed="' . esc_attr__('自动复制失败,请手动选中后复制。', 'aksrt') . '"></div>';
|
||||
echo '<div class="jio-coupon-toast" role="status" aria-live="polite" data-jio-coupon-toast'
|
||||
. ' data-copied="' . esc_attr__('已复制到剪贴板', 'jio') . '"'
|
||||
. ' data-copy-failed="' . esc_attr__('自动复制失败,请手动选中后复制。', 'jio') . '"></div>';
|
||||
|
||||
NativeUi::page_close();
|
||||
}
|
||||
|
||||
/** 批量生成后一次性展示本批代码(transient 读取即删),附「复制全部」。 */
|
||||
private function render_generated_codes(): void {
|
||||
$key = 'aksrt_pay_coupon_codes_' . get_current_user_id();
|
||||
$key = 'jio_pay_coupon_codes_' . get_current_user_id();
|
||||
$codes = get_transient($key);
|
||||
if (!is_array($codes) || !$codes) {
|
||||
return;
|
||||
@@ -101,46 +101,46 @@ class PayCoupons {
|
||||
delete_transient($key);
|
||||
$text = implode("\n", array_map('strval', $codes));
|
||||
$copy = '<button type="button" class="button button-small" data-clipboard-text="' . esc_attr($text) . '">'
|
||||
. '<span class="dashicons dashicons-admin-page" aria-hidden="true"></span>' . esc_html__('复制全部', 'aksrt') . '</button>';
|
||||
. '<span class="dashicons dashicons-admin-page" aria-hidden="true"></span>' . esc_html__('复制全部', 'jio') . '</button>';
|
||||
NativeUi::card_open(
|
||||
sprintf(
|
||||
/* translators: %d count */
|
||||
__('本次生成的 %d 个优惠码', 'aksrt'),
|
||||
__('本次生成的 %d 个优惠码', 'jio'),
|
||||
count($codes)
|
||||
),
|
||||
$copy,
|
||||
false,
|
||||
'aksrt-coupon-generated'
|
||||
'jio-coupon-generated'
|
||||
);
|
||||
echo '<p class="aksrt-native-field__hint">' . esc_html__('离开或刷新本页后不再显示,请及时复制保存。', 'aksrt') . '</p>';
|
||||
echo '<textarea class="large-text code" rows="' . esc_attr((string) min(12, max(3, count($codes)))) . '" readonly aria-label="' . esc_attr__('本次生成的优惠码', 'aksrt') . '">' . esc_textarea($text) . '</textarea>';
|
||||
echo '<p class="jio-native-field__hint">' . esc_html__('离开或刷新本页后不再显示,请及时复制保存。', 'jio') . '</p>';
|
||||
echo '<textarea class="large-text code" rows="' . esc_attr((string) min(12, max(3, count($codes)))) . '" readonly aria-label="' . esc_attr__('本次生成的优惠码', 'jio') . '">' . esc_textarea($text) . '</textarea>';
|
||||
NativeUi::card_close();
|
||||
}
|
||||
|
||||
private function render_list(\AKSRT\Payment\Coupon $svc): void {
|
||||
private function render_list(\Jio\Payment\Coupon $svc): void {
|
||||
$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 = AdminListPagination::DEFAULT_PER_PAGE;
|
||||
$type_labels = [
|
||||
'fixed' => __('固定金额', 'aksrt'),
|
||||
'percent' => __('折扣百分比', 'aksrt'),
|
||||
'fixed' => __('固定金额', 'jio'),
|
||||
'percent' => __('折扣百分比', 'jio'),
|
||||
];
|
||||
$status_map = [
|
||||
'active' => [__('可用', 'aksrt'), 'success'],
|
||||
'disabled' => [__('已禁用', 'aksrt'), 'neutral'],
|
||||
'active' => [__('可用', 'jio'), 'success'],
|
||||
'disabled' => [__('已禁用', 'jio'), 'neutral'],
|
||||
];
|
||||
|
||||
// 筛选栏(卡片头部的 .aksrt-native-filters 表单)。
|
||||
$filters = '<form method="get" class="aksrt-native-filters">';
|
||||
// 筛选栏(卡片头部的 .jio-native-filters 表单)。
|
||||
$filters = '<form method="get" class="jio-native-filters">';
|
||||
$filters .= '<input type="hidden" name="page" value="' . esc_attr(self::PAGE_SLUG) . '" />';
|
||||
$filters .= '<select name="status">';
|
||||
foreach (['' => __('全部状态', 'aksrt'), 'active' => __('可用', 'aksrt'), 'disabled' => __('已禁用', 'aksrt')] as $key => $label) {
|
||||
foreach (['' => __('全部状态', 'jio'), 'active' => __('可用', 'jio'), 'disabled' => __('已禁用', 'jio')] as $key => $label) {
|
||||
$filters .= sprintf('<option value="%s"%s>%s</option>', esc_attr($key), selected($status, $key, false), esc_html($label));
|
||||
}
|
||||
$filters .= '</select>';
|
||||
$filters .= '<input type="search" name="s" value="' . esc_attr($search) . '" placeholder="' . esc_attr__('搜索代码', 'aksrt') . '" />';
|
||||
$filters .= '<button type="submit" class="button">' . esc_html__('筛选', 'aksrt') . '</button>';
|
||||
$filters .= '<input type="search" name="s" value="' . esc_attr($search) . '" placeholder="' . esc_attr__('搜索代码', 'jio') . '" />';
|
||||
$filters .= '<button type="submit" class="button">' . esc_html__('筛选', 'jio') . '</button>';
|
||||
$filters .= '</form>';
|
||||
|
||||
$result = $svc->get_list([
|
||||
@@ -166,12 +166,12 @@ class PayCoupons {
|
||||
$items = $result['items'] ?? [];
|
||||
}
|
||||
|
||||
NativeUi::table_card_open(__('优惠码列表', 'aksrt'), $filters);
|
||||
NativeUi::table_card_open(__('优惠码列表', 'jio'), $filters);
|
||||
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>';
|
||||
echo '<th>ID</th><th>' . esc_html__('代码', 'jio') . '</th><th>' . esc_html__('类型', 'jio') . '</th><th>' . esc_html__('面值', 'jio') . '</th><th>' . esc_html__('门槛/封顶', 'jio') . '</th><th>' . esc_html__('范围', 'jio') . '</th><th>' . esc_html__('已用/上限/每人', 'jio') . '</th><th>' . esc_html__('状态', 'jio') . '</th><th>' . esc_html__('过期', 'jio') . '</th><th>' . esc_html__('操作', 'jio') . '</th>';
|
||||
echo '</tr></thead><tbody>';
|
||||
if (!$items) {
|
||||
echo NativeUi::empty_row(10, __('暂无优惠码', 'aksrt'), __('点击右上角「新建优惠码」或「批量生成」添加。', 'aksrt')); // phpcs:ignore WordPress.Security.EscapeOutput -- 内部构造,已转义。
|
||||
echo NativeUi::empty_row(10, __('暂无优惠码', 'jio'), __('点击右上角「新建优惠码」或「批量生成」添加。', 'jio')); // phpcs:ignore WordPress.Security.EscapeOutput -- 内部构造,已转义。
|
||||
}
|
||||
foreach ($items as $row) {
|
||||
$id = (int) ($row['id'] ?? 0);
|
||||
@@ -180,7 +180,7 @@ class PayCoupons {
|
||||
echo '<tr>';
|
||||
echo '<td>' . esc_html((string) $id) . '</td>';
|
||||
$code = (string) ($row['code'] ?? '');
|
||||
echo '<td><button type="button" class="aksrt-coupon-code aksrt-native-mono" data-clipboard-text="' . esc_attr($code) . '" aria-label="' . esc_attr(sprintf(__('复制优惠码 %s', 'aksrt'), $code)) . '">'
|
||||
echo '<td><button type="button" class="jio-coupon-code jio-native-mono" data-clipboard-text="' . esc_attr($code) . '" aria-label="' . esc_attr(sprintf(__('复制优惠码 %s', 'jio'), $code)) . '">'
|
||||
. esc_html($code) . '<span class="dashicons dashicons-admin-page" aria-hidden="true"></span></button></td>';
|
||||
echo '<td>' . esc_html($type_labels[$row_type] ?? $row_type) . '</td>';
|
||||
echo '<td>' . esc_html((string) ($row['value'] ?? '')) . ($row_type === 'percent' ? '%' : '') . '</td>';
|
||||
@@ -189,21 +189,21 @@ class PayCoupons {
|
||||
echo '<td>' . esc_html((string) ((int) ($row['used_count'] ?? 0)) . '/' . (string) ((int) ($row['usage_limit'] ?? 0)) . '/' . (string) ((int) ($row['per_user_limit'] ?? 0))) . '</td>';
|
||||
echo '<td>' . NativeUi::status_badge($st, $status_map) . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- 内部构造,已转义。
|
||||
echo '<td>' . esc_html((string) ($row['expires_at'] ?? '')) . '</td>';
|
||||
echo '<td><div class="aksrt-native-inline">';
|
||||
echo '<a class="button button-small" href="' . esc_url($this->page_url(['edit_id' => $id])) . '">' . esc_html__('编辑', 'aksrt') . '</a>';
|
||||
echo '<td><div class="jio-native-inline">';
|
||||
echo '<a class="button button-small" href="' . esc_url($this->page_url(['edit_id' => $id])) . '">' . esc_html__('编辑', 'jio') . '</a>';
|
||||
if ($st === 'active') {
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-native-inline">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_pay_disable_coupon" />';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-native-inline">';
|
||||
echo '<input type="hidden" name="action" value="jio_pay_disable_coupon" />';
|
||||
echo '<input type="hidden" name="coupon_id" value="' . esc_attr((string) $id) . '" />';
|
||||
wp_nonce_field('aksrt_pay_disable_coupon_' . $id);
|
||||
submit_button(__('禁用', 'aksrt'), 'small', '', false);
|
||||
wp_nonce_field('jio_pay_disable_coupon_' . $id);
|
||||
submit_button(__('禁用', 'jio'), 'small', '', false);
|
||||
echo '</form>';
|
||||
} elseif ($st === 'disabled') {
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-native-inline">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_pay_enable_coupon" />';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-native-inline">';
|
||||
echo '<input type="hidden" name="action" value="jio_pay_enable_coupon" />';
|
||||
echo '<input type="hidden" name="coupon_id" value="' . esc_attr((string) $id) . '" />';
|
||||
wp_nonce_field('aksrt_pay_enable_coupon_' . $id);
|
||||
submit_button(__('启用', 'aksrt'), 'small', '', false);
|
||||
wp_nonce_field('jio_pay_enable_coupon_' . $id);
|
||||
submit_button(__('启用', 'jio'), 'small', '', false);
|
||||
echo '</form>';
|
||||
}
|
||||
echo '</div></td></tr>';
|
||||
@@ -224,13 +224,13 @@ class PayCoupons {
|
||||
|
||||
public function handle_create_coupon(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
wp_die(esc_html__('权限不足', 'jio'));
|
||||
}
|
||||
check_admin_referer('aksrt_pay_create_coupon');
|
||||
check_admin_referer('jio_pay_create_coupon');
|
||||
|
||||
$ok = false;
|
||||
if (class_exists('\AKSRT\Payment\Coupon')) {
|
||||
$id = \AKSRT\Payment\Coupon::get_instance()->create([
|
||||
if (class_exists('\Jio\Payment\Coupon')) {
|
||||
$id = \Jio\Payment\Coupon::get_instance()->create([
|
||||
'code' => sanitize_text_field((string) wp_unslash($_POST['code'] ?? '')),
|
||||
'type' => sanitize_key((string) wp_unslash($_POST['type'] ?? 'fixed')),
|
||||
'value' => trim((string) wp_unslash($_POST['value'] ?? '0')),
|
||||
@@ -243,19 +243,19 @@ class PayCoupons {
|
||||
]);
|
||||
$ok = $id > 0;
|
||||
}
|
||||
wp_safe_redirect($this->page_url(['aksrt_pay_msg' => $ok ? 'coupon_created' : 'coupon_create_failed']));
|
||||
wp_safe_redirect($this->page_url(['jio_pay_msg' => $ok ? 'coupon_created' : 'coupon_create_failed']));
|
||||
exit;
|
||||
}
|
||||
|
||||
public function handle_batch_coupons(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
wp_die(esc_html__('权限不足', 'jio'));
|
||||
}
|
||||
check_admin_referer('aksrt_pay_batch_coupons');
|
||||
check_admin_referer('jio_pay_batch_coupons');
|
||||
|
||||
$ok = false;
|
||||
if (class_exists('\AKSRT\Payment\Coupon')) {
|
||||
$result = \AKSRT\Payment\Coupon::get_instance()->batch_create([
|
||||
if (class_exists('\Jio\Payment\Coupon')) {
|
||||
$result = \Jio\Payment\Coupon::get_instance()->batch_create([
|
||||
'type' => sanitize_key((string) wp_unslash($_POST['type'] ?? 'fixed')),
|
||||
'value' => trim((string) wp_unslash($_POST['value'] ?? '0')),
|
||||
'min_amount' => trim((string) wp_unslash($_POST['min_amount'] ?? '0')),
|
||||
@@ -268,24 +268,24 @@ class PayCoupons {
|
||||
$codes = is_array($result['codes'] ?? null) ? $result['codes'] : [];
|
||||
$ok = (int) ($result['created'] ?? 0) > 0;
|
||||
if ($ok) {
|
||||
set_transient('aksrt_pay_coupon_codes_' . get_current_user_id(), $codes, 10 * MINUTE_IN_SECONDS);
|
||||
set_transient('jio_pay_coupon_codes_' . get_current_user_id(), $codes, 10 * MINUTE_IN_SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
wp_safe_redirect($this->page_url(['aksrt_pay_msg' => $ok ? 'coupon_batch_ok' : 'coupon_batch_failed']));
|
||||
wp_safe_redirect($this->page_url(['jio_pay_msg' => $ok ? 'coupon_batch_ok' : 'coupon_batch_failed']));
|
||||
exit;
|
||||
}
|
||||
|
||||
public function handle_update_coupon(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
wp_die(esc_html__('权限不足', 'jio'));
|
||||
}
|
||||
$id = isset($_POST['coupon_id']) ? (int) $_POST['coupon_id'] : 0;
|
||||
check_admin_referer('aksrt_pay_update_coupon_' . $id);
|
||||
check_admin_referer('jio_pay_update_coupon_' . $id);
|
||||
|
||||
$ok = false;
|
||||
if ($id > 0 && class_exists('\AKSRT\Payment\Coupon')) {
|
||||
$ok = \AKSRT\Payment\Coupon::get_instance()->update($id, [
|
||||
if ($id > 0 && class_exists('\Jio\Payment\Coupon')) {
|
||||
$ok = \Jio\Payment\Coupon::get_instance()->update($id, [
|
||||
'type' => sanitize_key((string) wp_unslash($_POST['type'] ?? 'fixed')),
|
||||
'value' => trim((string) wp_unslash($_POST['value'] ?? '0')),
|
||||
'min_amount' => trim((string) wp_unslash($_POST['min_amount'] ?? '0')),
|
||||
@@ -297,7 +297,7 @@ class PayCoupons {
|
||||
]);
|
||||
}
|
||||
wp_safe_redirect($this->page_url([
|
||||
'aksrt_pay_msg' => $ok ? 'coupon_updated' : 'coupon_update_failed',
|
||||
'jio_pay_msg' => $ok ? 'coupon_updated' : 'coupon_update_failed',
|
||||
'edit_id' => $id,
|
||||
]));
|
||||
exit;
|
||||
@@ -305,23 +305,23 @@ class PayCoupons {
|
||||
|
||||
public function handle_disable_coupon(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
wp_die(esc_html__('权限不足', 'jio'));
|
||||
}
|
||||
$id = isset($_POST['coupon_id']) ? (int) $_POST['coupon_id'] : 0;
|
||||
check_admin_referer('aksrt_pay_disable_coupon_' . $id);
|
||||
$ok = $id > 0 && class_exists('\AKSRT\Payment\Coupon') && \AKSRT\Payment\Coupon::get_instance()->disable($id);
|
||||
wp_safe_redirect($this->page_url(['aksrt_pay_msg' => $ok ? 'coupon_disabled' : 'coupon_failed']));
|
||||
check_admin_referer('jio_pay_disable_coupon_' . $id);
|
||||
$ok = $id > 0 && class_exists('\Jio\Payment\Coupon') && \Jio\Payment\Coupon::get_instance()->disable($id);
|
||||
wp_safe_redirect($this->page_url(['jio_pay_msg' => $ok ? 'coupon_disabled' : 'coupon_failed']));
|
||||
exit;
|
||||
}
|
||||
|
||||
public function handle_enable_coupon(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
wp_die(esc_html__('权限不足', 'jio'));
|
||||
}
|
||||
$id = isset($_POST['coupon_id']) ? (int) $_POST['coupon_id'] : 0;
|
||||
check_admin_referer('aksrt_pay_enable_coupon_' . $id);
|
||||
$ok = $id > 0 && class_exists('\AKSRT\Payment\Coupon') && \AKSRT\Payment\Coupon::get_instance()->enable($id);
|
||||
wp_safe_redirect($this->page_url(['aksrt_pay_msg' => $ok ? 'coupon_enabled' : 'coupon_failed']));
|
||||
check_admin_referer('jio_pay_enable_coupon_' . $id);
|
||||
$ok = $id > 0 && class_exists('\Jio\Payment\Coupon') && \Jio\Payment\Coupon::get_instance()->enable($id);
|
||||
wp_safe_redirect($this->page_url(['jio_pay_msg' => $ok ? 'coupon_enabled' : 'coupon_failed']));
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -370,15 +370,15 @@ class PayCoupons {
|
||||
$scope = is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
if (!is_array($scope) || empty($scope)) {
|
||||
return __('全部', 'aksrt');
|
||||
return __('全部', 'jio');
|
||||
}
|
||||
$type = sanitize_key((string) ($scope['type'] ?? ''));
|
||||
$ids = array_map('intval', (array) ($scope['ids'] ?? []));
|
||||
$id_text = $ids ? implode(',', $ids) : '—';
|
||||
$map = [
|
||||
'include' => __('含商品', 'aksrt'),
|
||||
'exclude' => __('排除商品', 'aksrt'),
|
||||
'category' => __('分类', 'aksrt'),
|
||||
'include' => __('含商品', 'jio'),
|
||||
'exclude' => __('排除商品', 'jio'),
|
||||
'category' => __('分类', 'jio'),
|
||||
];
|
||||
$label = $map[$type] ?? $type;
|
||||
return $label . ':' . $id_text;
|
||||
@@ -386,25 +386,25 @@ class PayCoupons {
|
||||
|
||||
private function ensure_cap(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
}
|
||||
|
||||
private function render_flash(): void {
|
||||
if (empty($_GET['aksrt_pay_msg'])) {
|
||||
if (empty($_GET['jio_pay_msg'])) {
|
||||
return;
|
||||
}
|
||||
$code = sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg']));
|
||||
$code = sanitize_key((string) wp_unslash($_GET['jio_pay_msg']));
|
||||
$map = [
|
||||
'coupon_created' => __('优惠码已创建。', 'aksrt'),
|
||||
'coupon_create_failed' => __('优惠码创建失败(可能重复)。', 'aksrt'),
|
||||
'coupon_batch_ok' => __('优惠码批量生成完成。', 'aksrt'),
|
||||
'coupon_batch_failed' => __('优惠码批量生成失败。', 'aksrt'),
|
||||
'coupon_updated' => __('优惠码已更新。', 'aksrt'),
|
||||
'coupon_update_failed' => __('优惠码更新失败。', 'aksrt'),
|
||||
'coupon_disabled' => __('优惠码已禁用。', 'aksrt'),
|
||||
'coupon_enabled' => __('优惠码已启用。', 'aksrt'),
|
||||
'coupon_failed' => __('优惠码操作失败。', 'aksrt'),
|
||||
'coupon_created' => __('优惠码已创建。', 'jio'),
|
||||
'coupon_create_failed' => __('优惠码创建失败(可能重复)。', 'jio'),
|
||||
'coupon_batch_ok' => __('优惠码批量生成完成。', 'jio'),
|
||||
'coupon_batch_failed' => __('优惠码批量生成失败。', 'jio'),
|
||||
'coupon_updated' => __('优惠码已更新。', 'jio'),
|
||||
'coupon_update_failed' => __('优惠码更新失败。', 'jio'),
|
||||
'coupon_disabled' => __('优惠码已禁用。', 'jio'),
|
||||
'coupon_enabled' => __('优惠码已启用。', 'jio'),
|
||||
'coupon_failed' => __('优惠码操作失败。', 'jio'),
|
||||
];
|
||||
if (!isset($map[$code])) {
|
||||
return;
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
* 优惠码:新建 / 批量生成弹窗与编辑表单。
|
||||
*
|
||||
* 作为 flat sibling trait 被 PayCoupons 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
* 两个弹窗由页头按钮(data-aksrt-dialog-open)打开,交互在 admin-coupons.js;
|
||||
* 两个弹窗由页头按钮(data-jio-dialog-open)打开,交互在 admin-coupons.js;
|
||||
* 三张表单共用 render_rule_fields() 输出的优惠规则字段。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -18,15 +18,15 @@ trait PayCouponsForms {
|
||||
private function render_create_dialog(): void {
|
||||
$this->dialog_open(
|
||||
self::CREATE_DIALOG,
|
||||
__('新建优惠码', 'aksrt'),
|
||||
__('手动指定一个优惠码,用户结算时输入即可抵扣。', 'aksrt'),
|
||||
'aksrt_pay_create_coupon'
|
||||
__('新建优惠码', 'jio'),
|
||||
__('手动指定一个优惠码,用户结算时输入即可抵扣。', 'jio'),
|
||||
'jio_pay_create_coupon'
|
||||
);
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
echo '<div class="jio-native-form-grid">';
|
||||
$this->field(
|
||||
__('代码', 'aksrt'),
|
||||
'<input type="text" name="code" maxlength="60" required autofocus autocomplete="off" spellcheck="false" class="aksrt-native-mono">',
|
||||
__('需全站唯一,最多 60 个字符。', 'aksrt'),
|
||||
__('代码', 'jio'),
|
||||
'<input type="text" name="code" maxlength="60" required autofocus autocomplete="off" spellcheck="false" class="jio-native-mono">',
|
||||
__('需全站唯一,最多 60 个字符。', 'jio'),
|
||||
true
|
||||
);
|
||||
$this->render_rule_fields([
|
||||
@@ -38,24 +38,24 @@ trait PayCouponsForms {
|
||||
'per_user_limit' => '0',
|
||||
]);
|
||||
echo '</div>';
|
||||
$this->dialog_close(__('创建优惠码', 'aksrt'));
|
||||
$this->dialog_close(__('创建优惠码', 'jio'));
|
||||
}
|
||||
|
||||
private function render_batch_dialog(): void {
|
||||
$this->dialog_open(
|
||||
self::BATCH_DIALOG,
|
||||
__('批量生成优惠码', 'aksrt'),
|
||||
__('按相同规则随机生成一批优惠码,生成后会在页面上列出供复制。', 'aksrt'),
|
||||
'aksrt_pay_batch_coupons'
|
||||
__('批量生成优惠码', 'jio'),
|
||||
__('按相同规则随机生成一批优惠码,生成后会在页面上列出供复制。', 'jio'),
|
||||
'jio_pay_batch_coupons'
|
||||
);
|
||||
echo '<h3 class="aksrt-coupon-section">' . esc_html__('生成规则', 'aksrt') . '</h3>';
|
||||
echo '<div class="aksrt-native-form-grid aksrt-coupon-batch-grid">';
|
||||
$this->field(__('数量', 'aksrt'), '<input type="number" name="count" min="1" max="100" step="1" value="10" required autofocus>', __('单次 1–100 个。', 'aksrt'));
|
||||
$this->field(__('随机位数', 'aksrt'), '<input type="number" name="length" min="6" max="24" step="1" value="10" required>', __('6–24 位,不含前缀。', 'aksrt'));
|
||||
$this->field(__('前缀', 'aksrt'), '<input type="text" name="prefix" maxlength="36" autocomplete="off" spellcheck="false" class="aksrt-native-mono">', __('选填,如 VIP2026-。', 'aksrt'));
|
||||
echo '<h3 class="jio-coupon-section">' . esc_html__('生成规则', 'jio') . '</h3>';
|
||||
echo '<div class="jio-native-form-grid jio-coupon-batch-grid">';
|
||||
$this->field(__('数量', 'jio'), '<input type="number" name="count" min="1" max="100" step="1" value="10" required autofocus>', __('单次 1–100 个。', 'jio'));
|
||||
$this->field(__('随机位数', 'jio'), '<input type="number" name="length" min="6" max="24" step="1" value="10" required>', __('6–24 位,不含前缀。', 'jio'));
|
||||
$this->field(__('前缀', 'jio'), '<input type="text" name="prefix" maxlength="36" autocomplete="off" spellcheck="false" class="jio-native-mono">', __('选填,如 VIP2026-。', 'jio'));
|
||||
echo '</div>';
|
||||
echo '<h3 class="aksrt-coupon-section">' . esc_html__('优惠规则', 'aksrt') . '</h3>';
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
echo '<h3 class="jio-coupon-section">' . esc_html__('优惠规则', 'jio') . '</h3>';
|
||||
echo '<div class="jio-native-form-grid">';
|
||||
$this->render_rule_fields([
|
||||
'type' => 'fixed',
|
||||
'value' => '10',
|
||||
@@ -65,7 +65,7 @@ trait PayCouponsForms {
|
||||
'per_user_limit' => '1',
|
||||
]);
|
||||
echo '</div>';
|
||||
$this->dialog_close(__('批量生成', 'aksrt'));
|
||||
$this->dialog_close(__('批量生成', 'jio'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,18 +78,18 @@ trait PayCouponsForms {
|
||||
|
||||
$title = sprintf(
|
||||
/* translators: %d coupon id */
|
||||
__('编辑优惠码 #%d', 'aksrt'),
|
||||
__('编辑优惠码 #%d', 'jio'),
|
||||
$id
|
||||
);
|
||||
$back = NativeUi::button_link(__('返回列表', 'aksrt'), $this->page_url(), false, 'dashicons-arrow-left-alt');
|
||||
$back = NativeUi::button_link(__('返回列表', 'jio'), $this->page_url(), false, 'dashicons-arrow-left-alt');
|
||||
|
||||
NativeUi::card_open($title, $back);
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" data-aksrt-coupon-form data-busy-label="' . esc_attr__('正在保存…', 'aksrt') . '">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_pay_update_coupon" />';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" data-jio-coupon-form data-busy-label="' . esc_attr__('正在保存…', 'jio') . '">';
|
||||
echo '<input type="hidden" name="action" value="jio_pay_update_coupon" />';
|
||||
echo '<input type="hidden" name="coupon_id" value="' . esc_attr((string) $id) . '" />';
|
||||
wp_nonce_field('aksrt_pay_update_coupon_' . $id);
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
$this->field(__('代码', 'aksrt'), '<input type="text" value="' . esc_attr((string) ($row['code'] ?? '')) . '" readonly class="aksrt-native-mono" />', __('代码创建后不可修改。', 'aksrt'));
|
||||
wp_nonce_field('jio_pay_update_coupon_' . $id);
|
||||
echo '<div class="jio-native-form-grid">';
|
||||
$this->field(__('代码', 'jio'), '<input type="text" value="' . esc_attr((string) ($row['code'] ?? '')) . '" readonly class="jio-native-mono" />', __('代码创建后不可修改。', 'jio'));
|
||||
$this->render_rule_fields([
|
||||
'type' => (string) ($row['type'] ?? 'fixed'),
|
||||
'value' => (string) ($row['value'] ?? ''),
|
||||
@@ -102,8 +102,8 @@ trait PayCouponsForms {
|
||||
'expires_at' => $expires !== '' ? str_replace(' ', 'T', substr($expires, 0, 16)) : '',
|
||||
]);
|
||||
echo '</div>';
|
||||
echo '<div class="aksrt-native-form-foot">';
|
||||
echo '<button type="submit" class="button button-primary" data-aksrt-coupon-submit>' . esc_html__('保存修改', 'aksrt') . '</button>';
|
||||
echo '<div class="jio-native-form-foot">';
|
||||
echo '<button type="submit" class="button button-primary" data-jio-coupon-submit>' . esc_html__('保存修改', 'jio') . '</button>';
|
||||
echo '</div>';
|
||||
echo '</form>';
|
||||
NativeUi::card_close();
|
||||
@@ -119,39 +119,39 @@ trait PayCouponsForms {
|
||||
$percent = $type === 'percent';
|
||||
$currency = $this->currency_symbol();
|
||||
|
||||
$this->field(__('类型', 'aksrt'), $this->type_select($type));
|
||||
$this->field(__('类型', 'jio'), $this->type_select($type));
|
||||
$this->field(
|
||||
__('面值', 'aksrt'),
|
||||
'<span class="aksrt-coupon-inline"><input type="number" name="value" min="0" step="' . ($percent ? '1' : '0.01') . '"' . ($percent ? ' max="100"' : '')
|
||||
__('面值', 'jio'),
|
||||
'<span class="jio-coupon-inline"><input type="number" name="value" min="0" step="' . ($percent ? '1' : '0.01') . '"' . ($percent ? ' max="100"' : '')
|
||||
. ' value="' . esc_attr($v['value'] ?? '') . '" required>'
|
||||
. '<span class="aksrt-coupon-unit" data-aksrt-coupon-unit data-fixed="' . esc_attr($currency) . '" data-percent="%">' . esc_html($percent ? '%' : $currency) . '</span></span>',
|
||||
__('固定金额直接抵扣;折扣百分比填 20 表示减免 20%(0–100 的整数)。', 'aksrt')
|
||||
. '<span class="jio-coupon-unit" data-jio-coupon-unit data-fixed="' . esc_attr($currency) . '" data-percent="%">' . esc_html($percent ? '%' : $currency) . '</span></span>',
|
||||
__('固定金额直接抵扣;折扣百分比填 20 表示减免 20%(0–100 的整数)。', 'jio')
|
||||
);
|
||||
$this->field(__('最低消费', 'aksrt'), $this->money_input('min_amount', $v['min_amount'] ?? '0'), __('订单金额达到该值才可使用,0 表示无门槛。', 'aksrt'));
|
||||
$this->field(__('最高优惠', 'aksrt'), $this->money_input('max_discount', $v['max_discount'] ?? '0'), __('折扣百分比的封顶金额,0 表示不封顶。', 'aksrt'));
|
||||
$this->field(__('总次数', 'aksrt'), '<input type="number" name="usage_limit" min="0" step="1" value="' . esc_attr($v['usage_limit'] ?? '0') . '">', __('所有用户合计可用次数,0 表示不限。', 'aksrt'));
|
||||
$this->field(__('每人限次', 'aksrt'), '<input type="number" name="per_user_limit" min="0" step="1" value="' . esc_attr($v['per_user_limit'] ?? '0') . '">', __('单个用户可用次数,0 表示不限。', 'aksrt'));
|
||||
$this->field(__('最低消费', 'jio'), $this->money_input('min_amount', $v['min_amount'] ?? '0'), __('订单金额达到该值才可使用,0 表示无门槛。', 'jio'));
|
||||
$this->field(__('最高优惠', 'jio'), $this->money_input('max_discount', $v['max_discount'] ?? '0'), __('折扣百分比的封顶金额,0 表示不封顶。', 'jio'));
|
||||
$this->field(__('总次数', 'jio'), '<input type="number" name="usage_limit" min="0" step="1" value="' . esc_attr($v['usage_limit'] ?? '0') . '">', __('所有用户合计可用次数,0 表示不限。', 'jio'));
|
||||
$this->field(__('每人限次', 'jio'), '<input type="number" name="per_user_limit" min="0" step="1" value="' . esc_attr($v['per_user_limit'] ?? '0') . '">', __('单个用户可用次数,0 表示不限。', 'jio'));
|
||||
|
||||
$select = '<select name="scope_type">';
|
||||
foreach ([
|
||||
'' => __('全部商品', 'aksrt'),
|
||||
'include' => __('仅指定商品 ID', 'aksrt'),
|
||||
'exclude' => __('排除商品 ID', 'aksrt'),
|
||||
'category' => __('指定分类 ID', 'aksrt'),
|
||||
'' => __('全部商品', 'jio'),
|
||||
'include' => __('仅指定商品 ID', 'jio'),
|
||||
'exclude' => __('排除商品 ID', 'jio'),
|
||||
'category' => __('指定分类 ID', 'jio'),
|
||||
] as $k => $label) {
|
||||
$select .= sprintf('<option value="%s"%s>%s</option>', esc_attr($k), selected($v['scope_type'] ?? '', $k, false), esc_html($label));
|
||||
}
|
||||
$select .= '</select>';
|
||||
$this->field(__('适用范围', 'aksrt'), $select);
|
||||
$this->field(__('适用范围', 'jio'), $select);
|
||||
$this->field(
|
||||
__('范围 ID', 'aksrt'),
|
||||
'<input type="text" name="scope_ids" value="' . esc_attr($v['scope_ids'] ?? '') . '" placeholder="' . esc_attr__('如 12,34', 'aksrt') . '" autocomplete="off">',
|
||||
__('逗号分隔;选择「全部商品」时无需填写。', 'aksrt')
|
||||
__('范围 ID', 'jio'),
|
||||
'<input type="text" name="scope_ids" value="' . esc_attr($v['scope_ids'] ?? '') . '" placeholder="' . esc_attr__('如 12,34', 'jio') . '" autocomplete="off">',
|
||||
__('逗号分隔;选择「全部商品」时无需填写。', 'jio')
|
||||
);
|
||||
$this->field(
|
||||
__('过期时间', 'aksrt'),
|
||||
__('过期时间', 'jio'),
|
||||
'<input type="datetime-local" name="expires_at" value="' . esc_attr($v['expires_at'] ?? '') . '">',
|
||||
__('留空表示永不过期。', 'aksrt')
|
||||
__('留空表示永不过期。', 'jio')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -161,40 +161,40 @@ trait PayCouponsForms {
|
||||
|
||||
private function type_select(string $current): string {
|
||||
$html = '<select name="type">';
|
||||
foreach (['fixed' => __('固定金额', 'aksrt'), 'percent' => __('折扣百分比', 'aksrt')] as $k => $label) {
|
||||
foreach (['fixed' => __('固定金额', 'jio'), 'percent' => __('折扣百分比', 'jio')] as $k => $label) {
|
||||
$html .= sprintf('<option value="%s"%s>%s</option>', esc_attr($k), selected($current, $k, false), esc_html($label));
|
||||
}
|
||||
return $html . '</select>';
|
||||
}
|
||||
|
||||
/** 单字段单元(.aksrt-native-field);$html 为调用方已转义的控件片段。 */
|
||||
/** 单字段单元(.jio-native-field);$html 为调用方已转义的控件片段。 */
|
||||
private function field(string $label, string $html, string $hint = '', bool $wide = false): void {
|
||||
echo '<label class="aksrt-native-field' . ($wide ? ' is-wide' : '') . '">';
|
||||
echo '<span class="aksrt-native-field__label">' . esc_html($label) . '</span>';
|
||||
echo '<label class="jio-native-field' . ($wide ? ' is-wide' : '') . '">';
|
||||
echo '<span class="jio-native-field__label">' . esc_html($label) . '</span>';
|
||||
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput -- 调用方已转义。
|
||||
if ($hint !== '') {
|
||||
echo '<small class="aksrt-native-field__hint">' . esc_html($hint) . '</small>';
|
||||
echo '<small class="jio-native-field__hint">' . esc_html($hint) . '</small>';
|
||||
}
|
||||
echo '</label>';
|
||||
}
|
||||
|
||||
private function dialog_open(string $id, string $title, string $description, string $action): void {
|
||||
echo '<dialog class="aksrt-coupon-dialog" id="' . esc_attr($id) . '" aria-labelledby="' . esc_attr($id) . '-title" aria-describedby="' . esc_attr($id) . '-description">';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-coupon-dialog__frame" data-aksrt-coupon-form data-busy-label="' . esc_attr__('正在提交…', 'aksrt') . '">';
|
||||
echo '<dialog class="jio-coupon-dialog" id="' . esc_attr($id) . '" aria-labelledby="' . esc_attr($id) . '-title" aria-describedby="' . esc_attr($id) . '-description">';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-coupon-dialog__frame" data-jio-coupon-form data-busy-label="' . esc_attr__('正在提交…', 'jio') . '">';
|
||||
echo '<input type="hidden" name="action" value="' . esc_attr($action) . '">';
|
||||
wp_nonce_field($action);
|
||||
echo '<header class="aksrt-coupon-dialog__head"><div><h2 id="' . esc_attr($id) . '-title">' . esc_html($title) . '</h2><p id="' . esc_attr($id) . '-description">' . esc_html($description) . '</p></div>';
|
||||
echo '<button type="button" class="aksrt-coupon-dialog__close" data-aksrt-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
echo '<div class="aksrt-coupon-dialog__body">';
|
||||
echo '<header class="jio-coupon-dialog__head"><div><h2 id="' . esc_attr($id) . '-title">' . esc_html($title) . '</h2><p id="' . esc_attr($id) . '-description">' . esc_html($description) . '</p></div>';
|
||||
echo '<button type="button" class="jio-coupon-dialog__close" data-jio-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
echo '<div class="jio-coupon-dialog__body">';
|
||||
}
|
||||
|
||||
private function dialog_close(string $submit_label): void {
|
||||
echo '</div><footer class="aksrt-coupon-dialog__foot"><button type="button" class="button" data-aksrt-dialog-close>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary" data-aksrt-coupon-submit>' . esc_html($submit_label) . '</button></footer></form></dialog>';
|
||||
echo '</div><footer class="jio-coupon-dialog__foot"><button type="button" class="button" data-jio-dialog-close>' . esc_html__('取消', 'jio') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary" data-jio-coupon-submit>' . esc_html($submit_label) . '</button></footer></form></dialog>';
|
||||
}
|
||||
|
||||
private function currency_symbol(): string {
|
||||
$paid = \AKSRT\Settings\Settings::get_paid_content_settings();
|
||||
$paid = \Jio\Settings\Settings::get_paid_content_settings();
|
||||
return (string) ($paid['display']['currency_symbol'] ?? '¥');
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,10 @@
|
||||
* admin-post 处理器(结算 / 删除 / 批量操作)与共用的展示助手。页面样式在 assets/css/admin/income.css,
|
||||
* 批量操作、二次确认、全选与防重复提交在 assets/js/admin-income.js。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -18,7 +18,7 @@ class PayIncome {
|
||||
use PayIncomeList;
|
||||
|
||||
public const CAPABILITY = 'manage_options';
|
||||
public const PAGE_SLUG = 'aksrt_income';
|
||||
public const PAGE_SLUG = 'jio_income';
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
@@ -34,9 +34,9 @@ class PayIncome {
|
||||
return;
|
||||
}
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']);
|
||||
add_action('admin_post_aksrt_pay_settle_income', [$this, 'handle_settle_income']);
|
||||
add_action('admin_post_aksrt_pay_delete_income', [$this, 'handle_delete_income']);
|
||||
add_action('admin_post_aksrt_pay_bulk_income', [$this, 'handle_bulk_income']);
|
||||
add_action('admin_post_jio_pay_settle_income', [$this, 'handle_settle_income']);
|
||||
add_action('admin_post_jio_pay_delete_income', [$this, 'handle_delete_income']);
|
||||
add_action('admin_post_jio_pay_bulk_income', [$this, 'handle_bulk_income']);
|
||||
}
|
||||
|
||||
public function enqueue_assets(): void {
|
||||
@@ -46,17 +46,17 @@ class PayIncome {
|
||||
}
|
||||
$css = '/assets/css/admin/income.css';
|
||||
$js = '/assets/js/admin-income.js';
|
||||
wp_enqueue_style('aksrt-admin-income', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-income', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-income', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-income', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
public function render_income_page(): void {
|
||||
$this->ensure_cap();
|
||||
NativeUi::page_open('aksrt-income-page');
|
||||
NativeUi::page_open('jio-income-page');
|
||||
|
||||
if (!class_exists('\AKSRT\Payment\Revenue')) {
|
||||
NativeUi::header(__('创作分成', 'aksrt'), '', 'dashicons-chart-pie');
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('分成模块未初始化。', 'aksrt') . '</p></div>';
|
||||
if (!class_exists('\Jio\Payment\Revenue')) {
|
||||
NativeUi::header(__('创作分成', 'jio'), '', 'dashicons-chart-pie');
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('分成模块未初始化。', 'jio') . '</p></div>';
|
||||
NativeUi::page_close();
|
||||
return;
|
||||
}
|
||||
@@ -72,28 +72,28 @@ class PayIncome {
|
||||
public function handle_settle_income(): void {
|
||||
$this->ensure_cap();
|
||||
$id = isset($_POST['revenue_id']) ? (int) $_POST['revenue_id'] : 0;
|
||||
check_admin_referer('aksrt_pay_settle_income_' . $id);
|
||||
check_admin_referer('jio_pay_settle_income_' . $id);
|
||||
$operation_key = $this->request_operation_key('admin-revenue-settle');
|
||||
|
||||
$ok = $id > 0 && $operation_key !== ''
|
||||
&& \AKSRT\Payment\Revenue::get_instance()->settle_result($id, get_current_user_id(), $operation_key) instanceof \AKSRT\Ledger\LedgerResult;
|
||||
$this->redirect_back(['aksrt_pay_msg' => $ok ? 'income_settled' : 'income_failed']);
|
||||
&& \Jio\Payment\Revenue::get_instance()->settle_result($id, get_current_user_id(), $operation_key) instanceof \Jio\Ledger\LedgerResult;
|
||||
$this->redirect_back(['jio_pay_msg' => $ok ? 'income_settled' : 'income_failed']);
|
||||
}
|
||||
|
||||
/** 单条删除:只删待结算记录,已结算记录由 Revenue::delete_pending() 的状态条件挡住。 */
|
||||
public function handle_delete_income(): void {
|
||||
$this->ensure_cap();
|
||||
$id = isset($_POST['revenue_id']) ? (int) $_POST['revenue_id'] : 0;
|
||||
check_admin_referer('aksrt_pay_delete_income_' . $id);
|
||||
check_admin_referer('jio_pay_delete_income_' . $id);
|
||||
|
||||
$deleted = $id > 0 ? \AKSRT\Payment\Revenue::get_instance()->delete_pending([$id]) : 0;
|
||||
$this->redirect_back(['aksrt_pay_msg' => $deleted > 0 ? 'income_deleted' : 'income_delete_failed', 'done' => $deleted]);
|
||||
$deleted = $id > 0 ? \Jio\Payment\Revenue::get_instance()->delete_pending([$id]) : 0;
|
||||
$this->redirect_back(['jio_pay_msg' => $deleted > 0 ? 'income_deleted' : 'income_delete_failed', 'done' => $deleted]);
|
||||
}
|
||||
|
||||
/** 批量操作:bulk_action = settle | delete,作用于勾选的记录。 */
|
||||
public function handle_bulk_income(): void {
|
||||
$this->ensure_cap();
|
||||
check_admin_referer('aksrt_pay_bulk_income');
|
||||
check_admin_referer('jio_pay_bulk_income');
|
||||
|
||||
$action = isset($_POST['bulk_action']) ? sanitize_key((string) wp_unslash($_POST['bulk_action'])) : '';
|
||||
$raw_ids = isset($_POST['revenue_ids']) ? wp_unslash($_POST['revenue_ids']) : [];
|
||||
@@ -101,17 +101,17 @@ class PayIncome {
|
||||
? array_values(array_unique(array_filter(array_map('intval', $raw_ids), static fn (int $id): bool => $id > 0)))
|
||||
: [];
|
||||
if (!in_array($action, ['settle', 'delete'], true)) {
|
||||
$this->redirect_back(['aksrt_pay_msg' => 'income_bulk_invalid']);
|
||||
$this->redirect_back(['jio_pay_msg' => 'income_bulk_invalid']);
|
||||
}
|
||||
if ($ids === []) {
|
||||
$this->redirect_back(['aksrt_pay_msg' => 'income_none_selected']);
|
||||
$this->redirect_back(['jio_pay_msg' => 'income_none_selected']);
|
||||
}
|
||||
|
||||
if ($action === 'delete') {
|
||||
$deleted = \AKSRT\Payment\Revenue::get_instance()->delete_pending($ids);
|
||||
$deleted = \Jio\Payment\Revenue::get_instance()->delete_pending($ids);
|
||||
$failed = count($ids) - $deleted;
|
||||
$this->redirect_back([
|
||||
'aksrt_pay_msg' => $deleted === 0 ? 'income_delete_failed' : ($failed > 0 ? 'income_delete_partial' : 'income_deleted'),
|
||||
'jio_pay_msg' => $deleted === 0 ? 'income_delete_failed' : ($failed > 0 ? 'income_delete_partial' : 'income_deleted'),
|
||||
'done' => $deleted,
|
||||
'failed' => $failed,
|
||||
]);
|
||||
@@ -119,13 +119,13 @@ class PayIncome {
|
||||
|
||||
$operation_key = $this->request_operation_key('admin-revenue-settle-batch');
|
||||
$result = $operation_key !== ''
|
||||
? \AKSRT\Payment\Revenue::get_instance()->settle_batch($ids, get_current_user_id(), $operation_key)
|
||||
? \Jio\Payment\Revenue::get_instance()->settle_batch($ids, get_current_user_id(), $operation_key)
|
||||
: ['settled' => [], 'failed' => $ids];
|
||||
$settled = count($result['settled']);
|
||||
$failed = count($result['failed']);
|
||||
|
||||
$this->redirect_back([
|
||||
'aksrt_pay_msg' => $settled === 0 ? 'income_failed' : ($failed > 0 ? 'income_batch_partial' : 'income_batch_settled'),
|
||||
'jio_pay_msg' => $settled === 0 ? 'income_failed' : ($failed > 0 ? 'income_batch_partial' : 'income_batch_settled'),
|
||||
'done' => $settled,
|
||||
'failed' => $failed,
|
||||
]);
|
||||
@@ -135,7 +135,7 @@ class PayIncome {
|
||||
private function request_operation_key(string $scope): string {
|
||||
$request_key = sanitize_text_field((string) wp_unslash($_POST['operation_key'] ?? ''));
|
||||
return wp_is_uuid($request_key)
|
||||
? \AKSRT\Ledger\OperationKey::deterministic($scope, [get_current_user_id(), strtolower($request_key)])
|
||||
? \Jio\Ledger\OperationKey::deterministic($scope, [get_current_user_id(), strtolower($request_key)])
|
||||
: '';
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ class PayIncome {
|
||||
if (is_string($referer) && $referer !== '') {
|
||||
parse_str((string) wp_parse_url($referer, PHP_URL_QUERY), $parsed);
|
||||
if (($parsed['page'] ?? '') === self::PAGE_SLUG) {
|
||||
unset($parsed['page'], $parsed['aksrt_pay_msg'], $parsed['done'], $parsed['failed']);
|
||||
unset($parsed['page'], $parsed['jio_pay_msg'], $parsed['done'], $parsed['failed']);
|
||||
$query = $parsed;
|
||||
}
|
||||
}
|
||||
@@ -164,7 +164,7 @@ class PayIncome {
|
||||
|
||||
/** @return array{enabled:bool,cycle_days:int} */
|
||||
private function revenue_policy(): array {
|
||||
$settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_payment_revenue_settings() : [];
|
||||
$settings = class_exists('\Jio\Settings\Settings') ? \Jio\Settings\Settings::get_payment_revenue_settings() : [];
|
||||
return [
|
||||
'enabled' => !empty($settings['enabled']),
|
||||
'cycle_days' => max(1, (int) ($settings['cycle_days'] ?? 30)),
|
||||
@@ -174,8 +174,8 @@ class PayIncome {
|
||||
/** @return array<string, string> */
|
||||
private function status_labels(): array {
|
||||
return [
|
||||
'pending' => __('待结算', 'aksrt'),
|
||||
'settled' => __('已结算', 'aksrt'),
|
||||
'pending' => __('待结算', 'jio'),
|
||||
'settled' => __('已结算', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -191,51 +191,51 @@ class PayIncome {
|
||||
/** 分成金额:现金带两位小数与 ¥,积分取整加单位;现金与积分从不相加。 */
|
||||
private function format_amount($value, bool $is_points): string {
|
||||
if ($is_points) {
|
||||
return number_format_i18n(max(0, (int) $value)) . ' ' . __('积分', 'aksrt');
|
||||
return number_format_i18n(max(0, (int) $value)) . ' ' . __('积分', 'jio');
|
||||
}
|
||||
try {
|
||||
return '¥' . \AKSRT\Ledger\AssetAmount::balance(is_string($value) ? $value : '0')->formatted();
|
||||
return '¥' . \Jio\Ledger\AssetAmount::balance(is_string($value) ? $value : '0')->formatted();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return '¥0.00';
|
||||
}
|
||||
}
|
||||
|
||||
private function render_flash(): void {
|
||||
if (empty($_GET['aksrt_pay_msg'])) {
|
||||
if (empty($_GET['jio_pay_msg'])) {
|
||||
return;
|
||||
}
|
||||
$code = sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg']));
|
||||
$code = sanitize_key((string) wp_unslash($_GET['jio_pay_msg']));
|
||||
$done = max(0, (int) ($_GET['done'] ?? 0));
|
||||
$failed = max(0, (int) ($_GET['failed'] ?? 0));
|
||||
// 码 => [文案, notice 类型]
|
||||
$map = [
|
||||
'income_settled' => [__('分成已结算,已转入作者钱包。', 'aksrt'), 'success'],
|
||||
'income_settled' => [__('分成已结算,已转入作者钱包。', 'jio'), 'success'],
|
||||
'income_batch_settled' => [sprintf(
|
||||
/* translators: %d settled count */
|
||||
__('已结算 %d 条分成记录。', 'aksrt'),
|
||||
__('已结算 %d 条分成记录。', 'jio'),
|
||||
$done
|
||||
), 'success'],
|
||||
'income_batch_partial' => [sprintf(
|
||||
/* translators: 1 settled count 2 failed count */
|
||||
__('已结算 %1$d 条,%2$d 条失败(记录状态可能已变化),可刷新后重试。', 'aksrt'),
|
||||
__('已结算 %1$d 条,%2$d 条失败(记录状态可能已变化),可刷新后重试。', 'jio'),
|
||||
$done,
|
||||
$failed
|
||||
), 'warning'],
|
||||
'income_none_selected' => [__('请先勾选待结算记录。', 'aksrt'), 'warning'],
|
||||
'income_bulk_invalid' => [__('请先选择批量操作。', 'aksrt'), 'warning'],
|
||||
'income_failed' => [__('分成结算失败:记录可能已被结算,或分成功能未启用。', 'aksrt'), 'error'],
|
||||
'income_none_selected' => [__('请先勾选待结算记录。', 'jio'), 'warning'],
|
||||
'income_bulk_invalid' => [__('请先选择批量操作。', 'jio'), 'warning'],
|
||||
'income_failed' => [__('分成结算失败:记录可能已被结算,或分成功能未启用。', 'jio'), 'error'],
|
||||
'income_deleted' => [sprintf(
|
||||
/* translators: %d deleted count */
|
||||
__('已删除 %d 条待结算记录。', 'aksrt'),
|
||||
__('已删除 %d 条待结算记录。', 'jio'),
|
||||
$done
|
||||
), 'success'],
|
||||
'income_delete_partial' => [sprintf(
|
||||
/* translators: 1 deleted count 2 skipped count */
|
||||
__('已删除 %1$d 条,%2$d 条未删除(仅待结算记录可删除,可能已被结算)。', 'aksrt'),
|
||||
__('已删除 %1$d 条,%2$d 条未删除(仅待结算记录可删除,可能已被结算)。', 'jio'),
|
||||
$done,
|
||||
$failed
|
||||
), 'warning'],
|
||||
'income_delete_failed' => [__('删除失败:仅待结算记录可删除,记录可能已被结算或不存在。', 'aksrt'), 'error'],
|
||||
'income_delete_failed' => [__('删除失败:仅待结算记录可删除,记录可能已被结算或不存在。', 'jio'), 'error'],
|
||||
];
|
||||
if (!isset($map[$code])) {
|
||||
return;
|
||||
@@ -246,7 +246,7 @@ class PayIncome {
|
||||
|
||||
private function ensure_cap(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+100
-100
@@ -4,12 +4,12 @@
|
||||
*
|
||||
* 作为 flat sibling trait 被 PayIncome 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Payment\Revenue;
|
||||
use Jio\Payment\Revenue;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -17,12 +17,12 @@ trait PayIncomeList {
|
||||
|
||||
private function render_income_list_page(): void {
|
||||
NativeUi::header(
|
||||
__('创作分成', 'aksrt'),
|
||||
__('作者从付费内容订单获得的分成记录。待结算记录到期后自动结算,也可在这里提前手动结算。', 'aksrt'),
|
||||
__('创作分成', 'jio'),
|
||||
__('作者从付费内容订单获得的分成记录。待结算记录到期后自动结算,也可在这里提前手动结算。', 'jio'),
|
||||
'dashicons-chart-pie',
|
||||
[[
|
||||
'label' => __('分成配置', 'aksrt'),
|
||||
'url' => admin_url('admin.php?page=aksrt-settings#/payment/revenue'),
|
||||
'label' => __('分成配置', 'jio'),
|
||||
'url' => admin_url('admin.php?page=jio-settings#/payment/revenue'),
|
||||
'icon' => 'dashicons-admin-generic',
|
||||
]]
|
||||
);
|
||||
@@ -65,20 +65,20 @@ trait PayIncomeList {
|
||||
$has_pending = in_array('pending', array_column($items, 'status'), true);
|
||||
|
||||
// 复选框列与批量栏始终渲染;不可用时禁用并用 title 说明原因,而不是整块隐藏。
|
||||
NativeUi::table_card_open(__('分成记录', 'aksrt'), $this->bulk_form_html($can_settle, $has_pending));
|
||||
echo '<table class="wp-list-table widefat striped aksrt-income-table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('分成记录', 'aksrt') . '</caption><thead><tr>';
|
||||
NativeUi::table_card_open(__('分成记录', 'jio'), $this->bulk_form_html($can_settle, $has_pending));
|
||||
echo '<table class="wp-list-table widefat striped jio-income-table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('分成记录', 'jio') . '</caption><thead><tr>';
|
||||
echo '<td class="manage-column column-cb check-column">'
|
||||
. '<input type="checkbox" data-aksrt-income-check-all aria-label="' . esc_attr__('全选本页待结算记录', 'aksrt') . '"'
|
||||
. ($has_pending ? '' : ' disabled title="' . esc_attr__('本页没有待结算记录', 'aksrt') . '"') . ' /></td>';
|
||||
. '<input type="checkbox" data-jio-income-check-all aria-label="' . esc_attr__('全选本页待结算记录', 'jio') . '"'
|
||||
. ($has_pending ? '' : ' disabled title="' . esc_attr__('本页没有待结算记录', 'jio') . '"') . ' /></td>';
|
||||
foreach ([
|
||||
'record' => __('记录 / 产生时间', 'aksrt'),
|
||||
'author' => __('作者', 'aksrt'),
|
||||
'source' => __('来源文章 / 订单', 'aksrt'),
|
||||
'base' => __('订单金额 × 比例', 'aksrt'),
|
||||
'amount' => __('分成', 'aksrt'),
|
||||
'status' => __('状态', 'aksrt'),
|
||||
'actions' => __('操作', 'aksrt'),
|
||||
'record' => __('记录 / 产生时间', 'jio'),
|
||||
'author' => __('作者', 'jio'),
|
||||
'source' => __('来源文章 / 订单', 'jio'),
|
||||
'base' => __('订单金额 × 比例', 'jio'),
|
||||
'amount' => __('分成', 'jio'),
|
||||
'status' => __('状态', 'jio'),
|
||||
'actions' => __('操作', 'jio'),
|
||||
] as $key => $label) {
|
||||
echo '<th scope="col" class="column-' . esc_attr($key) . '">' . esc_html($label) . '</th>';
|
||||
}
|
||||
@@ -86,9 +86,9 @@ trait PayIncomeList {
|
||||
|
||||
if (!$items) {
|
||||
$filtered = array_diff_key($query_args, ['status' => true]) !== [];
|
||||
echo NativeUi::empty_row(8, __('暂无分成记录', 'aksrt'), $filtered // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
? __('没有符合当前筛选条件的记录,请调整或重置筛选。', 'aksrt')
|
||||
: __('付费内容订单支付成功后,作者分成会出现在这里。', 'aksrt'));
|
||||
echo NativeUi::empty_row(8, __('暂无分成记录', 'jio'), $filtered // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
? __('没有符合当前筛选条件的记录,请调整或重置筛选。', 'jio')
|
||||
: __('付费内容订单支付成功后,作者分成会出现在这里。', 'jio'));
|
||||
}
|
||||
foreach ($items as $row) {
|
||||
$this->render_income_row($row, $can_settle, $policy['cycle_days']);
|
||||
@@ -112,47 +112,47 @@ trait PayIncomeList {
|
||||
echo '<tr>';
|
||||
// 已结算记录已入账到钱包,不可结算也不可删除:复选框禁用占位,保持列对齐。
|
||||
echo '<th scope="row" class="check-column">'
|
||||
. '<input type="checkbox" form="aksrt-income-bulk" name="revenue_ids[]" value="' . esc_attr((string) $id) . '"'
|
||||
. ($pending ? ' data-aksrt-income-check' : ' disabled title="' . esc_attr__('已结算记录不可操作', 'aksrt') . '"')
|
||||
. ' aria-label="' . esc_attr(sprintf(__('选择分成记录 #%d', 'aksrt'), $id)) . '" /></th>';
|
||||
. '<input type="checkbox" form="jio-income-bulk" name="revenue_ids[]" value="' . esc_attr((string) $id) . '"'
|
||||
. ($pending ? ' data-jio-income-check' : ' disabled title="' . esc_attr__('已结算记录不可操作', 'jio') . '"')
|
||||
. ' aria-label="' . esc_attr(sprintf(__('选择分成记录 #%d', 'jio'), $id)) . '" /></th>';
|
||||
|
||||
echo '<td class="column-record"><span class="aksrt-income-id">#' . esc_html((string) $id) . '</span>'
|
||||
. '<div class="aksrt-income-sub">' . esc_html((string) ($row['created_at'] ?? '')) . '</div></td>';
|
||||
echo '<td class="column-record"><span class="jio-income-id">#' . esc_html((string) $id) . '</span>'
|
||||
. '<div class="jio-income-sub">' . esc_html((string) ($row['created_at'] ?? '')) . '</div></td>';
|
||||
echo '<td class="column-author">' . NativeUi::user_cell((int) ($row['author_id'] ?? 0)) . '</td>';
|
||||
|
||||
$title = $post_id > 0 ? (string) get_the_title($post_id) : '';
|
||||
$post_html = $post_id > 0
|
||||
? '<a class="aksrt-income-post" href="' . esc_url((string) get_edit_post_link($post_id)) . '">' . esc_html($title !== '' ? $title : '#' . $post_id) . '</a>'
|
||||
: '<span class="aksrt-native-muted">—</span>';
|
||||
? '<a class="jio-income-post" href="' . esc_url((string) get_edit_post_link($post_id)) . '">' . esc_html($title !== '' ? $title : '#' . $post_id) . '</a>'
|
||||
: '<span class="jio-native-muted">—</span>';
|
||||
$buyer = $buyer_id > 0 ? get_userdata($buyer_id) : false;
|
||||
$order_url = admin_url('admin.php?page=aksrt_orders&order=' . rawurlencode($order_number));
|
||||
$order_url = admin_url('admin.php?page=jio_orders&order=' . rawurlencode($order_number));
|
||||
echo '<td class="column-source">' . $post_html // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped above.
|
||||
. '<div class="aksrt-income-sub"><a class="aksrt-income-order" href="' . esc_url($order_url) . '">' . esc_html($order_number) . '</a>'
|
||||
. ($buyer ? ' · ' . esc_html(sprintf(__('买家 %s', 'aksrt'), $buyer->display_name)) : '')
|
||||
. '<div class="jio-income-sub"><a class="jio-income-order" href="' . esc_url($order_url) . '">' . esc_html($order_number) . '</a>'
|
||||
. ($buyer ? ' · ' . esc_html(sprintf(__('买家 %s', 'jio'), $buyer->display_name)) : '')
|
||||
. '</div></td>';
|
||||
|
||||
echo '<td class="column-base">' . esc_html($this->format_amount($row['original_amount'] ?? 0, $is_points))
|
||||
. '<div class="aksrt-income-sub">' . esc_html(sprintf(
|
||||
. '<div class="jio-income-sub">' . esc_html(sprintf(
|
||||
/* translators: 1 share ratio 2 author level label */
|
||||
__('比例 %1$d%% · %2$s', 'aksrt'),
|
||||
__('比例 %1$d%% · %2$s', 'jio'),
|
||||
(int) ($row['share_ratio'] ?? 0),
|
||||
$this->author_level_label((int) ($row['author_level'] ?? 0))
|
||||
)) . '</div></td>';
|
||||
echo '<td class="column-amount"><strong class="aksrt-native-amount' . ($pending ? '' : ' is-positive') . '">'
|
||||
echo '<td class="column-amount"><strong class="jio-native-amount' . ($pending ? '' : ' is-positive') . '">'
|
||||
. esc_html($this->format_amount($row['amount'] ?? 0, $is_points)) . '</strong>'
|
||||
. '<div class="aksrt-income-sub">' . esc_html($is_points ? __('积分钱包', 'aksrt') : __('余额钱包', 'aksrt')) . '</div></td>';
|
||||
. '<div class="jio-income-sub">' . esc_html($is_points ? __('积分钱包', 'jio') : __('余额钱包', 'jio')) . '</div></td>';
|
||||
|
||||
echo '<td class="column-status">' . NativeUi::status_badge($status, $this->status_badge_map())
|
||||
. $this->status_hint($row, $cycle_days) . '</td>';
|
||||
|
||||
echo '<td class="column-actions">';
|
||||
if ($pending) {
|
||||
echo '<div class="aksrt-income-actions">'
|
||||
echo '<div class="jio-income-actions">'
|
||||
. ($can_settle ? $this->settle_form_html($id) : '')
|
||||
. $this->delete_form_html($id)
|
||||
. '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
} else {
|
||||
echo '<span class="aksrt-native-muted">—</span>';
|
||||
echo '<span class="jio-native-muted">—</span>';
|
||||
}
|
||||
echo '</td></tr>';
|
||||
}
|
||||
@@ -161,7 +161,7 @@ trait PayIncomeList {
|
||||
private function status_hint(array $row, int $cycle_days): string {
|
||||
if (($row['status'] ?? '') === 'settled') {
|
||||
$at = (string) ($row['settled_at'] ?? '');
|
||||
return $at !== '' ? '<div class="aksrt-income-sub">' . esc_html(sprintf(__('%s 结算', 'aksrt'), $at)) . '</div>' : '';
|
||||
return $at !== '' ? '<div class="jio-income-sub">' . esc_html(sprintf(__('%s 结算', 'jio'), $at)) . '</div>' : '';
|
||||
}
|
||||
$created = (string) ($row['created_at'] ?? '');
|
||||
if ($created === '') {
|
||||
@@ -173,29 +173,29 @@ trait PayIncomeList {
|
||||
return '';
|
||||
}
|
||||
$text = $due <= new \DateTimeImmutable('now', wp_timezone())
|
||||
? __('已到期,等待自动结算', 'aksrt')
|
||||
? __('已到期,等待自动结算', 'jio')
|
||||
/* translators: %s expected settle time */
|
||||
: sprintf(__('预计 %s 自动结算', 'aksrt'), $due->format('m-d H:i'));
|
||||
return '<div class="aksrt-income-sub">' . esc_html($text) . '</div>';
|
||||
: sprintf(__('预计 %s 自动结算', 'jio'), $due->format('m-d H:i'));
|
||||
return '<div class="jio-income-sub">' . esc_html($text) . '</div>';
|
||||
}
|
||||
|
||||
private function author_level_label(int $level): string {
|
||||
return $level > 0
|
||||
/* translators: %d VIP level */
|
||||
? sprintf(__('VIP%d 作者', 'aksrt'), $level)
|
||||
: __('普通作者', 'aksrt');
|
||||
? sprintf(__('VIP%d 作者', 'jio'), $level)
|
||||
: __('普通作者', 'jio');
|
||||
}
|
||||
|
||||
/** @param array{enabled:bool,cycle_days:int} $policy */
|
||||
private function render_policy_callout(array $policy): void {
|
||||
if (!$policy['enabled']) {
|
||||
echo '<div class="aksrt-native-callout is-warning">' . esc_html__('创作分成功能未启用:不会产生新的分成记录,已有的待结算记录也无法结算。', 'aksrt')
|
||||
. ' <a href="' . esc_url(admin_url('admin.php?page=aksrt-settings#/payment/revenue')) . '">' . esc_html__('前往启用', 'aksrt') . '</a></div>';
|
||||
echo '<div class="jio-native-callout is-warning">' . esc_html__('创作分成功能未启用:不会产生新的分成记录,已有的待结算记录也无法结算。', 'jio')
|
||||
. ' <a href="' . esc_url(admin_url('admin.php?page=jio-settings#/payment/revenue')) . '">' . esc_html__('前往启用', 'jio') . '</a></div>';
|
||||
return;
|
||||
}
|
||||
echo '<div class="aksrt-native-callout">' . esc_html(sprintf(
|
||||
echo '<div class="jio-native-callout">' . esc_html(sprintf(
|
||||
/* translators: %d settlement cycle days */
|
||||
__('结算周期 %d 天:分成产生满周期后由系统每小时自动结算到作者钱包。手动结算会立即入账,不可撤销。', 'aksrt'),
|
||||
__('结算周期 %d 天:分成产生满周期后由系统每小时自动结算到作者钱包。手动结算会立即入账,不可撤销。', 'jio'),
|
||||
$policy['cycle_days']
|
||||
)) . '</div>';
|
||||
}
|
||||
@@ -206,22 +206,22 @@ trait PayIncomeList {
|
||||
$points = (array) ($stats['points'] ?? []);
|
||||
$scope = $user_id > 0
|
||||
/* translators: %d user ID */
|
||||
? sprintf(__('用户 #%d', 'aksrt'), $user_id)
|
||||
: __('全站', 'aksrt');
|
||||
? sprintf(__('用户 #%d', 'jio'), $user_id)
|
||||
: __('全站', 'jio');
|
||||
$count_hint = static function (array $bucket) use ($scope): string {
|
||||
/* translators: 1 scope 2 record count */
|
||||
return sprintf(__('%1$s · %2$s 条记录', 'aksrt'), $scope, number_format_i18n((int) ($bucket['count'] ?? 0)));
|
||||
return sprintf(__('%1$s · %2$s 条记录', 'jio'), $scope, number_format_i18n((int) ($bucket['count'] ?? 0)));
|
||||
};
|
||||
$pending_url = $this->page_url(array_filter(['status' => 'pending', 'user_id' => $user_id ?: null]));
|
||||
$settled_url = $this->page_url(array_filter(['status' => 'settled', 'user_id' => $user_id ?: null]));
|
||||
|
||||
NativeUi::stats([
|
||||
['label' => __('现金待结算', 'aksrt'), 'value' => $this->format_amount($cash['pending'] ?? '0', false), 'url' => add_query_arg('currency', 'cny', $pending_url), 'icon' => 'dashicons-clock', 'tone' => 'warning'],
|
||||
['label' => __('现金已结算', 'aksrt'), 'value' => $this->format_amount($cash['settled'] ?? '0', false), 'url' => add_query_arg('currency', 'cny', $settled_url), 'icon' => 'dashicons-yes-alt', 'tone' => 'success'],
|
||||
['label' => __('现金分成合计', 'aksrt'), 'value' => $this->format_amount($cash['total'] ?? '0', false), 'hint' => $count_hint($cash), 'icon' => 'dashicons-chart-area', 'tone' => 'info'],
|
||||
['label' => __('积分待结算', 'aksrt'), 'value' => $this->format_amount($points['pending'] ?? 0, true), 'url' => add_query_arg('currency', 'points', $pending_url), 'icon' => 'dashicons-clock', 'tone' => 'warning'],
|
||||
['label' => __('积分已结算', 'aksrt'), 'value' => $this->format_amount($points['settled'] ?? 0, true), 'url' => add_query_arg('currency', 'points', $settled_url), 'icon' => 'dashicons-yes-alt', 'tone' => 'success'],
|
||||
['label' => __('积分分成合计', 'aksrt'), 'value' => $this->format_amount($points['total'] ?? 0, true), 'hint' => $count_hint($points), 'icon' => 'dashicons-star-filled', 'tone' => 'info'],
|
||||
['label' => __('现金待结算', 'jio'), 'value' => $this->format_amount($cash['pending'] ?? '0', false), 'url' => add_query_arg('currency', 'cny', $pending_url), 'icon' => 'dashicons-clock', 'tone' => 'warning'],
|
||||
['label' => __('现金已结算', 'jio'), 'value' => $this->format_amount($cash['settled'] ?? '0', false), 'url' => add_query_arg('currency', 'cny', $settled_url), 'icon' => 'dashicons-yes-alt', 'tone' => 'success'],
|
||||
['label' => __('现金分成合计', 'jio'), 'value' => $this->format_amount($cash['total'] ?? '0', false), 'hint' => $count_hint($cash), 'icon' => 'dashicons-chart-area', 'tone' => 'info'],
|
||||
['label' => __('积分待结算', 'jio'), 'value' => $this->format_amount($points['pending'] ?? 0, true), 'url' => add_query_arg('currency', 'points', $pending_url), 'icon' => 'dashicons-clock', 'tone' => 'warning'],
|
||||
['label' => __('积分已结算', 'jio'), 'value' => $this->format_amount($points['settled'] ?? 0, true), 'url' => add_query_arg('currency', 'points', $settled_url), 'icon' => 'dashicons-yes-alt', 'tone' => 'success'],
|
||||
['label' => __('积分分成合计', 'jio'), 'value' => $this->format_amount($points['total'] ?? 0, true), 'hint' => $count_hint($points), 'icon' => 'dashicons-star-filled', 'tone' => 'info'],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -232,16 +232,16 @@ trait PayIncomeList {
|
||||
*/
|
||||
private function render_status_views(array $filters, array $query_args, array $counts): void {
|
||||
$base = array_diff_key($query_args, ['status' => true]);
|
||||
$views = ['' => [__('全部', 'aksrt'), array_sum($counts)]];
|
||||
$views = ['' => [__('全部', 'jio'), array_sum($counts)]];
|
||||
foreach ($this->status_labels() as $key => $label) {
|
||||
$views[$key] = [$label, (int) ($counts[$key] ?? 0)];
|
||||
}
|
||||
|
||||
echo '<nav class="aksrt-native-tabs aksrt-income-views" aria-label="' . esc_attr__('按结算状态查看', 'aksrt') . '">';
|
||||
echo '<nav class="jio-native-tabs jio-income-views" aria-label="' . esc_attr__('按结算状态查看', 'jio') . '">';
|
||||
foreach ($views as $key => [$label, $count]) {
|
||||
$url = $this->page_url($base + ($key !== '' ? ['status' => $key] : []));
|
||||
$active = $filters['status'] === $key;
|
||||
echo '<a class="aksrt-native-tabs__link' . ($active ? ' is-active' : '') . '" href="' . esc_url($url) . '"' . ($active ? ' aria-current="page"' : '') . '>'
|
||||
echo '<a class="jio-native-tabs__link' . ($active ? ' is-active' : '') . '" href="' . esc_url($url) . '"' . ($active ? ' aria-current="page"' : '') . '>'
|
||||
. esc_html($label) . ' <span class="count">' . esc_html(number_format_i18n($count)) . '</span></a>';
|
||||
}
|
||||
echo '</nav>';
|
||||
@@ -249,30 +249,30 @@ trait PayIncomeList {
|
||||
|
||||
/** @param array<string, mixed> $filters */
|
||||
private function render_filter_card(array $filters): void {
|
||||
NativeUi::card_open('', '', false, 'aksrt-income-filter-card');
|
||||
echo '<form method="get" class="aksrt-income-filters" role="search">';
|
||||
NativeUi::card_open('', '', false, 'jio-income-filter-card');
|
||||
echo '<form method="get" class="jio-income-filters" role="search">';
|
||||
echo '<input type="hidden" name="page" value="' . esc_attr(self::PAGE_SLUG) . '" />';
|
||||
if ($filters['status'] !== '') {
|
||||
echo '<input type="hidden" name="status" value="' . esc_attr($filters['status']) . '" />';
|
||||
}
|
||||
|
||||
echo '<label class="aksrt-income-filters__search"><span class="screen-reader-text">' . esc_html__('订单号', 'aksrt') . '</span>'
|
||||
. '<input type="search" name="order_number" value="' . esc_attr($filters['order_number']) . '" placeholder="' . esc_attr__('搜索订单号', 'aksrt') . '" /></label>';
|
||||
echo $this->filter_select('currency', __('分成币种', 'aksrt'), $this->currency_options(), $filters['currency']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->filter_select('order_status', __('订单状态', 'aksrt'), $this->order_status_options(), $filters['order_status']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<span class="aksrt-income-filters__range">'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('开始日期', 'aksrt') . '</span><input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '" /></label>'
|
||||
echo '<label class="jio-income-filters__search"><span class="screen-reader-text">' . esc_html__('订单号', 'jio') . '</span>'
|
||||
. '<input type="search" name="order_number" value="' . esc_attr($filters['order_number']) . '" placeholder="' . esc_attr__('搜索订单号', 'jio') . '" /></label>';
|
||||
echo $this->filter_select('currency', __('分成币种', 'jio'), $this->currency_options(), $filters['currency']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->filter_select('order_status', __('订单状态', 'jio'), $this->order_status_options(), $filters['order_status']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<span class="jio-income-filters__range">'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('开始日期', 'jio') . '</span><input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '" /></label>'
|
||||
. '<span aria-hidden="true">–</span>'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('结束日期', 'aksrt') . '</span><input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '" /></label>'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('结束日期', 'jio') . '</span><input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '" /></label>'
|
||||
. '</span>';
|
||||
foreach (['user_id' => __('作者 ID', 'aksrt'), 'post_id' => __('文章 ID', 'aksrt')] as $name => $label) {
|
||||
echo '<label><span class="screen-reader-text">' . esc_html($label) . '</span><input type="number" min="1" name="' . esc_attr($name) . '" class="aksrt-income-filters__id" value="'
|
||||
foreach (['user_id' => __('作者 ID', 'jio'), 'post_id' => __('文章 ID', 'jio')] as $name => $label) {
|
||||
echo '<label><span class="screen-reader-text">' . esc_html($label) . '</span><input type="number" min="1" name="' . esc_attr($name) . '" class="jio-income-filters__id" value="'
|
||||
. esc_attr($filters[$name] > 0 ? (string) $filters[$name] : '') . '" placeholder="' . esc_attr($label) . '" /></label>';
|
||||
}
|
||||
echo '<span class="aksrt-income-filters__submit"><button type="submit" class="button button-primary">' . esc_html__('筛选', 'aksrt') . '</button>';
|
||||
echo '<span class="jio-income-filters__submit"><button type="submit" class="button button-primary">' . esc_html__('筛选', 'jio') . '</button>';
|
||||
if (array_diff_key($this->list_query_args($filters), ['status' => true]) !== []) {
|
||||
$reset = $this->page_url($filters['status'] !== '' ? ['status' => $filters['status']] : []);
|
||||
echo '<a class="button" href="' . esc_url($reset) . '">' . esc_html__('重置', 'aksrt') . '</a>';
|
||||
echo '<a class="button" href="' . esc_url($reset) . '">' . esc_html__('重置', 'jio') . '</a>';
|
||||
}
|
||||
echo '</span></form>';
|
||||
NativeUi::card_close();
|
||||
@@ -290,60 +290,60 @@ trait PayIncomeList {
|
||||
/** @return array<string, string> */
|
||||
private function currency_options(): array {
|
||||
return [
|
||||
'' => __('全部币种', 'aksrt'),
|
||||
'cny' => __('现金(CNY)', 'aksrt'),
|
||||
'points' => __('积分', 'aksrt'),
|
||||
'' => __('全部币种', 'jio'),
|
||||
'cny' => __('现金(CNY)', 'jio'),
|
||||
'points' => __('积分', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
private function order_status_options(): array {
|
||||
return ['' => __('全部订单状态', 'aksrt')] + \AKSRT\Core\DomainModel::order_status_labels();
|
||||
return ['' => __('全部订单状态', 'jio')] + \Jio\Core\DomainModel::order_status_labels();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量操作栏:已选计数 + 「批量操作」下拉 + 应用。表格里的复选框通过 form="aksrt-income-bulk" 关联进来。
|
||||
* 批量操作栏:已选计数 + 「批量操作」下拉 + 应用。表格里的复选框通过 form="jio-income-bulk" 关联进来。
|
||||
* 各操作的确认文案挂在 option 的 data-confirm 上,由 admin-income.js 按所选操作取用。
|
||||
*/
|
||||
private function bulk_form_html(bool $can_settle, bool $has_pending): string {
|
||||
$settle_attrs = $can_settle
|
||||
? ' data-confirm="' . esc_attr__('确认立即结算选中的 {count} 条分成?金额会转入作者钱包,不可撤销。', 'aksrt') . '"'
|
||||
? ' data-confirm="' . esc_attr__('确认立即结算选中的 {count} 条分成?金额会转入作者钱包,不可撤销。', 'jio') . '"'
|
||||
: ' disabled';
|
||||
$settle_label = $can_settle ? __('立即结算', 'aksrt') : __('立即结算(分成功能未启用)', 'aksrt');
|
||||
$settle_label = $can_settle ? __('立即结算', 'jio') : __('立即结算(分成功能未启用)', 'jio');
|
||||
|
||||
return '<form id="aksrt-income-bulk" class="aksrt-native-bulk aksrt-income-bulk" method="post" action="' . esc_url(admin_url('admin-post.php')) . '">'
|
||||
. '<input type="hidden" name="action" value="aksrt_pay_bulk_income" />'
|
||||
return '<form id="jio-income-bulk" class="jio-native-bulk jio-income-bulk" method="post" action="' . esc_url(admin_url('admin-post.php')) . '">'
|
||||
. '<input type="hidden" name="action" value="jio_pay_bulk_income" />'
|
||||
. $this->operation_key_field()
|
||||
. wp_nonce_field('aksrt_pay_bulk_income', '_wpnonce', true, false)
|
||||
. '<span class="aksrt-income-bulk__count" data-aksrt-income-selected aria-live="polite">'
|
||||
. esc_html($has_pending ? __('未选择记录', 'aksrt') : __('本页没有待结算记录', 'aksrt')) . '</span>'
|
||||
. '<label class="screen-reader-text" for="aksrt-income-bulk-action">' . esc_html__('批量操作', 'aksrt') . '</label>'
|
||||
. '<select name="bulk_action" id="aksrt-income-bulk-action" data-aksrt-income-bulk-action>'
|
||||
. '<option value="">' . esc_html__('批量操作', 'aksrt') . '</option>'
|
||||
. wp_nonce_field('jio_pay_bulk_income', '_wpnonce', true, false)
|
||||
. '<span class="jio-income-bulk__count" data-jio-income-selected aria-live="polite">'
|
||||
. esc_html($has_pending ? __('未选择记录', 'jio') : __('本页没有待结算记录', 'jio')) . '</span>'
|
||||
. '<label class="screen-reader-text" for="jio-income-bulk-action">' . esc_html__('批量操作', 'jio') . '</label>'
|
||||
. '<select name="bulk_action" id="jio-income-bulk-action" data-jio-income-bulk-action>'
|
||||
. '<option value="">' . esc_html__('批量操作', 'jio') . '</option>'
|
||||
. '<option value="settle"' . $settle_attrs . '>' . esc_html($settle_label) . '</option>'
|
||||
. '<option value="delete" data-confirm="' . esc_attr__('确认删除选中的 {count} 条待结算分成?删除后作者不会获得这些分成,不可恢复。', 'aksrt') . '">' . esc_html__('删除', 'aksrt') . '</option>'
|
||||
. '<option value="delete" data-confirm="' . esc_attr__('确认删除选中的 {count} 条待结算分成?删除后作者不会获得这些分成,不可恢复。', 'jio') . '">' . esc_html__('删除', 'jio') . '</option>'
|
||||
. '</select>'
|
||||
. '<button type="submit" class="button" disabled data-aksrt-income-bulk-submit>' . esc_html__('应用', 'aksrt') . '</button>'
|
||||
. '<button type="submit" class="button" disabled data-jio-income-bulk-submit>' . esc_html__('应用', 'jio') . '</button>'
|
||||
. '</form>';
|
||||
}
|
||||
|
||||
private function delete_form_html(int $id): string {
|
||||
return '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-income-action"'
|
||||
. ' data-aksrt-confirm="' . esc_attr(sprintf(__('确认删除待结算分成记录 #%d?删除后作者不会获得这笔分成,不可恢复。', 'aksrt'), $id)) . '">'
|
||||
. '<input type="hidden" name="action" value="aksrt_pay_delete_income" />'
|
||||
return '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-income-action"'
|
||||
. ' data-jio-confirm="' . esc_attr(sprintf(__('确认删除待结算分成记录 #%d?删除后作者不会获得这笔分成,不可恢复。', 'jio'), $id)) . '">'
|
||||
. '<input type="hidden" name="action" value="jio_pay_delete_income" />'
|
||||
. '<input type="hidden" name="revenue_id" value="' . esc_attr((string) $id) . '" />'
|
||||
. wp_nonce_field('aksrt_pay_delete_income_' . $id, '_wpnonce', true, false)
|
||||
. '<button type="submit" class="button button-small is-danger">' . esc_html__('删除', 'aksrt') . '</button></form>';
|
||||
. wp_nonce_field('jio_pay_delete_income_' . $id, '_wpnonce', true, false)
|
||||
. '<button type="submit" class="button button-small is-danger">' . esc_html__('删除', 'jio') . '</button></form>';
|
||||
}
|
||||
|
||||
private function settle_form_html(int $id): string {
|
||||
return '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-income-action"'
|
||||
. ' data-aksrt-confirm="' . esc_attr(sprintf(__('确认立即结算分成记录 #%d?金额会转入作者钱包,不可撤销。', 'aksrt'), $id)) . '">'
|
||||
. '<input type="hidden" name="action" value="aksrt_pay_settle_income" />'
|
||||
return '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-income-action"'
|
||||
. ' data-jio-confirm="' . esc_attr(sprintf(__('确认立即结算分成记录 #%d?金额会转入作者钱包,不可撤销。', 'jio'), $id)) . '">'
|
||||
. '<input type="hidden" name="action" value="jio_pay_settle_income" />'
|
||||
. '<input type="hidden" name="revenue_id" value="' . esc_attr((string) $id) . '" />'
|
||||
. $this->operation_key_field()
|
||||
. wp_nonce_field('aksrt_pay_settle_income_' . $id, '_wpnonce', true, false)
|
||||
. '<button type="submit" class="button button-small">' . esc_html__('立即结算', 'aksrt') . '</button></form>';
|
||||
. wp_nonce_field('jio_pay_settle_income_' . $id, '_wpnonce', true, false)
|
||||
. '<button type="submit" class="button button-small">' . esc_html__('立即结算', 'jio') . '</button></form>';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class PayMenus {
|
||||
|
||||
public const CAPABILITY = 'manage_options';
|
||||
public const MENU_SLUG = 'aksrt_pay';
|
||||
public const MENU_SLUG = 'jio_pay';
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
@@ -42,85 +42,85 @@ class PayMenus {
|
||||
'slug' => self::MENU_SLUG,
|
||||
'icon' => 'dashicons-cart',
|
||||
'desc' => '',
|
||||
'title' => __('概览', 'aksrt'),
|
||||
'title' => __('概览', 'jio'),
|
||||
'callback' => [$this, 'render_overview_page'],
|
||||
],
|
||||
'orders' => [
|
||||
'slug' => 'aksrt_orders',
|
||||
'slug' => 'jio_orders',
|
||||
'icon' => 'dashicons-list-view',
|
||||
'desc' => __('收款确认、关闭与退款标记', 'aksrt'),
|
||||
'title' => __('订单明细', 'aksrt'),
|
||||
'desc' => __('收款确认、关闭与退款标记', 'jio'),
|
||||
'title' => __('订单明细', 'jio'),
|
||||
'callback' => static function (): void {
|
||||
if (class_exists('AKSRT\Admin\Native\PayOrders')) {
|
||||
if (class_exists('Jio\Admin\Native\PayOrders')) {
|
||||
PayOrders::get_instance()->render_orders_page();
|
||||
return;
|
||||
}
|
||||
wp_die(esc_html__('订单模块未初始化。', 'aksrt'));
|
||||
wp_die(esc_html__('订单模块未初始化。', 'jio'));
|
||||
},
|
||||
],
|
||||
'products' => [
|
||||
'slug' => 'aksrt_pay_products',
|
||||
'slug' => 'jio_pay_products',
|
||||
'icon' => 'dashicons-media-document',
|
||||
'desc' => __('付费文章定价与销量', 'aksrt'),
|
||||
'title' => __('付费文章', 'aksrt'),
|
||||
'desc' => __('付费文章定价与销量', 'jio'),
|
||||
'title' => __('付费文章', 'jio'),
|
||||
'callback' => static function (): void {
|
||||
if (class_exists('AKSRT\\Admin\\Native\\PayProducts')) {
|
||||
if (class_exists('Jio\\Admin\\Native\\PayProducts')) {
|
||||
PayProducts::get_instance()->render_products_page();
|
||||
return;
|
||||
}
|
||||
wp_die(esc_html__('付费文章模块未初始化。', 'aksrt'));
|
||||
wp_die(esc_html__('付费文章模块未初始化。', 'jio'));
|
||||
},
|
||||
],
|
||||
'coupons' => [
|
||||
'slug' => 'aksrt_coupons',
|
||||
'slug' => 'jio_coupons',
|
||||
'icon' => 'dashicons-tickets-alt',
|
||||
'desc' => __('创建与批量生成优惠码', 'aksrt'),
|
||||
'title' => __('优惠码', 'aksrt'),
|
||||
'desc' => __('创建与批量生成优惠码', 'jio'),
|
||||
'title' => __('优惠码', 'jio'),
|
||||
'callback' => static function (): void {
|
||||
if (class_exists('AKSRT\\Admin\\Native\\PayCoupons')) {
|
||||
if (class_exists('Jio\\Admin\\Native\\PayCoupons')) {
|
||||
PayCoupons::get_instance()->render_page();
|
||||
return;
|
||||
}
|
||||
wp_die(esc_html__('优惠码模块未初始化。', 'aksrt'));
|
||||
wp_die(esc_html__('优惠码模块未初始化。', 'jio'));
|
||||
},
|
||||
],
|
||||
'charge_cards' => [
|
||||
'slug' => 'aksrt_charge_cards',
|
||||
'slug' => 'jio_charge_cards',
|
||||
'icon' => 'dashicons-id-alt',
|
||||
'desc' => __('充值卡密生成与导出', 'aksrt'),
|
||||
'title' => __('卡密管理', 'aksrt'),
|
||||
'desc' => __('充值卡密生成与导出', 'jio'),
|
||||
'title' => __('卡密管理', 'jio'),
|
||||
'callback' => static function (): void {
|
||||
if (class_exists('AKSRT\\Admin\\Native\\PayChargeCards')) {
|
||||
if (class_exists('Jio\\Admin\\Native\\PayChargeCards')) {
|
||||
PayChargeCards::get_instance()->render_charge_cards_page();
|
||||
return;
|
||||
}
|
||||
wp_die(esc_html__('卡密模块未初始化。', 'aksrt'));
|
||||
wp_die(esc_html__('卡密模块未初始化。', 'jio'));
|
||||
},
|
||||
],
|
||||
'income' => [
|
||||
'slug' => 'aksrt_income',
|
||||
'slug' => 'jio_income',
|
||||
'icon' => 'dashicons-chart-pie',
|
||||
'desc' => __('创作者分成结算', 'aksrt'),
|
||||
'title' => __('创作分成', 'aksrt'),
|
||||
'desc' => __('创作者分成结算', 'jio'),
|
||||
'title' => __('创作分成', 'jio'),
|
||||
'callback' => static function (): void {
|
||||
if (class_exists('AKSRT\\Admin\\Native\\PayIncome')) {
|
||||
if (class_exists('Jio\\Admin\\Native\\PayIncome')) {
|
||||
PayIncome::get_instance()->render_income_page();
|
||||
return;
|
||||
}
|
||||
wp_die(esc_html__('分成模块未初始化。', 'aksrt'));
|
||||
wp_die(esc_html__('分成模块未初始化。', 'jio'));
|
||||
},
|
||||
],
|
||||
'withdraw' => [
|
||||
'slug' => 'aksrt_withdraw',
|
||||
'slug' => 'jio_withdraw',
|
||||
'icon' => 'dashicons-money-alt',
|
||||
'desc' => __('提现申请审核与打款', 'aksrt'),
|
||||
'title' => __('提现审核', 'aksrt'),
|
||||
'desc' => __('提现申请审核与打款', 'jio'),
|
||||
'title' => __('提现审核', 'jio'),
|
||||
'callback' => static function (): void {
|
||||
if (class_exists('AKSRT\\Admin\\Native\\PayWithdrawals')) {
|
||||
if (class_exists('Jio\\Admin\\Native\\PayWithdrawals')) {
|
||||
PayWithdrawals::get_instance()->render_withdrawals_page();
|
||||
return;
|
||||
}
|
||||
wp_die(esc_html__('提现模块未初始化。', 'aksrt'));
|
||||
wp_die(esc_html__('提现模块未初始化。', 'jio'));
|
||||
},
|
||||
],
|
||||
];
|
||||
@@ -132,8 +132,8 @@ class PayMenus {
|
||||
}
|
||||
|
||||
add_menu_page(
|
||||
__('支付中心', 'aksrt'),
|
||||
__('支付中心', 'aksrt'),
|
||||
__('支付中心', 'jio'),
|
||||
__('支付中心', 'jio'),
|
||||
self::CAPABILITY,
|
||||
self::MENU_SLUG,
|
||||
[$this, 'render_overview_page'],
|
||||
@@ -160,20 +160,20 @@ class PayMenus {
|
||||
|
||||
$page = isset($_GET['page']) ? sanitize_key((string) wp_unslash($_GET['page'])) : '';
|
||||
|
||||
if ($page !== 'aksrt_withdraw' && class_exists('\AKSRT\Payment\Withdrawal')) {
|
||||
$pending = (int) \AKSRT\Payment\Withdrawal::get_instance()->get_count(['status' => \AKSRT\Payment\Withdrawal::STATUS_PENDING]);
|
||||
$processing = (int) \AKSRT\Payment\Withdrawal::get_instance()->get_count(['status' => \AKSRT\Payment\Withdrawal::STATUS_PROCESSING]);
|
||||
if ($page !== 'jio_withdraw' && class_exists('\Jio\Payment\Withdrawal')) {
|
||||
$pending = (int) \Jio\Payment\Withdrawal::get_instance()->get_count(['status' => \Jio\Payment\Withdrawal::STATUS_PENDING]);
|
||||
$processing = (int) \Jio\Payment\Withdrawal::get_instance()->get_count(['status' => \Jio\Payment\Withdrawal::STATUS_PROCESSING]);
|
||||
$active = $pending + $processing;
|
||||
if ($active > 0) {
|
||||
$url = admin_url('admin.php?page=aksrt_withdraw&status=pending');
|
||||
$url = admin_url('admin.php?page=jio_withdraw&status=pending');
|
||||
echo '<div class="notice notice-info is-dismissible">';
|
||||
echo '<h3>' . esc_html__('提现申请待处理', 'aksrt') . '</h3>';
|
||||
echo '<h3>' . esc_html__('提现申请待处理', 'jio') . '</h3>';
|
||||
echo '<p>' . esc_html(sprintf(
|
||||
/* translators: %d: open withdrawal count */
|
||||
__('您有 %d 个提现申请待处理', 'aksrt'),
|
||||
__('您有 %d 个提现申请待处理', 'jio'),
|
||||
$active
|
||||
)) . '</p>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'aksrt') . '</a></p>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'jio') . '</a></p>';
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* PSR-4 要求与宿主类同目录);这里保留入口、admin-post 处理器与两个视图共用的展示助手。
|
||||
* 页面样式在 assets/css/admin/orders.css,危险操作确认在 assets/js/admin-orders.js。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Core\DomainModel;
|
||||
use Jio\Core\DomainModel;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -21,7 +21,7 @@ class PayOrders {
|
||||
use PayOrdersDetail;
|
||||
|
||||
public const CAPABILITY = 'manage_options';
|
||||
public const PAGE_SLUG = 'aksrt_orders';
|
||||
public const PAGE_SLUG = 'jio_orders';
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
@@ -40,11 +40,11 @@ class PayOrders {
|
||||
return;
|
||||
}
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']);
|
||||
add_action('admin_post_aksrt_pay_update_order_status', [$this, 'handle_update_order_status']);
|
||||
add_action('admin_post_aksrt_pay_update_order_note', [$this, 'handle_update_order_note']);
|
||||
add_action('admin_post_aksrt_pay_clear_closed_orders', [$this, 'handle_clear_closed_orders']);
|
||||
add_action('admin_post_aksrt_pay_delete_order', [$this, 'handle_delete_order']);
|
||||
add_action('admin_post_aksrt_pay_refund_order', [$this, 'handle_refund_order']);
|
||||
add_action('admin_post_jio_pay_update_order_status', [$this, 'handle_update_order_status']);
|
||||
add_action('admin_post_jio_pay_update_order_note', [$this, 'handle_update_order_note']);
|
||||
add_action('admin_post_jio_pay_clear_closed_orders', [$this, 'handle_clear_closed_orders']);
|
||||
add_action('admin_post_jio_pay_delete_order', [$this, 'handle_delete_order']);
|
||||
add_action('admin_post_jio_pay_refund_order', [$this, 'handle_refund_order']);
|
||||
}
|
||||
|
||||
public function enqueue_assets(): void {
|
||||
@@ -54,17 +54,17 @@ class PayOrders {
|
||||
}
|
||||
$css = '/assets/css/admin/orders.css';
|
||||
$js = '/assets/js/admin-orders.js';
|
||||
wp_enqueue_style('aksrt-admin-orders', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-orders', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-orders', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-orders', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
public function render_orders_page(): void {
|
||||
$this->ensure_cap();
|
||||
NativeUi::page_open('aksrt-orders-page');
|
||||
NativeUi::page_open('jio-orders-page');
|
||||
|
||||
if (!class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
NativeUi::header(__('订单明细', 'aksrt'), '', 'dashicons-list-view');
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('订单服务不可用。', 'aksrt') . '</p></div>';
|
||||
if (!class_exists('\Jio\Payment\OrderRepository')) {
|
||||
NativeUi::header(__('订单明细', 'jio'), '', 'dashicons-list-view');
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('订单服务不可用。', 'jio') . '</p></div>';
|
||||
NativeUi::page_close();
|
||||
return;
|
||||
}
|
||||
@@ -87,13 +87,13 @@ class PayOrders {
|
||||
$this->ensure_cap();
|
||||
$order_number = isset($_POST['order_number']) ? sanitize_text_field((string) wp_unslash($_POST['order_number'])) : '';
|
||||
$new_status = isset($_POST['new_status']) ? sanitize_key((string) wp_unslash($_POST['new_status'])) : '';
|
||||
check_admin_referer('aksrt_pay_update_order_status_' . $order_number);
|
||||
check_admin_referer('jio_pay_update_order_status_' . $order_number);
|
||||
|
||||
$ok = false;
|
||||
if ($order_number !== '' && $new_status !== '' && class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
$ok = \AKSRT\Payment\OrderRepository::get_instance()->update_status($order_number, $new_status);
|
||||
if ($order_number !== '' && $new_status !== '' && class_exists('\Jio\Payment\OrderRepository')) {
|
||||
$ok = \Jio\Payment\OrderRepository::get_instance()->update_status($order_number, $new_status);
|
||||
}
|
||||
$this->redirect_back(['aksrt_pay_msg' => $ok ? 'order_updated' : 'order_failed']);
|
||||
$this->redirect_back(['jio_pay_msg' => $ok ? 'order_updated' : 'order_failed']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,11 +105,11 @@ class PayOrders {
|
||||
public function handle_refund_order(): void {
|
||||
$this->ensure_cap();
|
||||
$order_number = isset($_POST['order_number']) ? sanitize_text_field((string) wp_unslash($_POST['order_number'])) : '';
|
||||
check_admin_referer('aksrt_pay_refund_order_' . $order_number);
|
||||
check_admin_referer('jio_pay_refund_order_' . $order_number);
|
||||
|
||||
$code = 'order_refund_failed';
|
||||
if ($order_number !== '' && class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
$repo = \AKSRT\Payment\OrderRepository::get_instance();
|
||||
if ($order_number !== '' && class_exists('\Jio\Payment\OrderRepository')) {
|
||||
$repo = \Jio\Payment\OrderRepository::get_instance();
|
||||
$order = $repo->get($order_number);
|
||||
if (!is_array($order)) {
|
||||
$code = 'order_refund_missing';
|
||||
@@ -118,37 +118,37 @@ class PayOrders {
|
||||
} else {
|
||||
$ok = $repo->update_status($order_number, DomainModel::ORDER_STATUS_REFUNDED, [
|
||||
'allow_refund' => true,
|
||||
'note' => __('管理员在订单明细中标记退款', 'aksrt'),
|
||||
'note' => __('管理员在订单明细中标记退款', 'jio'),
|
||||
]);
|
||||
$code = $ok ? 'order_refunded' : 'order_refund_failed';
|
||||
}
|
||||
}
|
||||
$this->redirect_back(['aksrt_pay_msg' => $code]);
|
||||
$this->redirect_back(['jio_pay_msg' => $code]);
|
||||
}
|
||||
|
||||
public function handle_update_order_note(): void {
|
||||
$this->ensure_cap();
|
||||
$order_number = isset($_POST['order_number']) ? sanitize_text_field((string) wp_unslash($_POST['order_number'])) : '';
|
||||
$admin_note = isset($_POST['admin_note']) ? sanitize_textarea_field((string) wp_unslash($_POST['admin_note'])) : '';
|
||||
check_admin_referer('aksrt_pay_update_order_note_' . $order_number);
|
||||
check_admin_referer('jio_pay_update_order_note_' . $order_number);
|
||||
|
||||
$ok = false;
|
||||
if ($order_number !== '' && class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
$ok = \AKSRT\Payment\OrderRepository::get_instance()->update_admin_note($order_number, $admin_note);
|
||||
if ($order_number !== '' && class_exists('\Jio\Payment\OrderRepository')) {
|
||||
$ok = \Jio\Payment\OrderRepository::get_instance()->update_admin_note($order_number, $admin_note);
|
||||
}
|
||||
$this->redirect_back(['aksrt_pay_msg' => $ok ? 'order_note_updated' : 'order_note_failed']);
|
||||
$this->redirect_back(['jio_pay_msg' => $ok ? 'order_note_updated' : 'order_note_failed']);
|
||||
}
|
||||
|
||||
public function handle_clear_closed_orders(): void {
|
||||
$this->ensure_cap();
|
||||
check_admin_referer('aksrt_pay_clear_closed_orders');
|
||||
check_admin_referer('jio_pay_clear_closed_orders');
|
||||
$days = isset($_POST['days']) ? max(1, min(3650, (int) $_POST['days'])) : 14;
|
||||
$deleted = 0;
|
||||
if (class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
$deleted = \AKSRT\Payment\OrderRepository::get_instance()->clear_closed_orders($days);
|
||||
if (class_exists('\Jio\Payment\OrderRepository')) {
|
||||
$deleted = \Jio\Payment\OrderRepository::get_instance()->clear_closed_orders($days);
|
||||
}
|
||||
$this->redirect_back([
|
||||
'aksrt_pay_msg' => $deleted > 0 ? 'orders_cleared' : 'orders_clear_empty',
|
||||
'jio_pay_msg' => $deleted > 0 ? 'orders_cleared' : 'orders_clear_empty',
|
||||
'cleared' => $deleted,
|
||||
], true);
|
||||
}
|
||||
@@ -156,10 +156,10 @@ class PayOrders {
|
||||
public function handle_delete_order(): void {
|
||||
$this->ensure_cap();
|
||||
$order_number = isset($_POST['order_number']) ? sanitize_text_field((string) wp_unslash($_POST['order_number'])) : '';
|
||||
check_admin_referer('aksrt_pay_delete_order_' . $order_number);
|
||||
check_admin_referer('jio_pay_delete_order_' . $order_number);
|
||||
$ok = false;
|
||||
if ($order_number !== '' && class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
$repo = \AKSRT\Payment\OrderRepository::get_instance();
|
||||
if ($order_number !== '' && class_exists('\Jio\Payment\OrderRepository')) {
|
||||
$repo = \Jio\Payment\OrderRepository::get_instance();
|
||||
$order = $repo->get($order_number);
|
||||
// 只允许硬删已关闭订单。
|
||||
if (is_array($order) && (string) ($order['status'] ?? '') === DomainModel::ORDER_STATUS_CLOSED) {
|
||||
@@ -167,7 +167,7 @@ class PayOrders {
|
||||
}
|
||||
}
|
||||
// 删除成功后详情已不存在,回到列表;失败时留在原处。
|
||||
$this->redirect_back(['aksrt_pay_msg' => $ok ? 'order_deleted' : 'order_delete_failed'], $ok);
|
||||
$this->redirect_back(['jio_pay_msg' => $ok ? 'order_deleted' : 'order_delete_failed'], $ok);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,7 +180,7 @@ class PayOrders {
|
||||
if (is_string($referer) && $referer !== '') {
|
||||
parse_str((string) wp_parse_url($referer, PHP_URL_QUERY), $parsed);
|
||||
if (($parsed['page'] ?? '') === self::PAGE_SLUG) {
|
||||
unset($parsed['page'], $parsed['aksrt_pay_msg'], $parsed['cleared']);
|
||||
unset($parsed['page'], $parsed['jio_pay_msg'], $parsed['cleared']);
|
||||
if ($to_list) {
|
||||
unset($parsed['order']);
|
||||
}
|
||||
@@ -198,12 +198,12 @@ class PayOrders {
|
||||
/** 当前状态可执行的动作按钮;没有可执行动作时返回空串。 */
|
||||
private function order_actions_html(string $order_number, string $status, bool $small = true): string {
|
||||
$labels = [
|
||||
DomainModel::ORDER_STATUS_PAID => __('确认收款', 'aksrt'),
|
||||
DomainModel::ORDER_STATUS_CLOSED => __('关闭订单', 'aksrt'),
|
||||
DomainModel::ORDER_STATUS_PAID => __('确认收款', 'jio'),
|
||||
DomainModel::ORDER_STATUS_CLOSED => __('关闭订单', 'jio'),
|
||||
];
|
||||
$confirms = [
|
||||
DomainModel::ORDER_STATUS_PAID => __('确认将该订单标记为已支付?系统会立即为用户发放对应权益。', 'aksrt'),
|
||||
DomainModel::ORDER_STATUS_CLOSED => __('确认关闭该订单?关闭后用户无法继续支付。', 'aksrt'),
|
||||
DomainModel::ORDER_STATUS_PAID => __('确认将该订单标记为已支付?系统会立即为用户发放对应权益。', 'jio'),
|
||||
DomainModel::ORDER_STATUS_CLOSED => __('确认关闭该订单?关闭后用户无法继续支付。', 'jio'),
|
||||
];
|
||||
|
||||
$html = '';
|
||||
@@ -213,8 +213,8 @@ class PayOrders {
|
||||
continue;
|
||||
}
|
||||
$html .= $this->action_form(
|
||||
'aksrt_pay_update_order_status',
|
||||
'aksrt_pay_update_order_status_' . $order_number,
|
||||
'jio_pay_update_order_status',
|
||||
'jio_pay_update_order_status_' . $order_number,
|
||||
['order_number' => $order_number, 'new_status' => $target],
|
||||
$labels[$target],
|
||||
$confirms[$target],
|
||||
@@ -225,22 +225,22 @@ class PayOrders {
|
||||
// 已支付是已退款的唯一前置状态。
|
||||
if ($status === DomainModel::ORDER_STATUS_PAID) {
|
||||
$html .= $this->action_form(
|
||||
'aksrt_pay_refund_order',
|
||||
'aksrt_pay_refund_order_' . $order_number,
|
||||
'jio_pay_refund_order',
|
||||
'jio_pay_refund_order_' . $order_number,
|
||||
['order_number' => $order_number],
|
||||
__('标记退款', 'aksrt'),
|
||||
__('只会把订单标记为已退款,不会自动原路退回款项。确认继续?此操作不可撤销。', 'aksrt'),
|
||||
__('标记退款', 'jio'),
|
||||
__('只会把订单标记为已退款,不会自动原路退回款项。确认继续?此操作不可撤销。', 'jio'),
|
||||
true,
|
||||
$small
|
||||
);
|
||||
}
|
||||
if ($status === DomainModel::ORDER_STATUS_CLOSED) {
|
||||
$html .= $this->action_form(
|
||||
'aksrt_pay_delete_order',
|
||||
'aksrt_pay_delete_order_' . $order_number,
|
||||
'jio_pay_delete_order',
|
||||
'jio_pay_delete_order_' . $order_number,
|
||||
['order_number' => $order_number],
|
||||
__('删除', 'aksrt'),
|
||||
__('确认永久删除此订单?不可恢复。', 'aksrt'),
|
||||
__('删除', 'jio'),
|
||||
__('确认永久删除此订单?不可恢复。', 'jio'),
|
||||
true,
|
||||
$small
|
||||
);
|
||||
@@ -249,13 +249,13 @@ class PayOrders {
|
||||
}
|
||||
|
||||
/**
|
||||
* 单按钮 POST 表单。确认文案放在 data-aksrt-confirm,由 admin-orders.js 拦截提交。
|
||||
* 单按钮 POST 表单。确认文案放在 data-jio-confirm,由 admin-orders.js 拦截提交。
|
||||
*
|
||||
* @param array<string, string> $fields
|
||||
*/
|
||||
private function action_form(string $action, string $nonce_action, array $fields, string $label, string $confirm = '', bool $danger = false, bool $small = true): string {
|
||||
$html = '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-orders-action"'
|
||||
. ($confirm !== '' ? ' data-aksrt-confirm="' . esc_attr($confirm) . '"' : '') . '>';
|
||||
$html = '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-orders-action"'
|
||||
. ($confirm !== '' ? ' data-jio-confirm="' . esc_attr($confirm) . '"' : '') . '>';
|
||||
$html .= '<input type="hidden" name="action" value="' . esc_attr($action) . '" />';
|
||||
foreach ($fields as $name => $value) {
|
||||
$html .= '<input type="hidden" name="' . esc_attr($name) . '" value="' . esc_attr($value) . '" />';
|
||||
@@ -312,11 +312,11 @@ class PayOrders {
|
||||
private function payment_method_label(string $method): string {
|
||||
if ($this->payment_labels === null) {
|
||||
$this->payment_labels = [
|
||||
'points' => __('积分', 'aksrt'),
|
||||
'free' => __('免费', 'aksrt'),
|
||||
'points' => __('积分', 'jio'),
|
||||
'free' => __('免费', 'jio'),
|
||||
];
|
||||
if (class_exists('\AKSRT\Payment\Gateway')) {
|
||||
foreach (\AKSRT\Payment\Gateway::get_instance()->get_all() as $id => $gateway) {
|
||||
if (class_exists('\Jio\Payment\Gateway')) {
|
||||
foreach (\Jio\Payment\Gateway::get_instance()->get_all() as $id => $gateway) {
|
||||
$this->payment_labels[(string) $id] = $gateway->get_name();
|
||||
}
|
||||
}
|
||||
@@ -327,7 +327,7 @@ class PayOrders {
|
||||
/** @return string[] */
|
||||
private function third_party_gateway_ids(): array {
|
||||
return array_values(array_diff(
|
||||
array_keys(\AKSRT\Payment\Gateway::get_instance()->get_all()),
|
||||
array_keys(\Jio\Payment\Gateway::get_instance()->get_all()),
|
||||
['balance', 'points']
|
||||
));
|
||||
}
|
||||
@@ -340,7 +340,7 @@ class PayOrders {
|
||||
/** @param array<string,mixed> $order */
|
||||
private function order_amount(array $order): string {
|
||||
if ($this->is_points_order($order)) {
|
||||
return max(0, (int) ($order['amount'] ?? 0)) . ' ' . __('积分', 'aksrt');
|
||||
return max(0, (int) ($order['amount'] ?? 0)) . ' ' . __('积分', 'jio');
|
||||
}
|
||||
return $this->cash_amount((string) ($order['amount'] ?? '0'), (string) ($order['currency'] ?? ''));
|
||||
}
|
||||
@@ -351,35 +351,35 @@ class PayOrders {
|
||||
|
||||
private function cash_decimal(string $value): string {
|
||||
try {
|
||||
return \AKSRT\Ledger\AssetAmount::balance($value)->decimal();
|
||||
return \Jio\Ledger\AssetAmount::balance($value)->decimal();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return '0.00';
|
||||
}
|
||||
}
|
||||
|
||||
private function render_flash(): void {
|
||||
if (empty($_GET['aksrt_pay_msg'])) {
|
||||
if (empty($_GET['jio_pay_msg'])) {
|
||||
return;
|
||||
}
|
||||
$code = sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg']));
|
||||
$code = sanitize_key((string) wp_unslash($_GET['jio_pay_msg']));
|
||||
// 码 => [文案, 是否失败]
|
||||
$map = [
|
||||
'order_updated' => [__('订单状态已更新。', 'aksrt'), false],
|
||||
'order_failed' => [__('订单状态更新失败,订单可能已被其他操作改变,请刷新后重试。', 'aksrt'), true],
|
||||
'order_refunded' => [__('订单已标记为已退款。款项请在支付渠道后台原路退回。', 'aksrt'), false],
|
||||
'order_refund_failed' => [__('订单标记退款失败。', 'aksrt'), true],
|
||||
'order_refund_missing' => [__('订单不存在。', 'aksrt'), true],
|
||||
'order_refund_not_paid' => [__('仅已支付订单可以标记退款。', 'aksrt'), true],
|
||||
'order_note_updated' => [__('运营备注已保存。', 'aksrt'), false],
|
||||
'order_note_failed' => [__('运营备注保存失败。', 'aksrt'), true],
|
||||
'order_updated' => [__('订单状态已更新。', 'jio'), false],
|
||||
'order_failed' => [__('订单状态更新失败,订单可能已被其他操作改变,请刷新后重试。', 'jio'), true],
|
||||
'order_refunded' => [__('订单已标记为已退款。款项请在支付渠道后台原路退回。', 'jio'), false],
|
||||
'order_refund_failed' => [__('订单标记退款失败。', 'jio'), true],
|
||||
'order_refund_missing' => [__('订单不存在。', 'jio'), true],
|
||||
'order_refund_not_paid' => [__('仅已支付订单可以标记退款。', 'jio'), true],
|
||||
'order_note_updated' => [__('运营备注已保存。', 'jio'), false],
|
||||
'order_note_failed' => [__('运营备注保存失败。', 'jio'), true],
|
||||
'orders_cleared' => [sprintf(
|
||||
/* translators: %d deleted count */
|
||||
__('已清理 %d 条关闭订单。', 'aksrt'),
|
||||
__('已清理 %d 条关闭订单。', 'jio'),
|
||||
max(0, (int) ($_GET['cleared'] ?? 0))
|
||||
), false],
|
||||
'orders_clear_empty' => [__('没有可清理的关闭订单。', 'aksrt'), false],
|
||||
'order_deleted' => [__('订单已删除。', 'aksrt'), false],
|
||||
'order_delete_failed' => [__('订单删除失败(仅允许删除已关闭订单)。', 'aksrt'), true],
|
||||
'orders_clear_empty' => [__('没有可清理的关闭订单。', 'jio'), false],
|
||||
'order_deleted' => [__('订单已删除。', 'jio'), false],
|
||||
'order_delete_failed' => [__('订单删除失败(仅允许删除已关闭订单)。', 'jio'), true],
|
||||
];
|
||||
if (!isset($map[$code])) {
|
||||
return;
|
||||
@@ -390,7 +390,7 @@ class PayOrders {
|
||||
|
||||
private function ensure_cap(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,52 +4,52 @@
|
||||
*
|
||||
* 作为 flat sibling trait 被 PayOrders 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Core\DomainModel;
|
||||
use AKSRT\Payment\OrderRepository;
|
||||
use Jio\Core\DomainModel;
|
||||
use Jio\Payment\OrderRepository;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait PayOrdersDetail {
|
||||
|
||||
private function render_order_detail_page(string $order_number): void {
|
||||
$back = NativeUi::button_link(__('返回订单列表', 'aksrt'), $this->list_back_url(), false, 'dashicons-arrow-left-alt');
|
||||
$back = NativeUi::button_link(__('返回订单列表', 'jio'), $this->list_back_url(), false, 'dashicons-arrow-left-alt');
|
||||
|
||||
$repo = OrderRepository::get_instance();
|
||||
$order = $repo->get($order_number);
|
||||
if (!$order) {
|
||||
echo '<div class="aksrt-native-header"><div class="aksrt-native-header__main"><h1 class="aksrt-native-header__title">'
|
||||
. esc_html__('订单详情', 'aksrt') . '</h1></div><div class="aksrt-native-header__actions">' . $back . '</div></div>';
|
||||
echo '<div class="jio-native-header"><div class="jio-native-header__main"><h1 class="jio-native-header__title">'
|
||||
. esc_html__('订单详情', 'jio') . '</h1></div><div class="jio-native-header__actions">' . $back . '</div></div>';
|
||||
echo '<hr class="wp-header-end">';
|
||||
$this->render_flash();
|
||||
echo NativeUi::empty_state(__('订单不存在', 'aksrt'), __('订单可能已被删除,或订单号有误。', 'aksrt'), 'dashicons-search'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo NativeUi::empty_state(__('订单不存在', 'jio'), __('订单可能已被删除,或订单号有误。', 'jio'), 'dashicons-search'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
return;
|
||||
}
|
||||
|
||||
$status = (string) ($order['status'] ?? '');
|
||||
$context = $repo->get_admin_order_context($order_number);
|
||||
|
||||
echo '<div class="aksrt-native-header aksrt-orders-detail__header"><div class="aksrt-native-header__main">';
|
||||
echo '<h1 class="aksrt-native-header__title"><span class="aksrt-native-header__icon dashicons dashicons-media-spreadsheet" aria-hidden="true"></span>'
|
||||
. esc_html__('订单详情', 'aksrt') . '</h1>';
|
||||
echo '<p class="aksrt-native-header__desc"><span class="aksrt-orders-number">' . esc_html($order_number) . '</span> '
|
||||
echo '<div class="jio-native-header jio-orders-detail__header"><div class="jio-native-header__main">';
|
||||
echo '<h1 class="jio-native-header__title"><span class="jio-native-header__icon dashicons dashicons-media-spreadsheet" aria-hidden="true"></span>'
|
||||
. esc_html__('订单详情', 'jio') . '</h1>';
|
||||
echo '<p class="jio-native-header__desc"><span class="jio-orders-number">' . esc_html($order_number) . '</span> '
|
||||
. NativeUi::status_badge($status, $this->status_badge_map()) . '</p>';
|
||||
echo '</div><div class="aksrt-native-header__actions">' . $back . '</div></div>';
|
||||
echo '</div><div class="jio-native-header__actions">' . $back . '</div></div>';
|
||||
echo '<hr class="wp-header-end">';
|
||||
$this->render_flash();
|
||||
|
||||
echo '<div class="aksrt-orders-detail">';
|
||||
echo '<div class="aksrt-orders-detail__main">';
|
||||
echo '<div class="jio-orders-detail">';
|
||||
echo '<div class="jio-orders-detail__main">';
|
||||
$this->render_detail_summary($order, $context);
|
||||
$this->render_detail_timeline($order);
|
||||
$this->render_detail_snapshot($order);
|
||||
echo '</div>';
|
||||
|
||||
echo '<aside class="aksrt-orders-detail__side">';
|
||||
echo '<aside class="jio-orders-detail__side">';
|
||||
$this->render_detail_actions($order_number, $status);
|
||||
$this->render_detail_buyer($order, $context);
|
||||
$this->render_detail_note($order_number, $order, $context);
|
||||
@@ -72,31 +72,31 @@ trait PayOrdersDetail {
|
||||
}
|
||||
}
|
||||
|
||||
NativeUi::card_open(__('订单概要', 'aksrt'));
|
||||
echo '<dl class="aksrt-orders-dl">';
|
||||
$this->dl_row(__('商品', 'aksrt'), $product);
|
||||
$this->dl_row(__('商品类型', 'aksrt'), esc_html($this->product_type_label($type) . ($id > 0 ? ' · #' . $id : '')));
|
||||
$this->dl_row(__('支付方式', 'aksrt'), esc_html($this->payment_method_label((string) ($order['payment_method'] ?? ''))));
|
||||
NativeUi::card_open(__('订单概要', 'jio'));
|
||||
echo '<dl class="jio-orders-dl">';
|
||||
$this->dl_row(__('商品', 'jio'), $product);
|
||||
$this->dl_row(__('商品类型', 'jio'), esc_html($this->product_type_label($type) . ($id > 0 ? ' · #' . $id : '')));
|
||||
$this->dl_row(__('支付方式', 'jio'), esc_html($this->payment_method_label((string) ($order['payment_method'] ?? ''))));
|
||||
$txn = (string) ($order['payment_transaction_id'] ?? '');
|
||||
$this->dl_row(__('渠道交易号', 'aksrt'), $txn !== '' ? '<code class="aksrt-native-mono">' . esc_html($txn) . '</code>' : '<span class="aksrt-native-muted">—</span>');
|
||||
$this->dl_row(__('渠道交易号', 'jio'), $txn !== '' ? '<code class="jio-native-mono">' . esc_html($txn) . '</code>' : '<span class="jio-native-muted">—</span>');
|
||||
echo '</dl>';
|
||||
|
||||
// 金额拆分:只在有折扣 / 运费时展开,否则只显示实付。
|
||||
$discount = (string) ($pricing['discount'] ?? '0');
|
||||
$shipping = (string) ($pricing['shipping_fee'] ?? '0');
|
||||
echo '<div class="aksrt-orders-amounts">';
|
||||
echo '<div class="jio-orders-amounts">';
|
||||
if ($this->has_amount($discount) || $this->has_amount($shipping)) {
|
||||
$this->amount_row(__('商品金额', 'aksrt'), $this->format_amount($order, (string) ($pricing['subtotal'] ?? '0')));
|
||||
$this->amount_row(__('商品金额', 'jio'), $this->format_amount($order, (string) ($pricing['subtotal'] ?? '0')));
|
||||
if ($this->has_amount($shipping)) {
|
||||
$this->amount_row(__('运费', 'aksrt'), $this->format_amount($order, $shipping));
|
||||
$this->amount_row(__('运费', 'jio'), $this->format_amount($order, $shipping));
|
||||
}
|
||||
if ($this->has_amount($discount)) {
|
||||
$code = (string) ($coupon['code'] ?? '');
|
||||
$label = $code !== '' ? sprintf(__('优惠(%s)', 'aksrt'), $code) : __('优惠', 'aksrt');
|
||||
$label = $code !== '' ? sprintf(__('优惠(%s)', 'jio'), $code) : __('优惠', 'jio');
|
||||
$this->amount_row($label, '− ' . $this->format_amount($order, $discount));
|
||||
}
|
||||
}
|
||||
echo '<div class="aksrt-orders-amounts__row is-total"><span>' . esc_html__('实付金额', 'aksrt') . '</span><strong class="aksrt-native-amount">'
|
||||
echo '<div class="jio-orders-amounts__row is-total"><span>' . esc_html__('实付金额', 'jio') . '</span><strong class="jio-native-amount">'
|
||||
. esc_html($this->order_amount($order)) . '</strong></div>';
|
||||
echo '</div>';
|
||||
NativeUi::card_close();
|
||||
@@ -105,26 +105,26 @@ trait PayOrdersDetail {
|
||||
private function render_detail_timeline(array $order): void {
|
||||
// created_at / paid_at / refunded_at 为站点时间;expires_at 为 UTC。
|
||||
$events = [
|
||||
[__('创建订单', 'aksrt'), (string) ($order['created_at'] ?? ''), 'info'],
|
||||
[__('完成支付', 'aksrt'), (string) ($order['paid_at'] ?? ''), 'success'],
|
||||
[__('标记退款', 'aksrt'), (string) ($order['refunded_at'] ?? ''), 'info'],
|
||||
[__('订单关闭', 'aksrt'), (string) ($order['cancelled_at'] ?? ''), 'neutral'],
|
||||
[__('创建订单', 'jio'), (string) ($order['created_at'] ?? ''), 'info'],
|
||||
[__('完成支付', 'jio'), (string) ($order['paid_at'] ?? ''), 'success'],
|
||||
[__('标记退款', 'jio'), (string) ($order['refunded_at'] ?? ''), 'info'],
|
||||
[__('订单关闭', 'jio'), (string) ($order['cancelled_at'] ?? ''), 'neutral'],
|
||||
];
|
||||
$expires = (string) ($order['expires_at'] ?? '');
|
||||
if (($order['status'] ?? '') === DomainModel::ORDER_STATUS_PENDING && $expires !== '') {
|
||||
$events[] = [__('超时自动关闭', 'aksrt'), get_date_from_gmt($expires, 'Y-m-d H:i:s'), 'warning', true];
|
||||
$events[] = [__('超时自动关闭', 'jio'), get_date_from_gmt($expires, 'Y-m-d H:i:s'), 'warning', true];
|
||||
}
|
||||
|
||||
NativeUi::card_open(__('订单进度', 'aksrt'));
|
||||
echo '<ol class="aksrt-orders-timeline">';
|
||||
NativeUi::card_open(__('订单进度', 'jio'));
|
||||
echo '<ol class="jio-orders-timeline">';
|
||||
foreach ($events as $event) {
|
||||
[$label, $time, $tone] = $event;
|
||||
if ($time === '' || str_starts_with($time, '0000-00-00')) {
|
||||
continue;
|
||||
}
|
||||
$planned = !empty($event[3]);
|
||||
echo '<li class="aksrt-orders-timeline__item is-' . esc_attr($tone) . ($planned ? ' is-planned' : '') . '">'
|
||||
. '<span class="aksrt-orders-timeline__label">' . esc_html($label) . ($planned ? ' <span class="aksrt-native-muted">' . esc_html__('(预计)', 'aksrt') . '</span>' : '') . '</span>'
|
||||
echo '<li class="jio-orders-timeline__item is-' . esc_attr($tone) . ($planned ? ' is-planned' : '') . '">'
|
||||
. '<span class="jio-orders-timeline__label">' . esc_html($label) . ($planned ? ' <span class="jio-native-muted">' . esc_html__('(预计)', 'jio') . '</span>' : '') . '</span>'
|
||||
. '<time>' . esc_html($time) . '</time></li>';
|
||||
}
|
||||
echo '</ol>';
|
||||
@@ -138,22 +138,22 @@ trait PayOrdersDetail {
|
||||
if ($meta === []) {
|
||||
return;
|
||||
}
|
||||
echo '<details class="aksrt-native-card aksrt-native-collapse">';
|
||||
echo '<summary><span class="dashicons dashicons-media-code" aria-hidden="true"></span>' . esc_html__('订单快照(原始数据)', 'aksrt') . '</summary>';
|
||||
echo '<div class="aksrt-native-card__body"><pre class="aksrt-orders-snapshot">'
|
||||
echo '<details class="jio-native-card jio-native-collapse">';
|
||||
echo '<summary><span class="dashicons dashicons-media-code" aria-hidden="true"></span>' . esc_html__('订单快照(原始数据)', 'jio') . '</summary>';
|
||||
echo '<div class="jio-native-card__body"><pre class="jio-orders-snapshot">'
|
||||
. esc_html((string) wp_json_encode($meta, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT))
|
||||
. '</pre></div></details>';
|
||||
}
|
||||
|
||||
private function render_detail_actions(string $order_number, string $status): void {
|
||||
$actions = $this->order_actions_html($order_number, $status, false);
|
||||
NativeUi::card_open(__('处理订单', 'aksrt'));
|
||||
NativeUi::card_open(__('处理订单', 'jio'));
|
||||
if ($actions === '') {
|
||||
echo '<p class="aksrt-native-muted">' . esc_html__('该订单已结束,没有可执行的操作。', 'aksrt') . '</p>';
|
||||
echo '<p class="jio-native-muted">' . esc_html__('该订单已结束,没有可执行的操作。', 'jio') . '</p>';
|
||||
} else {
|
||||
echo '<div class="aksrt-orders-actions is-stacked">' . $actions . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<div class="jio-orders-actions is-stacked">' . $actions . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
if ($status === DomainModel::ORDER_STATUS_PAID) {
|
||||
echo '<p class="aksrt-native-field__hint">' . esc_html__('「标记退款」只改订单状态;款项需在支付渠道后台原路退回。', 'aksrt') . '</p>';
|
||||
echo '<p class="jio-native-field__hint">' . esc_html__('「标记退款」只改订单状态;款项需在支付渠道后台原路退回。', 'jio') . '</p>';
|
||||
}
|
||||
}
|
||||
NativeUi::card_close();
|
||||
@@ -163,15 +163,15 @@ trait PayOrdersDetail {
|
||||
$user_id = (int) ($order['user_id'] ?? 0);
|
||||
$buyer = is_array($context['buyer'] ?? null) ? $context['buyer'] : [];
|
||||
|
||||
NativeUi::card_open(__('买家', 'aksrt'));
|
||||
echo '<div class="aksrt-orders-buyer">' . NativeUi::user_cell($user_id, __('游客', 'aksrt')) . '</div>';
|
||||
NativeUi::card_open(__('买家', 'jio'));
|
||||
echo '<div class="jio-orders-buyer">' . NativeUi::user_cell($user_id, __('游客', 'jio')) . '</div>';
|
||||
if ($user_id > 0) {
|
||||
echo '<dl class="aksrt-orders-dl is-compact">';
|
||||
$this->dl_row(__('用户名', 'aksrt'), esc_html((string) ($buyer['user_login'] ?? '')) ?: '—');
|
||||
$this->dl_row(__('邮箱', 'aksrt'), esc_html((string) ($buyer['user_email'] ?? '')) ?: '—');
|
||||
echo '<dl class="jio-orders-dl is-compact">';
|
||||
$this->dl_row(__('用户名', 'jio'), esc_html((string) ($buyer['user_login'] ?? '')) ?: '—');
|
||||
$this->dl_row(__('邮箱', 'jio'), esc_html((string) ($buyer['user_email'] ?? '')) ?: '—');
|
||||
echo '</dl>';
|
||||
echo '<p><a class="button button-small" href="' . esc_url($this->page_url(self::PAGE_SLUG, ['user_id' => $user_id])) . '">'
|
||||
. esc_html__('查看该用户全部订单', 'aksrt') . '</a></p>';
|
||||
. esc_html__('查看该用户全部订单', 'jio') . '</a></p>';
|
||||
}
|
||||
NativeUi::card_close();
|
||||
}
|
||||
@@ -181,17 +181,17 @@ trait PayOrdersDetail {
|
||||
$admin_note = (string) ($meta['admin_note'] ?? '');
|
||||
$customer_note = (string) ($context['customer_note'] ?? '');
|
||||
|
||||
NativeUi::card_open(__('备注', 'aksrt'));
|
||||
NativeUi::card_open(__('备注', 'jio'));
|
||||
if ($customer_note !== '') {
|
||||
echo '<div class="aksrt-native-callout"><strong>' . esc_html__('买家备注', 'aksrt') . '</strong><br>' . nl2br(esc_html($customer_note)) . '</div>';
|
||||
echo '<div class="jio-native-callout"><strong>' . esc_html__('买家备注', 'jio') . '</strong><br>' . nl2br(esc_html($customer_note)) . '</div>';
|
||||
}
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-orders-note">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_pay_update_order_note" />';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-orders-note">';
|
||||
echo '<input type="hidden" name="action" value="jio_pay_update_order_note" />';
|
||||
echo '<input type="hidden" name="order_number" value="' . esc_attr($order_number) . '" />';
|
||||
wp_nonce_field('aksrt_pay_update_order_note_' . $order_number);
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('运营备注', 'aksrt') . '</span>'
|
||||
. '<textarea name="admin_note" rows="4" placeholder="' . esc_attr__('仅管理员可见', 'aksrt') . '">' . esc_textarea($admin_note) . '</textarea></label>';
|
||||
echo '<div class="aksrt-native-form-foot"><button type="submit" class="button">' . esc_html__('保存备注', 'aksrt') . '</button></div>';
|
||||
wp_nonce_field('jio_pay_update_order_note_' . $order_number);
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('运营备注', 'jio') . '</span>'
|
||||
. '<textarea name="admin_note" rows="4" placeholder="' . esc_attr__('仅管理员可见', 'jio') . '">' . esc_textarea($admin_note) . '</textarea></label>';
|
||||
echo '<div class="jio-native-form-foot"><button type="submit" class="button">' . esc_html__('保存备注', 'jio') . '</button></div>';
|
||||
echo '</form>';
|
||||
NativeUi::card_close();
|
||||
}
|
||||
@@ -209,7 +209,7 @@ trait PayOrdersDetail {
|
||||
}
|
||||
|
||||
private function amount_row(string $label, string $value): void {
|
||||
echo '<div class="aksrt-orders-amounts__row"><span>' . esc_html($label) . '</span><span>' . esc_html($value) . '</span></div>';
|
||||
echo '<div class="jio-orders-amounts__row"><span>' . esc_html($label) . '</span><span>' . esc_html($value) . '</span></div>';
|
||||
}
|
||||
|
||||
private function has_amount(string $value): bool {
|
||||
@@ -218,7 +218,7 @@ trait PayOrdersDetail {
|
||||
|
||||
private function format_amount(array $order, string $value): string {
|
||||
if ($this->is_points_order($order)) {
|
||||
return max(0, (int) $value) . ' ' . __('积分', 'aksrt');
|
||||
return max(0, (int) $value) . ' ' . __('积分', 'jio');
|
||||
}
|
||||
return $this->cash_amount($value, (string) ($order['currency'] ?? ''));
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
*
|
||||
* 作为 flat sibling trait 被 PayOrders 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Core\DomainModel;
|
||||
use AKSRT\Payment\OrderRepository;
|
||||
use Jio\Core\DomainModel;
|
||||
use Jio\Payment\OrderRepository;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -18,8 +18,8 @@ trait PayOrdersList {
|
||||
|
||||
private function render_order_list_page(): void {
|
||||
NativeUi::header(
|
||||
__('订单明细', 'aksrt'),
|
||||
__('查看全站付费订单,处理收款确认、关闭订单与退款标记。', 'aksrt'),
|
||||
__('订单明细', 'jio'),
|
||||
__('查看全站付费订单,处理收款确认、关闭订单与退款标记。', 'jio'),
|
||||
'dashicons-list-view'
|
||||
);
|
||||
$this->render_flash();
|
||||
@@ -55,17 +55,17 @@ trait PayOrdersList {
|
||||
$this->render_filter_card($filters);
|
||||
|
||||
$row_args = $query_args + ($page_num > 1 ? ['paged' => $page_num] : []);
|
||||
NativeUi::table_card_open(__('订单列表', 'aksrt'), $this->cleanup_form_html());
|
||||
echo '<table class="wp-list-table widefat striped aksrt-orders-table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('订单列表', 'aksrt') . '</caption><thead><tr>';
|
||||
NativeUi::table_card_open(__('订单列表', 'jio'), $this->cleanup_form_html());
|
||||
echo '<table class="wp-list-table widefat striped jio-orders-table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('订单列表', 'jio') . '</caption><thead><tr>';
|
||||
foreach ([
|
||||
'order' => __('订单号 / 下单时间', 'aksrt'),
|
||||
'user' => __('用户', 'aksrt'),
|
||||
'product' => __('商品', 'aksrt'),
|
||||
'amount' => __('金额 / 支付方式', 'aksrt'),
|
||||
'status' => __('状态', 'aksrt'),
|
||||
'paid' => __('支付时间', 'aksrt'),
|
||||
'actions' => __('操作', 'aksrt'),
|
||||
'order' => __('订单号 / 下单时间', 'jio'),
|
||||
'user' => __('用户', 'jio'),
|
||||
'product' => __('商品', 'jio'),
|
||||
'amount' => __('金额 / 支付方式', 'jio'),
|
||||
'status' => __('状态', 'jio'),
|
||||
'paid' => __('支付时间', 'jio'),
|
||||
'actions' => __('操作', 'jio'),
|
||||
] as $key => $label) {
|
||||
echo '<th scope="col" class="column-' . esc_attr($key) . '">' . esc_html($label) . '</th>';
|
||||
}
|
||||
@@ -73,9 +73,9 @@ trait PayOrdersList {
|
||||
|
||||
if (!$items) {
|
||||
$filtered = array_diff_key($query_args, ['status' => true]) !== [];
|
||||
echo NativeUi::empty_row(7, __('暂无订单', 'aksrt'), $filtered // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
? __('没有符合当前筛选条件的订单,请调整或重置筛选。', 'aksrt')
|
||||
: __('用户下单后,订单会出现在这里。', 'aksrt'));
|
||||
echo NativeUi::empty_row(7, __('暂无订单', 'jio'), $filtered // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
? __('没有符合当前筛选条件的订单,请调整或重置筛选。', 'jio')
|
||||
: __('用户下单后,订单会出现在这里。', 'jio'));
|
||||
}
|
||||
foreach ($items as $row) {
|
||||
$this->render_order_row($row, $row_args);
|
||||
@@ -98,18 +98,18 @@ trait PayOrdersList {
|
||||
$detail_url = $this->page_url(self::PAGE_SLUG, ['order' => $order_number] + $row_args);
|
||||
|
||||
echo '<tr>';
|
||||
echo '<td class="column-order"><a class="aksrt-orders-number" href="' . esc_url($detail_url) . '">' . esc_html($order_number) . '</a>'
|
||||
. '<div class="aksrt-orders-sub">' . esc_html((string) ($row['created_at'] ?? '')) . '</div></td>';
|
||||
echo '<td class="column-user">' . NativeUi::user_cell((int) ($row['user_id'] ?? 0), __('游客', 'aksrt')) . '</td>';
|
||||
echo '<td class="column-product"><span class="aksrt-orders-product">' . esc_html($title !== '' ? $title : $type_label) . '</span>'
|
||||
. '<div class="aksrt-orders-sub">' . esc_html($type_label . ($product_id > 0 ? ' · #' . $product_id : '')) . '</div></td>';
|
||||
echo '<td class="column-amount"><strong class="aksrt-native-amount">' . esc_html($this->order_amount($row)) . '</strong>'
|
||||
. '<div class="aksrt-orders-sub">' . esc_html($this->payment_method_label((string) ($row['payment_method'] ?? ''))) . '</div></td>';
|
||||
echo '<td class="column-order"><a class="jio-orders-number" href="' . esc_url($detail_url) . '">' . esc_html($order_number) . '</a>'
|
||||
. '<div class="jio-orders-sub">' . esc_html((string) ($row['created_at'] ?? '')) . '</div></td>';
|
||||
echo '<td class="column-user">' . NativeUi::user_cell((int) ($row['user_id'] ?? 0), __('游客', 'jio')) . '</td>';
|
||||
echo '<td class="column-product"><span class="jio-orders-product">' . esc_html($title !== '' ? $title : $type_label) . '</span>'
|
||||
. '<div class="jio-orders-sub">' . esc_html($type_label . ($product_id > 0 ? ' · #' . $product_id : '')) . '</div></td>';
|
||||
echo '<td class="column-amount"><strong class="jio-native-amount">' . esc_html($this->order_amount($row)) . '</strong>'
|
||||
. '<div class="jio-orders-sub">' . esc_html($this->payment_method_label((string) ($row['payment_method'] ?? ''))) . '</div></td>';
|
||||
echo '<td class="column-status">' . NativeUi::status_badge($status, $this->status_badge_map()) . $this->expiry_hint($row) . '</td>';
|
||||
$paid_at = (string) ($row['paid_at'] ?? '');
|
||||
echo '<td class="column-paid">' . ($paid_at !== '' ? esc_html($paid_at) : '<span class="aksrt-native-muted">—</span>') . '</td>';
|
||||
echo '<td class="column-actions"><div class="aksrt-orders-actions">'
|
||||
. '<a class="button button-small" href="' . esc_url($detail_url) . '">' . esc_html__('详情', 'aksrt') . '</a>'
|
||||
echo '<td class="column-paid">' . ($paid_at !== '' ? esc_html($paid_at) : '<span class="jio-native-muted">—</span>') . '</td>';
|
||||
echo '<td class="column-actions"><div class="jio-orders-actions">'
|
||||
. '<a class="button button-small" href="' . esc_url($detail_url) . '">' . esc_html__('详情', 'jio') . '</a>'
|
||||
. $this->order_actions_html($order_number, $status)
|
||||
. '</div></td>';
|
||||
echo '</tr>';
|
||||
@@ -121,9 +121,9 @@ trait PayOrdersList {
|
||||
if (($order['status'] ?? '') !== DomainModel::ORDER_STATUS_PENDING || $expires === '') {
|
||||
return '';
|
||||
}
|
||||
return '<div class="aksrt-orders-sub">' . esc_html(sprintf(
|
||||
return '<div class="jio-orders-sub">' . esc_html(sprintf(
|
||||
/* translators: %s local expiry time */
|
||||
__('%s 前未支付将自动关闭', 'aksrt'),
|
||||
__('%s 前未支付将自动关闭', 'jio'),
|
||||
get_date_from_gmt($expires, 'm-d H:i')
|
||||
)) . '</div>';
|
||||
}
|
||||
@@ -135,16 +135,16 @@ trait PayOrdersList {
|
||||
*/
|
||||
private function render_status_views(array $filters, array $query_args, array $counts): void {
|
||||
$base = array_diff_key($query_args, ['status' => true]);
|
||||
$views = ['' => [__('全部', 'aksrt'), array_sum($counts)]];
|
||||
$views = ['' => [__('全部', 'jio'), array_sum($counts)]];
|
||||
foreach ($this->status_labels() as $key => $label) {
|
||||
$views[$key] = [$label, (int) ($counts[$key] ?? 0)];
|
||||
}
|
||||
|
||||
echo '<nav class="aksrt-native-tabs aksrt-orders-views" aria-label="' . esc_attr__('按订单状态查看', 'aksrt') . '">';
|
||||
echo '<nav class="jio-native-tabs jio-orders-views" aria-label="' . esc_attr__('按订单状态查看', 'jio') . '">';
|
||||
foreach ($views as $key => [$label, $count]) {
|
||||
$url = $this->page_url(self::PAGE_SLUG, $base + ($key !== '' ? ['status' => $key] : []));
|
||||
$active = $filters['status'] === $key;
|
||||
echo '<a class="aksrt-native-tabs__link' . ($active ? ' is-active' : '') . '" href="' . esc_url($url) . '"' . ($active ? ' aria-current="page"' : '') . '>'
|
||||
echo '<a class="jio-native-tabs__link' . ($active ? ' is-active' : '') . '" href="' . esc_url($url) . '"' . ($active ? ' aria-current="page"' : '') . '>'
|
||||
. esc_html($label) . ' <span class="count">' . esc_html(number_format_i18n($count)) . '</span></a>';
|
||||
}
|
||||
echo '</nav>';
|
||||
@@ -152,8 +152,8 @@ trait PayOrdersList {
|
||||
|
||||
/** @param array<string, mixed> $filters */
|
||||
private function render_filter_card(array $filters): void {
|
||||
NativeUi::card_open('', '', false, 'aksrt-orders-filter-card');
|
||||
echo '<form method="get" class="aksrt-orders-filters" role="search">';
|
||||
NativeUi::card_open('', '', false, 'jio-orders-filter-card');
|
||||
echo '<form method="get" class="jio-orders-filters" role="search">';
|
||||
echo '<input type="hidden" name="page" value="' . esc_attr(self::PAGE_SLUG) . '" />';
|
||||
if ($filters['status'] !== '') {
|
||||
echo '<input type="hidden" name="status" value="' . esc_attr($filters['status']) . '" />';
|
||||
@@ -162,21 +162,21 @@ trait PayOrdersList {
|
||||
echo '<input type="hidden" name="product_id" value="' . esc_attr((string) $filters['product_id']) . '" />';
|
||||
}
|
||||
|
||||
echo '<label class="aksrt-orders-filters__search"><span class="screen-reader-text">' . esc_html__('搜索订单', 'aksrt') . '</span>'
|
||||
. '<input type="search" name="s" value="' . esc_attr($filters['s']) . '" placeholder="' . esc_attr__('订单号 / 用户名 / 邮箱 / 商品名', 'aksrt') . '" /></label>';
|
||||
echo $this->filter_select('product_type', __('商品类型', 'aksrt'), $this->type_options(), $filters['product_type']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->filter_select('payment_method', __('支付方式', 'aksrt'), $this->method_options(), $filters['payment_method']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<span class="aksrt-orders-filters__range">'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('开始日期', 'aksrt') . '</span><input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '" /></label>'
|
||||
echo '<label class="jio-orders-filters__search"><span class="screen-reader-text">' . esc_html__('搜索订单', 'jio') . '</span>'
|
||||
. '<input type="search" name="s" value="' . esc_attr($filters['s']) . '" placeholder="' . esc_attr__('订单号 / 用户名 / 邮箱 / 商品名', 'jio') . '" /></label>';
|
||||
echo $this->filter_select('product_type', __('商品类型', 'jio'), $this->type_options(), $filters['product_type']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->filter_select('payment_method', __('支付方式', 'jio'), $this->method_options(), $filters['payment_method']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<span class="jio-orders-filters__range">'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('开始日期', 'jio') . '</span><input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '" /></label>'
|
||||
. '<span aria-hidden="true">–</span>'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('结束日期', 'aksrt') . '</span><input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '" /></label>'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('结束日期', 'jio') . '</span><input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '" /></label>'
|
||||
. '</span>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('用户 ID', 'aksrt') . '</span><input type="number" min="1" name="user_id" class="aksrt-orders-filters__user" value="'
|
||||
. esc_attr($filters['user_id'] > 0 ? (string) $filters['user_id'] : '') . '" placeholder="' . esc_attr__('用户 ID', 'aksrt') . '" /></label>';
|
||||
echo '<span class="aksrt-orders-filters__submit"><button type="submit" class="button button-primary">' . esc_html__('筛选', 'aksrt') . '</button>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('用户 ID', 'jio') . '</span><input type="number" min="1" name="user_id" class="jio-orders-filters__user" value="'
|
||||
. esc_attr($filters['user_id'] > 0 ? (string) $filters['user_id'] : '') . '" placeholder="' . esc_attr__('用户 ID', 'jio') . '" /></label>';
|
||||
echo '<span class="jio-orders-filters__submit"><button type="submit" class="button button-primary">' . esc_html__('筛选', 'jio') . '</button>';
|
||||
if (array_diff_key($this->list_query_args($filters), ['status' => true]) !== []) {
|
||||
$reset = $this->page_url(self::PAGE_SLUG, $filters['status'] !== '' ? ['status' => $filters['status']] : []);
|
||||
echo '<a class="button" href="' . esc_url($reset) . '">' . esc_html__('重置', 'aksrt') . '</a>';
|
||||
echo '<a class="button" href="' . esc_url($reset) . '">' . esc_html__('重置', 'jio') . '</a>';
|
||||
}
|
||||
echo '</span></form>';
|
||||
|
||||
@@ -185,9 +185,9 @@ trait PayOrdersList {
|
||||
if ($filters['product_id'] > 0) {
|
||||
$remove = $this->page_url(self::PAGE_SLUG, array_diff_key($this->list_query_args($filters), ['product_id' => true]));
|
||||
$title = (string) get_the_title($filters['product_id']);
|
||||
echo '<p class="aksrt-orders-chips"><span class="aksrt-orders-chip">'
|
||||
. esc_html(sprintf(__('商品:%s', 'aksrt'), $title !== '' ? $title . ' #' . $filters['product_id'] : '#' . $filters['product_id']))
|
||||
. '<a href="' . esc_url($remove) . '" aria-label="' . esc_attr__('移除商品筛选', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></a></span></p>';
|
||||
echo '<p class="jio-orders-chips"><span class="jio-orders-chip">'
|
||||
. esc_html(sprintf(__('商品:%s', 'jio'), $title !== '' ? $title . ' #' . $filters['product_id'] : '#' . $filters['product_id']))
|
||||
. '<a href="' . esc_url($remove) . '" aria-label="' . esc_attr__('移除商品筛选', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></a></span></p>';
|
||||
}
|
||||
NativeUi::card_close();
|
||||
}
|
||||
@@ -203,11 +203,11 @@ trait PayOrdersList {
|
||||
|
||||
private function cleanup_form_html(): string {
|
||||
return $this->action_form(
|
||||
'aksrt_pay_clear_closed_orders',
|
||||
'aksrt_pay_clear_closed_orders',
|
||||
'jio_pay_clear_closed_orders',
|
||||
'jio_pay_clear_closed_orders',
|
||||
['days' => '14'],
|
||||
__('清理 14 天前关闭订单', 'aksrt'),
|
||||
__('将永久删除 14 天前关闭的全部订单,不可恢复。确认清理?', 'aksrt'),
|
||||
__('清理 14 天前关闭订单', 'jio'),
|
||||
__('将永久删除 14 天前关闭的全部订单,不可恢复。确认清理?', 'jio'),
|
||||
true
|
||||
);
|
||||
}
|
||||
@@ -215,24 +215,24 @@ trait PayOrdersList {
|
||||
/** @return array<string, string> */
|
||||
private function type_options(): array {
|
||||
return [
|
||||
'' => __('全部类型', 'aksrt'),
|
||||
DomainModel::ORDER_PRODUCT_READING => __('付费阅读', 'aksrt'),
|
||||
DomainModel::ORDER_PRODUCT_DOWNLOAD => __('付费下载', 'aksrt'),
|
||||
DomainModel::ORDER_PRODUCT_IMAGE => __('付费图片', 'aksrt'),
|
||||
DomainModel::ORDER_PRODUCT_VIDEO => __('付费视频', 'aksrt'),
|
||||
DomainModel::ORDER_PRODUCT_VIP => __('购买会员', 'aksrt'),
|
||||
DomainModel::ORDER_PRODUCT_RECHARGE => __('余额充值', 'aksrt'),
|
||||
DomainModel::ORDER_PRODUCT_POINTS => __('购买积分', 'aksrt'),
|
||||
'' => __('全部类型', 'jio'),
|
||||
DomainModel::ORDER_PRODUCT_READING => __('付费阅读', 'jio'),
|
||||
DomainModel::ORDER_PRODUCT_DOWNLOAD => __('付费下载', 'jio'),
|
||||
DomainModel::ORDER_PRODUCT_IMAGE => __('付费图片', 'jio'),
|
||||
DomainModel::ORDER_PRODUCT_VIDEO => __('付费视频', 'jio'),
|
||||
DomainModel::ORDER_PRODUCT_VIP => __('购买会员', 'jio'),
|
||||
DomainModel::ORDER_PRODUCT_RECHARGE => __('余额充值', 'jio'),
|
||||
DomainModel::ORDER_PRODUCT_POINTS => __('购买积分', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
private function method_options(): array {
|
||||
return [
|
||||
'' => __('支付方式', 'aksrt'),
|
||||
'balance' => __('余额', 'aksrt'),
|
||||
'points' => __('积分', 'aksrt'),
|
||||
'gateway' => __('第三方网关支付', 'aksrt'),
|
||||
'' => __('支付方式', 'jio'),
|
||||
'balance' => __('余额', 'jio'),
|
||||
'points' => __('积分', 'jio'),
|
||||
'gateway' => __('第三方网关支付', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
+118
-118
@@ -2,25 +2,25 @@
|
||||
/**
|
||||
* 支付中心「概览」:今日实时、区间指标卡、成交趋势、分类成交、支付方式、转化与退款、热销排行、待办与快捷入口。
|
||||
*
|
||||
* 统计口径见 \AKSRT\Payment\OrderAnalytics。首屏报表(近 30 天)随页面输出为 JSON,
|
||||
* 之后切换区间 / 分类走 REST(aksrt/v1/admin/pay-overview,见 \AKSRT\Admin\PayOverviewController)。
|
||||
* 统计口径见 \Jio\Payment\OrderAnalytics。首屏报表(近 30 天)随页面输出为 JSON,
|
||||
* 之后切换区间 / 分类走 REST(jio/v1/admin/pay-overview,见 \Jio\Admin\PayOverviewController)。
|
||||
* 图表用按需打包的 ECharts(assets/js/runtime/libs/echarts-pay.min.js,tools/build-pay-charts.mjs 生成),
|
||||
* 绘制逻辑在 assets/js/admin-pay-overview.js,样式在 assets/css/admin/pay-overview.css。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Payment\OrderAnalytics;
|
||||
use AKSRT\Payment\OrderAnalyticsDaily;
|
||||
use Jio\Payment\OrderAnalytics;
|
||||
use Jio\Payment\OrderAnalyticsDaily;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
final class PayOverview {
|
||||
|
||||
private const DEFAULT_DAYS = 30;
|
||||
private const DATA_ID = 'aksrt-pay-overview-data';
|
||||
private const DATA_ID = 'jio-pay-overview-data';
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
@@ -43,14 +43,14 @@ final class PayOverview {
|
||||
$lib = '/assets/js/runtime/libs/echarts-pay.min.js';
|
||||
$css = '/assets/css/admin/pay-overview.css';
|
||||
$js = '/assets/js/admin-pay-overview.js';
|
||||
wp_enqueue_style('aksrt-admin-pay-overview', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-echarts-pay', AKSRT_URI . $lib, [], (string) filemtime(AKSRT_DIR . $lib), true);
|
||||
wp_enqueue_script('aksrt-admin-pay-overview', AKSRT_URI . $js, ['aksrt-echarts-pay'], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-pay-overview', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-echarts-pay', JIO_URI . $lib, [], (string) filemtime(JIO_DIR . $lib), true);
|
||||
wp_enqueue_script('jio-admin-pay-overview', JIO_URI . $js, ['jio-echarts-pay'], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
public function render_page(): void {
|
||||
if (!current_user_can(PayMenus::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
|
||||
$analytics = OrderAnalytics::get_instance();
|
||||
@@ -60,35 +60,35 @@ final class PayOverview {
|
||||
$realtime = $analytics->today_vs_yesterday();
|
||||
$symbol = $this->currency_symbol();
|
||||
|
||||
NativeUi::page_open('aksrt-po-page');
|
||||
NativeUi::page_open('jio-po-page');
|
||||
NativeUi::header(
|
||||
__('支付中心', 'aksrt'),
|
||||
__('成交走势、收入结构与待办一览。收款渠道、价格策略等配置在主题设置中修改。', 'aksrt'),
|
||||
__('支付中心', 'jio'),
|
||||
__('成交走势、收入结构与待办一览。收款渠道、价格策略等配置在主题设置中修改。', 'jio'),
|
||||
'dashicons-cart',
|
||||
[
|
||||
['label' => __('订单明细', 'aksrt'), 'url' => $this->page_url('aksrt_orders'), 'icon' => 'dashicons-list-view'],
|
||||
['label' => __('支付配置', 'aksrt'), 'url' => admin_url('admin.php?page=aksrt-settings#/payment/gateway'), 'icon' => 'dashicons-admin-generic', 'primary' => true],
|
||||
['label' => __('订单明细', 'jio'), 'url' => $this->page_url('jio_orders'), 'icon' => 'dashicons-list-view'],
|
||||
['label' => __('支付配置', 'jio'), 'url' => admin_url('admin.php?page=jio-settings#/payment/gateway'), 'icon' => 'dashicons-admin-generic', 'primary' => true],
|
||||
]
|
||||
);
|
||||
|
||||
$this->render_realtime($realtime, $symbol);
|
||||
$this->render_toolbar($from, $today);
|
||||
echo '<div class="aksrt-po-notice" data-aksrt-po-excluded hidden></div>';
|
||||
echo '<div class="aksrt-po-error notice notice-error" data-aksrt-po-error hidden><p></p></div>';
|
||||
echo '<div class="jio-po-notice" data-jio-po-excluded hidden></div>';
|
||||
echo '<div class="jio-po-error notice notice-error" data-jio-po-error hidden><p></p></div>';
|
||||
$this->render_kpis();
|
||||
|
||||
echo '<div class="aksrt-po-body" data-aksrt-po-body>';
|
||||
echo '<div class="aksrt-po-grid aksrt-po-grid--main">';
|
||||
$this->chart_card('trend', __('成交趋势', 'aksrt'), __('支付金额、净收入、退款金额曲线与支付订单数折线', 'aksrt'), 'lg');
|
||||
echo '<div class="jio-po-body" data-jio-po-body>';
|
||||
echo '<div class="jio-po-grid jio-po-grid--main">';
|
||||
$this->chart_card('trend', __('成交趋势', 'jio'), __('支付金额、净收入、退款金额曲线与支付订单数折线', 'jio'), 'lg');
|
||||
$this->render_funnel_card();
|
||||
echo '</div>';
|
||||
|
||||
echo '<div class="aksrt-po-grid aksrt-po-grid--main">';
|
||||
$this->chart_card('groups', __('分类成交', 'aksrt'), __('按商品分类的支付金额曲线', 'aksrt'), 'md');
|
||||
$this->chart_card('methods', __('支付方式', 'aksrt'), __('按支付金额的支付方式占比', 'aksrt'), 'md');
|
||||
echo '<div class="jio-po-grid jio-po-grid--main">';
|
||||
$this->chart_card('groups', __('分类成交', 'jio'), __('按商品分类的支付金额曲线', 'jio'), 'md');
|
||||
$this->chart_card('methods', __('支付方式', 'jio'), __('按支付金额的支付方式占比', 'jio'), 'md');
|
||||
echo '</div>';
|
||||
|
||||
echo '<div class="aksrt-po-grid aksrt-po-grid--main">';
|
||||
echo '<div class="jio-po-grid jio-po-grid--main">';
|
||||
$this->render_top_card();
|
||||
$this->render_todo_card();
|
||||
echo '</div>';
|
||||
@@ -100,23 +100,23 @@ final class PayOverview {
|
||||
'currency' => $symbol,
|
||||
'today' => $today,
|
||||
'maxDays' => OrderAnalytics::MAX_RANGE_DAYS,
|
||||
'rest' => ['url' => rest_url('aksrt/v1/admin/pay-overview'), 'nonce' => wp_create_nonce('wp_rest')],
|
||||
'rest' => ['url' => rest_url('jio/v1/admin/pay-overview'), 'nonce' => wp_create_nonce('wp_rest')],
|
||||
'initial' => ['from' => $from, 'to' => $today, 'group' => '', 'report' => $report],
|
||||
'i18n' => [
|
||||
'paid' => __('支付金额', 'aksrt'),
|
||||
'net' => __('净收入', 'aksrt'),
|
||||
'refund' => __('退款金额', 'aksrt'),
|
||||
'count' => __('支付订单数', 'aksrt'),
|
||||
'unit' => __('笔', 'aksrt'),
|
||||
'people' => __('人', 'aksrt'),
|
||||
'empty' => __('所选区间暂无成交', 'aksrt'),
|
||||
'loadError' => __('统计数据加载失败,请稍后重试。', 'aksrt'),
|
||||
'basis' => __('较上一周期', 'aksrt'),
|
||||
'new' => __('新增', 'aksrt'),
|
||||
'total' => __('合计', 'aksrt'),
|
||||
'excluded' => __('以下外币订单未计入统计:', 'aksrt'),
|
||||
'noTop' => __('所选区间暂无付费内容成交', 'aksrt'),
|
||||
'badRange' => __('请选择有效的日期区间。', 'aksrt'),
|
||||
'paid' => __('支付金额', 'jio'),
|
||||
'net' => __('净收入', 'jio'),
|
||||
'refund' => __('退款金额', 'jio'),
|
||||
'count' => __('支付订单数', 'jio'),
|
||||
'unit' => __('笔', 'jio'),
|
||||
'people' => __('人', 'jio'),
|
||||
'empty' => __('所选区间暂无成交', 'jio'),
|
||||
'loadError' => __('统计数据加载失败,请稍后重试。', 'jio'),
|
||||
'basis' => __('较上一周期', 'jio'),
|
||||
'new' => __('新增', 'jio'),
|
||||
'total' => __('合计', 'jio'),
|
||||
'excluded' => __('以下外币订单未计入统计:', 'jio'),
|
||||
'noTop' => __('所选区间暂无付费内容成交', 'jio'),
|
||||
'badRange' => __('请选择有效的日期区间。', 'jio'),
|
||||
],
|
||||
];
|
||||
echo '<script type="application/json" id="' . esc_attr(self::DATA_ID) . '">'
|
||||
@@ -134,17 +134,17 @@ final class PayOverview {
|
||||
private function render_realtime(array $data, string $symbol): void {
|
||||
$amount = (float) $data['today']['amount'];
|
||||
$prev = (float) $data['yesterday']['amount'];
|
||||
echo '<section class="aksrt-po-live" aria-label="' . esc_attr__('今日实时', 'aksrt') . '">';
|
||||
echo '<div class="aksrt-po-live__main"><span class="aksrt-po-live__dot" aria-hidden="true"></span>'
|
||||
. '<span class="aksrt-po-live__label">' . esc_html__('今日支付金额', 'aksrt') . '</span>'
|
||||
. '<strong class="aksrt-po-live__value">' . esc_html($symbol . number_format_i18n($amount, 2)) . '</strong>'
|
||||
echo '<section class="jio-po-live" aria-label="' . esc_attr__('今日实时', 'jio') . '">';
|
||||
echo '<div class="jio-po-live__main"><span class="jio-po-live__dot" aria-hidden="true"></span>'
|
||||
. '<span class="jio-po-live__label">' . esc_html__('今日支付金额', 'jio') . '</span>'
|
||||
. '<strong class="jio-po-live__value">' . esc_html($symbol . number_format_i18n($amount, 2)) . '</strong>'
|
||||
. $this->delta_html($amount, $prev) . '</div>';
|
||||
echo '<div class="aksrt-po-live__meta">';
|
||||
echo '<div class="jio-po-live__meta">';
|
||||
/* translators: %d order count */
|
||||
echo '<span>' . esc_html(sprintf(__('今日 %d 笔', 'aksrt'), (int) $data['today']['count'])) . '</span>';
|
||||
echo '<span>' . esc_html(sprintf(__('今日 %d 笔', 'jio'), (int) $data['today']['count'])) . '</span>';
|
||||
echo '<span>' . esc_html(sprintf(
|
||||
/* translators: 1 time 2 amount 3 order count */
|
||||
__('昨日 00:00–%1$s:%2$s,%3$d 笔', 'aksrt'),
|
||||
__('昨日 00:00–%1$s:%2$s,%3$d 笔', 'jio'),
|
||||
$data['until'],
|
||||
$symbol . number_format_i18n($prev, 2),
|
||||
(int) $data['yesterday']['count']
|
||||
@@ -154,16 +154,16 @@ final class PayOverview {
|
||||
|
||||
/** 同时段环比徽标(服务端渲染,仅今日实时使用;区间指标的环比由前端计算)。 */
|
||||
private function delta_html(float $current, float $previous): string {
|
||||
$basis = __('较昨日同时段', 'aksrt');
|
||||
$basis = __('较昨日同时段', 'jio');
|
||||
if ($previous <= 0) {
|
||||
$text = $current > 0 ? __('新增', 'aksrt') : '—';
|
||||
$text = $current > 0 ? __('新增', 'jio') : '—';
|
||||
$class = $current > 0 ? 'is-up' : 'is-flat';
|
||||
} else {
|
||||
$delta = round(($current - $previous) / $previous * 100, 1);
|
||||
$class = $delta > 0 ? 'is-up' : ($delta < 0 ? 'is-down' : 'is-flat');
|
||||
$text = ($delta > 0 ? '↑' : ($delta < 0 ? '↓' : '')) . number_format_i18n(abs($delta), 1) . '%';
|
||||
}
|
||||
return '<span class="aksrt-po-delta ' . esc_attr($class) . '">' . esc_html($basis . ' ' . $text) . '</span>';
|
||||
return '<span class="jio-po-delta ' . esc_attr($class) . '">' . esc_html($basis . ' ' . $text) . '</span>';
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
@@ -172,54 +172,54 @@ final class PayOverview {
|
||||
|
||||
private function render_toolbar(string $from, string $to): void {
|
||||
$presets = [
|
||||
'd7' => __('近 7 天', 'aksrt'),
|
||||
'd30' => __('近 30 天', 'aksrt'),
|
||||
'd90' => __('近 90 天', 'aksrt'),
|
||||
'this_month' => __('本月', 'aksrt'),
|
||||
'last_month' => __('上月', 'aksrt'),
|
||||
'd365' => __('近一年', 'aksrt'),
|
||||
'd7' => __('近 7 天', 'jio'),
|
||||
'd30' => __('近 30 天', 'jio'),
|
||||
'd90' => __('近 90 天', 'jio'),
|
||||
'this_month' => __('本月', 'jio'),
|
||||
'last_month' => __('上月', 'jio'),
|
||||
'd365' => __('近一年', 'jio'),
|
||||
];
|
||||
echo '<div class="aksrt-po-toolbar">';
|
||||
echo '<div class="aksrt-po-seg" role="group" data-aksrt-po-presets aria-label="' . esc_attr__('统计区间', 'aksrt') . '">';
|
||||
echo '<div class="jio-po-toolbar">';
|
||||
echo '<div class="jio-po-seg" role="group" data-jio-po-presets aria-label="' . esc_attr__('统计区间', 'jio') . '">';
|
||||
foreach ($presets as $key => $label) {
|
||||
echo '<button type="button" data-preset="' . esc_attr($key) . '" aria-pressed="' . ($key === 'd30' ? 'true' : 'false') . '">' . esc_html($label) . '</button>';
|
||||
}
|
||||
echo '</div>';
|
||||
|
||||
echo '<form class="aksrt-po-range" data-aksrt-po-range>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('开始日期', 'aksrt') . '</span>'
|
||||
echo '<form class="jio-po-range" data-jio-po-range>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('开始日期', 'jio') . '</span>'
|
||||
. '<input type="date" name="from" value="' . esc_attr($from) . '" max="' . esc_attr($to) . '" required></label>';
|
||||
echo '<span aria-hidden="true">–</span>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('结束日期', 'aksrt') . '</span>'
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('结束日期', 'jio') . '</span>'
|
||||
. '<input type="date" name="to" value="' . esc_attr($to) . '" max="' . esc_attr($to) . '" required></label>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('商品分类', 'aksrt') . '</span><select name="group">'
|
||||
. '<option value="">' . esc_html__('全部分类', 'aksrt') . '</option>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('商品分类', 'jio') . '</span><select name="group">'
|
||||
. '<option value="">' . esc_html__('全部分类', 'jio') . '</option>';
|
||||
foreach (OrderAnalytics::group_labels() as $key => $label) {
|
||||
echo '<option value="' . esc_attr($key) . '">' . esc_html($label) . '</option>';
|
||||
}
|
||||
echo '</select></label>';
|
||||
echo '<button type="submit" class="button">' . esc_html__('查询', 'aksrt') . '</button>';
|
||||
echo '<button type="submit" class="button">' . esc_html__('查询', 'jio') . '</button>';
|
||||
echo '</form>';
|
||||
echo '<span class="aksrt-po-range-label" data-aksrt-po-range-label aria-live="polite"></span>';
|
||||
echo '<span class="jio-po-range-label" data-jio-po-range-label aria-live="polite"></span>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
private function render_kpis(): void {
|
||||
$cards = [
|
||||
['paid_amount', __('支付金额', 'aksrt'), 'success'],
|
||||
['net_amount', __('净收入', 'aksrt'), 'info'],
|
||||
['paid_count', __('支付订单数', 'aksrt'), 'primary'],
|
||||
['buyers', __('支付人数', 'aksrt'), 'primary'],
|
||||
['avg_amount', __('客单价', 'aksrt'), 'warning'],
|
||||
['refund_amount', __('退款金额', 'aksrt'), 'danger'],
|
||||
['paid_amount', __('支付金额', 'jio'), 'success'],
|
||||
['net_amount', __('净收入', 'jio'), 'info'],
|
||||
['paid_count', __('支付订单数', 'jio'), 'primary'],
|
||||
['buyers', __('支付人数', 'jio'), 'primary'],
|
||||
['avg_amount', __('客单价', 'jio'), 'warning'],
|
||||
['refund_amount', __('退款金额', 'jio'), 'danger'],
|
||||
];
|
||||
echo '<div class="aksrt-po-kpis" data-aksrt-po-kpis>';
|
||||
echo '<div class="jio-po-kpis" data-jio-po-kpis>';
|
||||
foreach ($cards as [$key, $label, $tone]) {
|
||||
echo '<div class="aksrt-po-kpi aksrt-po-kpi--' . esc_attr($tone) . '" data-kpi="' . esc_attr($key) . '">'
|
||||
. '<span class="aksrt-po-kpi__label">' . esc_html($label) . '</span>'
|
||||
. '<span class="aksrt-po-kpi__value" data-kpi-value>—</span>'
|
||||
. '<span class="aksrt-po-kpi__foot"><span class="aksrt-po-delta is-flat" data-kpi-delta>—</span>'
|
||||
. '<span class="aksrt-po-kpi__meta" data-kpi-meta></span></span>'
|
||||
echo '<div class="jio-po-kpi jio-po-kpi--' . esc_attr($tone) . '" data-kpi="' . esc_attr($key) . '">'
|
||||
. '<span class="jio-po-kpi__label">' . esc_html($label) . '</span>'
|
||||
. '<span class="jio-po-kpi__value" data-kpi-value>—</span>'
|
||||
. '<span class="jio-po-kpi__foot"><span class="jio-po-delta is-flat" data-kpi-delta>—</span>'
|
||||
. '<span class="jio-po-kpi__meta" data-kpi-meta></span></span>'
|
||||
. '</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
@@ -230,65 +230,65 @@ final class PayOverview {
|
||||
* ----------------------------------------------------------------*/
|
||||
|
||||
private function chart_card(string $key, string $title, string $label, string $size): void {
|
||||
NativeUi::card_open($title, '', false, 'aksrt-po-card');
|
||||
echo '<div class="aksrt-po-chart aksrt-po-chart--' . esc_attr($size) . '" data-aksrt-po-chart="' . esc_attr($key) . '" role="img" aria-label="' . esc_attr($label) . '"></div>';
|
||||
NativeUi::card_open($title, '', false, 'jio-po-card');
|
||||
echo '<div class="jio-po-chart jio-po-chart--' . esc_attr($size) . '" data-jio-po-chart="' . esc_attr($key) . '" role="img" aria-label="' . esc_attr($label) . '"></div>';
|
||||
NativeUi::card_close();
|
||||
}
|
||||
|
||||
private function render_funnel_card(): void {
|
||||
$rows = [
|
||||
['created_count', __('下单数', 'aksrt'), __('区间内创建的订单', 'aksrt')],
|
||||
['conversion', __('支付转化率', 'aksrt'), __('下单后完成支付的比例', 'aksrt')],
|
||||
['close_rate', __('超时关闭率', 'aksrt'), __('未支付而关闭的比例', 'aksrt')],
|
||||
['refund_rate', __('退款率', 'aksrt'), __('退款金额 ÷ 支付金额', 'aksrt')],
|
||||
['created_count', __('下单数', 'jio'), __('区间内创建的订单', 'jio')],
|
||||
['conversion', __('支付转化率', 'jio'), __('下单后完成支付的比例', 'jio')],
|
||||
['close_rate', __('超时关闭率', 'jio'), __('未支付而关闭的比例', 'jio')],
|
||||
['refund_rate', __('退款率', 'jio'), __('退款金额 ÷ 支付金额', 'jio')],
|
||||
];
|
||||
NativeUi::card_open(__('转化与退款', 'aksrt'), '', false, 'aksrt-po-card');
|
||||
echo '<ul class="aksrt-po-funnel" data-aksrt-po-funnel>';
|
||||
NativeUi::card_open(__('转化与退款', 'jio'), '', false, 'jio-po-card');
|
||||
echo '<ul class="jio-po-funnel" data-jio-po-funnel>';
|
||||
foreach ($rows as [$key, $label, $hint]) {
|
||||
echo '<li data-metric="' . esc_attr($key) . '"><div class="aksrt-po-funnel__head">'
|
||||
. '<span class="aksrt-po-funnel__label">' . esc_html($label) . '</span>'
|
||||
. '<strong class="aksrt-po-funnel__value" data-value>—</strong></div>'
|
||||
. '<span class="aksrt-po-funnel__bar" aria-hidden="true"><i data-bar></i></span>'
|
||||
. '<span class="aksrt-po-funnel__hint">' . esc_html($hint) . ' <span data-delta></span></span></li>';
|
||||
echo '<li data-metric="' . esc_attr($key) . '"><div class="jio-po-funnel__head">'
|
||||
. '<span class="jio-po-funnel__label">' . esc_html($label) . '</span>'
|
||||
. '<strong class="jio-po-funnel__value" data-value>—</strong></div>'
|
||||
. '<span class="jio-po-funnel__bar" aria-hidden="true"><i data-bar></i></span>'
|
||||
. '<span class="jio-po-funnel__hint">' . esc_html($hint) . ' <span data-delta></span></span></li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
NativeUi::card_close();
|
||||
}
|
||||
|
||||
private function render_top_card(): void {
|
||||
NativeUi::card_open(__('付费内容热销 TOP 10', 'aksrt'), '', true, 'aksrt-po-card');
|
||||
echo '<div class="aksrt-native-table-wrap"><table class="widefat aksrt-po-top">'
|
||||
. '<caption class="screen-reader-text">' . esc_html__('付费内容热销排行', 'aksrt') . '</caption>'
|
||||
. '<thead><tr><th scope="col" class="column-rank">#</th><th scope="col">' . esc_html__('文章', 'aksrt') . '</th>'
|
||||
. '<th scope="col" class="column-num">' . esc_html__('销量', 'aksrt') . '</th>'
|
||||
. '<th scope="col" class="column-num">' . esc_html__('支付金额', 'aksrt') . '</th>'
|
||||
. '<th scope="col" class="column-share">' . esc_html__('占比', 'aksrt') . '</th></tr></thead>'
|
||||
. '<tbody data-aksrt-po-top></tbody></table></div>';
|
||||
NativeUi::card_open(__('付费内容热销 TOP 10', 'jio'), '', true, 'jio-po-card');
|
||||
echo '<div class="jio-native-table-wrap"><table class="widefat jio-po-top">'
|
||||
. '<caption class="screen-reader-text">' . esc_html__('付费内容热销排行', 'jio') . '</caption>'
|
||||
. '<thead><tr><th scope="col" class="column-rank">#</th><th scope="col">' . esc_html__('文章', 'jio') . '</th>'
|
||||
. '<th scope="col" class="column-num">' . esc_html__('销量', 'jio') . '</th>'
|
||||
. '<th scope="col" class="column-num">' . esc_html__('支付金额', 'jio') . '</th>'
|
||||
. '<th scope="col" class="column-share">' . esc_html__('占比', 'jio') . '</th></tr></thead>'
|
||||
. '<tbody data-jio-po-top></tbody></table></div>';
|
||||
NativeUi::card_close();
|
||||
}
|
||||
|
||||
private function render_todo_card(): void {
|
||||
$orders = class_exists('\AKSRT\Payment\OrderRepository') ? \AKSRT\Payment\OrderRepository::get_instance()->get_orders_status_counts() : [];
|
||||
$withdraw = class_exists('\AKSRT\Payment\Withdrawal') ? \AKSRT\Payment\Withdrawal::get_instance()->get_stats() : [];
|
||||
$income = class_exists('\AKSRT\Payment\Revenue') ? \AKSRT\Payment\Revenue::get_instance()->get_stats() : [];
|
||||
$cards = class_exists('\AKSRT\Payment\ChargeCard') ? \AKSRT\Payment\ChargeCard::get_instance()->get_stats() : [];
|
||||
$orders = class_exists('\Jio\Payment\OrderRepository') ? \Jio\Payment\OrderRepository::get_instance()->get_orders_status_counts() : [];
|
||||
$withdraw = class_exists('\Jio\Payment\Withdrawal') ? \Jio\Payment\Withdrawal::get_instance()->get_stats() : [];
|
||||
$income = class_exists('\Jio\Payment\Revenue') ? \Jio\Payment\Revenue::get_instance()->get_stats() : [];
|
||||
$cards = class_exists('\Jio\Payment\ChargeCard') ? \Jio\Payment\ChargeCard::get_instance()->get_stats() : [];
|
||||
|
||||
$items = [
|
||||
[__('待审提现', 'aksrt'), (int) ($withdraw['pending'] ?? 0), $this->page_url('aksrt_withdraw', ['status' => 'pending']), 'dashicons-money-alt', 'danger'],
|
||||
[__('打款中提现', 'aksrt'), (int) ($withdraw['processing'] ?? 0), $this->page_url('aksrt_withdraw', ['status' => 'processing']), 'dashicons-update', 'warning'],
|
||||
[__('待结算分成', 'aksrt'), (string) ($income['cash']['pending'] ?? '0.00'), $this->page_url('aksrt_income', ['status' => 'pending']), 'dashicons-chart-pie', 'warning'],
|
||||
[__('待支付订单', 'aksrt'), (int) ($orders['pending'] ?? 0), $this->page_url('aksrt_orders', ['status' => 'pending']), 'dashicons-clock', 'info'],
|
||||
[__('可用卡密', 'aksrt'), (int) ($cards['active'] ?? 0), $this->page_url('aksrt_charge_cards', ['status' => 'active']), 'dashicons-id-alt', 'neutral'],
|
||||
[__('待审提现', 'jio'), (int) ($withdraw['pending'] ?? 0), $this->page_url('jio_withdraw', ['status' => 'pending']), 'dashicons-money-alt', 'danger'],
|
||||
[__('打款中提现', 'jio'), (int) ($withdraw['processing'] ?? 0), $this->page_url('jio_withdraw', ['status' => 'processing']), 'dashicons-update', 'warning'],
|
||||
[__('待结算分成', 'jio'), (string) ($income['cash']['pending'] ?? '0.00'), $this->page_url('jio_income', ['status' => 'pending']), 'dashicons-chart-pie', 'warning'],
|
||||
[__('待支付订单', 'jio'), (int) ($orders['pending'] ?? 0), $this->page_url('jio_orders', ['status' => 'pending']), 'dashicons-clock', 'info'],
|
||||
[__('可用卡密', 'jio'), (int) ($cards['active'] ?? 0), $this->page_url('jio_charge_cards', ['status' => 'active']), 'dashicons-id-alt', 'neutral'],
|
||||
];
|
||||
|
||||
NativeUi::card_open(__('待办事项', 'aksrt'), '', false, 'aksrt-po-card');
|
||||
echo '<ul class="aksrt-po-todo">';
|
||||
NativeUi::card_open(__('待办事项', 'jio'), '', false, 'jio-po-card');
|
||||
echo '<ul class="jio-po-todo">';
|
||||
foreach ($items as [$label, $value, $url, $icon, $tone]) {
|
||||
$idle = (string) $value === '0' || (string) $value === '0.00';
|
||||
echo '<li><a class="aksrt-po-todo__item aksrt-po-todo__item--' . esc_attr($idle ? 'neutral' : $tone) . '" href="' . esc_url($url) . '">'
|
||||
. '<span class="aksrt-po-todo__icon dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>'
|
||||
. '<span class="aksrt-po-todo__label">' . esc_html($label) . '</span>'
|
||||
. '<span class="aksrt-po-todo__value">' . esc_html(is_int($value) ? number_format_i18n($value) : $this->currency_symbol() . $value) . '</span>'
|
||||
echo '<li><a class="jio-po-todo__item jio-po-todo__item--' . esc_attr($idle ? 'neutral' : $tone) . '" href="' . esc_url($url) . '">'
|
||||
. '<span class="jio-po-todo__icon dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>'
|
||||
. '<span class="jio-po-todo__label">' . esc_html($label) . '</span>'
|
||||
. '<span class="jio-po-todo__value">' . esc_html(is_int($value) ? number_format_i18n($value) : $this->currency_symbol() . $value) . '</span>'
|
||||
. '</a></li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
@@ -296,12 +296,12 @@ final class PayOverview {
|
||||
}
|
||||
|
||||
private function render_shortcuts_card(): void {
|
||||
NativeUi::card_open(__('快捷入口', 'aksrt'), '', false, 'aksrt-po-card');
|
||||
echo '<ul class="aksrt-po-links">';
|
||||
NativeUi::card_open(__('快捷入口', 'jio'), '', false, 'jio-po-card');
|
||||
echo '<ul class="jio-po-links">';
|
||||
foreach (PayMenus::get_instance()->shortcuts() as $item) {
|
||||
echo '<li><a href="' . esc_url($this->page_url($item['slug'])) . '">'
|
||||
. '<span class="aksrt-po-links__icon dashicons ' . esc_attr($item['icon']) . '" aria-hidden="true"></span>'
|
||||
. '<span class="aksrt-po-links__text"><strong>' . esc_html($item['title']) . '</strong>'
|
||||
. '<span class="jio-po-links__icon dashicons ' . esc_attr($item['icon']) . '" aria-hidden="true"></span>'
|
||||
. '<span class="jio-po-links__text"><strong>' . esc_html($item['title']) . '</strong>'
|
||||
. ($item['desc'] !== '' ? '<span>' . esc_html($item['desc']) . '</span>' : '')
|
||||
. '</span></a></li>';
|
||||
}
|
||||
@@ -314,7 +314,7 @@ final class PayOverview {
|
||||
* ----------------------------------------------------------------*/
|
||||
|
||||
private function currency_symbol(): string {
|
||||
$paid = \AKSRT\Settings\Settings::get_paid_content_settings();
|
||||
$paid = \Jio\Settings\Settings::get_paid_content_settings();
|
||||
return (string) ($paid['display']['currency_symbol'] ?? '¥');
|
||||
}
|
||||
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
* - PayProductsList 列表查询与表格渲染
|
||||
* - PayProductsEditor 快速设置弹窗与保存
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Core\DomainModel;
|
||||
use Jio\Core\DomainModel;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -25,8 +25,8 @@ class PayProducts {
|
||||
use PayProductsEditor;
|
||||
|
||||
public const CAPABILITY = 'manage_options';
|
||||
public const PAGE_SLUG = 'aksrt_pay_products';
|
||||
public const SAVE_ACTION = 'aksrt_pay_save_product';
|
||||
public const PAGE_SLUG = 'jio_pay_products';
|
||||
public const SAVE_ACTION = 'jio_pay_save_product';
|
||||
|
||||
/** 列表纳入的文章状态。 */
|
||||
private const LIST_STATUSES = ['publish', 'future', 'draft', 'pending', 'private'];
|
||||
@@ -74,21 +74,21 @@ class PayProducts {
|
||||
}
|
||||
$css = '/assets/css/admin/pay-products.css';
|
||||
$js = '/assets/js/admin-pay-products.js';
|
||||
wp_enqueue_style('aksrt-admin-pay-products', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-pay-products', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-pay-products', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-pay-products', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
public function render_products_page(): void {
|
||||
$this->ensure_cap();
|
||||
NativeUi::page_open('aksrt-products-page');
|
||||
NativeUi::page_open('jio-products-page');
|
||||
NativeUi::header(
|
||||
__('付费文章', 'aksrt'),
|
||||
__('管理付费内容的售卖配置。点击「快速设置」即可在本页修改价格、权限与销量,无需进入文章编辑页。', 'aksrt'),
|
||||
__('付费文章', 'jio'),
|
||||
__('管理付费内容的售卖配置。点击「快速设置」即可在本页修改价格、权限与销量,无需进入文章编辑页。', 'jio'),
|
||||
'dashicons-media-document'
|
||||
);
|
||||
|
||||
if (!class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('订单服务不可用。', 'aksrt') . '</p></div>';
|
||||
if (!class_exists('\Jio\Payment\OrderRepository')) {
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('订单服务不可用。', 'jio') . '</p></div>';
|
||||
NativeUi::page_close();
|
||||
return;
|
||||
}
|
||||
@@ -109,19 +109,19 @@ class PayProducts {
|
||||
/** @return array<int, string> */
|
||||
private function pay_limit_labels(): array {
|
||||
return [
|
||||
0 => __('所有人可购买', 'aksrt'),
|
||||
1 => __('VIP1 及以上可购买', 'aksrt'),
|
||||
2 => __('仅 VIP2 可购买', 'aksrt'),
|
||||
0 => __('所有人可购买', 'jio'),
|
||||
1 => __('VIP1 及以上可购买', 'jio'),
|
||||
2 => __('仅 VIP2 可购买', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
private function currency_symbol(): string {
|
||||
$paid = \AKSRT\Settings\Settings::get_paid_content_settings();
|
||||
$paid = \Jio\Settings\Settings::get_paid_content_settings();
|
||||
return (string) ($paid['display']['currency_symbol'] ?? '¥');
|
||||
}
|
||||
|
||||
private function render_flash(): void {
|
||||
$code = isset($_GET['aksrt_pay_msg']) ? sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg'])) : '';
|
||||
$code = isset($_GET['jio_pay_msg']) ? sanitize_key((string) wp_unslash($_GET['jio_pay_msg'])) : '';
|
||||
if ($code === '') {
|
||||
return;
|
||||
}
|
||||
@@ -129,11 +129,11 @@ class PayProducts {
|
||||
$title = $post instanceof \WP_Post ? get_the_title($post) : '';
|
||||
// 码 => [文案, 是否失败]
|
||||
$map = [
|
||||
'product_saved' => [sprintf(__('已保存「%s」的商品配置。', 'aksrt'), $title), false],
|
||||
'product_disabled' => [sprintf(__('已关闭「%s」的付费功能,文章已移出本列表。', 'aksrt'), $title), false],
|
||||
'product_invalid_amount' => [__('保存失败:金额最多两位小数,积分须为非负整数。', 'aksrt'), true],
|
||||
'product_vip_price' => [__('保存失败:会员价格不能高于执行价(积分商品不能高于积分售价)。', 'aksrt'), true],
|
||||
'product_missing' => [__('保存失败:文章不存在或您无权编辑。', 'aksrt'), true],
|
||||
'product_saved' => [sprintf(__('已保存「%s」的商品配置。', 'jio'), $title), false],
|
||||
'product_disabled' => [sprintf(__('已关闭「%s」的付费功能,文章已移出本列表。', 'jio'), $title), false],
|
||||
'product_invalid_amount' => [__('保存失败:金额最多两位小数,积分须为非负整数。', 'jio'), true],
|
||||
'product_vip_price' => [__('保存失败:会员价格不能高于执行价(积分商品不能高于积分售价)。', 'jio'), true],
|
||||
'product_missing' => [__('保存失败:文章不存在或您无权编辑。', 'jio'), true],
|
||||
];
|
||||
if (!isset($map[$code])) {
|
||||
return;
|
||||
@@ -144,13 +144,13 @@ class PayProducts {
|
||||
|
||||
private function ensure_cap(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
}
|
||||
|
||||
private function cash_decimal(string $value): string {
|
||||
try {
|
||||
return \AKSRT\Ledger\AssetAmount::balance($value)->decimal();
|
||||
return \Jio\Ledger\AssetAmount::balance($value)->decimal();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return '0.00';
|
||||
}
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
* 付费文章:快速设置弹窗与保存。
|
||||
*
|
||||
* 作为 flat sibling trait 被 PayProducts 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
* 弹窗是单个 <dialog>,由 admin-pay-products.js 按行内 data-aksrt-product-edit 的 JSON 填充。
|
||||
* 弹窗是单个 <dialog>,由 admin-pay-products.js 按行内 data-jio-product-edit 的 JSON 填充。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Core\DomainModel;
|
||||
use AKSRT\Ledger\AssetAmount;
|
||||
use Jio\Core\DomainModel;
|
||||
use Jio\Ledger\AssetAmount;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -32,7 +32,7 @@ trait PayProductsEditor {
|
||||
$title = html_entity_decode(get_the_title($post), ENT_QUOTES, 'UTF-8');
|
||||
return [
|
||||
'id' => (int) $post->ID,
|
||||
'title' => $title !== '' ? $title : sprintf(__('(无标题 #%d)', 'aksrt'), (int) $post->ID),
|
||||
'title' => $title !== '' ? $title : sprintf(__('(无标题 #%d)', 'jio'), (int) $post->ID),
|
||||
'editUrl' => $edit_url,
|
||||
'fields' => $fields,
|
||||
];
|
||||
@@ -41,12 +41,12 @@ trait PayProductsEditor {
|
||||
/** @param array<string, string|int> $context 列表筛选与页码,保存后据此回到原位置。 */
|
||||
private function render_product_editor(array $context): void {
|
||||
$currency = $this->currency_symbol();
|
||||
echo '<dialog class="aksrt-product-dialog" id="aksrt-product-editor" aria-labelledby="aksrt-product-editor-title" aria-describedby="aksrt-product-editor-subject">';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-product-dialog__frame" data-aksrt-product-form'
|
||||
. ' data-discard="' . esc_attr__('有未保存的修改,确定放弃?', 'aksrt') . '"'
|
||||
. ' data-vip-error="' . esc_attr__('会员价格不能高于执行价。', 'aksrt') . '"'
|
||||
. ' data-vip-points-error="' . esc_attr__('会员积分价不能高于积分售价。', 'aksrt') . '"'
|
||||
. ' data-busy-label="' . esc_attr__('正在保存…', 'aksrt') . '">';
|
||||
echo '<dialog class="jio-product-dialog" id="jio-product-editor" aria-labelledby="jio-product-editor-title" aria-describedby="jio-product-editor-subject">';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-product-dialog__frame" data-jio-product-form'
|
||||
. ' data-discard="' . esc_attr__('有未保存的修改,确定放弃?', 'jio') . '"'
|
||||
. ' data-vip-error="' . esc_attr__('会员价格不能高于执行价。', 'jio') . '"'
|
||||
. ' data-vip-points-error="' . esc_attr__('会员积分价不能高于积分售价。', 'jio') . '"'
|
||||
. ' data-busy-label="' . esc_attr__('正在保存…', 'jio') . '">';
|
||||
echo '<input type="hidden" name="action" value="' . esc_attr(self::SAVE_ACTION) . '">';
|
||||
echo '<input type="hidden" name="post_id" value="">';
|
||||
wp_nonce_field(self::SAVE_ACTION, '_wpnonce', false);
|
||||
@@ -54,83 +54,83 @@ trait PayProductsEditor {
|
||||
echo '<input type="hidden" name="ctx[' . esc_attr((string) $key) . ']" value="' . esc_attr((string) $value) . '">';
|
||||
}
|
||||
|
||||
echo '<header class="aksrt-product-dialog__head"><div>';
|
||||
echo '<h2 id="aksrt-product-editor-title">' . esc_html__('快速设置', 'aksrt') . '</h2>';
|
||||
echo '<p id="aksrt-product-editor-subject" data-aksrt-product-subject></p></div>';
|
||||
echo '<button type="button" class="aksrt-product-dialog__close" data-aksrt-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button>';
|
||||
echo '</header><div class="aksrt-product-dialog__body">';
|
||||
echo '<header class="jio-product-dialog__head"><div>';
|
||||
echo '<h2 id="jio-product-editor-title">' . esc_html__('快速设置', 'jio') . '</h2>';
|
||||
echo '<p id="jio-product-editor-subject" data-jio-product-subject></p></div>';
|
||||
echo '<button type="button" class="jio-product-dialog__close" data-jio-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button>';
|
||||
echo '</header><div class="jio-product-dialog__body">';
|
||||
|
||||
$this->editor_section_open(__('售卖方式', 'aksrt'));
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('付费类型', 'aksrt') . '</span><select name="aksrt_pay[pay_type]">';
|
||||
$this->editor_section_open(__('售卖方式', 'jio'));
|
||||
echo '<div class="jio-native-form-grid">';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('付费类型', 'jio') . '</span><select name="jio_pay[pay_type]">';
|
||||
foreach ($this->paid_type_labels() as $key => $label) {
|
||||
echo '<option value="' . esc_attr($key) . '">' . esc_html($label) . '</option>';
|
||||
}
|
||||
echo '<option value="' . esc_attr(DomainModel::PAY_TYPE_NO) . '">' . esc_html__('关闭付费', 'aksrt') . '</option></select></label>';
|
||||
$this->editor_radios('pay_modo', __('支付方式', 'aksrt'), ['0' => __('现金', 'aksrt'), 'points' => __('积分', 'aksrt')], 'enabled');
|
||||
echo '<option value="' . esc_attr(DomainModel::PAY_TYPE_NO) . '">' . esc_html__('关闭付费', 'jio') . '</option></select></label>';
|
||||
$this->editor_radios('pay_modo', __('支付方式', 'jio'), ['0' => __('现金', 'jio'), 'points' => __('积分', 'jio')], 'enabled');
|
||||
echo '</div>';
|
||||
echo '<p class="aksrt-native-callout is-warning" data-aksrt-product-pane="disabled" hidden>'
|
||||
. esc_html__('关闭后文章恢复免费阅读并移出本列表,价格等配置会保留,重新开启即可恢复。', 'aksrt') . '</p>';
|
||||
echo '<div data-aksrt-product-pane="enabled">';
|
||||
$this->editor_radios('pay_limit', __('购买权限', 'aksrt'), array_map('strval', $this->pay_limit_labels()));
|
||||
echo '<p class="jio-native-callout is-warning" data-jio-product-pane="disabled" hidden>'
|
||||
. esc_html__('关闭后文章恢复免费阅读并移出本列表,价格等配置会保留,重新开启即可恢复。', 'jio') . '</p>';
|
||||
echo '<div data-jio-product-pane="enabled">';
|
||||
$this->editor_radios('pay_limit', __('购买权限', 'jio'), array_map('strval', $this->pay_limit_labels()));
|
||||
echo '</div>';
|
||||
$this->editor_section_close();
|
||||
|
||||
echo '<div data-aksrt-product-pane="enabled">';
|
||||
$this->editor_section_open(__('价格', 'aksrt'), __('会员价填 0 表示该等级免费,且不能高于售价。', 'aksrt'));
|
||||
echo '<fieldset class="aksrt-native-form-grid aksrt-product-pane" data-aksrt-product-pane="cash"><legend class="screen-reader-text">' . esc_html__('现金价格', 'aksrt') . '</legend>';
|
||||
$this->editor_number('price', __('执行价', 'aksrt'), $currency, '0.01', 'data-aksrt-product-base="cash"');
|
||||
$this->editor_number('original_price', __('原价', 'aksrt'), $currency, '0.01', '', __('高于执行价时划线显示。', 'aksrt'));
|
||||
$this->editor_number('vip1_price', __('VIP1 价格', 'aksrt'), $currency, '0.01', 'data-aksrt-product-vip="cash"');
|
||||
$this->editor_number('vip2_price', __('VIP2 价格', 'aksrt'), $currency, '0.01', 'data-aksrt-product-vip="cash"');
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('促销标签', 'aksrt') . '</span>'
|
||||
. '<input type="text" name="aksrt_pay[promotion_tag]"><small class="aksrt-native-field__hint">' . esc_html__('显示在原价上方,支持安全 HTML,例如图标。', 'aksrt') . '</small></label>';
|
||||
echo '<div data-jio-product-pane="enabled">';
|
||||
$this->editor_section_open(__('价格', 'jio'), __('会员价填 0 表示该等级免费,且不能高于售价。', 'jio'));
|
||||
echo '<fieldset class="jio-native-form-grid jio-product-pane" data-jio-product-pane="cash"><legend class="screen-reader-text">' . esc_html__('现金价格', 'jio') . '</legend>';
|
||||
$this->editor_number('price', __('执行价', 'jio'), $currency, '0.01', 'data-jio-product-base="cash"');
|
||||
$this->editor_number('original_price', __('原价', 'jio'), $currency, '0.01', '', __('高于执行价时划线显示。', 'jio'));
|
||||
$this->editor_number('vip1_price', __('VIP1 价格', 'jio'), $currency, '0.01', 'data-jio-product-vip="cash"');
|
||||
$this->editor_number('vip2_price', __('VIP2 价格', 'jio'), $currency, '0.01', 'data-jio-product-vip="cash"');
|
||||
echo '<label class="jio-native-field is-wide"><span class="jio-native-field__label">' . esc_html__('促销标签', 'jio') . '</span>'
|
||||
. '<input type="text" name="jio_pay[promotion_tag]"><small class="jio-native-field__hint">' . esc_html__('显示在原价上方,支持安全 HTML,例如图标。', 'jio') . '</small></label>';
|
||||
echo '</fieldset>';
|
||||
echo '<fieldset class="aksrt-native-form-grid aksrt-product-pane" data-aksrt-product-pane="points" hidden disabled><legend class="screen-reader-text">' . esc_html__('积分价格', 'aksrt') . '</legend>';
|
||||
$this->editor_number('points_price', __('积分售价', 'aksrt'), __('积分', 'aksrt'), '1', 'data-aksrt-product-base="points"');
|
||||
$this->editor_number('vip1_points', __('VIP1 积分价', 'aksrt'), __('积分', 'aksrt'), '1', 'data-aksrt-product-vip="points"');
|
||||
$this->editor_number('vip2_points', __('VIP2 积分价', 'aksrt'), __('积分', 'aksrt'), '1', 'data-aksrt-product-vip="points"');
|
||||
echo '<fieldset class="jio-native-form-grid jio-product-pane" data-jio-product-pane="points" hidden disabled><legend class="screen-reader-text">' . esc_html__('积分价格', 'jio') . '</legend>';
|
||||
$this->editor_number('points_price', __('积分售价', 'jio'), __('积分', 'jio'), '1', 'data-jio-product-base="points"');
|
||||
$this->editor_number('vip1_points', __('VIP1 积分价', 'jio'), __('积分', 'jio'), '1', 'data-jio-product-vip="points"');
|
||||
$this->editor_number('vip2_points', __('VIP2 积分价', 'jio'), __('积分', 'jio'), '1', 'data-jio-product-vip="points"');
|
||||
echo '</fieldset>';
|
||||
$this->editor_section_close();
|
||||
|
||||
$this->editor_section_open(__('销售', 'aksrt'));
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
$this->editor_number('pay_count', __('销量浮动', 'aksrt'), '', '1', '', __('在真实销量上增加或减少的数量,可填负数。', 'aksrt'), false);
|
||||
echo '<div class="aksrt-product-switches" role="group" aria-label="' . esc_attr__('销售选项', 'aksrt') . '">';
|
||||
$this->editor_checkbox('show_sales', __('前台显示销量', 'aksrt'));
|
||||
$this->editor_checkbox('coupon_s', __('允许使用优惠码', 'aksrt'));
|
||||
$this->editor_checkbox('order_expired_s', __('启用订单有效期', 'aksrt'));
|
||||
$this->editor_section_open(__('销售', 'jio'));
|
||||
echo '<div class="jio-native-form-grid">';
|
||||
$this->editor_number('pay_count', __('销量浮动', 'jio'), '', '1', '', __('在真实销量上增加或减少的数量,可填负数。', 'jio'), false);
|
||||
echo '<div class="jio-product-switches" role="group" aria-label="' . esc_attr__('销售选项', 'jio') . '">';
|
||||
$this->editor_checkbox('show_sales', __('前台显示销量', 'jio'));
|
||||
$this->editor_checkbox('coupon_s', __('允许使用优惠码', 'jio'));
|
||||
$this->editor_checkbox('order_expired_s', __('启用订单有效期', 'jio'));
|
||||
echo '</div></div>';
|
||||
echo '<fieldset class="aksrt-product-pane aksrt-product-expire" data-aksrt-product-pane="expire" hidden disabled><legend class="screen-reader-text">' . esc_html__('订单有效期', 'aksrt') . '</legend>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('有效时长', 'aksrt') . '</span><span class="aksrt-product-inline">'
|
||||
. '<input type="number" name="aksrt_pay[order_expired_time]" min="1" step="1" required>'
|
||||
. '<select name="aksrt_pay[order_expired_unit]" aria-label="' . esc_attr__('时长单位', 'aksrt') . '">'
|
||||
. '<option value="day">' . esc_html__('天', 'aksrt') . '</option><option value="hour">' . esc_html__('小时', 'aksrt') . '</option><option value="minute">' . esc_html__('分钟', 'aksrt') . '</option>'
|
||||
. '</select></span><small class="aksrt-native-field__hint">' . esc_html__('购买后仅在有效期内可查看,过期需再次购买。', 'aksrt') . '</small></label></fieldset>';
|
||||
echo '<fieldset class="jio-product-pane jio-product-expire" data-jio-product-pane="expire" hidden disabled><legend class="screen-reader-text">' . esc_html__('订单有效期', 'jio') . '</legend>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('有效时长', 'jio') . '</span><span class="jio-product-inline">'
|
||||
. '<input type="number" name="jio_pay[order_expired_time]" min="1" step="1" required>'
|
||||
. '<select name="jio_pay[order_expired_unit]" aria-label="' . esc_attr__('时长单位', 'jio') . '">'
|
||||
. '<option value="day">' . esc_html__('天', 'jio') . '</option><option value="hour">' . esc_html__('小时', 'jio') . '</option><option value="minute">' . esc_html__('分钟', 'jio') . '</option>'
|
||||
. '</select></span><small class="jio-native-field__hint">' . esc_html__('购买后仅在有效期内可查看,过期需再次购买。', 'jio') . '</small></label></fieldset>';
|
||||
$this->editor_section_close();
|
||||
|
||||
$this->editor_section_open(__('商品文案', 'aksrt'));
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('商品标题', 'aksrt') . '</span><input type="text" name="aksrt_pay[pay_title]" maxlength="200">'
|
||||
. '<small class="aksrt-native-field__hint">' . esc_html__('留空则使用文章标题。', 'aksrt') . '</small></label>';
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('商品简介', 'aksrt') . '</span><textarea name="aksrt_pay[pay_doc]" rows="2"></textarea></label>';
|
||||
$this->editor_section_open(__('商品文案', 'jio'));
|
||||
echo '<div class="jio-native-form-grid">';
|
||||
echo '<label class="jio-native-field is-wide"><span class="jio-native-field__label">' . esc_html__('商品标题', 'jio') . '</span><input type="text" name="jio_pay[pay_title]" maxlength="200">'
|
||||
. '<small class="jio-native-field__hint">' . esc_html__('留空则使用文章标题。', 'jio') . '</small></label>';
|
||||
echo '<label class="jio-native-field is-wide"><span class="jio-native-field__label">' . esc_html__('商品简介', 'jio') . '</span><textarea name="jio_pay[pay_doc]" rows="2"></textarea></label>';
|
||||
echo '</div>';
|
||||
$this->editor_section_close();
|
||||
echo '</div>';
|
||||
|
||||
echo '<p class="aksrt-native-callout aksrt-product-note">' . esc_html__('下载资源、付费图片、视频剧集和隐藏内容请在文章编辑页的「付费功能」面板维护。', 'aksrt')
|
||||
. ' <a href="#" target="_blank" rel="noopener noreferrer" data-aksrt-product-edit-link>' . esc_html__('打开文章编辑页', 'aksrt') . '</a></p>';
|
||||
echo '<p class="jio-native-callout jio-product-note">' . esc_html__('下载资源、付费图片、视频剧集和隐藏内容请在文章编辑页的「付费功能」面板维护。', 'jio')
|
||||
. ' <a href="#" target="_blank" rel="noopener noreferrer" data-jio-product-edit-link>' . esc_html__('打开文章编辑页', 'jio') . '</a></p>';
|
||||
|
||||
echo '</div><footer class="aksrt-product-dialog__foot">';
|
||||
echo '<button type="button" class="button" data-aksrt-dialog-close>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary" data-aksrt-product-submit>' . esc_html__('保存设置', 'aksrt') . '</button>';
|
||||
echo '</div><footer class="jio-product-dialog__foot">';
|
||||
echo '<button type="button" class="button" data-jio-dialog-close>' . esc_html__('取消', 'jio') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary" data-jio-product-submit>' . esc_html__('保存设置', 'jio') . '</button>';
|
||||
echo '</footer></form></dialog>';
|
||||
}
|
||||
|
||||
private function editor_section_open(string $title, string $hint = ''): void {
|
||||
echo '<section class="aksrt-product-section"><h3>' . esc_html($title) . '</h3>';
|
||||
echo '<section class="jio-product-section"><h3>' . esc_html($title) . '</h3>';
|
||||
if ($hint !== '') {
|
||||
echo '<p class="aksrt-product-section__hint">' . esc_html($hint) . '</p>';
|
||||
echo '<p class="jio-product-section__hint">' . esc_html($hint) . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,29 +140,29 @@ trait PayProductsEditor {
|
||||
|
||||
/** @param array<string, string> $options */
|
||||
private function editor_radios(string $key, string $title, array $options, string $pane = ''): void {
|
||||
echo '<fieldset class="aksrt-native-field aksrt-product-radios"' . ($pane !== '' ? ' data-aksrt-product-pane="' . esc_attr($pane) . '"' : '') . '>';
|
||||
echo '<legend class="aksrt-native-field__label">' . esc_html($title) . '</legend><div>';
|
||||
echo '<fieldset class="jio-native-field jio-product-radios"' . ($pane !== '' ? ' data-jio-product-pane="' . esc_attr($pane) . '"' : '') . '>';
|
||||
echo '<legend class="jio-native-field__label">' . esc_html($title) . '</legend><div>';
|
||||
foreach ($options as $value => $label) {
|
||||
echo '<label><input type="radio" name="aksrt_pay[' . esc_attr($key) . ']" value="' . esc_attr((string) $value) . '"><span>' . esc_html($label) . '</span></label>';
|
||||
echo '<label><input type="radio" name="jio_pay[' . esc_attr($key) . ']" value="' . esc_attr((string) $value) . '"><span>' . esc_html($label) . '</span></label>';
|
||||
}
|
||||
echo '</div></fieldset>';
|
||||
}
|
||||
|
||||
private function editor_number(string $key, string $title, string $unit, string $step, string $attributes = '', string $hint = '', bool $non_negative = true): void {
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html($title) . '</span><span class="aksrt-product-inline">';
|
||||
echo '<input type="number" name="aksrt_pay[' . esc_attr($key) . ']" step="' . esc_attr($step) . '"' . ($non_negative ? ' min="0"' : '') . ' required ' . $attributes . '>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html($title) . '</span><span class="jio-product-inline">';
|
||||
echo '<input type="number" name="jio_pay[' . esc_attr($key) . ']" step="' . esc_attr($step) . '"' . ($non_negative ? ' min="0"' : '') . ' required ' . $attributes . '>';
|
||||
if ($unit !== '') {
|
||||
echo '<span class="aksrt-product-unit">' . esc_html($unit) . '</span>';
|
||||
echo '<span class="jio-product-unit">' . esc_html($unit) . '</span>';
|
||||
}
|
||||
echo '</span>';
|
||||
if ($hint !== '') {
|
||||
echo '<small class="aksrt-native-field__hint">' . esc_html($hint) . '</small>';
|
||||
echo '<small class="jio-native-field__hint">' . esc_html($hint) . '</small>';
|
||||
}
|
||||
echo '</label>';
|
||||
}
|
||||
|
||||
private function editor_checkbox(string $key, string $label): void {
|
||||
echo '<label class="aksrt-product-check"><input type="checkbox" name="aksrt_pay[' . esc_attr($key) . ']" value="1">' . esc_html($label) . '</label>';
|
||||
echo '<label class="jio-product-check"><input type="checkbox" name="jio_pay[' . esc_attr($key) . ']" value="1">' . esc_html($label) . '</label>';
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
@@ -171,7 +171,7 @@ trait PayProductsEditor {
|
||||
|
||||
public function handle_save(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限执行此操作。', 'aksrt'), '', ['response' => 403]);
|
||||
wp_die(esc_html__('您没有权限执行此操作。', 'jio'), '', ['response' => 403]);
|
||||
}
|
||||
check_admin_referer(self::SAVE_ACTION);
|
||||
|
||||
@@ -182,8 +182,8 @@ trait PayProductsEditor {
|
||||
$this->redirect_after_save($context, 'product_missing', 0);
|
||||
}
|
||||
|
||||
$raw = isset($_POST['aksrt_pay']) && is_array($_POST['aksrt_pay']) ? wp_unslash($_POST['aksrt_pay']) : [];
|
||||
$meta = aksrt_get_pay_meta($post);
|
||||
$raw = isset($_POST['jio_pay']) && is_array($_POST['jio_pay']) ? wp_unslash($_POST['jio_pay']) : [];
|
||||
$meta = jio_get_pay_meta($post);
|
||||
|
||||
$pay_type = DomainModel::normalize_pay_type((string) ($raw['pay_type'] ?? ''));
|
||||
if ($pay_type === DomainModel::PAY_TYPE_NO) {
|
||||
@@ -282,7 +282,7 @@ trait PayProductsEditor {
|
||||
|
||||
/** @param array<string, string|int> $context */
|
||||
private function redirect_after_save(array $context, string $code, int $post_id): void {
|
||||
$args = $context + ['aksrt_pay_msg' => $code];
|
||||
$args = $context + ['jio_pay_msg' => $code];
|
||||
if ($post_id > 0) {
|
||||
$args['product'] = $post_id;
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
*
|
||||
* 作为 flat sibling trait 被 PayProducts 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Core\DomainModel;
|
||||
use AKSRT\Ledger\AssetAmount;
|
||||
use AKSRT\Payment\OrderRepository;
|
||||
use Jio\Core\DomainModel;
|
||||
use Jio\Ledger\AssetAmount;
|
||||
use Jio\Payment\OrderRepository;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -54,25 +54,25 @@ trait PayProductsList {
|
||||
// 翻页链接显式带上当前筛选条件(不含 paged)。
|
||||
$query_args = array_filter(['pay_type' => $pay_type, 's' => $search], static fn(string $value): bool => $value !== '');
|
||||
|
||||
NativeUi::card_open('', '', true, 'aksrt-products-card');
|
||||
NativeUi::card_open('', '', true, 'jio-products-card');
|
||||
$this->render_product_toolbar($pay_type, $search, $type_labels);
|
||||
echo '<div class="aksrt-native-table-wrap">';
|
||||
echo '<table class="wp-list-table widefat fixed striped aksrt-products-table"><thead><tr>';
|
||||
echo '<th scope="col" class="column-product">' . esc_html__('商品', 'aksrt') . '</th>';
|
||||
echo '<th scope="col" class="column-type">' . esc_html__('付费类型', 'aksrt') . '</th>';
|
||||
echo '<th scope="col" class="column-price">' . esc_html__('价格', 'aksrt') . '</th>';
|
||||
echo '<th scope="col" class="column-sales">' . esc_html__('销量', 'aksrt') . '</th>';
|
||||
echo '<th scope="col" class="column-revenue">' . esc_html__('销售额', 'aksrt') . '</th>';
|
||||
echo '<th scope="col" class="column-actions">' . esc_html__('操作', 'aksrt') . '</th>';
|
||||
echo '<div class="jio-native-table-wrap">';
|
||||
echo '<table class="wp-list-table widefat fixed striped jio-products-table"><thead><tr>';
|
||||
echo '<th scope="col" class="column-product">' . esc_html__('商品', 'jio') . '</th>';
|
||||
echo '<th scope="col" class="column-type">' . esc_html__('付费类型', 'jio') . '</th>';
|
||||
echo '<th scope="col" class="column-price">' . esc_html__('价格', 'jio') . '</th>';
|
||||
echo '<th scope="col" class="column-sales">' . esc_html__('销量', 'jio') . '</th>';
|
||||
echo '<th scope="col" class="column-revenue">' . esc_html__('销售额', 'jio') . '</th>';
|
||||
echo '<th scope="col" class="column-actions">' . esc_html__('操作', 'jio') . '</th>';
|
||||
echo '</tr></thead><tbody>';
|
||||
|
||||
if (!$posts) {
|
||||
echo NativeUi::empty_row(
|
||||
6,
|
||||
__('暂无付费文章', 'aksrt'),
|
||||
__('暂无付费文章', 'jio'),
|
||||
$query_args !== []
|
||||
? __('调整类型或搜索条件后重试。', 'aksrt')
|
||||
: __('在文章编辑页的「付费功能」面板开启付费后,文章会出现在这里。', 'aksrt')
|
||||
? __('调整类型或搜索条件后重试。', 'jio')
|
||||
: __('在文章编辑页的「付费功能」面板开启付费后,文章会出现在这里。', 'jio')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ trait PayProductsList {
|
||||
}
|
||||
|
||||
/**
|
||||
* 付费文章 = `_aksrt_pay_type` 为任一付费类型;所有写入口都同步这个派生键,直接按它查询分页。
|
||||
* 付费文章 = `_jio_pay_type` 为任一付费类型;所有写入口都同步这个派生键,直接按它查询分页。
|
||||
*
|
||||
* @return array{posts: \WP_Post[], total: int}
|
||||
*/
|
||||
@@ -152,9 +152,9 @@ trait PayProductsList {
|
||||
$counts = $this->paid_type_counts();
|
||||
$keep = $search !== '' ? ['s' => $search] : [];
|
||||
|
||||
echo '<div class="aksrt-products-toolbar">';
|
||||
echo '<ul class="subsubsub" aria-label="' . esc_attr__('付费类型筛选', 'aksrt') . '">';
|
||||
foreach (['' => __('全部', 'aksrt')] + $type_labels as $key => $label) {
|
||||
echo '<div class="jio-products-toolbar">';
|
||||
echo '<ul class="subsubsub" aria-label="' . esc_attr__('付费类型筛选', 'jio') . '">';
|
||||
foreach (['' => __('全部', 'jio')] + $type_labels as $key => $label) {
|
||||
$key = (string) $key;
|
||||
$count = $key === '' ? array_sum($counts) : (int) ($counts[$key] ?? 0);
|
||||
$url = $this->page_url(self::PAGE_SLUG, $key === '' ? $keep : $keep + ['pay_type' => $key]);
|
||||
@@ -163,17 +163,17 @@ trait PayProductsList {
|
||||
}
|
||||
echo '</ul>';
|
||||
|
||||
echo '<form method="get" action="' . esc_url(admin_url('admin.php')) . '" class="aksrt-native-filters" role="search">';
|
||||
echo '<form method="get" action="' . esc_url(admin_url('admin.php')) . '" class="jio-native-filters" role="search">';
|
||||
echo '<input type="hidden" name="page" value="' . esc_attr(self::PAGE_SLUG) . '">';
|
||||
if ($pay_type !== '') {
|
||||
echo '<input type="hidden" name="pay_type" value="' . esc_attr($pay_type) . '">';
|
||||
}
|
||||
echo '<label class="screen-reader-text" for="aksrt-products-search">' . esc_html__('搜索标题或文章 ID', 'aksrt') . '</label>';
|
||||
echo '<input type="search" id="aksrt-products-search" name="s" value="' . esc_attr($search) . '" placeholder="' . esc_attr__('搜索标题或文章 ID', 'aksrt') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('搜索', 'aksrt') . '</button>';
|
||||
echo '<label class="screen-reader-text" for="jio-products-search">' . esc_html__('搜索标题或文章 ID', 'jio') . '</label>';
|
||||
echo '<input type="search" id="jio-products-search" name="s" value="' . esc_attr($search) . '" placeholder="' . esc_attr__('搜索标题或文章 ID', 'jio') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('搜索', 'jio') . '</button>';
|
||||
if ($search !== '') {
|
||||
$clear = $this->page_url(self::PAGE_SLUG, $pay_type !== '' ? ['pay_type' => $pay_type] : []);
|
||||
echo '<a class="button-link" href="' . esc_url($clear) . '">' . esc_html__('清除搜索', 'aksrt') . '</a>';
|
||||
echo '<a class="button-link" href="' . esc_url($clear) . '">' . esc_html__('清除搜索', 'jio') . '</a>';
|
||||
}
|
||||
echo '</form></div>';
|
||||
}
|
||||
@@ -184,11 +184,11 @@ trait PayProductsList {
|
||||
*/
|
||||
private function render_product_row(\WP_Post $post, array $sales, array $type_labels, string $currency): void {
|
||||
$post_id = (int) $post->ID;
|
||||
$meta = aksrt_get_pay_meta($post);
|
||||
$meta = jio_get_pay_meta($post);
|
||||
$pay_type = DomainModel::normalize_pay_type((string) $meta['pay_type']);
|
||||
$title = get_the_title($post);
|
||||
if ($title === '') {
|
||||
$title = sprintf(__('(无标题 #%d)', 'aksrt'), $post_id);
|
||||
$title = sprintf(__('(无标题 #%d)', 'jio'), $post_id);
|
||||
}
|
||||
$edit_url = (string) get_edit_post_link($post_id, 'raw');
|
||||
$permalink = (string) get_permalink($post_id);
|
||||
@@ -201,60 +201,60 @@ trait PayProductsList {
|
||||
echo $edit_url !== ''
|
||||
? '<a class="row-title" href="' . esc_url($edit_url) . '">' . esc_html($title) . '</a>'
|
||||
: esc_html($title);
|
||||
echo '</strong><div class="aksrt-products-sub">#' . esc_html((string) $post_id);
|
||||
echo '</strong><div class="jio-products-sub">#' . esc_html((string) $post_id);
|
||||
if ($post->post_status !== 'publish') {
|
||||
$status = get_post_status_object($post->post_status);
|
||||
echo ' · ' . esc_html($status ? (string) $status->label : $post->post_status);
|
||||
}
|
||||
if ((string) $meta['pay_title'] !== '') {
|
||||
echo ' · ' . esc_html(sprintf(__('商品标题:%s', 'aksrt'), (string) $meta['pay_title']));
|
||||
echo ' · ' . esc_html(sprintf(__('商品标题:%s', 'jio'), (string) $meta['pay_title']));
|
||||
}
|
||||
echo '</div></td>';
|
||||
|
||||
echo '<td class="column-type">' . NativeUi::badge($type_labels[$pay_type] ?? $pay_type, self::TYPE_TONES[$pay_type] ?? 'neutral');
|
||||
$limit = (int) $meta['pay_limit'];
|
||||
if ($limit > 0) {
|
||||
echo '<div class="aksrt-products-sub">' . esc_html($this->pay_limit_labels()[$limit] ?? '') . '</div>';
|
||||
echo '<div class="jio-products-sub">' . esc_html($this->pay_limit_labels()[$limit] ?? '') . '</div>';
|
||||
}
|
||||
echo '</td>';
|
||||
|
||||
echo '<td class="column-price">' . $this->price_cell_html($meta, $currency) . '</td>';
|
||||
|
||||
echo '<td class="column-sales"><span class="aksrt-native-amount">' . esc_html(sprintf(__('%d 单', 'aksrt'), $orders)) . '</span>';
|
||||
echo '<div class="aksrt-products-sub">' . esc_html(!empty($meta['show_sales'])
|
||||
? sprintf(__('前台显示 %d', 'aksrt'), aksrt_get_content_sales_count($post, $meta))
|
||||
: __('前台不显示销量', 'aksrt')) . '</div></td>';
|
||||
echo '<td class="column-sales"><span class="jio-native-amount">' . esc_html(sprintf(__('%d 单', 'jio'), $orders)) . '</span>';
|
||||
echo '<div class="jio-products-sub">' . esc_html(!empty($meta['show_sales'])
|
||||
? sprintf(__('前台显示 %d', 'jio'), jio_get_content_sales_count($post, $meta))
|
||||
: __('前台不显示销量', 'jio')) . '</div></td>';
|
||||
|
||||
echo '<td class="column-revenue"><span class="aksrt-native-amount">' . esc_html($this->sales_amount($sales['revenue'] ?? '0.00', $currency)) . '</span>';
|
||||
echo '<td class="column-revenue"><span class="jio-native-amount">' . esc_html($this->sales_amount($sales['revenue'] ?? '0.00', $currency)) . '</span>';
|
||||
if ($orders > 0) {
|
||||
echo '<div class="aksrt-products-sub">' . esc_html(sprintf(__('均价 %s', 'aksrt'), $this->sales_amount($sales['avg_amount'] ?? '0.00', $currency))) . '</div>';
|
||||
echo '<div class="jio-products-sub">' . esc_html(sprintf(__('均价 %s', 'jio'), $this->sales_amount($sales['avg_amount'] ?? '0.00', $currency))) . '</div>';
|
||||
}
|
||||
echo '</td>';
|
||||
|
||||
$payload = $this->product_payload($post, $meta, $edit_url);
|
||||
echo '<td class="column-actions">';
|
||||
echo '<button type="button" class="button button-small button-primary" data-aksrt-product-edit="' . esc_attr((string) wp_json_encode($payload)) . '" aria-haspopup="dialog" aria-controls="aksrt-product-editor">'
|
||||
. esc_html__('快速设置', 'aksrt') . '</button>';
|
||||
echo '<div class="aksrt-products-links">';
|
||||
echo '<button type="button" class="button button-small button-primary" data-jio-product-edit="' . esc_attr((string) wp_json_encode($payload)) . '" aria-haspopup="dialog" aria-controls="jio-product-editor">'
|
||||
. esc_html__('快速设置', 'jio') . '</button>';
|
||||
echo '<div class="jio-products-links">';
|
||||
if ($edit_url !== '') {
|
||||
echo '<a href="' . esc_url($edit_url) . '">' . esc_html__('编辑文章', 'aksrt') . '</a>';
|
||||
echo '<a href="' . esc_url($edit_url) . '">' . esc_html__('编辑文章', 'jio') . '</a>';
|
||||
}
|
||||
$orders_url = $this->page_url('aksrt_orders', [
|
||||
$orders_url = $this->page_url('jio_orders', [
|
||||
'product_type' => DomainModel::ORDER_PRODUCT_RESOURCE,
|
||||
'product_id' => $post_id,
|
||||
]);
|
||||
echo '<a href="' . esc_url($orders_url) . '">' . esc_html__('订单', 'aksrt') . '</a>';
|
||||
echo '<a href="' . esc_url($orders_url) . '">' . esc_html__('订单', 'jio') . '</a>';
|
||||
if ($permalink !== '') {
|
||||
echo '<a href="' . esc_url($permalink) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('查看', 'aksrt') . '</a>';
|
||||
echo '<a href="' . esc_url($permalink) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('查看', 'jio') . '</a>';
|
||||
}
|
||||
echo '</div></td></tr>';
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $meta */
|
||||
private function price_cell_html(array $meta, string $currency): string {
|
||||
$free = __('免费', 'aksrt');
|
||||
$free = __('免费', 'jio');
|
||||
if ($meta['pay_modo'] === 'points') {
|
||||
$fmt = static fn(int $points): string => $points > 0 ? sprintf(__('%d 积分', 'aksrt'), $points) : $free;
|
||||
$fmt = static fn(int $points): string => $points > 0 ? sprintf(__('%d 积分', 'jio'), $points) : $free;
|
||||
$main = $fmt((int) $meta['points_price']);
|
||||
$original = '';
|
||||
$vip = [$fmt((int) $meta['vip1_points']), $fmt((int) $meta['vip2_points'])];
|
||||
@@ -267,15 +267,15 @@ trait PayProductsList {
|
||||
$vip = [$fmt((string) $meta['vip1_price']), $fmt((string) $meta['vip2_price'])];
|
||||
}
|
||||
|
||||
return '<span class="aksrt-native-amount">' . esc_html($main) . '</span>'
|
||||
. ($original !== '' ? ' <del class="aksrt-native-muted">' . esc_html($original) . '</del>' : '')
|
||||
. '<div class="aksrt-products-sub">' . esc_html(sprintf(__('VIP1 %1$s · VIP2 %2$s', 'aksrt'), $vip[0], $vip[1])) . '</div>';
|
||||
return '<span class="jio-native-amount">' . esc_html($main) . '</span>'
|
||||
. ($original !== '' ? ' <del class="jio-native-muted">' . esc_html($original) . '</del>' : '')
|
||||
. '<div class="jio-products-sub">' . esc_html(sprintf(__('VIP1 %1$s · VIP2 %2$s', 'jio'), $vip[0], $vip[1])) . '</div>';
|
||||
}
|
||||
|
||||
/** 订单统计里积分订单为 int、现金订单为两位小数字符串。 */
|
||||
private function sales_amount($amount, string $currency): string {
|
||||
return is_int($amount)
|
||||
? sprintf(__('%d 积分', 'aksrt'), $amount)
|
||||
? sprintf(__('%d 积分', 'jio'), $amount)
|
||||
: $currency . $this->cash_decimal((string) $amount);
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,12 @@
|
||||
* admin-post 处理器与共用的展示助手。页面样式在 assets/css/admin/withdrawals.css,
|
||||
* 处理弹窗、复制收款账户与防重复提交在 assets/js/admin-withdrawals.js。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Payment\Withdrawal;
|
||||
use Jio\Payment\Withdrawal;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -20,7 +20,7 @@ class PayWithdrawals {
|
||||
use PayWithdrawalsList;
|
||||
|
||||
public const CAPABILITY = 'manage_options';
|
||||
public const PAGE_SLUG = 'aksrt_withdraw';
|
||||
public const PAGE_SLUG = 'jio_withdraw';
|
||||
|
||||
/** 后台可发起的目标状态。 */
|
||||
private const TARGET_STATUSES = [Withdrawal::STATUS_PROCESSING, Withdrawal::STATUS_COMPLETED, Withdrawal::STATUS_REJECTED];
|
||||
@@ -39,7 +39,7 @@ class PayWithdrawals {
|
||||
return;
|
||||
}
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']);
|
||||
add_action('admin_post_aksrt_pay_update_withdrawal', [$this, 'handle_update_withdrawal']);
|
||||
add_action('admin_post_jio_pay_update_withdrawal', [$this, 'handle_update_withdrawal']);
|
||||
}
|
||||
|
||||
public function enqueue_assets(): void {
|
||||
@@ -49,17 +49,17 @@ class PayWithdrawals {
|
||||
}
|
||||
$css = '/assets/css/admin/withdrawals.css';
|
||||
$js = '/assets/js/admin-withdrawals.js';
|
||||
wp_enqueue_style('aksrt-admin-withdrawals', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-withdrawals', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-withdrawals', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-withdrawals', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
public function render_withdrawals_page(): void {
|
||||
$this->ensure_cap();
|
||||
NativeUi::page_open('aksrt-wd-page');
|
||||
NativeUi::page_open('jio-wd-page');
|
||||
|
||||
if (!class_exists('\AKSRT\Payment\Withdrawal')) {
|
||||
NativeUi::header(__('提现审核', 'aksrt'), '', 'dashicons-money-alt');
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('提现模块未初始化。', 'aksrt') . '</p></div>';
|
||||
if (!class_exists('\Jio\Payment\Withdrawal')) {
|
||||
NativeUi::header(__('提现审核', 'jio'), '', 'dashicons-money-alt');
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('提现模块未初始化。', 'jio') . '</p></div>';
|
||||
NativeUi::page_close();
|
||||
return;
|
||||
}
|
||||
@@ -75,39 +75,39 @@ class PayWithdrawals {
|
||||
public function handle_update_withdrawal(): void {
|
||||
$this->ensure_cap();
|
||||
$id = isset($_POST['withdrawal_id']) ? (int) $_POST['withdrawal_id'] : 0;
|
||||
check_admin_referer('aksrt_pay_update_withdrawal_' . $id);
|
||||
check_admin_referer('jio_pay_update_withdrawal_' . $id);
|
||||
|
||||
$new_status = isset($_POST['new_status']) ? sanitize_key((string) wp_unslash($_POST['new_status'])) : '';
|
||||
$note = isset($_POST['note']) ? trim(sanitize_textarea_field((string) wp_unslash($_POST['note']))) : '';
|
||||
$request_key = isset($_POST['idempotency_key']) ? sanitize_text_field((string) wp_unslash($_POST['idempotency_key'])) : '';
|
||||
|
||||
if ($id <= 0 || !in_array($new_status, self::TARGET_STATUSES, true) || !wp_is_uuid($request_key)) {
|
||||
$this->redirect_back(['aksrt_pay_msg' => 'withdraw_failed']);
|
||||
$this->redirect_back(['jio_pay_msg' => 'withdraw_failed']);
|
||||
}
|
||||
// 拒绝原因会随站内信通知用户,必须填写。
|
||||
if ($new_status === Withdrawal::STATUS_REJECTED && $note === '') {
|
||||
$this->redirect_back(['aksrt_pay_msg' => 'withdraw_note_required']);
|
||||
$this->redirect_back(['jio_pay_msg' => 'withdraw_note_required']);
|
||||
}
|
||||
|
||||
$operation_key = \AKSRT\Ledger\OperationKey::deterministic('admin-withdraw-transition', [get_current_user_id(), strtolower($request_key)]);
|
||||
$operation_key = \Jio\Ledger\OperationKey::deterministic('admin-withdraw-transition', [get_current_user_id(), strtolower($request_key)]);
|
||||
$result = Withdrawal::get_instance()->process_status_change($id, $new_status, $note, $operation_key);
|
||||
|
||||
if (is_array($result)) {
|
||||
$this->redirect_back([
|
||||
'aksrt_pay_msg' => 'withdraw_' . $new_status,
|
||||
'jio_pay_msg' => 'withdraw_' . $new_status,
|
||||
'number' => (string) ($result['withdrawal_number'] ?? ''),
|
||||
]);
|
||||
}
|
||||
|
||||
$code = is_wp_error($result) ? $result->get_error_code() : '';
|
||||
$msg = [
|
||||
'aksrt_withdraw_transition' => 'withdraw_stale',
|
||||
'aksrt_withdraw_idempotency_conflict' => 'withdraw_stale',
|
||||
'aksrt_balance_insufficient' => 'withdraw_refund_failed',
|
||||
'jio_withdraw_transition' => 'withdraw_stale',
|
||||
'jio_withdraw_idempotency_conflict' => 'withdraw_stale',
|
||||
'jio_balance_insufficient' => 'withdraw_refund_failed',
|
||||
][$code] ?? 'withdraw_failed';
|
||||
|
||||
// 可重试的失败带回同一个幂等键:重提同一操作不会重复入账。
|
||||
$args = ['aksrt_pay_msg' => $msg];
|
||||
$args = ['jio_pay_msg' => $msg];
|
||||
if ($msg !== 'withdraw_stale') {
|
||||
$args['retry_id'] = $id;
|
||||
$args['retry_key'] = $request_key;
|
||||
@@ -122,7 +122,7 @@ class PayWithdrawals {
|
||||
if (is_string($referer) && $referer !== '') {
|
||||
parse_str((string) wp_parse_url($referer, PHP_URL_QUERY), $parsed);
|
||||
if (($parsed['page'] ?? '') === self::PAGE_SLUG) {
|
||||
unset($parsed['page'], $parsed['aksrt_pay_msg'], $parsed['number'], $parsed['retry_id'], $parsed['retry_key']);
|
||||
unset($parsed['page'], $parsed['jio_pay_msg'], $parsed['number'], $parsed['retry_id'], $parsed['retry_key']);
|
||||
$query = $parsed;
|
||||
}
|
||||
}
|
||||
@@ -137,10 +137,10 @@ class PayWithdrawals {
|
||||
/** @return array<string, string> */
|
||||
private function status_labels(): array {
|
||||
return [
|
||||
Withdrawal::STATUS_PENDING => __('待处理', 'aksrt'),
|
||||
Withdrawal::STATUS_PROCESSING => __('处理中', 'aksrt'),
|
||||
Withdrawal::STATUS_COMPLETED => __('已完成', 'aksrt'),
|
||||
Withdrawal::STATUS_REJECTED => __('已拒绝', 'aksrt'),
|
||||
Withdrawal::STATUS_PENDING => __('待处理', 'jio'),
|
||||
Withdrawal::STATUS_PROCESSING => __('处理中', 'jio'),
|
||||
Withdrawal::STATUS_COMPLETED => __('已完成', 'jio'),
|
||||
Withdrawal::STATUS_REJECTED => __('已拒绝', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -171,22 +171,22 @@ class PayWithdrawals {
|
||||
/** @return array<string, string> */
|
||||
private function source_labels(): array {
|
||||
return [
|
||||
Withdrawal::SOURCE_BALANCE => __('余额钱包', 'aksrt'),
|
||||
Withdrawal::SOURCE_COMMISSION => __('推广佣金', 'aksrt'),
|
||||
Withdrawal::SOURCE_BALANCE => __('余额钱包', 'jio'),
|
||||
Withdrawal::SOURCE_COMMISSION => __('推广佣金', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
private function method_labels(): array {
|
||||
return [
|
||||
'alipay' => __('支付宝', 'aksrt'),
|
||||
'wechat' => __('微信', 'aksrt'),
|
||||
'alipay' => __('支付宝', 'jio'),
|
||||
'wechat' => __('微信', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
private function format_money(string $decimal): string {
|
||||
try {
|
||||
return '¥' . \AKSRT\Ledger\AssetAmount::balance($decimal)->formatted();
|
||||
return '¥' . \Jio\Ledger\AssetAmount::balance($decimal)->formatted();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return '¥0.00';
|
||||
}
|
||||
@@ -195,11 +195,11 @@ class PayWithdrawals {
|
||||
/** 手续费 = 申请毛额 − 到账净额,按分计算避免浮点误差。 */
|
||||
private function fee_decimal(string $gross, string $net): string {
|
||||
try {
|
||||
$fee = \AKSRT\Ledger\AssetAmount::balance($gross)->minor() - \AKSRT\Ledger\AssetAmount::balance($net)->minor();
|
||||
$fee = \Jio\Ledger\AssetAmount::balance($gross)->minor() - \Jio\Ledger\AssetAmount::balance($net)->minor();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return '0.00';
|
||||
}
|
||||
return \AKSRT\Ledger\AssetAmount::decimal_from_minor(max(0, $fee));
|
||||
return \Jio\Ledger\AssetAmount::decimal_from_minor(max(0, $fee));
|
||||
}
|
||||
|
||||
/** 行内操作的幂等键:上次失败的那一行沿用带回的键,其余行每次渲染新生成。 */
|
||||
@@ -208,21 +208,21 @@ class PayWithdrawals {
|
||||
}
|
||||
|
||||
private function render_flash(): void {
|
||||
if (empty($_GET['aksrt_pay_msg'])) {
|
||||
if (empty($_GET['jio_pay_msg'])) {
|
||||
return;
|
||||
}
|
||||
$code = sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg']));
|
||||
$code = sanitize_key((string) wp_unslash($_GET['jio_pay_msg']));
|
||||
$number = isset($_GET['number']) ? sanitize_text_field((string) wp_unslash($_GET['number'])) : '';
|
||||
$suffix = $number !== '' ? sprintf(__('(订单号 %s)', 'aksrt'), $number) : '';
|
||||
$suffix = $number !== '' ? sprintf(__('(订单号 %s)', 'jio'), $number) : '';
|
||||
// 码 => [文案, notice 类型]
|
||||
$map = [
|
||||
'withdraw_processing' => [__('已标记为处理中,请尽快完成打款。', 'aksrt') . $suffix, 'success'],
|
||||
'withdraw_completed' => [__('已确认打款完成,用户已收到通知。', 'aksrt') . $suffix, 'success'],
|
||||
'withdraw_rejected' => [__('已拒绝提现,冻结金额已退回用户,拒绝原因已通知用户。', 'aksrt') . $suffix, 'success'],
|
||||
'withdraw_note_required' => [__('拒绝提现必须填写拒绝原因。', 'aksrt'), 'warning'],
|
||||
'withdraw_stale' => [__('操作未执行:该申请的状态已被更新,请查看最新状态后再处理。', 'aksrt'), 'warning'],
|
||||
'withdraw_refund_failed' => [__('状态未更新:冻结金额退回失败,请检查用户钱包后重试。', 'aksrt'), 'error'],
|
||||
'withdraw_failed' => [__('提现状态更新失败,请重试。重试不会重复入账。', 'aksrt'), 'error'],
|
||||
'withdraw_processing' => [__('已标记为处理中,请尽快完成打款。', 'jio') . $suffix, 'success'],
|
||||
'withdraw_completed' => [__('已确认打款完成,用户已收到通知。', 'jio') . $suffix, 'success'],
|
||||
'withdraw_rejected' => [__('已拒绝提现,冻结金额已退回用户,拒绝原因已通知用户。', 'jio') . $suffix, 'success'],
|
||||
'withdraw_note_required' => [__('拒绝提现必须填写拒绝原因。', 'jio'), 'warning'],
|
||||
'withdraw_stale' => [__('操作未执行:该申请的状态已被更新,请查看最新状态后再处理。', 'jio'), 'warning'],
|
||||
'withdraw_refund_failed' => [__('状态未更新:冻结金额退回失败,请检查用户钱包后重试。', 'jio'), 'error'],
|
||||
'withdraw_failed' => [__('提现状态更新失败,请重试。重试不会重复入账。', 'jio'), 'error'],
|
||||
];
|
||||
if (!isset($map[$code])) {
|
||||
return;
|
||||
@@ -233,7 +233,7 @@ class PayWithdrawals {
|
||||
|
||||
private function ensure_cap(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
*
|
||||
* 作为 flat sibling trait 被 PayWithdrawals 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Payment\Withdrawal;
|
||||
use Jio\Payment\Withdrawal;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -17,12 +17,12 @@ trait PayWithdrawalsList {
|
||||
|
||||
private function render_withdrawals_list_page(): void {
|
||||
NativeUi::header(
|
||||
__('提现审核', 'aksrt'),
|
||||
__('用户发起的余额与推广佣金提现申请。按收款信息线下打款后,在这里确认完成;拒绝会把冻结金额退回原资金池。', 'aksrt'),
|
||||
__('提现审核', 'jio'),
|
||||
__('用户发起的余额与推广佣金提现申请。按收款信息线下打款后,在这里确认完成;拒绝会把冻结金额退回原资金池。', 'jio'),
|
||||
'dashicons-money-alt',
|
||||
[[
|
||||
'label' => __('提现策略', 'aksrt'),
|
||||
'url' => admin_url('admin.php?page=aksrt-settings#/payment/withdrawal'),
|
||||
'label' => __('提现策略', 'jio'),
|
||||
'url' => admin_url('admin.php?page=jio-settings#/payment/withdrawal'),
|
||||
'icon' => 'dashicons-admin-generic',
|
||||
]]
|
||||
);
|
||||
@@ -78,17 +78,17 @@ trait PayWithdrawalsList {
|
||||
$this->render_status_views($filters['status'], $query_args, $counts);
|
||||
$this->render_filter_card($filters, $query_args);
|
||||
|
||||
NativeUi::table_card_open(__('提现申请', 'aksrt'));
|
||||
echo '<table class="wp-list-table widefat striped aksrt-wd-table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('提现申请', 'aksrt') . '</caption><thead><tr>';
|
||||
NativeUi::table_card_open(__('提现申请', 'jio'));
|
||||
echo '<table class="wp-list-table widefat striped jio-wd-table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('提现申请', 'jio') . '</caption><thead><tr>';
|
||||
foreach ([
|
||||
'number' => esc_html__('订单号', 'aksrt'),
|
||||
'user' => esc_html__('用户', 'aksrt'),
|
||||
'amount' => esc_html__('金额', 'aksrt'),
|
||||
'payee' => esc_html__('收款信息', 'aksrt'),
|
||||
'status' => esc_html__('状态', 'aksrt'),
|
||||
'note' => esc_html__('备注', 'aksrt'),
|
||||
'actions' => esc_html__('操作', 'aksrt'),
|
||||
'number' => esc_html__('订单号', 'jio'),
|
||||
'user' => esc_html__('用户', 'jio'),
|
||||
'amount' => esc_html__('金额', 'jio'),
|
||||
'payee' => esc_html__('收款信息', 'jio'),
|
||||
'status' => esc_html__('状态', 'jio'),
|
||||
'note' => esc_html__('备注', 'jio'),
|
||||
'actions' => esc_html__('操作', 'jio'),
|
||||
] as $key => $label) {
|
||||
echo '<th scope="col" class="column-' . esc_attr($key) . '">' . $label . '</th>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped above.
|
||||
}
|
||||
@@ -96,9 +96,9 @@ trait PayWithdrawalsList {
|
||||
|
||||
if (!$items) {
|
||||
$filtered = array_diff_key($query_args, ['status' => true]) !== [];
|
||||
echo NativeUi::empty_row(7, __('暂无提现记录', 'aksrt'), $filtered // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
? __('没有符合当前筛选条件的申请,请调整或重置筛选。', 'aksrt')
|
||||
: __('用户在用户中心发起余额或佣金提现后,申请会出现在这里。', 'aksrt'));
|
||||
echo NativeUi::empty_row(7, __('暂无提现记录', 'jio'), $filtered // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
? __('没有符合当前筛选条件的申请,请调整或重置筛选。', 'jio')
|
||||
: __('用户在用户中心发起余额或佣金提现后,申请会出现在这里。', 'jio'));
|
||||
}
|
||||
foreach ($items as $row) {
|
||||
$this->render_withdrawal_row($row, $retry_id, $retry_key);
|
||||
@@ -110,9 +110,9 @@ trait PayWithdrawalsList {
|
||||
NativeUi::card_close();
|
||||
|
||||
$this->render_process_dialog();
|
||||
echo '<div class="aksrt-wd-toast" role="status" aria-live="polite" data-aksrt-wd-toast'
|
||||
. ' data-copied="' . esc_attr__('收款信息已复制', 'aksrt') . '"'
|
||||
. ' data-copy-failed="' . esc_attr__('自动复制失败,请手动选中后复制。', 'aksrt') . '"></div>';
|
||||
echo '<div class="jio-wd-toast" role="status" aria-live="polite" data-jio-wd-toast'
|
||||
. ' data-copied="' . esc_attr__('收款信息已复制', 'jio') . '"'
|
||||
. ' data-copy-failed="' . esc_attr__('自动复制失败,请手动选中后复制。', 'jio') . '"></div>';
|
||||
}
|
||||
|
||||
private function render_withdrawal_row(array $row, int $retry_id, string $retry_key): void {
|
||||
@@ -131,44 +131,44 @@ trait PayWithdrawalsList {
|
||||
$user = $user_id > 0 ? get_userdata($user_id) : false;
|
||||
|
||||
echo '<tr' . ($retry_id === $id ? ' class="is-retry"' : '') . '>';
|
||||
echo '<td class="column-number"><code class="aksrt-native-mono">' . esc_html($number !== '' ? $number : '#' . $id) . '</code>'
|
||||
. '<div class="aksrt-wd-sub">' . esc_html(sprintf(__('%s 申请', 'aksrt'), (string) ($row['created_at'] ?? ''))) . '</div></td>';
|
||||
echo '<td class="column-number"><code class="jio-native-mono">' . esc_html($number !== '' ? $number : '#' . $id) . '</code>'
|
||||
. '<div class="jio-wd-sub">' . esc_html(sprintf(__('%s 申请', 'jio'), (string) ($row['created_at'] ?? ''))) . '</div></td>';
|
||||
echo '<td class="column-user">' . NativeUi::user_cell($user_id) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
. '<div class="aksrt-wd-sub">' . esc_html(sprintf(__('来源:%s', 'aksrt'), $source_label)) . '</div></td>';
|
||||
. '<div class="jio-wd-sub">' . esc_html(sprintf(__('来源:%s', 'jio'), $source_label)) . '</div></td>';
|
||||
|
||||
echo '<td class="column-amount"><strong class="aksrt-native-amount">' . esc_html($this->format_money($net)) . '</strong>'
|
||||
. '<div class="aksrt-wd-sub">' . esc_html(sprintf(
|
||||
echo '<td class="column-amount"><strong class="jio-native-amount">' . esc_html($this->format_money($net)) . '</strong>'
|
||||
. '<div class="jio-wd-sub">' . esc_html(sprintf(
|
||||
/* translators: 1 gross 2 fee 3 fee percent */
|
||||
__('申请 %1$s · 手续费 %2$s(%3$d%%)', 'aksrt'),
|
||||
__('申请 %1$s · 手续费 %2$s(%3$d%%)', 'jio'),
|
||||
$this->format_money($gross),
|
||||
$this->format_money($this->fee_decimal($gross, $net)),
|
||||
(int) ($row['fee_rate'] ?? 0)
|
||||
)) . '</div></td>';
|
||||
|
||||
echo '<td class="column-payee"><span class="aksrt-wd-method aksrt-wd-method--' . esc_attr(sanitize_html_class($method)) . '">' . esc_html($method_label) . '</span>';
|
||||
echo '<td class="column-payee"><span class="jio-wd-method jio-wd-method--' . esc_attr(sanitize_html_class($method)) . '">' . esc_html($method_label) . '</span>';
|
||||
if ($account !== '') {
|
||||
echo '<button type="button" class="aksrt-wd-account" data-clipboard-text="' . esc_attr($account) . '" aria-label="' . esc_attr(sprintf(__('复制收款信息:%s', 'aksrt'), $account)) . '">'
|
||||
echo '<button type="button" class="jio-wd-account" data-clipboard-text="' . esc_attr($account) . '" aria-label="' . esc_attr(sprintf(__('复制收款信息:%s', 'jio'), $account)) . '">'
|
||||
. '<span>' . esc_html($account) . '</span><span class="dashicons dashicons-admin-page" aria-hidden="true"></span></button>';
|
||||
}
|
||||
echo '</td>';
|
||||
|
||||
$processed = (string) ($row['processed_at'] ?? '');
|
||||
echo '<td class="column-status">' . NativeUi::status_badge($status, $this->status_badge_map()) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
. ($processed !== '' ? '<div class="aksrt-wd-sub">' . esc_html(sprintf(__('%s 处理', 'aksrt'), $processed)) . '</div>' : '') . '</td>';
|
||||
. ($processed !== '' ? '<div class="jio-wd-sub">' . esc_html(sprintf(__('%s 处理', 'jio'), $processed)) . '</div>' : '') . '</td>';
|
||||
|
||||
echo '<td class="column-note">' . ($note !== ''
|
||||
? '<div class="aksrt-wd-note">' . nl2br(esc_html($note)) . '</div>'
|
||||
: '<span class="aksrt-native-muted">—</span>') . '</td>';
|
||||
? '<div class="jio-wd-note">' . nl2br(esc_html($note)) . '</div>'
|
||||
: '<span class="jio-native-muted">—</span>') . '</td>';
|
||||
|
||||
echo '<td class="column-actions">';
|
||||
$next = $this->next_statuses($status);
|
||||
if ($next === []) {
|
||||
echo '<span class="aksrt-native-muted">—</span></td></tr>';
|
||||
echo '<span class="jio-native-muted">—</span></td></tr>';
|
||||
return;
|
||||
}
|
||||
$data = [
|
||||
'id' => (string) $id,
|
||||
'nonce' => wp_create_nonce('aksrt_pay_update_withdrawal_' . $id),
|
||||
'nonce' => wp_create_nonce('jio_pay_update_withdrawal_' . $id),
|
||||
'key' => $this->request_key_for($id, $retry_id, $retry_key),
|
||||
'number' => $number !== '' ? $number : '#' . $id,
|
||||
'user' => $user ? $user->display_name . ' #' . $user_id : '#' . $user_id,
|
||||
@@ -182,38 +182,38 @@ trait PayWithdrawalsList {
|
||||
$attrs .= ' data-' . $key . '="' . esc_attr($value) . '"';
|
||||
}
|
||||
$buttons = [
|
||||
Withdrawal::STATUS_PROCESSING => [__('开始处理', 'aksrt'), 'button-primary'],
|
||||
Withdrawal::STATUS_COMPLETED => [__('确认打款', 'aksrt'), 'button-primary'],
|
||||
Withdrawal::STATUS_REJECTED => [__('拒绝', 'aksrt'), 'is-danger'],
|
||||
Withdrawal::STATUS_PROCESSING => [__('开始处理', 'jio'), 'button-primary'],
|
||||
Withdrawal::STATUS_COMPLETED => [__('确认打款', 'jio'), 'button-primary'],
|
||||
Withdrawal::STATUS_REJECTED => [__('拒绝', 'jio'), 'is-danger'],
|
||||
];
|
||||
echo '<div class="aksrt-wd-actions">';
|
||||
echo '<div class="jio-wd-actions">';
|
||||
foreach ($next as $target) {
|
||||
[$label, $class] = $buttons[$target];
|
||||
echo '<button type="button" class="button button-small ' . esc_attr($class) . '" data-aksrt-wd-open="' . esc_attr($target) . '"' . $attrs // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped above.
|
||||
. ' aria-haspopup="dialog" aria-controls="aksrt-wd-dialog">' . esc_html($label) . '</button>';
|
||||
echo '<button type="button" class="button button-small ' . esc_attr($class) . '" data-jio-wd-open="' . esc_attr($target) . '"' . $attrs // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped above.
|
||||
. ' aria-haspopup="dialog" aria-controls="jio-wd-dialog">' . esc_html($label) . '</button>';
|
||||
}
|
||||
echo '</div></td></tr>';
|
||||
}
|
||||
|
||||
/** 处理流程 + 当前提现策略;有待办时用警示色提醒冻结金额。 */
|
||||
private function render_process_callout(array $stats): void {
|
||||
$settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_withdrawal_settings() : [];
|
||||
$settings = class_exists('\Jio\Settings\Settings') ? \Jio\Settings\Settings::get_withdrawal_settings() : [];
|
||||
$open = (int) ($stats['pending'] ?? 0) + (int) ($stats['processing'] ?? 0);
|
||||
$policy = sprintf(
|
||||
/* translators: 1 min amount 2 fee percent 3 daily count 4 daily limit */
|
||||
__('当前策略:最低 %1$s,手续费 %2$d%%,每日 %3$d 次 / %4$s。', 'aksrt'),
|
||||
__('当前策略:最低 %1$s,手续费 %2$d%%,每日 %3$d 次 / %4$s。', 'jio'),
|
||||
$this->format_money((string) ($settings['min_amount'] ?? '0.00')),
|
||||
(int) ($settings['service_charge_rate'] ?? 0),
|
||||
(int) ($settings['daily_count'] ?? 0),
|
||||
$this->format_money((string) ($settings['daily_limit'] ?? '0.00'))
|
||||
);
|
||||
$flow = __('处理流程:开始处理 → 按收款信息线下打款 → 确认打款。确认打款与拒绝都不可撤销。', 'aksrt');
|
||||
$flow = __('处理流程:开始处理 → 按收款信息线下打款 → 确认打款。确认打款与拒绝都不可撤销。', 'jio');
|
||||
|
||||
echo '<div class="aksrt-native-callout' . ($open > 0 ? ' is-warning' : '') . '">';
|
||||
echo '<div class="jio-native-callout' . ($open > 0 ? ' is-warning' : '') . '">';
|
||||
if ($open > 0) {
|
||||
echo '<strong>' . esc_html(sprintf(
|
||||
/* translators: 1 open count 2 frozen amount */
|
||||
__('%1$d 笔申请待办,冻结中 %2$s。', 'aksrt'),
|
||||
__('%1$d 笔申请待办,冻结中 %2$s。', 'jio'),
|
||||
$open,
|
||||
$this->format_money((string) ($stats['open_gross'] ?? '0.00'))
|
||||
)) . '</strong> ';
|
||||
@@ -223,32 +223,32 @@ trait PayWithdrawalsList {
|
||||
|
||||
private function render_withdrawal_stats(array $stats): void {
|
||||
NativeUi::stats([
|
||||
['label' => __('待处理', 'aksrt'), 'value' => number_format_i18n((int) ($stats['pending'] ?? 0)), 'url' => $this->page_url(['status' => Withdrawal::STATUS_PENDING]), 'icon' => 'dashicons-clock', 'tone' => 'warning'],
|
||||
['label' => __('处理中', 'aksrt'), 'value' => number_format_i18n((int) ($stats['processing'] ?? 0)), 'url' => $this->page_url(['status' => Withdrawal::STATUS_PROCESSING]), 'icon' => 'dashicons-update', 'tone' => 'info'],
|
||||
['label' => __('冻结中金额', 'aksrt'), 'value' => $this->format_money((string) ($stats['open_gross'] ?? '0.00')), 'hint' => __('待处理 + 处理中的申请毛额', 'aksrt'), 'icon' => 'dashicons-lock', 'tone' => 'warning'],
|
||||
['label' => __('已打款', 'aksrt'), 'value' => $this->format_money((string) ($stats['paid_net'] ?? '0.00')), 'url' => $this->page_url(['status' => Withdrawal::STATUS_COMPLETED]), 'hint' => sprintf(
|
||||
['label' => __('待处理', 'jio'), 'value' => number_format_i18n((int) ($stats['pending'] ?? 0)), 'url' => $this->page_url(['status' => Withdrawal::STATUS_PENDING]), 'icon' => 'dashicons-clock', 'tone' => 'warning'],
|
||||
['label' => __('处理中', 'jio'), 'value' => number_format_i18n((int) ($stats['processing'] ?? 0)), 'url' => $this->page_url(['status' => Withdrawal::STATUS_PROCESSING]), 'icon' => 'dashicons-update', 'tone' => 'info'],
|
||||
['label' => __('冻结中金额', 'jio'), 'value' => $this->format_money((string) ($stats['open_gross'] ?? '0.00')), 'hint' => __('待处理 + 处理中的申请毛额', 'jio'), 'icon' => 'dashicons-lock', 'tone' => 'warning'],
|
||||
['label' => __('已打款', 'jio'), 'value' => $this->format_money((string) ($stats['paid_net'] ?? '0.00')), 'url' => $this->page_url(['status' => Withdrawal::STATUS_COMPLETED]), 'hint' => sprintf(
|
||||
/* translators: 1 completed count 2 fee total */
|
||||
__('%1$s 笔 · 手续费 %2$s', 'aksrt'),
|
||||
__('%1$s 笔 · 手续费 %2$s', 'jio'),
|
||||
number_format_i18n((int) ($stats['completed'] ?? 0)),
|
||||
$this->format_money((string) ($stats['paid_fee'] ?? '0.00'))
|
||||
), 'icon' => 'dashicons-yes-alt', 'tone' => 'success'],
|
||||
['label' => __('已拒绝', 'aksrt'), 'value' => number_format_i18n((int) ($stats['rejected'] ?? 0)), 'url' => $this->page_url(['status' => Withdrawal::STATUS_REJECTED]), 'icon' => 'dashicons-no-alt', 'tone' => 'danger'],
|
||||
['label' => __('已拒绝', 'jio'), 'value' => number_format_i18n((int) ($stats['rejected'] ?? 0)), 'url' => $this->page_url(['status' => Withdrawal::STATUS_REJECTED]), 'icon' => 'dashicons-no-alt', 'tone' => 'danger'],
|
||||
]);
|
||||
}
|
||||
|
||||
/** @param array<string, int> $counts */
|
||||
private function render_status_views(string $current, array $query_args, array $counts): void {
|
||||
$base = array_diff_key($query_args, ['status' => true]);
|
||||
$views = ['' => [__('全部', 'aksrt'), array_sum($counts)]];
|
||||
$views = ['' => [__('全部', 'jio'), array_sum($counts)]];
|
||||
foreach ($this->status_labels() as $key => $label) {
|
||||
$views[$key] = [$label, (int) ($counts[$key] ?? 0)];
|
||||
}
|
||||
|
||||
echo '<nav class="aksrt-native-tabs aksrt-wd-views" aria-label="' . esc_attr__('按处理状态查看', 'aksrt') . '">';
|
||||
echo '<nav class="jio-native-tabs jio-wd-views" aria-label="' . esc_attr__('按处理状态查看', 'jio') . '">';
|
||||
foreach ($views as $key => [$label, $count]) {
|
||||
$url = $this->page_url($base + ($key !== '' ? ['status' => $key] : []));
|
||||
$active = $current === $key;
|
||||
echo '<a class="aksrt-native-tabs__link' . ($active ? ' is-active' : '') . '" href="' . esc_url($url) . '"' . ($active ? ' aria-current="page"' : '') . '>'
|
||||
echo '<a class="jio-native-tabs__link' . ($active ? ' is-active' : '') . '" href="' . esc_url($url) . '"' . ($active ? ' aria-current="page"' : '') . '>'
|
||||
. esc_html($label) . ' <span class="count">' . esc_html(number_format_i18n($count)) . '</span></a>';
|
||||
}
|
||||
echo '</nav>';
|
||||
@@ -259,28 +259,28 @@ trait PayWithdrawalsList {
|
||||
* @param array<string, string|int> $query_args
|
||||
*/
|
||||
private function render_filter_card(array $filters, array $query_args): void {
|
||||
NativeUi::card_open('', '', false, 'aksrt-wd-filter-card');
|
||||
echo '<form method="get" class="aksrt-wd-filters" role="search">';
|
||||
NativeUi::card_open('', '', false, 'jio-wd-filter-card');
|
||||
echo '<form method="get" class="jio-wd-filters" role="search">';
|
||||
echo '<input type="hidden" name="page" value="' . esc_attr(self::PAGE_SLUG) . '" />';
|
||||
if ($filters['status'] !== '') {
|
||||
echo '<input type="hidden" name="status" value="' . esc_attr($filters['status']) . '" />';
|
||||
}
|
||||
|
||||
echo '<label class="aksrt-wd-filters__search"><span class="screen-reader-text">' . esc_html__('订单号 / 用户 ID', 'aksrt') . '</span>'
|
||||
. '<input type="search" name="s" value="' . esc_attr($filters['keyword']) . '" placeholder="' . esc_attr__('搜索订单号 / 用户 ID', 'aksrt') . '" /></label>';
|
||||
echo $this->filter_select('source', __('提现来源', 'aksrt'), ['' => __('全部来源', 'aksrt')] + $this->source_labels(), $filters['source']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->filter_select('method', __('收款方式', 'aksrt'), ['' => __('全部收款方式', 'aksrt')] + $this->method_labels(), $filters['method']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<span class="aksrt-wd-filters__range">'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('开始日期', 'aksrt') . '</span><input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '" /></label>'
|
||||
echo '<label class="jio-wd-filters__search"><span class="screen-reader-text">' . esc_html__('订单号 / 用户 ID', 'jio') . '</span>'
|
||||
. '<input type="search" name="s" value="' . esc_attr($filters['keyword']) . '" placeholder="' . esc_attr__('搜索订单号 / 用户 ID', 'jio') . '" /></label>';
|
||||
echo $this->filter_select('source', __('提现来源', 'jio'), ['' => __('全部来源', 'jio')] + $this->source_labels(), $filters['source']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->filter_select('method', __('收款方式', 'jio'), ['' => __('全部收款方式', 'jio')] + $this->method_labels(), $filters['method']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<span class="jio-wd-filters__range">'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('开始日期', 'jio') . '</span><input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '" /></label>'
|
||||
. '<span aria-hidden="true">–</span>'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('结束日期', 'aksrt') . '</span><input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '" /></label>'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('结束日期', 'jio') . '</span><input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '" /></label>'
|
||||
. '</span>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('用户 ID', 'aksrt') . '</span><input type="number" min="1" name="user_id" class="aksrt-wd-filters__id" value="'
|
||||
. esc_attr($filters['user_id'] > 0 ? (string) $filters['user_id'] : '') . '" placeholder="' . esc_attr__('用户 ID', 'aksrt') . '" /></label>';
|
||||
echo '<span class="aksrt-wd-filters__submit"><button type="submit" class="button button-primary">' . esc_html__('筛选', 'aksrt') . '</button>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('用户 ID', 'jio') . '</span><input type="number" min="1" name="user_id" class="jio-wd-filters__id" value="'
|
||||
. esc_attr($filters['user_id'] > 0 ? (string) $filters['user_id'] : '') . '" placeholder="' . esc_attr__('用户 ID', 'jio') . '" /></label>';
|
||||
echo '<span class="jio-wd-filters__submit"><button type="submit" class="button button-primary">' . esc_html__('筛选', 'jio') . '</button>';
|
||||
if (array_diff_key($query_args, ['status' => true]) !== []) {
|
||||
$reset = $this->page_url($filters['status'] !== '' ? ['status' => $filters['status']] : []);
|
||||
echo '<a class="button" href="' . esc_url($reset) . '">' . esc_html__('重置', 'aksrt') . '</a>';
|
||||
echo '<a class="button" href="' . esc_url($reset) . '">' . esc_html__('重置', 'jio') . '</a>';
|
||||
}
|
||||
echo '</span></form>';
|
||||
NativeUi::card_close();
|
||||
@@ -300,9 +300,9 @@ trait PayWithdrawalsList {
|
||||
* 三种目标状态各自的说明与备注要求用 data-mode 区分。
|
||||
*/
|
||||
private function render_process_dialog(): void {
|
||||
echo '<dialog class="aksrt-wd-dialog" id="aksrt-wd-dialog" aria-labelledby="aksrt-wd-dialog-title">';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-wd-dialog__frame" data-aksrt-wd-form data-busy-label="' . esc_attr__('正在提交…', 'aksrt') . '">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_pay_update_withdrawal" />';
|
||||
echo '<dialog class="jio-wd-dialog" id="jio-wd-dialog" aria-labelledby="jio-wd-dialog-title">';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-wd-dialog__frame" data-jio-wd-form data-busy-label="' . esc_attr__('正在提交…', 'jio') . '">';
|
||||
echo '<input type="hidden" name="action" value="jio_pay_update_withdrawal" />';
|
||||
echo '<input type="hidden" name="withdrawal_id" value="" data-field="id" />';
|
||||
echo '<input type="hidden" name="new_status" value="" data-field="status" />';
|
||||
echo '<input type="hidden" name="idempotency_key" value="" data-field="key" />';
|
||||
@@ -310,52 +310,52 @@ trait PayWithdrawalsList {
|
||||
wp_referer_field();
|
||||
|
||||
$titles = [
|
||||
Withdrawal::STATUS_PROCESSING => __('开始处理提现', 'aksrt'),
|
||||
Withdrawal::STATUS_COMPLETED => __('确认已打款', 'aksrt'),
|
||||
Withdrawal::STATUS_REJECTED => __('拒绝提现', 'aksrt'),
|
||||
Withdrawal::STATUS_PROCESSING => __('开始处理提现', 'jio'),
|
||||
Withdrawal::STATUS_COMPLETED => __('确认已打款', 'jio'),
|
||||
Withdrawal::STATUS_REJECTED => __('拒绝提现', 'jio'),
|
||||
];
|
||||
echo '<header class="aksrt-wd-dialog__head"><h2 id="aksrt-wd-dialog-title">';
|
||||
echo '<header class="jio-wd-dialog__head"><h2 id="jio-wd-dialog-title">';
|
||||
foreach ($titles as $mode => $title) {
|
||||
echo '<span data-mode="' . esc_attr($mode) . '" hidden>' . esc_html($title) . '</span>';
|
||||
}
|
||||
echo '</h2><button type="button" class="aksrt-wd-dialog__close" data-aksrt-wd-close aria-label="' . esc_attr__('关闭弹窗', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
echo '</h2><button type="button" class="jio-wd-dialog__close" data-jio-wd-close aria-label="' . esc_attr__('关闭弹窗', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
|
||||
echo '<div class="aksrt-wd-dialog__body"><dl class="aksrt-wd-summary">';
|
||||
echo '<div class="jio-wd-dialog__body"><dl class="jio-wd-summary">';
|
||||
foreach ([
|
||||
'number' => __('订单号', 'aksrt'),
|
||||
'user' => __('用户', 'aksrt'),
|
||||
'net' => __('应打款', 'aksrt'),
|
||||
'gross' => __('申请金额', 'aksrt'),
|
||||
'source' => __('来源', 'aksrt'),
|
||||
'payee' => __('收款信息', 'aksrt'),
|
||||
'number' => __('订单号', 'jio'),
|
||||
'user' => __('用户', 'jio'),
|
||||
'net' => __('应打款', 'jio'),
|
||||
'gross' => __('申请金额', 'jio'),
|
||||
'source' => __('来源', 'jio'),
|
||||
'payee' => __('收款信息', 'jio'),
|
||||
] as $field => $label) {
|
||||
echo '<div class="aksrt-wd-summary__item aksrt-wd-summary__item--' . esc_attr($field) . '"><dt>' . esc_html($label) . '</dt><dd data-summary="' . esc_attr($field) . '"></dd></div>';
|
||||
echo '<div class="jio-wd-summary__item jio-wd-summary__item--' . esc_attr($field) . '"><dt>' . esc_html($label) . '</dt><dd data-summary="' . esc_attr($field) . '"></dd></div>';
|
||||
}
|
||||
echo '</dl>';
|
||||
|
||||
$hints = [
|
||||
Withdrawal::STATUS_PROCESSING => [__('标记为处理中后,请按上方收款信息线下打款,完成后回到这里确认打款。', 'aksrt'), ''],
|
||||
Withdrawal::STATUS_COMPLETED => [__('请确认已向用户实际打出「应打款」金额。确认后状态不可更改,用户会收到完成通知。', 'aksrt'), 'is-warning'],
|
||||
Withdrawal::STATUS_REJECTED => [__('拒绝后,冻结的申请金额会退回用户的原资金池,拒绝原因会通知用户。此操作不可撤销。', 'aksrt'), 'is-danger'],
|
||||
Withdrawal::STATUS_PROCESSING => [__('标记为处理中后,请按上方收款信息线下打款,完成后回到这里确认打款。', 'jio'), ''],
|
||||
Withdrawal::STATUS_COMPLETED => [__('请确认已向用户实际打出「应打款」金额。确认后状态不可更改,用户会收到完成通知。', 'jio'), 'is-warning'],
|
||||
Withdrawal::STATUS_REJECTED => [__('拒绝后,冻结的申请金额会退回用户的原资金池,拒绝原因会通知用户。此操作不可撤销。', 'jio'), 'is-danger'],
|
||||
];
|
||||
foreach ($hints as $mode => [$hint, $tone]) {
|
||||
echo '<p class="aksrt-native-callout ' . esc_attr($tone) . '" data-mode="' . esc_attr($mode) . '" hidden>' . esc_html($hint) . '</p>';
|
||||
echo '<p class="jio-native-callout ' . esc_attr($tone) . '" data-mode="' . esc_attr($mode) . '" hidden>' . esc_html($hint) . '</p>';
|
||||
}
|
||||
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">'
|
||||
. '<span data-mode="' . esc_attr(Withdrawal::STATUS_REJECTED) . '" hidden>' . esc_html__('拒绝原因(必填)', 'aksrt') . '</span>'
|
||||
. '<span data-mode="' . esc_attr(Withdrawal::STATUS_PROCESSING) . ' ' . esc_attr(Withdrawal::STATUS_COMPLETED) . '" hidden>' . esc_html__('备注(选填)', 'aksrt') . '</span>'
|
||||
. '</span><textarea name="note" rows="3" maxlength="1000" data-aksrt-wd-note placeholder="' . esc_attr__('如打款流水号、拒绝原因等,追加记录在申请上', 'aksrt') . '"></textarea></label>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">'
|
||||
. '<span data-mode="' . esc_attr(Withdrawal::STATUS_REJECTED) . '" hidden>' . esc_html__('拒绝原因(必填)', 'jio') . '</span>'
|
||||
. '<span data-mode="' . esc_attr(Withdrawal::STATUS_PROCESSING) . ' ' . esc_attr(Withdrawal::STATUS_COMPLETED) . '" hidden>' . esc_html__('备注(选填)', 'jio') . '</span>'
|
||||
. '</span><textarea name="note" rows="3" maxlength="1000" data-jio-wd-note placeholder="' . esc_attr__('如打款流水号、拒绝原因等,追加记录在申请上', 'jio') . '"></textarea></label>';
|
||||
echo '</div>';
|
||||
|
||||
$submits = [
|
||||
Withdrawal::STATUS_PROCESSING => [__('开始处理', 'aksrt'), 'button-primary'],
|
||||
Withdrawal::STATUS_COMPLETED => [__('确认已打款', 'aksrt'), 'button-primary'],
|
||||
Withdrawal::STATUS_REJECTED => [__('确认拒绝', 'aksrt'), 'button-primary is-danger'],
|
||||
Withdrawal::STATUS_PROCESSING => [__('开始处理', 'jio'), 'button-primary'],
|
||||
Withdrawal::STATUS_COMPLETED => [__('确认已打款', 'jio'), 'button-primary'],
|
||||
Withdrawal::STATUS_REJECTED => [__('确认拒绝', 'jio'), 'button-primary is-danger'],
|
||||
];
|
||||
echo '<footer class="aksrt-wd-dialog__foot"><button type="button" class="button" data-aksrt-wd-close>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
echo '<footer class="jio-wd-dialog__foot"><button type="button" class="button" data-jio-wd-close>' . esc_html__('取消', 'jio') . '</button>';
|
||||
foreach ($submits as $mode => [$label, $class]) {
|
||||
echo '<button type="submit" class="button ' . esc_attr($class) . '" data-mode="' . esc_attr($mode) . '" data-aksrt-wd-submit hidden>' . esc_html($label) . '</button>';
|
||||
echo '<button type="submit" class="button ' . esc_attr($class) . '" data-mode="' . esc_attr($mode) . '" data-jio-wd-submit hidden>' . esc_html($label) . '</button>';
|
||||
}
|
||||
echo '</footer></form></dialog>';
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
* DomainModel::default_post_meta() or the custom-filter settings — the
|
||||
* post-list-quick-edit contract test enforces that pairing.
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -27,40 +27,40 @@ class PostListEditFields {
|
||||
[
|
||||
'id' => 'views',
|
||||
'type' => 'number',
|
||||
'title' => __('阅读量', 'aksrt'),
|
||||
'title' => __('阅读量', 'jio'),
|
||||
],
|
||||
[
|
||||
'id' => 'like',
|
||||
'type' => 'number',
|
||||
'title' => __('点赞数', 'aksrt'),
|
||||
'title' => __('点赞数', 'jio'),
|
||||
],
|
||||
[
|
||||
'id' => 'show_layout',
|
||||
'type' => 'radio',
|
||||
'title' => __('显示布局', 'aksrt'),
|
||||
'title' => __('显示布局', 'jio'),
|
||||
'default' => 'false',
|
||||
'options' => [
|
||||
'false' => __('跟随主题', 'aksrt'),
|
||||
'no_sidebar' => __('无侧边栏', 'aksrt'),
|
||||
'sidebar_left' => __('侧边栏靠左', 'aksrt'),
|
||||
'sidebar_right' => __('侧边栏靠右', 'aksrt'),
|
||||
'false' => __('跟随主题', 'jio'),
|
||||
'no_sidebar' => __('无侧边栏', 'jio'),
|
||||
'sidebar_left' => __('侧边栏靠左', 'jio'),
|
||||
'sidebar_right' => __('侧边栏靠右', 'jio'),
|
||||
],
|
||||
],
|
||||
[
|
||||
'id' => 'no_article-navs',
|
||||
'type' => 'checkbox',
|
||||
'label' => __('不显示目录树', 'aksrt'),
|
||||
'label' => __('不显示目录树', 'jio'),
|
||||
],
|
||||
[
|
||||
'id' => 'article_maxheight_xz',
|
||||
'type' => 'checkbox',
|
||||
'label' => __('限制内容最大高度', 'aksrt'),
|
||||
'label' => __('限制内容最大高度', 'jio'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Pay parameters (saved into the _aksrt_pay meta bag).
|
||||
* Pay parameters (saved into the _jio_pay meta bag).
|
||||
*
|
||||
* @return array<int,array<string,mixed>>
|
||||
*/
|
||||
@@ -69,132 +69,132 @@ class PostListEditFields {
|
||||
[
|
||||
'id' => 'pay_type',
|
||||
'type' => 'radio',
|
||||
'title' => __('付费模式', 'aksrt'),
|
||||
'options' => \AKSRT\Core\DomainModel::pay_type_labels(),
|
||||
'title' => __('付费模式', 'jio'),
|
||||
'options' => \Jio\Core\DomainModel::pay_type_labels(),
|
||||
],
|
||||
[
|
||||
'id' => 'pay_limit',
|
||||
'type' => 'radio',
|
||||
'title' => __('购买权限', 'aksrt'),
|
||||
'title' => __('购买权限', 'jio'),
|
||||
'options' => [
|
||||
'0' => __('所有人可购买', 'aksrt'),
|
||||
'1' => __('VIP1 及以上会员可购买', 'aksrt'),
|
||||
'2' => __('仅 VIP2 可购买', 'aksrt'),
|
||||
'0' => __('所有人可购买', 'jio'),
|
||||
'1' => __('VIP1 及以上会员可购买', 'jio'),
|
||||
'2' => __('仅 VIP2 可购买', 'jio'),
|
||||
],
|
||||
],
|
||||
[
|
||||
'id' => 'pay_modo',
|
||||
'type' => 'radio',
|
||||
'title' => __('支付类型', 'aksrt'),
|
||||
'title' => __('支付类型', 'jio'),
|
||||
'options' => [
|
||||
'0' => __('普通商品(金钱购买)', 'aksrt'),
|
||||
'points' => __('积分商品(积分购买,依赖于用户积分功能)', 'aksrt'),
|
||||
'0' => __('普通商品(金钱购买)', 'jio'),
|
||||
'points' => __('积分商品(积分购买,依赖于用户积分功能)', 'jio'),
|
||||
],
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no'], ['pay_modo', '==', 'points']],
|
||||
'id' => 'points_price',
|
||||
'title' => __('积分售价', 'aksrt'),
|
||||
'title' => __('积分售价', 'jio'),
|
||||
'type' => 'number',
|
||||
'unit' => __('积分', 'aksrt'),
|
||||
'unit' => __('积分', 'jio'),
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no'], ['pay_modo', '==', 'points']],
|
||||
'title' => __('VIP1 积分售价', 'aksrt'),
|
||||
'title' => __('VIP1 积分售价', 'jio'),
|
||||
'id' => 'vip1_points',
|
||||
'class' => 'compact',
|
||||
'subtitle' => __('填 0 则为 VIP1 免费', 'aksrt'),
|
||||
'subtitle' => __('填 0 则为 VIP1 免费', 'jio'),
|
||||
'type' => 'number',
|
||||
'unit' => __('积分', 'aksrt'),
|
||||
'unit' => __('积分', 'jio'),
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no'], ['pay_modo', '==', 'points']],
|
||||
'title' => __('VIP2 积分售价', 'aksrt'),
|
||||
'title' => __('VIP2 积分售价', 'jio'),
|
||||
'id' => 'vip2_points',
|
||||
'class' => 'compact',
|
||||
'subtitle' => __('填 0 则为 VIP2 免费', 'aksrt'),
|
||||
'desc' => __('会员价格不能高于售价', 'aksrt'),
|
||||
'subtitle' => __('填 0 则为 VIP2 免费', 'jio'),
|
||||
'desc' => __('会员价格不能高于售价', 'jio'),
|
||||
'type' => 'number',
|
||||
'unit' => __('积分', 'aksrt'),
|
||||
'unit' => __('积分', 'jio'),
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no'], ['pay_modo', '!=', 'points']],
|
||||
'id' => 'price',
|
||||
'title' => __('执行价', 'aksrt'),
|
||||
'title' => __('执行价', 'jio'),
|
||||
'type' => 'number',
|
||||
'unit' => $currency,
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no'], ['pay_modo', '!=', 'points']],
|
||||
'id' => 'original_price',
|
||||
'title' => __('原价', 'aksrt'),
|
||||
'title' => __('原价', 'jio'),
|
||||
'class' => 'compact',
|
||||
'subtitle' => __('显示在执行价格前面,并划掉', 'aksrt'),
|
||||
'subtitle' => __('显示在执行价格前面,并划掉', 'jio'),
|
||||
'type' => 'number',
|
||||
'unit' => $currency,
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no'], ['pay_modo', '!=', 'points']],
|
||||
'title' => __('VIP1 价格', 'aksrt'),
|
||||
'title' => __('VIP1 价格', 'jio'),
|
||||
'id' => 'vip1_price',
|
||||
'class' => 'compact',
|
||||
'subtitle' => __('填 0 则为 VIP1 免费', 'aksrt'),
|
||||
'subtitle' => __('填 0 则为 VIP1 免费', 'jio'),
|
||||
'type' => 'number',
|
||||
'unit' => $currency,
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no'], ['pay_modo', '!=', 'points']],
|
||||
'title' => __('VIP2 价格', 'aksrt'),
|
||||
'title' => __('VIP2 价格', 'jio'),
|
||||
'id' => 'vip2_price',
|
||||
'class' => 'compact',
|
||||
'subtitle' => __('填 0 则为 VIP2 免费', 'aksrt'),
|
||||
'desc' => __('会员价格不能高于执行价', 'aksrt'),
|
||||
'subtitle' => __('填 0 则为 VIP2 免费', 'jio'),
|
||||
'desc' => __('会员价格不能高于执行价', 'jio'),
|
||||
'type' => 'number',
|
||||
'unit' => $currency,
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no']],
|
||||
'id' => 'pay_count',
|
||||
'title' => __('销量浮动', 'aksrt'),
|
||||
'subtitle' => __('为真实销量增加或减少的数量', 'aksrt'),
|
||||
'title' => __('销量浮动', 'jio'),
|
||||
'subtitle' => __('为真实销量增加或减少的数量', 'jio'),
|
||||
'type' => 'number',
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no']],
|
||||
'id' => 'show_sales',
|
||||
'title' => __('显示销量', 'aksrt'),
|
||||
'title' => __('显示销量', 'jio'),
|
||||
'default' => true,
|
||||
'type' => 'switcher',
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no']],
|
||||
'id' => 'coupon_s',
|
||||
'title' => __('允许使用优惠码', 'aksrt'),
|
||||
'title' => __('允许使用优惠码', 'jio'),
|
||||
'default' => false,
|
||||
'type' => 'switcher',
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no']],
|
||||
'id' => 'order_expired_s',
|
||||
'title' => __('订单时效', 'aksrt'),
|
||||
'title' => __('订单时效', 'jio'),
|
||||
'default' => false,
|
||||
'type' => 'switcher',
|
||||
],
|
||||
[
|
||||
'id' => 'order_expired_time',
|
||||
'title' => __('订单时效有效期数值', 'aksrt'),
|
||||
'title' => __('订单时效有效期数值', 'jio'),
|
||||
'default' => 0,
|
||||
'type' => 'number',
|
||||
],
|
||||
[
|
||||
'title' => __('订单时效有效期单位', 'aksrt'),
|
||||
'title' => __('订单时效有效期单位', 'jio'),
|
||||
'type' => 'select',
|
||||
'class' => 'compact',
|
||||
'id' => 'order_expired_unit',
|
||||
'options' => [
|
||||
'day' => __('天', 'aksrt'),
|
||||
'hour' => __('小时', 'aksrt'),
|
||||
'minute' => __('分钟', 'aksrt'),
|
||||
'day' => __('天', 'jio'),
|
||||
'hour' => __('小时', 'jio'),
|
||||
'minute' => __('分钟', 'jio'),
|
||||
],
|
||||
'default' => 'day',
|
||||
],
|
||||
|
||||
@@ -9,17 +9,17 @@
|
||||
* those boxes onto its topics taxonomy column; this project has no topics
|
||||
* taxonomy, so they hang off the interaction-data column instead.
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class PostListEnhance {
|
||||
|
||||
public const COLUMN_STATS = 'aksrt_stats';
|
||||
public const COLUMN_SALES = 'aksrt_sales';
|
||||
public const COLUMN_STATS = 'jio_stats';
|
||||
public const COLUMN_SALES = 'jio_sales';
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
@@ -61,17 +61,17 @@ class PostListEnhance {
|
||||
return;
|
||||
}
|
||||
|
||||
if (function_exists('aksrt_enqueue_fontawesome')) {
|
||||
aksrt_enqueue_fontawesome();
|
||||
if (function_exists('jio_enqueue_fontawesome')) {
|
||||
jio_enqueue_fontawesome();
|
||||
}
|
||||
|
||||
$css = AKSRT_DIR . '/assets/css/admin-main.css';
|
||||
$css = JIO_DIR . '/assets/css/admin-main.css';
|
||||
if (is_file($css)) {
|
||||
wp_enqueue_style('aksrt-admin-main', AKSRT_URI . '/assets/css/admin-main.css', ['aksrt-font-awesome'], (string) filemtime($css));
|
||||
wp_enqueue_style('jio-admin-main', JIO_URI . '/assets/css/admin-main.css', ['jio-font-awesome'], (string) filemtime($css));
|
||||
}
|
||||
$js = AKSRT_DIR . '/assets/js/admin-post-list.js';
|
||||
$js = JIO_DIR . '/assets/js/admin-post-list.js';
|
||||
if (is_file($js)) {
|
||||
wp_enqueue_script('aksrt-post-list', AKSRT_URI . '/assets/js/admin-post-list.js', [], (string) filemtime($js), true);
|
||||
wp_enqueue_script('jio-post-list', JIO_URI . '/assets/js/admin-post-list.js', [], (string) filemtime($js), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,8 +102,8 @@ class PostListEnhance {
|
||||
if ($key !== 'title') {
|
||||
continue;
|
||||
}
|
||||
$new_columns[self::COLUMN_STATS] = __('互动数据', 'aksrt');
|
||||
$new_columns[self::COLUMN_SALES] = '<a href="' . esc_url(add_query_arg(['orderby' => 'sales_volume', 'order' => $order])) . '"><span>' . esc_html__('销售数据', 'aksrt') . ($order_by === 'sales_volume' ? $o_icon : '') . '</span></a>';
|
||||
$new_columns[self::COLUMN_STATS] = __('互动数据', 'jio');
|
||||
$new_columns[self::COLUMN_SALES] = '<a href="' . esc_url(add_query_arg(['orderby' => 'sales_volume', 'order' => $order])) . '"><span>' . esc_html__('销售数据', 'jio') . ($order_by === 'sales_volume' ? $o_icon : '') . '</span></a>';
|
||||
}
|
||||
|
||||
return $new_columns;
|
||||
@@ -124,13 +124,13 @@ class PostListEnhance {
|
||||
}
|
||||
|
||||
private function render_stats_column(int $post_id): void {
|
||||
$views = function_exists('aksrt_get_post_views') ? (int) aksrt_get_post_views($post_id) : (int) get_post_meta($post_id, \AKSRT\Core\DomainModel::POST_VIEW_COUNT_KEY, true);
|
||||
$likes = function_exists('aksrt_get_post_likes') ? (int) aksrt_get_post_likes($post_id) : (int) get_post_meta($post_id, '_aksrt_like_count', true);
|
||||
$favorites = function_exists('aksrt_get_post_favorites') ? (int) aksrt_get_post_favorites($post_id) : (int) get_post_meta($post_id, '_aksrt_favorite_count', true);
|
||||
$views = function_exists('jio_get_post_views') ? (int) jio_get_post_views($post_id) : (int) get_post_meta($post_id, \Jio\Core\DomainModel::POST_VIEW_COUNT_KEY, true);
|
||||
$likes = function_exists('jio_get_post_likes') ? (int) jio_get_post_likes($post_id) : (int) get_post_meta($post_id, '_jio_like_count', true);
|
||||
$favorites = function_exists('jio_get_post_favorites') ? (int) jio_get_post_favorites($post_id) : (int) get_post_meta($post_id, '_jio_favorite_count', true);
|
||||
|
||||
$format = static function (int $n): string {
|
||||
if (function_exists('aksrt_format_number')) {
|
||||
return (string) aksrt_format_number($n);
|
||||
if (function_exists('jio_format_number')) {
|
||||
return (string) jio_format_number($n);
|
||||
}
|
||||
if ($n >= 10000) {
|
||||
return round($n / 10000, 1) . 'W+';
|
||||
@@ -142,45 +142,45 @@ class PostListEnhance {
|
||||
};
|
||||
|
||||
$parts = [];
|
||||
$parts[] = '<span title="' . esc_attr__('浏览', 'aksrt') . '" style="margin-right:4px;"><i class="fa fas fa-eye" style="color:#8a909c;font-size:12px;"></i> ' . esc_html($format($views)) . '</span>';
|
||||
$parts[] = '<span title="' . esc_attr__('点赞', 'aksrt') . '" style="margin-right:4px;"><i class="fa fas fa-thumbs-up" style="color:#1890ff;font-size:12px;"></i> ' . esc_html($format($likes)) . '</span>';
|
||||
$parts[] = '<span title="' . esc_attr__('收藏', 'aksrt') . '" style="margin-right:4px;"><i class="fa fas fa-star" style="color:#f39c12;font-size:12px;"></i> ' . esc_html($format($favorites)) . '</span>';
|
||||
$parts[] = '<span title="' . esc_attr__('浏览', 'jio') . '" style="margin-right:4px;"><i class="fa fas fa-eye" style="color:#8a909c;font-size:12px;"></i> ' . esc_html($format($views)) . '</span>';
|
||||
$parts[] = '<span title="' . esc_attr__('点赞', 'jio') . '" style="margin-right:4px;"><i class="fa fas fa-thumbs-up" style="color:#1890ff;font-size:12px;"></i> ' . esc_html($format($likes)) . '</span>';
|
||||
$parts[] = '<span title="' . esc_attr__('收藏', 'jio') . '" style="margin-right:4px;"><i class="fa fas fa-star" style="color:#f39c12;font-size:12px;"></i> ' . esc_html($format($favorites)) . '</span>';
|
||||
|
||||
echo implode(' · ', $parts);
|
||||
}
|
||||
|
||||
private function render_sales_column(int $post_id): void {
|
||||
$pay_meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post_id) : [];
|
||||
$pay_meta = function_exists('jio_get_pay_meta') ? jio_get_pay_meta($post_id) : [];
|
||||
$pay_type = (string) ($pay_meta['pay_type'] ?? '');
|
||||
if ($pay_type === '' || $pay_type === \AKSRT\Core\DomainModel::PAY_TYPE_NO) {
|
||||
echo '<div style="font-size: 12px;">' . esc_html__('未开启付费', 'aksrt') . '</div>';
|
||||
if ($pay_type === '' || $pay_type === \Jio\Core\DomainModel::PAY_TYPE_NO) {
|
||||
echo '<div style="font-size: 12px;">' . esc_html__('未开启付费', 'jio') . '</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
$pay_modo = (string) ($pay_meta['pay_modo'] ?? '0');
|
||||
$type_labels = \AKSRT\Core\DomainModel::pay_type_labels();
|
||||
$type_labels = \Jio\Core\DomainModel::pay_type_labels();
|
||||
$type_label = $type_labels[$pay_type] ?? $pay_type;
|
||||
$type_color = $this->pay_type_color($pay_type);
|
||||
|
||||
if ($pay_modo === 'points') {
|
||||
$price_text = sprintf(__('积分:%s', 'aksrt'), (string) max(0, (int) ($pay_meta['points_price'] ?? 0)));
|
||||
$price_text = sprintf(__('积分:%s', 'jio'), (string) max(0, (int) ($pay_meta['points_price'] ?? 0)));
|
||||
} else {
|
||||
$price_text = $this->currency_symbol() . ':' . (string) ($pay_meta['price'] ?? '0.00');
|
||||
}
|
||||
|
||||
// 列表列与前台同一口径:真实订单 + 后台「销量浮动」(pay_count)。
|
||||
// 真实销量取订单表统计,_aksrt_sales_volume 只是订单流水累加的副本。
|
||||
// 真实销量取订单表统计,_jio_sales_volume 只是订单流水累加的副本。
|
||||
$real_sales = $this->real_sales_count($post_id);
|
||||
$meta_sales = max(0, (int) get_post_meta($post_id, \AKSRT\Core\DomainModel::RESOURCE_SALES_VOLUME_KEY, true));
|
||||
$meta_sales = max(0, (int) get_post_meta($post_id, \Jio\Core\DomainModel::RESOURCE_SALES_VOLUME_KEY, true));
|
||||
$sales_volume = max($real_sales, $meta_sales) + max(0, (int) ($pay_meta['pay_count'] ?? 0));
|
||||
$real_html = $real_sales > 0
|
||||
? ' <a title="' . esc_attr__('点击查看销售明细', 'aksrt') . '" href="' . esc_url($this->orders_url($post_id)) . '">[' . esc_html(sprintf(__('真实销量%s', 'aksrt'), $real_sales)) . ']</a>'
|
||||
? ' <a title="' . esc_attr__('点击查看销售明细', 'jio') . '" href="' . esc_url($this->orders_url($post_id)) . '">[' . esc_html(sprintf(__('真实销量%s', 'jio'), $real_sales)) . ']</a>'
|
||||
: '';
|
||||
|
||||
$link = add_query_arg(['aksrt_pay_type' => $pay_type]);
|
||||
$link = add_query_arg(['jio_pay_type' => $pay_type]);
|
||||
echo '<div>';
|
||||
echo '<a style="color: ' . esc_attr($type_color) . ';" href="' . esc_url($link) . '">' . esc_html($type_label) . '</a>';
|
||||
echo '<div style="font-size: 12px;">' . esc_html($price_text) . ' · ' . esc_html__('销量', 'aksrt') . esc_html((string) $sales_volume) . wp_kses_post($real_html) . '</div>';
|
||||
echo '<div style="font-size: 12px;">' . esc_html($price_text) . ' · ' . esc_html__('销量', 'jio') . esc_html((string) $sales_volume) . wp_kses_post($real_html) . '</div>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
@@ -189,11 +189,11 @@ class PostListEnhance {
|
||||
*/
|
||||
private function pay_type_color(string $pay_type): string {
|
||||
$palette = [
|
||||
\AKSRT\Core\DomainModel::PAY_TYPE_READING => '#ff4747',
|
||||
\AKSRT\Core\DomainModel::PAY_TYPE_DOWNLOAD => '#ee5307',
|
||||
\AKSRT\Core\DomainModel::PAY_TYPE_GALLERY => '#1e8608',
|
||||
\AKSRT\Core\DomainModel::PAY_TYPE_VIDEO => '#086ae8',
|
||||
\AKSRT\Core\DomainModel::PAY_TYPE_VIP => '#3353fd',
|
||||
\Jio\Core\DomainModel::PAY_TYPE_READING => '#ff4747',
|
||||
\Jio\Core\DomainModel::PAY_TYPE_DOWNLOAD => '#ee5307',
|
||||
\Jio\Core\DomainModel::PAY_TYPE_GALLERY => '#1e8608',
|
||||
\Jio\Core\DomainModel::PAY_TYPE_VIDEO => '#086ae8',
|
||||
\Jio\Core\DomainModel::PAY_TYPE_VIP => '#3353fd',
|
||||
];
|
||||
return $palette[$pay_type] ?? '#464646';
|
||||
}
|
||||
@@ -207,12 +207,12 @@ class PostListEnhance {
|
||||
return;
|
||||
}
|
||||
|
||||
$current = isset($_GET['aksrt_pay_type']) ? sanitize_key((string) wp_unslash($_GET['aksrt_pay_type'])) : '';
|
||||
$labels = \AKSRT\Core\DomainModel::pay_type_labels();
|
||||
unset($labels[\AKSRT\Core\DomainModel::PAY_TYPE_NO]);
|
||||
$current = isset($_GET['jio_pay_type']) ? sanitize_key((string) wp_unslash($_GET['jio_pay_type'])) : '';
|
||||
$labels = \Jio\Core\DomainModel::pay_type_labels();
|
||||
unset($labels[\Jio\Core\DomainModel::PAY_TYPE_NO]);
|
||||
|
||||
echo '<select name="aksrt_pay_type">';
|
||||
echo '<option value="">' . esc_html__('商品类型', 'aksrt') . '</option>';
|
||||
echo '<select name="jio_pay_type">';
|
||||
echo '<option value="">' . esc_html__('商品类型', 'jio') . '</option>';
|
||||
foreach ($labels as $key => $label) {
|
||||
printf('<option %s value="%s">%s</option>', selected($current, $key, false), esc_attr($key), esc_html($label));
|
||||
}
|
||||
@@ -232,25 +232,25 @@ class PostListEnhance {
|
||||
|
||||
$orderby = isset($_GET['orderby']) ? sanitize_key((string) wp_unslash($_GET['orderby'])) : '';
|
||||
if ($orderby === 'sales_volume') {
|
||||
$query->set('meta_key', \AKSRT\Core\DomainModel::RESOURCE_SALES_VOLUME_KEY);
|
||||
$query->set('meta_key', \Jio\Core\DomainModel::RESOURCE_SALES_VOLUME_KEY);
|
||||
$query->set('orderby', 'meta_value_num');
|
||||
} elseif ($orderby === 'views' || $orderby === 'like' || $orderby === 'favorite') {
|
||||
$query->set('meta_key', $this->stats_meta_key($orderby));
|
||||
$query->set('orderby', 'meta_value_num');
|
||||
}
|
||||
|
||||
$pay_type = isset($_GET['aksrt_pay_type']) ? sanitize_key((string) wp_unslash($_GET['aksrt_pay_type'])) : '';
|
||||
if ($pay_type !== '' && isset(\AKSRT\Core\DomainModel::pay_type_labels()[$pay_type])) {
|
||||
$pay_type = isset($_GET['jio_pay_type']) ? sanitize_key((string) wp_unslash($_GET['jio_pay_type'])) : '';
|
||||
if ($pay_type !== '' && isset(\Jio\Core\DomainModel::pay_type_labels()[$pay_type])) {
|
||||
$meta_query = $query->get('meta_query');
|
||||
$meta_query = is_array($meta_query) ? $meta_query : [];
|
||||
$meta_query[] = [
|
||||
'relation' => 'OR',
|
||||
[
|
||||
'key' => \AKSRT\Core\DomainModel::PAY_TYPE_KEY,
|
||||
'key' => \Jio\Core\DomainModel::PAY_TYPE_KEY,
|
||||
'value' => $pay_type,
|
||||
],
|
||||
[
|
||||
'key' => \AKSRT\Core\DomainModel::PAY_META_KEY,
|
||||
'key' => \Jio\Core\DomainModel::PAY_META_KEY,
|
||||
'value' => '"pay_type";s:' . strlen($pay_type) . ':"' . $pay_type . '"',
|
||||
'compare' => 'LIKE',
|
||||
],
|
||||
@@ -264,11 +264,11 @@ class PostListEnhance {
|
||||
*/
|
||||
private function stats_meta_key(string $orderby): string {
|
||||
$map = [
|
||||
'views' => \AKSRT\Core\DomainModel::POST_VIEW_COUNT_KEY,
|
||||
'like' => '_aksrt_like_count',
|
||||
'favorite' => '_aksrt_favorite_count',
|
||||
'views' => \Jio\Core\DomainModel::POST_VIEW_COUNT_KEY,
|
||||
'like' => '_jio_like_count',
|
||||
'favorite' => '_jio_favorite_count',
|
||||
];
|
||||
return $map[$orderby] ?? \AKSRT\Core\DomainModel::POST_VIEW_COUNT_KEY;
|
||||
return $map[$orderby] ?? \Jio\Core\DomainModel::POST_VIEW_COUNT_KEY;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -291,7 +291,7 @@ class PostListEnhance {
|
||||
|
||||
private function edit_boxes_html(): string {
|
||||
$html = $this->fields_html(PostListEditFields::extended(), 'extended');
|
||||
$html .= $this->fields_html(PostListEditFields::pay($this->currency_symbol()), 'pay', __('付费参数', 'aksrt'), '<div class="c-yellow">' . esc_html__('批量设置付费参数,请仔细检查配置内容,更新后无法撤回。请注意配置逻辑,例如会员价不能高于普通价,执行价不能高于原价等', 'aksrt') . '</div>');
|
||||
$html .= $this->fields_html(PostListEditFields::pay($this->currency_symbol()), 'pay', __('付费参数', 'jio'), '<div class="c-yellow">' . esc_html__('批量设置付费参数,请仔细检查配置内容,更新后无法撤回。请注意配置逻辑,例如会员价不能高于普通价,执行价不能高于原价等', 'jio') . '</div>');
|
||||
$html .= $this->filter_fields_html();
|
||||
return $html;
|
||||
}
|
||||
@@ -302,8 +302,8 @@ class PostListEnhance {
|
||||
*/
|
||||
private function filter_fields_html(): string {
|
||||
$dimensions = [];
|
||||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||||
$settings = \AKSRT\Settings\Settings::get_post_filter_settings();
|
||||
if (class_exists('\Jio\Settings\Settings')) {
|
||||
$settings = \Jio\Settings\Settings::get_post_filter_settings();
|
||||
$dimensions = is_array($settings['custom_filter'] ?? null) ? $settings['custom_filter'] : [];
|
||||
}
|
||||
if (!$dimensions) {
|
||||
@@ -332,7 +332,7 @@ class PostListEnhance {
|
||||
'title' => (string) ($dimension['name'] ?? $key),
|
||||
'type' => 'checkbox',
|
||||
'options' => $options,
|
||||
'placeholder' => sprintf(__('请选择%s', 'aksrt'), (string) ($dimension['name'] ?? $key)),
|
||||
'placeholder' => sprintf(__('请选择%s', 'jio'), (string) ($dimension['name'] ?? $key)),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -340,7 +340,7 @@ class PostListEnhance {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->fields_html($fields, 'filter', __('高级筛选', 'aksrt'), '<div class="px12 aksrt-bulk-edit-desc">' . esc_html__('让文章实现更加精细化的分类,同时方便用户进行文章筛选', 'aksrt') . '</div>');
|
||||
return $this->fields_html($fields, 'filter', __('高级筛选', 'jio'), '<div class="px12 jio-bulk-edit-desc">' . esc_html__('让文章实现更加精细化的分类,同时方便用户进行文章筛选', 'jio') . '</div>');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -357,15 +357,15 @@ class PostListEnhance {
|
||||
$options_html = ' ';
|
||||
switch ($field['type']) {
|
||||
case 'number':
|
||||
$name = 'aksrt_bulk_edit[' . $id . '][' . $field['id'] . ']';
|
||||
$name = 'jio_bulk_edit[' . $id . '][' . $field['id'] . ']';
|
||||
$options_html = '<div class="flex ac hh">'
|
||||
. '<select name="' . esc_attr($name) . '[operation]">'
|
||||
. '<option value="ignore" selected="selected">' . esc_html__('不更改', 'aksrt') . '</option>'
|
||||
. '<option value="set">' . esc_html__('设置为', 'aksrt') . '</option>'
|
||||
. '<option value="plus">' . esc_html__('加', 'aksrt') . '</option>'
|
||||
. '<option value="subtract">' . esc_html__('减', 'aksrt') . '</option>'
|
||||
. '<option value="multiply">' . esc_html__('乘', 'aksrt') . '</option>'
|
||||
. '<option value="division">' . esc_html__('除', 'aksrt') . '</option>'
|
||||
. '<option value="ignore" selected="selected">' . esc_html__('不更改', 'jio') . '</option>'
|
||||
. '<option value="set">' . esc_html__('设置为', 'jio') . '</option>'
|
||||
. '<option value="plus">' . esc_html__('加', 'jio') . '</option>'
|
||||
. '<option value="subtract">' . esc_html__('减', 'jio') . '</option>'
|
||||
. '<option value="multiply">' . esc_html__('乘', 'jio') . '</option>'
|
||||
. '<option value="division">' . esc_html__('除', 'jio') . '</option>'
|
||||
. '</select>'
|
||||
. '<input type="text" name="' . esc_attr($name) . '[val]" value="">'
|
||||
. '</div>';
|
||||
@@ -374,32 +374,32 @@ class PostListEnhance {
|
||||
case 'checkbox':
|
||||
$select_options = !empty($field['options']) ? $field['options'] : [];
|
||||
if ($select_options) {
|
||||
$name = 'aksrt_bulk_edit[' . $id . '][' . $field['id'] . ']';
|
||||
$name = 'jio_bulk_edit[' . $id . '][' . $field['id'] . ']';
|
||||
$options_html = $this->checkbox_options_html($name, $select_options);
|
||||
break;
|
||||
}
|
||||
$field['options'] = [
|
||||
'ignore' => __('不更改', 'aksrt'),
|
||||
'1' => __('开启', 'aksrt'),
|
||||
'0' => __('关闭', 'aksrt'),
|
||||
'ignore' => __('不更改', 'jio'),
|
||||
'1' => __('开启', 'jio'),
|
||||
'0' => __('关闭', 'jio'),
|
||||
];
|
||||
// Falls through to the switcher renderer below.
|
||||
|
||||
case 'switcher':
|
||||
$field['options'] = [
|
||||
'ignore' => __('不更改', 'aksrt'),
|
||||
'1' => __('开启', 'aksrt'),
|
||||
'0' => __('关闭', 'aksrt'),
|
||||
'ignore' => __('不更改', 'jio'),
|
||||
'1' => __('开启', 'jio'),
|
||||
'0' => __('关闭', 'jio'),
|
||||
];
|
||||
// Falls through to the select renderer below.
|
||||
|
||||
case 'radio':
|
||||
case 'select':
|
||||
$options = isset($field['options']) && is_array($field['options']) ? $field['options'] : [];
|
||||
$name = 'aksrt_bulk_edit[' . $id . '][' . $field['id'] . ']';
|
||||
$name = 'jio_bulk_edit[' . $id . '][' . $field['id'] . ']';
|
||||
$options_html = '<div class="flex ac hh">'
|
||||
. '<select name="' . esc_attr($name) . '">'
|
||||
. '<option selected="selected" value="ignore">--' . esc_html__('不更改', 'aksrt') . '--</option>';
|
||||
. '<option selected="selected" value="ignore">--' . esc_html__('不更改', 'jio') . '--</option>';
|
||||
foreach ($options as $key => $value) {
|
||||
$options_html .= '<option value="' . esc_attr((string) $key) . '">' . esc_html((string) $value) . '</option>';
|
||||
}
|
||||
@@ -412,11 +412,11 @@ class PostListEnhance {
|
||||
}
|
||||
|
||||
$title_html = !empty($field['title']) ? (string) $field['title'] : (!empty($field['label']) ? (string) $field['label'] : '');
|
||||
$desc_html = !empty($field['desc']) ? '<div class="px12 aksrt-bulk-edit-desc">' . esc_html((string) $field['desc']) . '</div>' : '';
|
||||
$subtitle = !empty($field['subtitle']) ? '<div class="px12 aksrt-bulk-edit-desc">' . esc_html((string) $field['subtitle']) . '</div>' : '';
|
||||
$desc_html = !empty($field['desc']) ? '<div class="px12 jio-bulk-edit-desc">' . esc_html((string) $field['desc']) . '</div>' : '';
|
||||
$subtitle = !empty($field['subtitle']) ? '<div class="px12 jio-bulk-edit-desc">' . esc_html((string) $field['subtitle']) . '</div>' : '';
|
||||
$dependency = !empty($field['dependency']) ? ' data-dependency="' . esc_attr(wp_json_encode($field['dependency'])) . '"' : '';
|
||||
|
||||
$item_html .= '<div class="aksrt-bulk-edit-box"' . $dependency . '>'
|
||||
$item_html .= '<div class="jio-bulk-edit-box"' . $dependency . '>'
|
||||
. '<div>' . esc_html($title_html) . '</div>'
|
||||
. $options_html . $subtitle . $desc_html
|
||||
. '</div>';
|
||||
@@ -438,8 +438,8 @@ class PostListEnhance {
|
||||
*/
|
||||
private function checkbox_options_html(string $name, array $options): string {
|
||||
$html = '<select class="mr10" name="' . esc_attr($name) . '[operation]">'
|
||||
. '<option value="ignore" selected="selected">' . esc_html__('不更改', 'aksrt') . '</option>'
|
||||
. '<option value="set">' . esc_html__('设置为', 'aksrt') . '</option>'
|
||||
. '<option value="ignore" selected="selected">' . esc_html__('不更改', 'jio') . '</option>'
|
||||
. '<option value="set">' . esc_html__('设置为', 'jio') . '</option>'
|
||||
. '</select>';
|
||||
|
||||
foreach ($options as $key => $value) {
|
||||
@@ -476,7 +476,7 @@ class PostListEnhance {
|
||||
if ($screen !== 'edit-post') {
|
||||
return;
|
||||
}
|
||||
$submitted = isset($_POST['aksrt_bulk_edit']) && is_array($_POST['aksrt_bulk_edit']) ? wp_unslash($_POST['aksrt_bulk_edit']) : [];
|
||||
$submitted = isset($_POST['jio_bulk_edit']) && is_array($_POST['jio_bulk_edit']) ? wp_unslash($_POST['jio_bulk_edit']) : [];
|
||||
if (!$submitted) {
|
||||
return;
|
||||
}
|
||||
@@ -538,9 +538,9 @@ class PostListEnhance {
|
||||
* @param array<string,mixed> $fields
|
||||
*/
|
||||
private function save_pay(int $post_id, array $fields): void {
|
||||
$pay_meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post_id) : [];
|
||||
$pay_meta = function_exists('jio_get_pay_meta') ? jio_get_pay_meta($post_id) : [];
|
||||
if (!is_array($pay_meta) || $pay_meta === []) {
|
||||
$pay_meta = \AKSRT\Core\DomainModel::default_pay_meta();
|
||||
$pay_meta = \Jio\Core\DomainModel::default_pay_meta();
|
||||
}
|
||||
|
||||
foreach ($fields as $field_id => $field_value) {
|
||||
@@ -600,19 +600,19 @@ class PostListEnhance {
|
||||
}
|
||||
$current = (string) ($pay_meta[$field_id] ?? '0.00');
|
||||
try {
|
||||
$current_minor = (float) \AKSRT\Ledger\AssetAmount::balance_minor($current);
|
||||
$current_minor = (float) \Jio\Ledger\AssetAmount::balance_minor($current);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$current_minor = 0.0;
|
||||
}
|
||||
$new = $this->apply_operation($current_minor, (float) $field_value['val'], $operation);
|
||||
if ($new !== null) {
|
||||
$pay_meta[$field_id] = \AKSRT\Ledger\AssetAmount::decimal_from_minor(max(0, (int) round($new)));
|
||||
$pay_meta[$field_id] = \Jio\Ledger\AssetAmount::decimal_from_minor(max(0, (int) round($new)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
\AKSRT\Core\DomainModel::persist_pay_meta($post_id, $pay_meta);
|
||||
\Jio\Core\DomainModel::persist_pay_meta($post_id, $pay_meta);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -620,8 +620,8 @@ class PostListEnhance {
|
||||
*/
|
||||
private function save_filters(int $post_id, array $fields): void {
|
||||
$dimensions = [];
|
||||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||||
$settings = \AKSRT\Settings\Settings::get_post_filter_settings();
|
||||
if (class_exists('\Jio\Settings\Settings')) {
|
||||
$settings = \Jio\Settings\Settings::get_post_filter_settings();
|
||||
$dimensions = is_array($settings['custom_filter'] ?? null) ? $settings['custom_filter'] : [];
|
||||
}
|
||||
if (!$dimensions) {
|
||||
@@ -652,9 +652,9 @@ class PostListEnhance {
|
||||
: [];
|
||||
$values = array_values(array_intersect($allowed_dimensions[$field_id], $values));
|
||||
if ($values) {
|
||||
update_post_meta($post_id, '_aksrt_cf_' . $field_id, $values);
|
||||
update_post_meta($post_id, '_jio_cf_' . $field_id, $values);
|
||||
} else {
|
||||
delete_post_meta($post_id, '_aksrt_cf_' . $field_id);
|
||||
delete_post_meta($post_id, '_jio_cf_' . $field_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -664,7 +664,7 @@ class PostListEnhance {
|
||||
* meta box.
|
||||
*/
|
||||
private function update_post_meta_bag(int $post_id, string $key, string $value): void {
|
||||
$bag_key = \AKSRT\Core\PostMetaBox::META_KEY;
|
||||
$bag_key = \Jio\Core\PostMetaBox::META_KEY;
|
||||
$bag = get_post_meta($post_id, $bag_key, true);
|
||||
$bag = is_array($bag) ? $bag : [];
|
||||
$bag[$key] = $value;
|
||||
@@ -698,8 +698,8 @@ class PostListEnhance {
|
||||
* Configured cash symbol, shared by the sales column and the pay fields.
|
||||
*/
|
||||
private function currency_symbol(): string {
|
||||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||||
$paid = \AKSRT\Settings\Settings::get_paid_content_settings();
|
||||
if (class_exists('\Jio\Settings\Settings')) {
|
||||
$paid = \Jio\Settings\Settings::get_paid_content_settings();
|
||||
return (string) ($paid['display']['currency_symbol'] ?? '¥');
|
||||
}
|
||||
return '¥';
|
||||
@@ -732,7 +732,7 @@ class PostListEnhance {
|
||||
if (!$wp_query instanceof \WP_Query || empty($wp_query->posts)) {
|
||||
return [];
|
||||
}
|
||||
if (!class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
if (!class_exists('\Jio\Payment\OrderRepository')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -746,9 +746,9 @@ class PostListEnhance {
|
||||
return [];
|
||||
}
|
||||
|
||||
$map = \AKSRT\Payment\OrderRepository::get_instance()->get_content_sales_map(
|
||||
$map = \Jio\Payment\OrderRepository::get_instance()->get_content_sales_map(
|
||||
$ids,
|
||||
\AKSRT\Core\DomainModel::ORDER_PRODUCT_RESOURCE
|
||||
\Jio\Core\DomainModel::ORDER_PRODUCT_RESOURCE
|
||||
);
|
||||
$out = [];
|
||||
foreach ($map as $pid => $row) {
|
||||
@@ -760,8 +760,8 @@ class PostListEnhance {
|
||||
private function orders_url(int $post_id): string {
|
||||
return add_query_arg(
|
||||
[
|
||||
'page' => 'aksrt_orders',
|
||||
'product_type' => \AKSRT\Core\DomainModel::ORDER_PRODUCT_RESOURCE,
|
||||
'page' => 'jio_orders',
|
||||
'product_type' => \Jio\Core\DomainModel::ORDER_PRODUCT_RESOURCE,
|
||||
'product_id' => $post_id,
|
||||
],
|
||||
admin_url('admin.php')
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -35,13 +35,13 @@ class UsersMenus {
|
||||
NativeUi::boot();
|
||||
add_action('admin_menu', [$this, 'register_menus']);
|
||||
add_action('admin_notices', [$this, 'pending_notices']);
|
||||
add_action('admin_post_aksrt_users_send_message', [$this, 'handle_send_message']);
|
||||
add_action('admin_post_aksrt_users_bulk_messages', [$this, 'handle_bulk_messages']);
|
||||
add_action('admin_post_aksrt_users_review_verify', [$this, 'handle_review_verify']);
|
||||
add_action('admin_post_aksrt_users_delete_verify', [$this, 'handle_delete_verify']);
|
||||
add_action('admin_post_aksrt_users_update_report', [$this, 'handle_update_report']);
|
||||
add_action('admin_post_aksrt_users_process_appeal', [$this, 'handle_process_appeal']);
|
||||
add_action('admin_post_aksrt_users_mutate_vip', [$this, 'handle_mutate_vip']);
|
||||
add_action('admin_post_jio_users_send_message', [$this, 'handle_send_message']);
|
||||
add_action('admin_post_jio_users_bulk_messages', [$this, 'handle_bulk_messages']);
|
||||
add_action('admin_post_jio_users_review_verify', [$this, 'handle_review_verify']);
|
||||
add_action('admin_post_jio_users_delete_verify', [$this, 'handle_delete_verify']);
|
||||
add_action('admin_post_jio_users_update_report', [$this, 'handle_update_report']);
|
||||
add_action('admin_post_jio_users_process_appeal', [$this, 'handle_process_appeal']);
|
||||
add_action('admin_post_jio_users_mutate_vip', [$this, 'handle_mutate_vip']);
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_ticket_assets']);
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_message_assets']);
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_invite_assets']);
|
||||
@@ -53,10 +53,10 @@ class UsersMenus {
|
||||
public function register_menus(): void {
|
||||
add_submenu_page(
|
||||
'users.php',
|
||||
__('邀请码管理', 'aksrt'),
|
||||
__('邀请码管理', 'aksrt'),
|
||||
__('邀请码管理', 'jio'),
|
||||
__('邀请码管理', 'jio'),
|
||||
'edit_users',
|
||||
'aksrt_invite_codes',
|
||||
'jio_invite_codes',
|
||||
[$this, 'render_invite_codes_page']
|
||||
);
|
||||
|
||||
@@ -66,42 +66,42 @@ class UsersMenus {
|
||||
|
||||
add_submenu_page(
|
||||
'users.php',
|
||||
__('消息管理', 'aksrt'),
|
||||
__('消息管理', 'aksrt'),
|
||||
__('消息管理', 'jio'),
|
||||
__('消息管理', 'jio'),
|
||||
self::CAPABILITY,
|
||||
'aksrt_messages',
|
||||
'jio_messages',
|
||||
[$this, 'render_messages_page']
|
||||
);
|
||||
add_submenu_page(
|
||||
'users.php',
|
||||
__('用户会员', 'aksrt'),
|
||||
__('用户会员', 'aksrt'),
|
||||
__('用户会员', 'jio'),
|
||||
__('用户会员', 'jio'),
|
||||
self::CAPABILITY,
|
||||
'aksrt_user_vip',
|
||||
'jio_user_vip',
|
||||
[$this, 'render_vip_page']
|
||||
);
|
||||
add_submenu_page(
|
||||
'users.php',
|
||||
__('身份认证', 'aksrt'),
|
||||
__('身份认证', 'aksrt'),
|
||||
__('身份认证', 'jio'),
|
||||
__('身份认证', 'jio'),
|
||||
self::CAPABILITY,
|
||||
'aksrt_user_auth',
|
||||
'jio_user_auth',
|
||||
[$this, 'render_verify_page']
|
||||
);
|
||||
add_submenu_page(
|
||||
'users.php',
|
||||
__('处理举报及禁封申诉', 'aksrt'),
|
||||
__('举报&禁封申诉', 'aksrt'),
|
||||
__('处理举报及禁封申诉', 'jio'),
|
||||
__('举报&禁封申诉', 'jio'),
|
||||
self::CAPABILITY,
|
||||
'aksrt_user_ban',
|
||||
'jio_user_ban',
|
||||
[$this, 'render_ban_page']
|
||||
);
|
||||
add_submenu_page(
|
||||
'users.php',
|
||||
__('工单管理', 'aksrt'),
|
||||
__('工单管理', 'aksrt'),
|
||||
__('工单管理', 'jio'),
|
||||
__('工单管理', 'jio'),
|
||||
self::CAPABILITY,
|
||||
'aksrt_tickets',
|
||||
'jio_tickets',
|
||||
[$this, 'render_tickets_page']
|
||||
);
|
||||
}
|
||||
@@ -112,115 +112,115 @@ class UsersMenus {
|
||||
}
|
||||
$page = isset($_GET['page']) ? sanitize_key((string) wp_unslash($_GET['page'])) : '';
|
||||
|
||||
if ($page !== 'aksrt_user_auth') {
|
||||
if ($page !== 'jio_user_auth') {
|
||||
$pending_verify = $this->count_verify_status('pending');
|
||||
if ($pending_verify > 0) {
|
||||
$url = admin_url('users.php?page=aksrt_user_auth&status=pending');
|
||||
echo '<div class="notice notice-info is-dismissible"><h3>' . esc_html__('用户身份认证申请待处理', 'aksrt') . '</h3>';
|
||||
$url = admin_url('users.php?page=jio_user_auth&status=pending');
|
||||
echo '<div class="notice notice-info is-dismissible"><h3>' . esc_html__('用户身份认证申请待处理', 'jio') . '</h3>';
|
||||
echo '<p>' . esc_html(sprintf(
|
||||
/* translators: %d count */
|
||||
__('您有 %d 个用户身份认证申请待处理', 'aksrt'),
|
||||
__('您有 %d 个用户身份认证申请待处理', 'jio'),
|
||||
$pending_verify
|
||||
)) . '</p>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'aksrt') . '</a></p></div>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'jio') . '</a></p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
if ($page !== 'aksrt_user_ban' && class_exists('\AKSRT\Messages\ReportCenter')) {
|
||||
$pending_reports = (int) \AKSRT\Messages\ReportCenter::get_instance()->get_pending_count();
|
||||
if ($page !== 'jio_user_ban' && class_exists('\Jio\Messages\ReportCenter')) {
|
||||
$pending_reports = (int) \Jio\Messages\ReportCenter::get_instance()->get_pending_count();
|
||||
if ($pending_reports > 0) {
|
||||
$url = admin_url('users.php?page=aksrt_user_ban&status=pending');
|
||||
echo '<div class="notice notice-info is-dismissible"><h3>' . esc_html__('举报待处理', 'aksrt') . '</h3>';
|
||||
$url = admin_url('users.php?page=jio_user_ban&status=pending');
|
||||
echo '<div class="notice notice-info is-dismissible"><h3>' . esc_html__('举报待处理', 'jio') . '</h3>';
|
||||
echo '<p>' . esc_html(sprintf(
|
||||
/* translators: %d count */
|
||||
__('您有 %d 条举报待处理', 'aksrt'),
|
||||
__('您有 %d 条举报待处理', 'jio'),
|
||||
$pending_reports
|
||||
)) . '</p>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'aksrt') . '</a></p></div>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'jio') . '</a></p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
if ($page !== 'aksrt_user_ban' && class_exists('\AKSRT\User\BanAppeal')) {
|
||||
if ($page !== 'jio_user_ban' && class_exists('\Jio\User\BanAppeal')) {
|
||||
$pending_appeals = count(get_users([
|
||||
'meta_key' => \AKSRT\User\BanAppeal::META_STATUS,
|
||||
'meta_key' => \Jio\User\BanAppeal::META_STATUS,
|
||||
'meta_value' => 'pending',
|
||||
'number' => 50,
|
||||
'fields' => 'ID',
|
||||
]));
|
||||
if ($pending_appeals > 0) {
|
||||
$url = admin_url('users.php?page=aksrt_user_ban&type=appeal&status=pending');
|
||||
echo '<div class="notice notice-warning is-dismissible"><h3>' . esc_html__('禁封申诉待处理', 'aksrt') . '</h3>';
|
||||
echo '<p>' . esc_html(sprintf(__('您有 %d 条禁封申诉待处理', 'aksrt'), $pending_appeals)) . '</p>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'aksrt') . '</a></p></div>';
|
||||
$url = admin_url('users.php?page=jio_user_ban&type=appeal&status=pending');
|
||||
echo '<div class="notice notice-warning is-dismissible"><h3>' . esc_html__('禁封申诉待处理', 'jio') . '</h3>';
|
||||
echo '<p>' . esc_html(sprintf(__('您有 %d 条禁封申诉待处理', 'jio'), $pending_appeals)) . '</p>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'jio') . '</a></p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
if ($page !== 'aksrt_tickets' && class_exists('\AKSRT\Ticket\TicketSettings') && \AKSRT\Ticket\TicketSettings::enabled()) {
|
||||
if ($page !== 'jio_tickets' && class_exists('\Jio\Ticket\TicketSettings') && \Jio\Ticket\TicketSettings::enabled()) {
|
||||
$pending_tickets = $this->count_pending_tickets();
|
||||
if ($pending_tickets > 0) {
|
||||
$url = admin_url('users.php?page=aksrt_tickets&status=' . \AKSRT\Ticket\TicketStatusService::KEY_PENDING);
|
||||
echo '<div class="notice notice-info is-dismissible"><h3>' . esc_html__('工单待接单', 'aksrt') . '</h3>';
|
||||
$url = admin_url('users.php?page=jio_tickets&status=' . \Jio\Ticket\TicketStatusService::KEY_PENDING);
|
||||
echo '<div class="notice notice-info is-dismissible"><h3>' . esc_html__('工单待接单', 'jio') . '</h3>';
|
||||
echo '<p>' . esc_html(sprintf(
|
||||
/* translators: %d count */
|
||||
__('您有 %d 个工单待接单', 'aksrt'),
|
||||
__('您有 %d 个工单待接单', 'jio'),
|
||||
$pending_tickets
|
||||
)) . '</p>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'aksrt') . '</a></p></div>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'jio') . '</a></p></div>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function ensure_cap(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
}
|
||||
|
||||
private function flash(): void {
|
||||
if (empty($_GET['aksrt_users_msg'])) {
|
||||
if (empty($_GET['jio_users_msg'])) {
|
||||
return;
|
||||
}
|
||||
$code = sanitize_key((string) wp_unslash($_GET['aksrt_users_msg']));
|
||||
$code = sanitize_key((string) wp_unslash($_GET['jio_users_msg']));
|
||||
$map = [
|
||||
'verify_updated' => __('认证状态已更新。', 'aksrt'),
|
||||
'verify_deleted' => __('认证记录已删除。', 'aksrt'),
|
||||
'verify_failed' => __('认证审核失败。', 'aksrt'),
|
||||
'report_updated' => __('举报状态已更新。', 'aksrt'),
|
||||
'report_failed' => __('举报更新失败。', 'aksrt'),
|
||||
'appeal_approved' => __('申诉已通过,限制已解除。', 'aksrt'),
|
||||
'appeal_rejected' => __('申诉已驳回。', 'aksrt'),
|
||||
'appeal_failed' => __('申诉处理失败。', 'aksrt'),
|
||||
'vip_updated' => __('会员状态已更新。', 'aksrt'),
|
||||
'vip_partial' => __('部分用户的会员状态已更新。', 'aksrt'),
|
||||
'vip_failed' => __('会员操作失败。', 'aksrt'),
|
||||
'verify_updated' => __('认证状态已更新。', 'jio'),
|
||||
'verify_deleted' => __('认证记录已删除。', 'jio'),
|
||||
'verify_failed' => __('认证审核失败。', 'jio'),
|
||||
'report_updated' => __('举报状态已更新。', 'jio'),
|
||||
'report_failed' => __('举报更新失败。', 'jio'),
|
||||
'appeal_approved' => __('申诉已通过,限制已解除。', 'jio'),
|
||||
'appeal_rejected' => __('申诉已驳回。', 'jio'),
|
||||
'appeal_failed' => __('申诉处理失败。', 'jio'),
|
||||
'vip_updated' => __('会员状态已更新。', 'jio'),
|
||||
'vip_partial' => __('部分用户的会员状态已更新。', 'jio'),
|
||||
'vip_failed' => __('会员操作失败。', 'jio'),
|
||||
];
|
||||
if (!isset($map[$code])) {
|
||||
return;
|
||||
}
|
||||
$class = str_contains($code, 'failed') ? 'notice-error' : 'notice-success';
|
||||
$message = $map[$code];
|
||||
if ($code === 'verify_failed' && isset($_GET['aksrt_verify_error'])) {
|
||||
$message = sanitize_text_field((string) wp_unslash($_GET['aksrt_verify_error']));
|
||||
if ($code === 'verify_failed' && isset($_GET['jio_verify_error'])) {
|
||||
$message = sanitize_text_field((string) wp_unslash($_GET['jio_verify_error']));
|
||||
}
|
||||
if (in_array($code, ['report_failed', 'appeal_failed'], true) && isset($_GET['aksrt_ban_error'])) {
|
||||
$message = sanitize_text_field((string) wp_unslash($_GET['aksrt_ban_error']));
|
||||
if (in_array($code, ['report_failed', 'appeal_failed'], true) && isset($_GET['jio_ban_error'])) {
|
||||
$message = sanitize_text_field((string) wp_unslash($_GET['jio_ban_error']));
|
||||
}
|
||||
if ($code === 'vip_failed' && isset($_GET['aksrt_vip_error'])) {
|
||||
$message = sanitize_text_field((string) wp_unslash($_GET['aksrt_vip_error']));
|
||||
if ($code === 'vip_failed' && isset($_GET['jio_vip_error'])) {
|
||||
$message = sanitize_text_field((string) wp_unslash($_GET['jio_vip_error']));
|
||||
}
|
||||
if (in_array($code, ['vip_updated', 'vip_partial'], true)) {
|
||||
$message .= ' ' . sprintf(
|
||||
__('成功:%1$d;失败:%2$d;跳过:%3$d。', 'aksrt'),
|
||||
absint($_GET['aksrt_vip_ok'] ?? 0),
|
||||
absint($_GET['aksrt_vip_failed_count'] ?? 0),
|
||||
absint($_GET['aksrt_vip_skipped'] ?? 0)
|
||||
__('成功:%1$d;失败:%2$d;跳过:%3$d。', 'jio'),
|
||||
absint($_GET['jio_vip_ok'] ?? 0),
|
||||
absint($_GET['jio_vip_failed_count'] ?? 0),
|
||||
absint($_GET['jio_vip_skipped'] ?? 0)
|
||||
);
|
||||
}
|
||||
echo '<div class="notice ' . esc_attr($class) . ' is-dismissible"><p>' . esc_html($message) . '</p></div>';
|
||||
}
|
||||
|
||||
private function redirect(string $page, string $msg, array $extra = []): void {
|
||||
wp_safe_redirect(add_query_arg(array_merge(['page' => $page, 'aksrt_users_msg' => $msg], $extra), admin_url('users.php')));
|
||||
wp_safe_redirect(add_query_arg(array_merge(['page' => $page, 'jio_users_msg' => $msg], $extra), admin_url('users.php')));
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/** Native report and ban appeal queue. @package AKSRT */
|
||||
/** Native report and ban appeal queue. @package Jio */
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\User\Restriction;
|
||||
use Jio\User\Restriction;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -15,11 +15,11 @@ trait UsersMenusBan {
|
||||
private array $ban_restrictions = [];
|
||||
|
||||
public function enqueue_ban_assets(string $hook): void {
|
||||
if ($hook !== 'users_page_aksrt_user_ban') return;
|
||||
if ($hook !== 'users_page_jio_user_ban') return;
|
||||
$css = '/assets/css/admin/user-ban.css';
|
||||
$js = '/assets/js/admin-user-ban.js';
|
||||
wp_enqueue_style('aksrt-admin-user-ban', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-user-ban', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-user-ban', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-user-ban', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
public function render_ban_page(): void {
|
||||
@@ -28,71 +28,71 @@ trait UsersMenusBan {
|
||||
$data = $this->query_ban_requests($filters, max(1, (int) ($_GET['paged'] ?? 1)));
|
||||
$this->cache_ban_targets($data['items']);
|
||||
$this->load_ban_restrictions($data['items']);
|
||||
NativeUi::page_open('aksrt-ban-page');
|
||||
NativeUi::page_open('jio-ban-page');
|
||||
NativeUi::header(
|
||||
__('举报与禁封申诉', 'aksrt'),
|
||||
__('核对举报内容与申诉资料,在审核弹窗中记录处理结果。用户禁封操作仍在前台用户主页完成。', 'aksrt'),
|
||||
__('举报与禁封申诉', 'jio'),
|
||||
__('核对举报内容与申诉资料,在审核弹窗中记录处理结果。用户禁封操作仍在前台用户主页完成。', 'jio'),
|
||||
'dashicons-shield'
|
||||
);
|
||||
$this->flash();
|
||||
NativeUi::card_open('', '', true, 'aksrt-ban-list-card');
|
||||
NativeUi::card_open('', '', true, 'jio-ban-list-card');
|
||||
$this->render_ban_filters($filters, $data['counts']);
|
||||
echo '<div class="aksrt-native-table-wrap">';
|
||||
echo '<div class="jio-native-table-wrap">';
|
||||
$this->render_ban_table($data['items'], $filters, $data['page']);
|
||||
NativeUi::table_wrap_close();
|
||||
AdminListPagination::render('aksrt_user_ban', $data['total'], $data['page'], self::BAN_PER_PAGE, $filters, 'users.php');
|
||||
AdminListPagination::render('jio_user_ban', $data['total'], $data['page'], self::BAN_PER_PAGE, $filters, 'users.php');
|
||||
NativeUi::card_close();
|
||||
echo '<dialog id="aksrt-ban-dialog" class="aksrt-ban-dialog" aria-labelledby="aksrt-ban-dialog-title" aria-describedby="aksrt-ban-dialog-description"></dialog>';
|
||||
echo '<noscript><p class="aksrt-native-callout">' . esc_html__('请启用 JavaScript 后查看详情和处理审核。', 'aksrt') . '</p></noscript>';
|
||||
echo '<dialog id="jio-ban-dialog" class="jio-ban-dialog" aria-labelledby="jio-ban-dialog-title" aria-describedby="jio-ban-dialog-description"></dialog>';
|
||||
echo '<noscript><p class="jio-native-callout">' . esc_html__('请启用 JavaScript 后查看详情和处理审核。', 'jio') . '</p></noscript>';
|
||||
NativeUi::page_close();
|
||||
}
|
||||
|
||||
private function ban_page_url(array $args = []): string {
|
||||
return add_query_arg(array_merge(['page' => 'aksrt_user_ban'], $args), admin_url('users.php'));
|
||||
return add_query_arg(array_merge(['page' => 'jio_user_ban'], $args), admin_url('users.php'));
|
||||
}
|
||||
|
||||
private function render_ban_filters(array $filters, array $counts): void {
|
||||
echo '<div class="aksrt-ban-toolbar"><div class="aksrt-ban-toolbar__top"><h2 class="aksrt-native-card__title">' . esc_html__('处理记录', 'aksrt') . '</h2>';
|
||||
echo '<nav class="aksrt-ban-types" aria-label="' . esc_attr__('记录类型', 'aksrt') . '">';
|
||||
foreach (['' => __('全部类型', 'aksrt'), 'report' => __('用户举报', 'aksrt'), 'appeal' => __('禁封申诉', 'aksrt')] as $key => $label) {
|
||||
echo '<div class="jio-ban-toolbar"><div class="jio-ban-toolbar__top"><h2 class="jio-native-card__title">' . esc_html__('处理记录', 'jio') . '</h2>';
|
||||
echo '<nav class="jio-ban-types" aria-label="' . esc_attr__('记录类型', 'jio') . '">';
|
||||
foreach (['' => __('全部类型', 'jio'), 'report' => __('用户举报', 'jio'), 'appeal' => __('禁封申诉', 'jio')] as $key => $label) {
|
||||
echo '<a href="' . esc_url($this->ban_page_url(array_merge($filters, ['type' => $key]))) . '"' . ($key === $filters['type'] ? ' aria-current="page"' : '') . '>' . esc_html($label) . '</a>';
|
||||
}
|
||||
echo '</nav></div><div class="aksrt-ban-toolbar__bottom"><ul class="subsubsub" aria-label="' . esc_attr__('处理状态筛选', 'aksrt') . '">';
|
||||
foreach (['pending' => __('待处理', 'aksrt'), 'resolved' => __('已处理 / 已通过', 'aksrt'), 'dismissed' => __('已驳回', 'aksrt'), '' => __('全部状态', 'aksrt')] as $key => $label) {
|
||||
if ($filters['type'] === 'report' && $key === 'resolved') $label = __('已处理', 'aksrt');
|
||||
if ($filters['type'] === 'appeal' && $key === 'resolved') $label = __('已通过', 'aksrt');
|
||||
echo '</nav></div><div class="jio-ban-toolbar__bottom"><ul class="subsubsub" aria-label="' . esc_attr__('处理状态筛选', 'jio') . '">';
|
||||
foreach (['pending' => __('待处理', 'jio'), 'resolved' => __('已处理 / 已通过', 'jio'), 'dismissed' => __('已驳回', 'jio'), '' => __('全部状态', 'jio')] as $key => $label) {
|
||||
if ($filters['type'] === 'report' && $key === 'resolved') $label = __('已处理', 'jio');
|
||||
if ($filters['type'] === 'appeal' && $key === 'resolved') $label = __('已通过', 'jio');
|
||||
echo '<li><a href="' . esc_url($this->ban_page_url(array_merge($filters, ['status' => $key]))) . '"' . ($key === $filters['status'] ? ' class="current" aria-current="page"' : '') . '>' . esc_html($label)
|
||||
. ' <span class="count">' . esc_html(number_format_i18n($counts[$key === '' ? 'all' : $key])) . '</span></a></li>';
|
||||
}
|
||||
echo '</ul><form method="get" action="' . esc_url(admin_url('users.php')) . '" class="aksrt-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="aksrt_user_ban">';
|
||||
echo '</ul><form method="get" action="' . esc_url(admin_url('users.php')) . '" class="jio-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="jio_user_ban">';
|
||||
foreach (['type', 'status', 'send_user', 'orderby', 'order'] as $key) {
|
||||
echo '<input type="hidden" name="' . esc_attr($key) . '" value="' . esc_attr((string) $filters[$key]) . '">';
|
||||
}
|
||||
echo '<label class="screen-reader-text" for="aksrt-ban-search">' . esc_html__('搜索用户或记录内容', 'aksrt') . '</label>';
|
||||
echo '<input type="search" id="aksrt-ban-search" name="s" value="' . esc_attr($filters['s']) . '" placeholder="' . esc_attr__('用户 / 目标 ID / 原因 / 申诉资料', 'aksrt') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('搜索', 'aksrt') . '</button>';
|
||||
echo '<label class="screen-reader-text" for="jio-ban-search">' . esc_html__('搜索用户或记录内容', 'jio') . '</label>';
|
||||
echo '<input type="search" id="jio-ban-search" name="s" value="' . esc_attr($filters['s']) . '" placeholder="' . esc_attr__('用户 / 目标 ID / 原因 / 申诉资料', 'jio') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('搜索', 'jio') . '</button>';
|
||||
if ($filters['s'] !== '' || $filters['send_user'] > 0) {
|
||||
echo '<a class="button-link" href="' . esc_url($this->ban_page_url(array_merge($filters, ['s' => '', 'send_user' => 0]))) . '">' . esc_html__('清除筛选', 'aksrt') . '</a>';
|
||||
echo '<a class="button-link" href="' . esc_url($this->ban_page_url(array_merge($filters, ['s' => '', 'send_user' => 0]))) . '">' . esc_html__('清除筛选', 'jio') . '</a>';
|
||||
}
|
||||
echo '</form></div>';
|
||||
if ($filters['send_user'] > 0) {
|
||||
$user = get_userdata($filters['send_user']);
|
||||
echo '<p class="aksrt-ban-filter-hint">' . esc_html(sprintf(__('当前仅显示 %s 提交的记录。', 'aksrt'), $user ? $user->display_name : '#' . $filters['send_user'])) . '</p>';
|
||||
echo '<p class="jio-ban-filter-hint">' . esc_html(sprintf(__('当前仅显示 %s 提交的记录。', 'jio'), $user ? $user->display_name : '#' . $filters['send_user'])) . '</p>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
private function render_ban_table(array $items, array $filters, int $page): void {
|
||||
echo '<table class="widefat fixed striped aksrt-ban-table"><caption class="screen-reader-text">' . esc_html__('举报与禁封申诉处理记录', 'aksrt') . '</caption><thead><tr>';
|
||||
foreach (['type' => __('类型 / 编号', 'aksrt'), 'user' => __('提交用户', 'aksrt'), 'summary' => __('事项摘要', 'aksrt'), 'status' => __('状态', 'aksrt'), 'time' => __('提交时间', 'aksrt'), 'updated' => __('更新时间', 'aksrt'), 'actions' => __('操作', 'aksrt')] as $key => $label) {
|
||||
echo '<table class="widefat fixed striped jio-ban-table"><caption class="screen-reader-text">' . esc_html__('举报与禁封申诉处理记录', 'jio') . '</caption><thead><tr>';
|
||||
foreach (['type' => __('类型 / 编号', 'jio'), 'user' => __('提交用户', 'jio'), 'summary' => __('事项摘要', 'jio'), 'status' => __('状态', 'jio'), 'time' => __('提交时间', 'jio'), 'updated' => __('更新时间', 'jio'), 'actions' => __('操作', 'jio')] as $key => $label) {
|
||||
$sortable = in_array($key, ['type', 'status', 'time', 'updated'], true);
|
||||
$active = $sortable && $filters['orderby'] === $key;
|
||||
echo '<th scope="col" class="column-' . esc_attr($key) . '"' . ($active ? ' aria-sort="' . ($filters['order'] === 'ASC' ? 'ascending' : 'descending') . '"' : '') . '>';
|
||||
if ($sortable) {
|
||||
$order = $active && $filters['order'] === 'DESC' ? 'ASC' : 'DESC';
|
||||
$title = sprintf($order === 'ASC' ? __('按%s升序排列', 'aksrt') : __('按%s降序排列', 'aksrt'), $label);
|
||||
echo '<a class="aksrt-ban-sort" href="' . esc_url($this->ban_page_url(array_merge($filters, ['orderby' => $key, 'order' => $order]))) . '" aria-label="' . esc_attr($title) . '">' . esc_html($label) . '<span aria-hidden="true">' . ($active ? ($filters['order'] === 'ASC' ? '↑' : '↓') : '↕') . '</span></a>';
|
||||
$title = sprintf($order === 'ASC' ? __('按%s升序排列', 'jio') : __('按%s降序排列', 'jio'), $label);
|
||||
echo '<a class="jio-ban-sort" href="' . esc_url($this->ban_page_url(array_merge($filters, ['orderby' => $key, 'order' => $order]))) . '" aria-label="' . esc_attr($title) . '">' . esc_html($label) . '<span aria-hidden="true">' . ($active ? ($filters['order'] === 'ASC' ? '↑' : '↓') : '↕') . '</span></a>';
|
||||
} else {
|
||||
echo esc_html($label);
|
||||
}
|
||||
@@ -102,8 +102,8 @@ trait UsersMenusBan {
|
||||
if ($items === []) {
|
||||
$filtered = $filters['s'] !== '' || $filters['send_user'] > 0;
|
||||
echo NativeUi::empty_row(7,
|
||||
!$filtered && $filters['status'] === 'pending' ? __('暂无待处理记录', 'aksrt') : __('没有符合条件的记录', 'aksrt'),
|
||||
$filtered ? __('请调整关键词、清除用户筛选或切换类型和状态。', 'aksrt') : __('可切换状态查看处理记录,新举报和申诉将在这里显示。', 'aksrt')
|
||||
!$filtered && $filters['status'] === 'pending' ? __('暂无待处理记录', 'jio') : __('没有符合条件的记录', 'jio'),
|
||||
$filtered ? __('请调整关键词、清除用户筛选或切换类型和状态。', 'jio') : __('可切换状态查看处理记录,新举报和申诉将在这里显示。', 'jio')
|
||||
);
|
||||
}
|
||||
foreach ($items as $row) $this->render_ban_row($row, $filters, $page);
|
||||
@@ -115,31 +115,31 @@ trait UsersMenusBan {
|
||||
$payload = $row['payload'];
|
||||
$key = $row['kind'] . '-' . $row['id'];
|
||||
$user_id = (int) $row['user_id'];
|
||||
echo '<tr><td class="column-type">' . NativeUi::badge($report ? __('用户举报', 'aksrt') : __('禁封申诉', 'aksrt'), $report ? 'info' : 'neutral', false);
|
||||
echo '<div class="aksrt-ban-sub">#' . (int) $row['id'] . '</div></td><td class="column-user">' . NativeUi::user_cell($user_id, __('用户已删除', 'aksrt'));
|
||||
echo '<tr><td class="column-type">' . NativeUi::badge($report ? __('用户举报', 'jio') : __('禁封申诉', 'jio'), $report ? 'info' : 'neutral', false);
|
||||
echo '<div class="jio-ban-sub">#' . (int) $row['id'] . '</div></td><td class="column-user">' . NativeUi::user_cell($user_id, __('用户已删除', 'jio'));
|
||||
if ($user_id > 0) {
|
||||
echo '<a class="aksrt-ban-user-filter" href="' . esc_url($this->ban_page_url(array_merge($filters, ['send_user' => $user_id]))) . '">' . esc_html__('筛选此用户', 'aksrt') . '</a>';
|
||||
echo '<a class="jio-ban-user-filter" href="' . esc_url($this->ban_page_url(array_merge($filters, ['send_user' => $user_id]))) . '">' . esc_html__('筛选此用户', 'jio') . '</a>';
|
||||
}
|
||||
echo '</td><td class="column-summary">';
|
||||
if ($report) {
|
||||
echo '<strong>' . $this->ban_target_link((string) ($payload['target_type'] ?? ''), (int) ($payload['target_id'] ?? 0)) . '</strong>';
|
||||
echo '<div class="aksrt-ban-summary">' . esc_html((string) ($payload['category'] ?? '')) . '</div>';
|
||||
echo '<div class="jio-ban-summary">' . esc_html((string) ($payload['category'] ?? '')) . '</div>';
|
||||
} else {
|
||||
echo '<strong>' . esc_html__('申请解除禁封', 'aksrt') . '</strong>';
|
||||
echo '<strong>' . esc_html__('申请解除禁封', 'jio') . '</strong>';
|
||||
}
|
||||
if ((string) ($payload['reason'] ?? '') !== '') echo '<div class="aksrt-ban-summary">' . esc_html((string) $payload['reason']) . '</div>';
|
||||
if ((string) ($payload['reason'] ?? '') !== '') echo '<div class="jio-ban-summary">' . esc_html((string) $payload['reason']) . '</div>';
|
||||
$this->render_ban_detail_template($row, $filters, $page);
|
||||
echo '</td><td class="column-status">' . $this->ban_status_badge($row) . '</td>';
|
||||
echo '<td class="column-time">' . esc_html($this->ban_time((string) $row['time'])) . '</td>';
|
||||
echo '<td class="column-updated">' . esc_html($this->ban_time((string) $row['updated'])) . '</td>';
|
||||
echo '<td class="column-actions"><button type="button" class="button' . ($row['status'] === 'pending' ? ' button-primary' : '') . '" data-aksrt-ban-view="' . esc_attr($key) . '" aria-haspopup="dialog" aria-controls="aksrt-ban-dialog">'
|
||||
. esc_html($row['status'] === 'pending' ? __('审核', 'aksrt') : __('查看详情', 'aksrt')) . '</button></td></tr>';
|
||||
echo '<td class="column-actions"><button type="button" class="button' . ($row['status'] === 'pending' ? ' button-primary' : '') . '" data-jio-ban-view="' . esc_attr($key) . '" aria-haspopup="dialog" aria-controls="jio-ban-dialog">'
|
||||
. esc_html($row['status'] === 'pending' ? __('审核', 'jio') : __('查看详情', 'jio')) . '</button></td></tr>';
|
||||
}
|
||||
|
||||
private function ban_status_badge(array $row): string {
|
||||
if ($row['status'] === 'pending') return NativeUi::badge($row['raw_status'] === 'reviewing' ? __('审核中', 'aksrt') : __('待处理', 'aksrt'), 'warning');
|
||||
if ($row['status'] === 'resolved') return NativeUi::badge($row['kind'] === 'report' ? __('已处理', 'aksrt') : __('已通过', 'aksrt'), 'success');
|
||||
return NativeUi::badge(__('已驳回', 'aksrt'), 'danger');
|
||||
if ($row['status'] === 'pending') return NativeUi::badge($row['raw_status'] === 'reviewing' ? __('审核中', 'jio') : __('待处理', 'jio'), 'warning');
|
||||
if ($row['status'] === 'resolved') return NativeUi::badge($row['kind'] === 'report' ? __('已处理', 'jio') : __('已通过', 'jio'), 'success');
|
||||
return NativeUi::badge(__('已驳回', 'jio'), 'danger');
|
||||
}
|
||||
|
||||
private function ban_time(string $time): string {
|
||||
@@ -147,12 +147,12 @@ trait UsersMenusBan {
|
||||
}
|
||||
|
||||
private function ban_user_home(int $user_id): string {
|
||||
return function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url($user_id) : get_author_posts_url($user_id);
|
||||
return function_exists('jio_get_user_home_url') ? jio_get_user_home_url($user_id) : get_author_posts_url($user_id);
|
||||
}
|
||||
|
||||
private function ban_target_link(string $type, int $id): string {
|
||||
$labels = ['post' => __('文章', 'aksrt'), 'comment' => __('评论', 'aksrt'), 'user' => __('用户', 'aksrt')];
|
||||
$label = ($labels[$type] ?? __('目标', 'aksrt')) . ' #' . $id;
|
||||
$labels = ['post' => __('文章', 'jio'), 'comment' => __('评论', 'jio'), 'user' => __('用户', 'jio')];
|
||||
$label = ($labels[$type] ?? __('目标', 'jio')) . ' #' . $id;
|
||||
$url = '';
|
||||
if ($type === 'post' && get_post($id)) {
|
||||
$label = get_the_title($id) ?: $label;
|
||||
@@ -163,7 +163,7 @@ trait UsersMenusBan {
|
||||
$label = $user->display_name . ' #' . $id;
|
||||
$url = $this->ban_user_home($id);
|
||||
}
|
||||
return $url !== '' ? '<a href="' . esc_url($url) . '" target="_blank" rel="noopener noreferrer">' . esc_html($label) . '</a>' : esc_html($label . ' ' . __('(目标已不可用)', 'aksrt'));
|
||||
return $url !== '' ? '<a href="' . esc_url($url) . '" target="_blank" rel="noopener noreferrer">' . esc_html($label) . '</a>' : esc_html($label . ' ' . __('(目标已不可用)', 'jio'));
|
||||
}
|
||||
|
||||
private function cache_ban_targets(array $items): void {
|
||||
@@ -194,7 +194,7 @@ trait UsersMenusBan {
|
||||
global $wpdb;
|
||||
$placeholders = implode(',', array_fill(0, count($ids), '%d'));
|
||||
$rows = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT user_id, type, scope, reason, created_at, expires_at FROM {$wpdb->prefix}aksrt_user_restriction WHERE user_id IN ({$placeholders}) AND (expires_at IS NULL OR expires_at > %s) ORDER BY created_at DESC",
|
||||
"SELECT user_id, type, scope, reason, created_at, expires_at FROM {$wpdb->prefix}jio_user_restriction WHERE user_id IN ({$placeholders}) AND (expires_at IS NULL OR expires_at > %s) ORDER BY created_at DESC",
|
||||
array_merge($ids, [current_time('mysql', true)])
|
||||
), ARRAY_A);
|
||||
foreach ((array) $rows as $row) $this->ban_restrictions[(int) $row['user_id']][] = $row;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -11,87 +11,87 @@ trait UsersMenusBanActions {
|
||||
|
||||
public function handle_update_report(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
wp_die(esc_html__('权限不足', 'jio'));
|
||||
}
|
||||
|
||||
$report_id = absint($this->ban_post_value('report_id'));
|
||||
$new_status = sanitize_key($this->ban_post_value('new_status'));
|
||||
$note = trim(sanitize_textarea_field($this->ban_post_value('note')));
|
||||
check_admin_referer('aksrt_users_update_report_' . $report_id);
|
||||
check_admin_referer('jio_users_update_report_' . $report_id);
|
||||
|
||||
if ($report_id <= 0 || !in_array($new_status, ['resolved', 'dismissed'], true)) {
|
||||
$this->redirect_ban_action('report_failed', __('举报处理请求无效。', 'aksrt'));
|
||||
$this->redirect_ban_action('report_failed', __('举报处理请求无效。', 'jio'));
|
||||
}
|
||||
if ($new_status === 'dismissed' && $note === '') {
|
||||
$this->redirect_ban_action('report_failed', __('驳回举报时必须填写处理说明。', 'aksrt'));
|
||||
$this->redirect_ban_action('report_failed', __('驳回举报时必须填写处理说明。', 'jio'));
|
||||
}
|
||||
|
||||
$row = $this->find_ban_request('report', $report_id);
|
||||
if (!$this->ban_request_is_current($row, 'pending')) {
|
||||
$this->redirect_ban_action('report_failed', __('举报记录已变化或已处理,请刷新后重试。', 'aksrt'));
|
||||
$this->redirect_ban_action('report_failed', __('举报记录已变化或已处理,请刷新后重试。', 'jio'));
|
||||
}
|
||||
if (!class_exists('\\AKSRT\\Messages\\ReportCenter')) {
|
||||
$this->redirect_ban_action('report_failed', __('举报处理服务不可用。', 'aksrt'));
|
||||
if (!class_exists('\\Jio\\Messages\\ReportCenter')) {
|
||||
$this->redirect_ban_action('report_failed', __('举报处理服务不可用。', 'jio'));
|
||||
}
|
||||
|
||||
$ok = \AKSRT\Messages\ReportCenter::get_instance()->update_status($report_id, $new_status, $note);
|
||||
$ok = \Jio\Messages\ReportCenter::get_instance()->update_status($report_id, $new_status, $note);
|
||||
$this->redirect_ban_action(
|
||||
$ok ? 'report_updated' : 'report_failed',
|
||||
$ok ? '' : __('举报状态更新失败,请稍后重试。', 'aksrt')
|
||||
$ok ? '' : __('举报状态更新失败,请稍后重试。', 'jio')
|
||||
);
|
||||
}
|
||||
|
||||
public function handle_process_appeal(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
wp_die(esc_html__('权限不足', 'jio'));
|
||||
}
|
||||
|
||||
$user_id = absint($this->ban_post_value('user_id'));
|
||||
$decision = sanitize_key($this->ban_post_value('decision'));
|
||||
$note = trim(sanitize_textarea_field($this->ban_post_value('note')));
|
||||
check_admin_referer('aksrt_users_process_appeal_' . $user_id);
|
||||
check_admin_referer('jio_users_process_appeal_' . $user_id);
|
||||
|
||||
if ($user_id <= 0 || !in_array($decision, ['approve', 'reject'], true)) {
|
||||
$this->redirect_ban_action('appeal_failed', __('申诉处理请求无效。', 'aksrt'));
|
||||
$this->redirect_ban_action('appeal_failed', __('申诉处理请求无效。', 'jio'));
|
||||
}
|
||||
if ($decision === 'reject' && $note === '') {
|
||||
$this->redirect_ban_action('appeal_failed', __('驳回申诉时必须填写拒绝原因。', 'aksrt'));
|
||||
$this->redirect_ban_action('appeal_failed', __('驳回申诉时必须填写拒绝原因。', 'jio'));
|
||||
}
|
||||
|
||||
$row = $this->find_ban_request('appeal', $user_id);
|
||||
if (!$this->ban_request_is_current($row, 'pending')) {
|
||||
$this->redirect_ban_action('appeal_failed', __('申诉记录已变化或已处理,请刷新后重试。', 'aksrt'));
|
||||
$this->redirect_ban_action('appeal_failed', __('申诉记录已变化或已处理,请刷新后重试。', 'jio'));
|
||||
}
|
||||
|
||||
$user = get_userdata($user_id);
|
||||
if (!($user instanceof \WP_User)) {
|
||||
$this->redirect_ban_action('appeal_failed', __('申诉用户不存在,无法处理。', 'aksrt'));
|
||||
$this->redirect_ban_action('appeal_failed', __('申诉用户不存在,无法处理。', 'jio'));
|
||||
}
|
||||
|
||||
if ($decision === 'approve') {
|
||||
if (!class_exists('\\AKSRT\\User\\Restriction')) {
|
||||
$this->redirect_ban_action('appeal_failed', __('用户限制服务不可用,无法通过申诉。', 'aksrt'));
|
||||
if (!class_exists('\\Jio\\User\\Restriction')) {
|
||||
$this->redirect_ban_action('appeal_failed', __('用户限制服务不可用,无法通过申诉。', 'jio'));
|
||||
}
|
||||
global $wpdb;
|
||||
$restriction_service = \AKSRT\User\Restriction::get_instance();
|
||||
$restriction_service = \Jio\User\Restriction::get_instance();
|
||||
$restriction_service->lift_user_restriction($user_id);
|
||||
$lift_error = (string) $wpdb->last_error;
|
||||
if ($lift_error !== '') {
|
||||
$this->redirect_ban_action('appeal_failed', __('解除限制时发生数据库错误,申诉未通过。', 'aksrt'));
|
||||
$this->redirect_ban_action('appeal_failed', __('解除限制时发生数据库错误,申诉未通过。', 'jio'));
|
||||
}
|
||||
|
||||
$active_restrictions = $restriction_service->get_user_restrictions($user_id, true);
|
||||
$check_error = (string) $wpdb->last_error;
|
||||
if ($check_error !== '') {
|
||||
$this->redirect_ban_action('appeal_failed', __('无法核实限制是否解除,限制可能已解除,请检查用户当前状态。', 'aksrt'));
|
||||
$this->redirect_ban_action('appeal_failed', __('无法核实限制是否解除,限制可能已解除,请检查用户当前状态。', 'jio'));
|
||||
}
|
||||
foreach ($active_restrictions as $restriction) {
|
||||
if (in_array($restriction['type'] ?? '', [
|
||||
\AKSRT\User\Restriction::TYPE_BAN,
|
||||
\AKSRT\User\Restriction::TYPE_SUSPEND,
|
||||
\AKSRT\User\Restriction::TYPE_MUTE,
|
||||
\Jio\User\Restriction::TYPE_BAN,
|
||||
\Jio\User\Restriction::TYPE_SUSPEND,
|
||||
\Jio\User\Restriction::TYPE_MUTE,
|
||||
], true)) {
|
||||
$this->redirect_ban_action('appeal_failed', __('仍有生效限制,申诉未通过。', 'aksrt'));
|
||||
$this->redirect_ban_action('appeal_failed', __('仍有生效限制,申诉未通过。', 'jio'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,58 +103,58 @@ trait UsersMenusBanActions {
|
||||
$payload['handled_by'] = get_current_user_id();
|
||||
$payload['decision'] = $raw_status;
|
||||
|
||||
$history = get_user_meta($user_id, \AKSRT\User\BanAppeal::META_HISTORY, true);
|
||||
$history = get_user_meta($user_id, \Jio\User\BanAppeal::META_HISTORY, true);
|
||||
$history = is_array($history) ? $history : [];
|
||||
$history[] = $payload;
|
||||
$history = array_slice($history, -30);
|
||||
|
||||
$old_payload = $row['payload'];
|
||||
$old_history = get_user_meta($user_id, \AKSRT\User\BanAppeal::META_HISTORY, true);
|
||||
$payload_updated = update_user_meta($user_id, \AKSRT\User\BanAppeal::META_PAYLOAD, $payload, $old_payload);
|
||||
$old_history = get_user_meta($user_id, \Jio\User\BanAppeal::META_HISTORY, true);
|
||||
$payload_updated = update_user_meta($user_id, \Jio\User\BanAppeal::META_PAYLOAD, $payload, $old_payload);
|
||||
if ($payload_updated === false) {
|
||||
$this->redirect_ban_action('appeal_failed', $this->appeal_metadata_error(__('申诉处理记录保存失败。', 'aksrt'), $decision));
|
||||
$this->redirect_ban_action('appeal_failed', $this->appeal_metadata_error(__('申诉处理记录保存失败。', 'jio'), $decision));
|
||||
}
|
||||
|
||||
$status_updated = update_user_meta($user_id, \AKSRT\User\BanAppeal::META_STATUS, $raw_status, 'pending');
|
||||
$status_updated = update_user_meta($user_id, \Jio\User\BanAppeal::META_STATUS, $raw_status, 'pending');
|
||||
if ($status_updated === false) {
|
||||
update_user_meta($user_id, \AKSRT\User\BanAppeal::META_PAYLOAD, $old_payload);
|
||||
$this->redirect_ban_action('appeal_failed', $this->appeal_metadata_error(__('申诉状态保存失败。', 'aksrt'), $decision));
|
||||
update_user_meta($user_id, \Jio\User\BanAppeal::META_PAYLOAD, $old_payload);
|
||||
$this->redirect_ban_action('appeal_failed', $this->appeal_metadata_error(__('申诉状态保存失败。', 'jio'), $decision));
|
||||
}
|
||||
|
||||
$history_updated = update_user_meta($user_id, \AKSRT\User\BanAppeal::META_HISTORY, $history);
|
||||
$history_updated = update_user_meta($user_id, \Jio\User\BanAppeal::META_HISTORY, $history);
|
||||
if ($history_updated === false) {
|
||||
update_user_meta($user_id, \AKSRT\User\BanAppeal::META_STATUS, 'pending');
|
||||
update_user_meta($user_id, \AKSRT\User\BanAppeal::META_PAYLOAD, $old_payload);
|
||||
update_user_meta($user_id, \Jio\User\BanAppeal::META_STATUS, 'pending');
|
||||
update_user_meta($user_id, \Jio\User\BanAppeal::META_PAYLOAD, $old_payload);
|
||||
if ($old_history === '' || $old_history === null) {
|
||||
delete_user_meta($user_id, \AKSRT\User\BanAppeal::META_HISTORY);
|
||||
delete_user_meta($user_id, \Jio\User\BanAppeal::META_HISTORY);
|
||||
} else {
|
||||
update_user_meta($user_id, \AKSRT\User\BanAppeal::META_HISTORY, $old_history);
|
||||
update_user_meta($user_id, \Jio\User\BanAppeal::META_HISTORY, $old_history);
|
||||
}
|
||||
$this->redirect_ban_action('appeal_failed', $this->appeal_metadata_error(__('申诉历史保存失败。', 'aksrt'), $decision));
|
||||
$this->redirect_ban_action('appeal_failed', $this->appeal_metadata_error(__('申诉历史保存失败。', 'jio'), $decision));
|
||||
}
|
||||
|
||||
if (class_exists('\\AKSRT\\Messages\\Message')) {
|
||||
if (class_exists('\\Jio\\Messages\\Message')) {
|
||||
$message = $decision === 'approve'
|
||||
? __('你的禁封申诉已通过,限制已解除。', 'aksrt')
|
||||
: __('你的禁封申诉未通过,可修改后重新提交。', 'aksrt');
|
||||
? __('你的禁封申诉已通过,限制已解除。', 'jio')
|
||||
: __('你的禁封申诉未通过,可修改后重新提交。', 'jio');
|
||||
if ($note !== '') {
|
||||
$message .= ' ' . sprintf(__('备注:%s', 'aksrt'), $note);
|
||||
$message .= ' ' . sprintf(__('备注:%s', 'jio'), $note);
|
||||
}
|
||||
\AKSRT\Messages\Message::get_instance()->send(
|
||||
\Jio\Messages\Message::get_instance()->send(
|
||||
$user_id,
|
||||
'ban_appeal',
|
||||
$message,
|
||||
['title' => $decision === 'approve' ? __('申诉通过', 'aksrt') : __('申诉驳回', 'aksrt')]
|
||||
['title' => $decision === 'approve' ? __('申诉通过', 'jio') : __('申诉驳回', 'jio')]
|
||||
);
|
||||
}
|
||||
|
||||
do_action('aksrt_ban_appeal_processed', $user_id, $decision, $note);
|
||||
do_action('jio_ban_appeal_processed', $user_id, $decision, $note);
|
||||
$this->redirect_ban_action($decision === 'approve' ? 'appeal_approved' : 'appeal_rejected');
|
||||
}
|
||||
|
||||
private function appeal_metadata_error(string $message, string $decision): string {
|
||||
if ($decision === 'approve') {
|
||||
return $message . ' ' . __('限制可能已解除,请核对用户当前状态。', 'aksrt');
|
||||
return $message . ' ' . __('限制可能已解除,请核对用户当前状态。', 'jio');
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
@@ -193,8 +193,8 @@ trait UsersMenusBanActions {
|
||||
'paged' => max(1, absint($this->ban_post_value('paged', '1'))),
|
||||
];
|
||||
if ($error !== '') {
|
||||
$extra['aksrt_ban_error'] = $error;
|
||||
$extra['jio_ban_error'] = $error;
|
||||
}
|
||||
$this->redirect('aksrt_user_ban', $message, $extra);
|
||||
$this->redirect('jio_user_ban', $message, $extra);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/** Report and ban appeal detail/review dialog templates. @package AKSRT */
|
||||
/** Report and ban appeal detail/review dialog templates. @package Jio */
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\User\Restriction;
|
||||
use Jio\User\Restriction;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -13,24 +13,24 @@ trait UsersMenusBanDialogs {
|
||||
$pending = $row['status'] === 'pending';
|
||||
$report = $row['kind'] === 'report';
|
||||
$key = $row['kind'] . '-' . $row['id'];
|
||||
echo '<template id="aksrt-ban-detail-' . esc_attr($key) . '">';
|
||||
echo '<template id="jio-ban-detail-' . esc_attr($key) . '">';
|
||||
if ($pending) {
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-ban-dialog__frame" data-aksrt-ban-review>';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-ban-dialog__frame" data-jio-ban-review>';
|
||||
$this->render_ban_context($row, $filters, $page);
|
||||
} else {
|
||||
echo '<div class="aksrt-ban-dialog__frame">';
|
||||
echo '<div class="jio-ban-dialog__frame">';
|
||||
}
|
||||
$title = $report
|
||||
? ($pending ? __('审核用户举报', 'aksrt') : __('用户举报详情', 'aksrt'))
|
||||
: ($pending ? __('审核禁封申诉', 'aksrt') : __('禁封申诉详情', 'aksrt'));
|
||||
echo '<div class="aksrt-ban-dialog__head"><div><h2 id="aksrt-ban-dialog-title" tabindex="-1" autofocus>' . esc_html($title) . '</h2>';
|
||||
echo '<p id="aksrt-ban-dialog-description">' . esc_html($pending ? __('先核对原始资料,再选择处理结果;提交后将通知用户。', 'aksrt') : __('该记录已处理,以下资料和结果仅供查看。', 'aksrt')) . '</p></div>';
|
||||
echo '<button type="button" class="aksrt-ban-dialog__close" data-aksrt-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></div>';
|
||||
echo '<div class="aksrt-ban-dialog__body"><div class="aksrt-ban-applicant">' . NativeUi::user_cell((int) $row['user_id'], __('用户已删除', 'aksrt')) . $this->ban_status_badge($row) . '</div>';
|
||||
echo '<dl class="aksrt-ban-meta">';
|
||||
$this->render_ban_meta_item(__('记录编号', 'aksrt'), ($report ? __('举报', 'aksrt') : __('申诉', 'aksrt')) . ' #' . $row['id']);
|
||||
$this->render_ban_meta_item(__('提交时间', 'aksrt'), $this->ban_time((string) $row['time']));
|
||||
$this->render_ban_meta_item(__('更新时间', 'aksrt'), $this->ban_time((string) $row['updated']));
|
||||
? ($pending ? __('审核用户举报', 'jio') : __('用户举报详情', 'jio'))
|
||||
: ($pending ? __('审核禁封申诉', 'jio') : __('禁封申诉详情', 'jio'));
|
||||
echo '<div class="jio-ban-dialog__head"><div><h2 id="jio-ban-dialog-title" tabindex="-1" autofocus>' . esc_html($title) . '</h2>';
|
||||
echo '<p id="jio-ban-dialog-description">' . esc_html($pending ? __('先核对原始资料,再选择处理结果;提交后将通知用户。', 'jio') : __('该记录已处理,以下资料和结果仅供查看。', 'jio')) . '</p></div>';
|
||||
echo '<button type="button" class="jio-ban-dialog__close" data-jio-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></div>';
|
||||
echo '<div class="jio-ban-dialog__body"><div class="jio-ban-applicant">' . NativeUi::user_cell((int) $row['user_id'], __('用户已删除', 'jio')) . $this->ban_status_badge($row) . '</div>';
|
||||
echo '<dl class="jio-ban-meta">';
|
||||
$this->render_ban_meta_item(__('记录编号', 'jio'), ($report ? __('举报', 'jio') : __('申诉', 'jio')) . ' #' . $row['id']);
|
||||
$this->render_ban_meta_item(__('提交时间', 'jio'), $this->ban_time((string) $row['time']));
|
||||
$this->render_ban_meta_item(__('更新时间', 'jio'), $this->ban_time((string) $row['updated']));
|
||||
echo '</dl>';
|
||||
if ($report) {
|
||||
$this->render_ban_report_details($row);
|
||||
@@ -39,20 +39,20 @@ trait UsersMenusBanDialogs {
|
||||
}
|
||||
$this->render_ban_review_history($row);
|
||||
if ($pending) $this->render_ban_review_fields($report);
|
||||
echo '</div><div class="aksrt-ban-dialog__foot"><button type="button" class="button" data-aksrt-dialog-close>' . esc_html($pending ? __('取消', 'aksrt') : __('关闭', 'aksrt')) . '</button>';
|
||||
echo '</div><div class="jio-ban-dialog__foot"><button type="button" class="button" data-jio-dialog-close>' . esc_html($pending ? __('取消', 'jio') : __('关闭', 'jio')) . '</button>';
|
||||
if ($pending) {
|
||||
echo '<button type="submit" class="button button-primary" data-aksrt-ban-submit disabled'
|
||||
. ' data-idle-label="' . esc_attr__('请选择处理结果', 'aksrt') . '"'
|
||||
. ' data-approve-label="' . esc_attr($report ? __('确认标记已处理', 'aksrt') : __('确认通过并解除限制', 'aksrt')) . '"'
|
||||
. ' data-reject-label="' . esc_attr__('确认驳回', 'aksrt') . '"'
|
||||
. ' data-busy-label="' . esc_attr__('正在提交…', 'aksrt') . '">' . esc_html__('请选择处理结果', 'aksrt') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary" data-jio-ban-submit disabled'
|
||||
. ' data-idle-label="' . esc_attr__('请选择处理结果', 'jio') . '"'
|
||||
. ' data-approve-label="' . esc_attr($report ? __('确认标记已处理', 'jio') : __('确认通过并解除限制', 'jio')) . '"'
|
||||
. ' data-reject-label="' . esc_attr__('确认驳回', 'jio') . '"'
|
||||
. ' data-busy-label="' . esc_attr__('正在提交…', 'jio') . '">' . esc_html__('请选择处理结果', 'jio') . '</button>';
|
||||
}
|
||||
echo '</div>' . ($pending ? '</form>' : '</div>') . '</template>';
|
||||
}
|
||||
|
||||
private function render_ban_context(array $row, array $filters, int $page): void {
|
||||
$report = $row['kind'] === 'report';
|
||||
$action = $report ? 'aksrt_users_update_report' : 'aksrt_users_process_appeal';
|
||||
$action = $report ? 'jio_users_update_report' : 'jio_users_process_appeal';
|
||||
$fields = [
|
||||
'action' => $action,
|
||||
$report ? 'report_id' : 'user_id' => (int) $row['id'],
|
||||
@@ -76,7 +76,7 @@ trait UsersMenusBanDialogs {
|
||||
}
|
||||
|
||||
private function render_ban_text_section(string $title, string $text): void {
|
||||
echo '<section class="aksrt-ban-section"><h3>' . esc_html($title) . '</h3><div class="aksrt-ban-copy">';
|
||||
echo '<section class="jio-ban-section"><h3>' . esc_html($title) . '</h3><div class="jio-ban-copy">';
|
||||
// Evidence URLs remain links: opening a case must not load reporter-supplied remote images.
|
||||
$parts = preg_split('~(https?://[^\s<>"\x27]+)~u', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
foreach ($parts ?: [$text] as $index => $part) {
|
||||
@@ -84,7 +84,7 @@ trait UsersMenusBanDialogs {
|
||||
? '<a href="' . esc_url($part, ['http', 'https']) . '" target="_blank" rel="noopener noreferrer">' . esc_html($part) . '</a>'
|
||||
: esc_html($part !== '' ? $part : '');
|
||||
}
|
||||
if ($text === '') echo esc_html__('未填写', 'aksrt');
|
||||
if ($text === '') echo esc_html__('未填写', 'jio');
|
||||
echo '</div></section>';
|
||||
}
|
||||
|
||||
@@ -92,59 +92,59 @@ trait UsersMenusBanDialogs {
|
||||
$payload = $row['payload'];
|
||||
$type = (string) ($payload['target_type'] ?? '');
|
||||
$id = (int) ($payload['target_id'] ?? 0);
|
||||
echo '<section class="aksrt-ban-section"><h3>' . esc_html__('举报对象', 'aksrt') . '</h3><div class="aksrt-ban-target">' . $this->ban_target_link($type, $id) . '</div></section>';
|
||||
if ((string) ($payload['category'] ?? '') !== '') $this->render_ban_text_section(__('举报分类', 'aksrt'), (string) $payload['category']);
|
||||
$this->render_ban_text_section(__('举报内容与举证', 'aksrt'), (string) ($payload['reason'] ?? ''));
|
||||
echo '<section class="jio-ban-section"><h3>' . esc_html__('举报对象', 'jio') . '</h3><div class="jio-ban-target">' . $this->ban_target_link($type, $id) . '</div></section>';
|
||||
if ((string) ($payload['category'] ?? '') !== '') $this->render_ban_text_section(__('举报分类', 'jio'), (string) $payload['category']);
|
||||
$this->render_ban_text_section(__('举报内容与举证', 'jio'), (string) ($payload['reason'] ?? ''));
|
||||
if ($type === 'user') {
|
||||
$this->render_ban_restriction_section($id);
|
||||
if (get_userdata($id)) {
|
||||
echo '<p class="aksrt-ban-profile-link"><a href="' . esc_url($this->ban_user_home($id)) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('查看用户主页并管理禁封(新窗口)', 'aksrt') . '</a></p>';
|
||||
echo '<p class="jio-ban-profile-link"><a href="' . esc_url($this->ban_user_home($id)) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('查看用户主页并管理禁封(新窗口)', 'jio') . '</a></p>';
|
||||
}
|
||||
}
|
||||
if ($row['status'] === 'pending') {
|
||||
echo '<p class="aksrt-native-callout">' . esc_html__('本次审核仅记录举报处理结果,不会自动禁封用户或删除内容。请先核实举证并完成必要处置。', 'aksrt') . '</p>';
|
||||
echo '<p class="jio-native-callout">' . esc_html__('本次审核仅记录举报处理结果,不会自动禁封用户或删除内容。请先核实举证并完成必要处置。', 'jio') . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
private function render_ban_appeal_details(array $row): void {
|
||||
$payload = $row['payload'];
|
||||
$this->render_ban_restriction_section((int) $row['user_id']);
|
||||
if (!empty($payload['reason_key'])) $this->render_ban_text_section(__('申诉类型', 'aksrt'), (string) $payload['reason_key']);
|
||||
if (!empty($payload['reason_key'])) $this->render_ban_text_section(__('申诉类型', 'jio'), (string) $payload['reason_key']);
|
||||
$data = isset($payload['data']) && is_array($payload['data']) ? $payload['data'] : [];
|
||||
if ($data !== []) {
|
||||
echo '<section class="aksrt-ban-section"><h3>' . esc_html__('申诉资料', 'aksrt') . '</h3><dl class="aksrt-ban-materials">';
|
||||
echo '<section class="jio-ban-section"><h3>' . esc_html__('申诉资料', 'jio') . '</h3><dl class="jio-ban-materials">';
|
||||
foreach ($data as $label => $value) {
|
||||
if (is_scalar($value)) $this->render_ban_meta_item((string) $label, (string) $value);
|
||||
}
|
||||
echo '</dl></section>';
|
||||
}
|
||||
$reason = (string) ($payload['reason'] ?? '');
|
||||
if ($data === [] || !in_array($reason, $data, true)) $this->render_ban_text_section(__('申诉说明', 'aksrt'), $reason);
|
||||
if ($data === [] || !in_array($reason, $data, true)) $this->render_ban_text_section(__('申诉说明', 'jio'), $reason);
|
||||
if (get_userdata((int) $row['user_id'])) {
|
||||
echo '<p class="aksrt-ban-profile-link"><a href="' . esc_url($this->ban_user_home((int) $row['user_id'])) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('查看申诉用户主页(新窗口)', 'aksrt') . '</a></p>';
|
||||
echo '<p class="jio-ban-profile-link"><a href="' . esc_url($this->ban_user_home((int) $row['user_id'])) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('查看申诉用户主页(新窗口)', 'jio') . '</a></p>';
|
||||
}
|
||||
}
|
||||
|
||||
private function render_ban_restriction_section(int $user_id): void {
|
||||
echo '<section class="aksrt-ban-section"><h3>' . esc_html__('当前限制', 'aksrt') . '</h3>';
|
||||
echo '<section class="jio-ban-section"><h3>' . esc_html__('当前限制', 'jio') . '</h3>';
|
||||
if (!class_exists(Restriction::class)) {
|
||||
echo '<p class="aksrt-native-callout is-warning">' . esc_html__('限制服务不可用,暂时无法核对当前状态。', 'aksrt') . '</p></section>';
|
||||
echo '<p class="jio-native-callout is-warning">' . esc_html__('限制服务不可用,暂时无法核对当前状态。', 'jio') . '</p></section>';
|
||||
return;
|
||||
}
|
||||
$rows = $this->ban_restrictions[$user_id] ?? [];
|
||||
if ($rows === []) {
|
||||
echo '<p class="aksrt-ban-normal">' . esc_html__('当前无生效限制。此处仅展示当前状态,不会自动更改处理结果。', 'aksrt') . '</p></section>';
|
||||
echo '<p class="jio-ban-normal">' . esc_html__('当前无生效限制。此处仅展示当前状态,不会自动更改处理结果。', 'jio') . '</p></section>';
|
||||
return;
|
||||
}
|
||||
$labels = [Restriction::TYPE_WARN => __('警告', 'aksrt'), Restriction::TYPE_MUTE => __('禁言', 'aksrt'), Restriction::TYPE_SUSPEND => __('停权', 'aksrt'), Restriction::TYPE_BAN => __('封禁', 'aksrt')];
|
||||
echo '<ul class="aksrt-ban-restrictions">';
|
||||
$labels = [Restriction::TYPE_WARN => __('警告', 'jio'), Restriction::TYPE_MUTE => __('禁言', 'jio'), Restriction::TYPE_SUSPEND => __('停权', 'jio'), Restriction::TYPE_BAN => __('封禁', 'jio')];
|
||||
echo '<ul class="jio-ban-restrictions">';
|
||||
foreach ($rows as $restriction) {
|
||||
echo '<li><div class="aksrt-ban-restriction__title">' . NativeUi::badge($labels[$restriction['type']] ?? (string) $restriction['type'], 'warning', false)
|
||||
. '<span>' . esc_html(($restriction['scope'] ?? '') === Restriction::SCOPE_COMMENT ? __('评论范围', 'aksrt') : __('全站范围', 'aksrt')) . '</span></div>';
|
||||
echo '<div class="aksrt-ban-copy">' . esc_html((string) ($restriction['reason'] ?? '')) . '</div>';
|
||||
echo '<li><div class="jio-ban-restriction__title">' . NativeUi::badge($labels[$restriction['type']] ?? (string) $restriction['type'], 'warning', false)
|
||||
. '<span>' . esc_html(($restriction['scope'] ?? '') === Restriction::SCOPE_COMMENT ? __('评论范围', 'jio') : __('全站范围', 'jio')) . '</span></div>';
|
||||
echo '<div class="jio-ban-copy">' . esc_html((string) ($restriction['reason'] ?? '')) . '</div>';
|
||||
$start = !empty($restriction['created_at']) ? get_date_from_gmt((string) $restriction['created_at'], 'Y-m-d H:i') : '—';
|
||||
$end = !empty($restriction['expires_at']) ? get_date_from_gmt((string) $restriction['expires_at'], 'Y-m-d H:i') : __('永久', 'aksrt');
|
||||
echo '<div class="aksrt-ban-sub">' . esc_html(sprintf(__('开始:%1$s;结束:%2$s', 'aksrt'), $start, $end)) . '</div></li>';
|
||||
$end = !empty($restriction['expires_at']) ? get_date_from_gmt((string) $restriction['expires_at'], 'Y-m-d H:i') : __('永久', 'jio');
|
||||
echo '<div class="jio-ban-sub">' . esc_html(sprintf(__('开始:%1$s;结束:%2$s', 'jio'), $start, $end)) . '</div></li>';
|
||||
}
|
||||
echo '</ul></section>';
|
||||
}
|
||||
@@ -156,30 +156,30 @@ trait UsersMenusBanDialogs {
|
||||
$notes[] = ['note' => $payload['admin_note'] ?? '', 'operator' => $payload['handled_by'] ?? 0, 'timestamp' => $payload['handled_at'], 'status' => $payload['decision'] ?? $row['raw_status']];
|
||||
}
|
||||
if ($notes === []) return;
|
||||
$labels = ['pending' => __('待处理', 'aksrt'), 'reviewing' => __('审核中', 'aksrt'), 'resolved' => __('已处理', 'aksrt'), 'dismissed' => __('已驳回', 'aksrt'), 'approved' => __('已通过', 'aksrt'), 'rejected' => __('已驳回', 'aksrt')];
|
||||
echo '<section class="aksrt-ban-section"><h3>' . esc_html__('处理记录', 'aksrt') . '</h3><ol class="aksrt-ban-history">';
|
||||
$labels = ['pending' => __('待处理', 'jio'), 'reviewing' => __('审核中', 'jio'), 'resolved' => __('已处理', 'jio'), 'dismissed' => __('已驳回', 'jio'), 'approved' => __('已通过', 'jio'), 'rejected' => __('已驳回', 'jio')];
|
||||
echo '<section class="jio-ban-section"><h3>' . esc_html__('处理记录', 'jio') . '</h3><ol class="jio-ban-history">';
|
||||
foreach ($notes as $note) {
|
||||
if (!is_array($note)) continue;
|
||||
$operator = get_userdata((int) ($note['operator'] ?? 0));
|
||||
echo '<li><div class="aksrt-ban-history__meta"><strong>' . esc_html($labels[$note['status'] ?? ''] ?? (string) ($note['status'] ?? '')) . '</strong><span>'
|
||||
. esc_html($operator ? $operator->display_name : __('管理员', 'aksrt')) . '</span><time>' . esc_html((string) ($note['timestamp'] ?? '')) . '</time></div>';
|
||||
echo '<div class="aksrt-ban-copy">' . esc_html((string) ($note['note'] ?? '') !== '' ? (string) $note['note'] : __('未填写处理留言', 'aksrt')) . '</div></li>';
|
||||
echo '<li><div class="jio-ban-history__meta"><strong>' . esc_html($labels[$note['status'] ?? ''] ?? (string) ($note['status'] ?? '')) . '</strong><span>'
|
||||
. esc_html($operator ? $operator->display_name : __('管理员', 'jio')) . '</span><time>' . esc_html((string) ($note['timestamp'] ?? '')) . '</time></div>';
|
||||
echo '<div class="jio-ban-copy">' . esc_html((string) ($note['note'] ?? '') !== '' ? (string) $note['note'] : __('未填写处理留言', 'jio')) . '</div></li>';
|
||||
}
|
||||
echo '</ol></section>';
|
||||
}
|
||||
|
||||
private function render_ban_review_fields(bool $report): void {
|
||||
echo '<section class="aksrt-ban-review"><h3>' . esc_html__('审核处理', 'aksrt') . '</h3><fieldset class="aksrt-ban-decisions"><legend>' . esc_html__('处理结果(必选)', 'aksrt') . '</legend>';
|
||||
$choices = $report ? ['resolved' => __('标记已处理', 'aksrt'), 'dismissed' => __('驳回举报', 'aksrt')] : ['approve' => __('通过申诉', 'aksrt'), 'reject' => __('驳回申诉', 'aksrt')];
|
||||
echo '<section class="jio-ban-review"><h3>' . esc_html__('审核处理', 'jio') . '</h3><fieldset class="jio-ban-decisions"><legend>' . esc_html__('处理结果(必选)', 'jio') . '</legend>';
|
||||
$choices = $report ? ['resolved' => __('标记已处理', 'jio'), 'dismissed' => __('驳回举报', 'jio')] : ['approve' => __('通过申诉', 'jio'), 'reject' => __('驳回申诉', 'jio')];
|
||||
foreach ($choices as $value => $label) {
|
||||
echo '<label><input type="radio" name="' . ($report ? 'new_status' : 'decision') . '" value="' . esc_attr($value) . '" required><span>' . esc_html($label) . '</span></label>';
|
||||
}
|
||||
echo '</fieldset>';
|
||||
if (!$report) {
|
||||
echo '<p class="aksrt-native-callout is-warning">' . esc_html__('通过申诉将解除该用户当前生效的封禁、停权和禁言,保留警告记录;驳回不会改变限制。', 'aksrt') . '</p>';
|
||||
echo '<p class="jio-native-callout is-warning">' . esc_html__('通过申诉将解除该用户当前生效的封禁、停权和禁言,保留警告记录;驳回不会改变限制。', 'jio') . '</p>';
|
||||
}
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label" data-aksrt-ban-note-label data-optional="' . esc_attr__('处理说明(选填)', 'aksrt') . '" data-required="' . esc_attr__('驳回原因(必填)', 'aksrt') . '">' . esc_html__('处理说明(选填)', 'aksrt') . '</span>';
|
||||
echo '<textarea name="note" rows="3" data-required-error="' . esc_attr__('请填写驳回原因,不能只包含空格', 'aksrt') . '" aria-describedby="aksrt-ban-note-hint"></textarea></label>';
|
||||
echo '<p id="aksrt-ban-note-hint" class="aksrt-native-field__hint">' . esc_html($report ? __('处理结果及说明会反馈给举报人。驳回时请填写具体原因。', 'aksrt') : __('处理结果及说明会反馈给申诉用户。驳回时请说明原因,便于用户补充资料。', 'aksrt')) . '</p></section>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label" data-jio-ban-note-label data-optional="' . esc_attr__('处理说明(选填)', 'jio') . '" data-required="' . esc_attr__('驳回原因(必填)', 'jio') . '">' . esc_html__('处理说明(选填)', 'jio') . '</span>';
|
||||
echo '<textarea name="note" rows="3" data-required-error="' . esc_attr__('请填写驳回原因,不能只包含空格', 'jio') . '" aria-describedby="jio-ban-note-hint"></textarea></label>';
|
||||
echo '<p id="jio-ban-note-hint" class="jio-native-field__hint">' . esc_html($report ? __('处理结果及说明会反馈给举报人。驳回时请填写具体原因。', 'jio') : __('处理结果及说明会反馈给申诉用户。驳回时请说明原因,便于用户补充资料。', 'jio')) . '</p></section>';
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -105,10 +105,10 @@ trait UsersMenusBanQuery {
|
||||
private function query_ban_report_rows(array $filters): array {
|
||||
global $wpdb;
|
||||
|
||||
if (!class_exists('\\AKSRT\\Core\\DBMigrator')) {
|
||||
if (!class_exists('\\Jio\\Core\\DBMigrator')) {
|
||||
return [];
|
||||
}
|
||||
$table = \AKSRT\Core\DBMigrator::table('report');
|
||||
$table = \Jio\Core\DBMigrator::table('report');
|
||||
if ($table === '') {
|
||||
return [];
|
||||
}
|
||||
@@ -147,13 +147,13 @@ trait UsersMenusBanQuery {
|
||||
|
||||
/** @return array<int,array<string,mixed>> */
|
||||
private function query_ban_appeal_rows(array $filters): array {
|
||||
if (!class_exists('\\AKSRT\\User\\BanAppeal')) {
|
||||
if (!class_exists('\\Jio\\User\\BanAppeal')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$args = [
|
||||
'meta_query' => [[
|
||||
'key' => \AKSRT\User\BanAppeal::META_STATUS,
|
||||
'key' => \Jio\User\BanAppeal::META_STATUS,
|
||||
'value' => ['pending', 'approved', 'rejected'],
|
||||
'compare' => 'IN',
|
||||
]],
|
||||
@@ -216,12 +216,12 @@ trait UsersMenusBanQuery {
|
||||
}
|
||||
}
|
||||
$report_ids = array_values(array_unique($report_ids));
|
||||
if ($report_ids === [] || !class_exists('\\AKSRT\\Core\\DBMigrator')) {
|
||||
if ($report_ids === [] || !class_exists('\\Jio\\Core\\DBMigrator')) {
|
||||
return;
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$table = \AKSRT\Core\DBMigrator::table('report');
|
||||
$table = \Jio\Core\DBMigrator::table('report');
|
||||
if ($table === '') {
|
||||
return;
|
||||
}
|
||||
@@ -281,12 +281,12 @@ trait UsersMenusBanQuery {
|
||||
if ($kind === 'appeal') {
|
||||
return $this->get_ban_appeal_row($id);
|
||||
}
|
||||
if ($kind !== 'report' || !class_exists('\\AKSRT\\Core\\DBMigrator')) {
|
||||
if ($kind !== 'report' || !class_exists('\\Jio\\Core\\DBMigrator')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$table = \AKSRT\Core\DBMigrator::table('report');
|
||||
$table = \Jio\Core\DBMigrator::table('report');
|
||||
if ($table === '') {
|
||||
return null;
|
||||
}
|
||||
@@ -333,17 +333,17 @@ trait UsersMenusBanQuery {
|
||||
}
|
||||
|
||||
private function get_ban_appeal_row(int $user_id): ?array {
|
||||
if ($user_id <= 0 || !class_exists('\\AKSRT\\User\\BanAppeal')) {
|
||||
if ($user_id <= 0 || !class_exists('\\Jio\\User\\BanAppeal')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$raw_status = \AKSRT\User\BanAppeal::get_status($user_id);
|
||||
$raw_status = \Jio\User\BanAppeal::get_status($user_id);
|
||||
$status = $raw_status === 'approved' ? 'resolved' : ($raw_status === 'rejected' ? 'dismissed' : $raw_status);
|
||||
if (!in_array($status, ['pending', 'resolved', 'dismissed'], true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$payload = \AKSRT\User\BanAppeal::get_payload($user_id);
|
||||
$payload = \Jio\User\BanAppeal::get_payload($user_id);
|
||||
return [
|
||||
'kind' => 'appeal',
|
||||
'id' => $user_id,
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
/**
|
||||
* Native invite-code list and page assets.
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -13,44 +13,44 @@ trait UsersMenusInviteCode {
|
||||
use UsersMenusInviteForms;
|
||||
|
||||
public function enqueue_invite_assets(string $hook): void {
|
||||
if ($hook !== 'users_page_aksrt_invite_codes') {
|
||||
if ($hook !== 'users_page_jio_invite_codes') {
|
||||
return;
|
||||
}
|
||||
$css = '/assets/css/admin/invite-codes.css';
|
||||
$js = '/assets/js/admin-invite-codes.js';
|
||||
wp_enqueue_style('aksrt-admin-invite-codes', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-invite-codes', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-invite-codes', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-invite-codes', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
public function render_invite_codes_page(): void {
|
||||
if (!current_user_can('edit_users')) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
|
||||
$service = \AKSRT\Payment\InviteCode::get_instance();
|
||||
$this_url = admin_url('users.php?page=aksrt_invite_codes');
|
||||
$nonce = wp_create_nonce('aksrt_invite_admin');
|
||||
NativeUi::page_open('aksrt-inv-page');
|
||||
$service = \Jio\Payment\InviteCode::get_instance();
|
||||
$this_url = admin_url('users.php?page=jio_invite_codes');
|
||||
$nonce = wp_create_nonce('jio_invite_admin');
|
||||
NativeUi::page_open('jio-inv-page');
|
||||
NativeUi::header(
|
||||
__('邀请码管理', 'aksrt'),
|
||||
__('管理注册邀请码与使用奖励,点击邀请码即可复制。', 'aksrt'),
|
||||
__('邀请码管理', 'jio'),
|
||||
__('管理注册邀请码与使用奖励,点击邀请码即可复制。', 'jio'),
|
||||
'dashicons-tickets-alt',
|
||||
[
|
||||
['label' => __('添加邀请码', 'aksrt'), 'dialog' => 'aksrt-inv-add', 'primary' => true, 'icon' => 'dashicons-plus-alt2'],
|
||||
['label' => __('导出邀请码', 'aksrt'), 'dialog' => 'aksrt-inv-export', 'icon' => 'dashicons-download'],
|
||||
['label' => __('添加邀请码', 'jio'), 'dialog' => 'jio-inv-add', 'primary' => true, 'icon' => 'dashicons-plus-alt2'],
|
||||
['label' => __('导出邀请码', 'jio'), 'dialog' => 'jio-inv-export', 'icon' => 'dashicons-download'],
|
||||
]
|
||||
);
|
||||
|
||||
$settings = $service->registration_settings();
|
||||
if (empty($settings['enabled'])) {
|
||||
$settings_url = admin_url('admin.php?page=aksrt-settings#/users/auth');
|
||||
echo '<div class="notice notice-warning"><p>' . esc_html__('邀请码注册尚未启用,已添加的邀请码暂不能用于注册。', 'aksrt')
|
||||
. ' <a href="' . esc_url($settings_url) . '">' . esc_html__('前往登录注册设置', 'aksrt') . '</a></p></div>';
|
||||
$settings_url = admin_url('admin.php?page=jio-settings#/users/auth');
|
||||
echo '<div class="notice notice-warning"><p>' . esc_html__('邀请码注册尚未启用,已添加的邀请码暂不能用于注册。', 'jio')
|
||||
. ' <a href="' . esc_url($settings_url) . '">' . esc_html__('前往登录注册设置', 'jio') . '</a></p></div>';
|
||||
}
|
||||
|
||||
$title = isset($_GET['aksrt_inv_title']) ? sanitize_text_field(rawurldecode((string) wp_unslash($_GET['aksrt_inv_title']))) : '';
|
||||
$message = isset($_GET['aksrt_inv_msg']) ? sanitize_text_field(rawurldecode((string) wp_unslash($_GET['aksrt_inv_msg']))) : '';
|
||||
$type = ($_GET['aksrt_inv_type'] ?? '') === 'error' ? 'error' : 'success';
|
||||
$title = isset($_GET['jio_inv_title']) ? sanitize_text_field(rawurldecode((string) wp_unslash($_GET['jio_inv_title']))) : '';
|
||||
$message = isset($_GET['jio_inv_msg']) ? sanitize_text_field(rawurldecode((string) wp_unslash($_GET['jio_inv_msg']))) : '';
|
||||
$type = ($_GET['jio_inv_type'] ?? '') === 'error' ? 'error' : 'success';
|
||||
if ($title !== '') {
|
||||
echo '<div class="notice notice-' . esc_attr($type) . ' is-dismissible"><p><strong>' . esc_html($title) . '</strong>'
|
||||
. ($message !== '' ? ' ' . esc_html($message) : '') . '</p></div>';
|
||||
@@ -59,14 +59,14 @@ trait UsersMenusInviteCode {
|
||||
$this->render_invite_codes_list($service, $this_url, $nonce);
|
||||
$this->render_invite_codes_add($nonce);
|
||||
$this->render_invite_codes_export($nonce);
|
||||
echo '<div class="aksrt-inv-toast" role="status" aria-live="polite" data-aksrt-inv-toast'
|
||||
. ' data-copied="' . esc_attr__('邀请码已复制', 'aksrt') . '"'
|
||||
. ' data-copy-failed="' . esc_attr__('自动复制失败,请选中邀请码后手动复制。', 'aksrt') . '"'
|
||||
. ' data-exported="' . esc_attr__('下载请求已提交,请查看浏览器下载列表。', 'aksrt') . '"></div>';
|
||||
echo '<div class="jio-inv-toast" role="status" aria-live="polite" data-jio-inv-toast'
|
||||
. ' data-copied="' . esc_attr__('邀请码已复制', 'jio') . '"'
|
||||
. ' data-copy-failed="' . esc_attr__('自动复制失败,请选中邀请码后手动复制。', 'jio') . '"'
|
||||
. ' data-exported="' . esc_attr__('下载请求已提交,请查看浏览器下载列表。', 'jio') . '"></div>';
|
||||
NativeUi::page_close();
|
||||
}
|
||||
|
||||
private function render_invite_codes_list(\AKSRT\Payment\InviteCode $service, string $this_url, string $nonce): void {
|
||||
private function render_invite_codes_list(\Jio\Payment\InviteCode $service, string $this_url, string $nonce): void {
|
||||
$status = in_array(($_GET['status'] ?? 'all'), ['all', 'unused', 'used'], true) ? (string) $_GET['status'] : 'all';
|
||||
$s = isset($_GET['s']) ? sanitize_text_field((string) wp_unslash($_GET['s'])) : '';
|
||||
$remark = isset($_GET['remark']) ? sanitize_text_field((string) wp_unslash($_GET['remark'])) : '';
|
||||
@@ -81,44 +81,44 @@ trait UsersMenusInviteCode {
|
||||
$filters = ['status' => $status, 's' => $s, 'remark' => $remark, 'orderby' => $data['orderby'], 'desc' => $data['desc']];
|
||||
$filter_url = add_query_arg($filters, $this_url);
|
||||
|
||||
NativeUi::card_open('', '', true, 'aksrt-inv-list-card');
|
||||
echo '<div class="aksrt-inv-toolbar"><div class="aksrt-native-toolbar"><div class="aksrt-inv-toolbar__views">';
|
||||
echo '<h2 class="aksrt-native-card__title">' . esc_html__('邀请码列表', 'aksrt') . '</h2>';
|
||||
echo '<ul class="subsubsub" aria-label="' . esc_attr__('使用状态筛选', 'aksrt') . '">';
|
||||
foreach (['all' => __('全部', 'aksrt'), 'unused' => __('未使用', 'aksrt'), 'used' => __('已使用', 'aksrt')] as $key => $label) {
|
||||
NativeUi::card_open('', '', true, 'jio-inv-list-card');
|
||||
echo '<div class="jio-inv-toolbar"><div class="jio-native-toolbar"><div class="jio-inv-toolbar__views">';
|
||||
echo '<h2 class="jio-native-card__title">' . esc_html__('邀请码列表', 'jio') . '</h2>';
|
||||
echo '<ul class="subsubsub" aria-label="' . esc_attr__('使用状态筛选', 'jio') . '">';
|
||||
foreach (['all' => __('全部', 'jio'), 'unused' => __('未使用', 'jio'), 'used' => __('已使用', 'jio')] as $key => $label) {
|
||||
$url = add_query_arg('status', $key, $filter_url);
|
||||
echo '<li><a href="' . esc_url($url) . '"' . ($key === $status ? ' class="current" aria-current="page"' : '') . '>' . esc_html($label) . '</a></li>';
|
||||
}
|
||||
echo '</ul></div><form method="get" action="' . esc_url(admin_url('users.php')) . '" class="aksrt-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="aksrt_invite_codes">';
|
||||
echo '</ul></div><form method="get" action="' . esc_url(admin_url('users.php')) . '" class="jio-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="jio_invite_codes">';
|
||||
foreach (array_diff_key($filters, ['s' => '']) as $key => $value) {
|
||||
echo '<input type="hidden" name="' . esc_attr($key) . '" value="' . esc_attr($value) . '">';
|
||||
}
|
||||
echo '<label class="screen-reader-text" for="aksrt-inv-search">' . esc_html__('搜索邀请码或标识', 'aksrt') . '</label>';
|
||||
echo '<input type="search" id="aksrt-inv-search" name="s" value="' . esc_attr($s) . '" placeholder="' . esc_attr__('搜索邀请码或标识', 'aksrt') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('搜索', 'aksrt') . '</button>';
|
||||
echo '<label class="screen-reader-text" for="jio-inv-search">' . esc_html__('搜索邀请码或标识', 'jio') . '</label>';
|
||||
echo '<input type="search" id="jio-inv-search" name="s" value="' . esc_attr($s) . '" placeholder="' . esc_attr__('搜索邀请码或标识', 'jio') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('搜索', 'jio') . '</button>';
|
||||
if ($remark !== '') {
|
||||
echo '<span class="aksrt-inv-filter-remark">' . esc_html(sprintf(__('标识:%s', 'aksrt'), $remark)) . '</span>';
|
||||
echo '<span class="jio-inv-filter-remark">' . esc_html(sprintf(__('标识:%s', 'jio'), $remark)) . '</span>';
|
||||
}
|
||||
if ($s !== '' || $remark !== '' || $status !== 'all') {
|
||||
echo '<a class="button-link" href="' . esc_url($this_url) . '">' . esc_html__('清除筛选', 'aksrt') . '</a>';
|
||||
echo '<a class="button-link" href="' . esc_url($this_url) . '">' . esc_html__('清除筛选', 'jio') . '</a>';
|
||||
}
|
||||
echo '</form></div></div>';
|
||||
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" data-aksrt-inv-list'
|
||||
. ' data-confirm="' . esc_attr__('确定删除选中的 %d 个邀请码及其使用记录?此操作不可撤销。', 'aksrt') . '">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_invite_delete"><input type="hidden" name="aksrt_nonce" value="' . esc_attr($nonce) . '">';
|
||||
echo '<div class="aksrt-native-bulk aksrt-inv-bulk"><span class="aksrt-inv-selection" data-aksrt-inv-selection data-label="' . esc_attr__('已选 %d 项', 'aksrt') . '" aria-live="polite"></span>'
|
||||
. '<label class="screen-reader-text" for="bulk-action-selector-top">' . esc_html__('批量操作', 'aksrt') . '</label>'
|
||||
. '<select name="bulk_action" id="bulk-action-selector-top"><option value="-1">' . esc_html__('批量操作', 'aksrt') . '</option><option value="delete">' . esc_html__('删除', 'aksrt') . '</option></select>'
|
||||
. '<button type="submit" class="button" data-aksrt-inv-bulk disabled>' . esc_html__('应用', 'aksrt') . '</button></div>';
|
||||
echo '<div class="aksrt-native-table-wrap">';
|
||||
echo '<table class="widefat fixed striped aksrt-inv-table"><thead><tr><td class="manage-column column-cb check-column">';
|
||||
echo '<input type="checkbox" data-aksrt-inv-check-all aria-label="' . esc_attr__('全选本页邀请码', 'aksrt') . '"></td>';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" data-jio-inv-list'
|
||||
. ' data-confirm="' . esc_attr__('确定删除选中的 %d 个邀请码及其使用记录?此操作不可撤销。', 'jio') . '">';
|
||||
echo '<input type="hidden" name="action" value="jio_invite_delete"><input type="hidden" name="jio_nonce" value="' . esc_attr($nonce) . '">';
|
||||
echo '<div class="jio-native-bulk jio-inv-bulk"><span class="jio-inv-selection" data-jio-inv-selection data-label="' . esc_attr__('已选 %d 项', 'jio') . '" aria-live="polite"></span>'
|
||||
. '<label class="screen-reader-text" for="bulk-action-selector-top">' . esc_html__('批量操作', 'jio') . '</label>'
|
||||
. '<select name="bulk_action" id="bulk-action-selector-top"><option value="-1">' . esc_html__('批量操作', 'jio') . '</option><option value="delete">' . esc_html__('删除', 'jio') . '</option></select>'
|
||||
. '<button type="submit" class="button" data-jio-inv-bulk disabled>' . esc_html__('应用', 'jio') . '</button></div>';
|
||||
echo '<div class="jio-native-table-wrap">';
|
||||
echo '<table class="widefat fixed striped jio-inv-table"><thead><tr><td class="manage-column column-cb check-column">';
|
||||
echo '<input type="checkbox" data-jio-inv-check-all aria-label="' . esc_attr__('全选本页邀请码', 'jio') . '"></td>';
|
||||
$columns = [
|
||||
'code' => __('邀请码', 'aksrt'), 'rewards' => __('使用奖励', 'aksrt'),
|
||||
'created_at' => __('创建时间', 'aksrt'), 'updated_at' => __('更新时间', 'aksrt'),
|
||||
'used_count' => __('使用状态', 'aksrt'), 'remark' => __('标识', 'aksrt'),
|
||||
'code' => __('邀请码', 'jio'), 'rewards' => __('使用奖励', 'jio'),
|
||||
'created_at' => __('创建时间', 'jio'), 'updated_at' => __('更新时间', 'jio'),
|
||||
'used_count' => __('使用状态', 'jio'), 'remark' => __('标识', 'jio'),
|
||||
];
|
||||
foreach ($columns as $key => $label) {
|
||||
$active = $key === $data['orderby'];
|
||||
@@ -130,38 +130,38 @@ trait UsersMenusInviteCode {
|
||||
$next = $active && $data['desc'] === 'ASC' ? 'DESC' : 'ASC';
|
||||
$url = add_query_arg(['orderby' => $key, 'desc' => $next], $filter_url);
|
||||
$icon = $active && $data['desc'] === 'ASC' ? 'dashicons-arrow-up-alt2' : 'dashicons-arrow-down-alt2';
|
||||
echo '<a class="orderby-but' . ($active ? ' is-active' : '') . '" href="' . esc_url($url) . '" title="' . esc_attr($next === 'ASC' ? __('按此列升序排列', 'aksrt') : __('按此列降序排列', 'aksrt')) . '">'
|
||||
echo '<a class="orderby-but' . ($active ? ' is-active' : '') . '" href="' . esc_url($url) . '" title="' . esc_attr($next === 'ASC' ? __('按此列升序排列', 'jio') : __('按此列降序排列', 'jio')) . '">'
|
||||
. esc_html($label) . '<span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span></a>';
|
||||
}
|
||||
echo '</th>';
|
||||
}
|
||||
echo '</tr></thead><tbody>';
|
||||
if ($data['items'] === []) {
|
||||
echo NativeUi::empty_row(7, __('暂无符合条件的邀请码', 'aksrt'), __('调整筛选条件,或点击“添加邀请码”生成、导入新的邀请码。', 'aksrt'));
|
||||
echo NativeUi::empty_row(7, __('暂无符合条件的邀请码', 'jio'), __('调整筛选条件,或点击“添加邀请码”生成、导入新的邀请码。', 'jio'));
|
||||
}
|
||||
foreach ($data['items'] as $item) {
|
||||
$used = (int) $item['used_count'];
|
||||
$max_uses = (int) $item['max_uses'];
|
||||
echo '<tr><th scope="row" class="check-column"><input type="checkbox" name="action_id[]" value="' . esc_attr((string) $item['id']) . '" aria-label="' . esc_attr(sprintf(__('选择邀请码 %s', 'aksrt'), $item['code'])) . '"></th>';
|
||||
echo '<td><button type="button" class="aksrt-inv-code aksrt-native-mono" data-clipboard-text="' . esc_attr($item['code']) . '" aria-label="' . esc_attr(sprintf(__('复制邀请码 %s', 'aksrt'), $item['code'])) . '">' . esc_html($item['code']) . '<span class="dashicons dashicons-admin-page" aria-hidden="true"></span></button></td>';
|
||||
echo '<tr><th scope="row" class="check-column"><input type="checkbox" name="action_id[]" value="' . esc_attr((string) $item['id']) . '" aria-label="' . esc_attr(sprintf(__('选择邀请码 %s', 'jio'), $item['code'])) . '"></th>';
|
||||
echo '<td><button type="button" class="jio-inv-code jio-native-mono" data-clipboard-text="' . esc_attr($item['code']) . '" aria-label="' . esc_attr(sprintf(__('复制邀请码 %s', 'jio'), $item['code'])) . '">' . esc_html($item['code']) . '<span class="dashicons dashicons-admin-page" aria-hidden="true"></span></button></td>';
|
||||
echo '<td>' . wp_kses((string) $item['reward_text'], ['br' => []]) . '</td>';
|
||||
echo '<td class="aksrt-inv-date">' . esc_html($item['created_at']) . '</td><td class="aksrt-inv-date">' . esc_html($item['updated_at']) . '</td><td>';
|
||||
$label = $used > 0 && $max_uses > 0 && $used < $max_uses ? sprintf(__('已使用 %1$d/%2$d', 'aksrt'), $used, $max_uses) : ($used > 0 ? __('已使用', 'aksrt') : __('未使用', 'aksrt'));
|
||||
echo '<td class="jio-inv-date">' . esc_html($item['created_at']) . '</td><td class="jio-inv-date">' . esc_html($item['updated_at']) . '</td><td>';
|
||||
$label = $used > 0 && $max_uses > 0 && $used < $max_uses ? sprintf(__('已使用 %1$d/%2$d', 'jio'), $used, $max_uses) : ($used > 0 ? __('已使用', 'jio') : __('未使用', 'jio'));
|
||||
echo NativeUi::badge($label, $used > 0 ? 'neutral' : 'success');
|
||||
foreach ((array) $item['recent_users'] as $user) {
|
||||
echo '<div class="aksrt-inv-user"><a href="' . esc_url(get_edit_user_link((int) $user['user_id'])) . '">' . esc_html($user['name']) . '</a></div>';
|
||||
echo '<div class="jio-inv-user"><a href="' . esc_url(get_edit_user_link((int) $user['user_id'])) . '">' . esc_html($user['name']) . '</a></div>';
|
||||
}
|
||||
echo '</td><td>';
|
||||
if ($item['remark'] !== '') {
|
||||
echo '<a href="' . esc_url(add_query_arg('remark', $item['remark'], $this_url)) . '">' . esc_html($item['remark']) . '</a>';
|
||||
} else {
|
||||
echo '<span class="aksrt-native-muted">—</span>';
|
||||
echo '<span class="jio-native-muted">—</span>';
|
||||
}
|
||||
echo '</td></tr>';
|
||||
}
|
||||
echo '</tbody></table>';
|
||||
NativeUi::table_wrap_close();
|
||||
echo '<div class="tablenav bottom"><div class="tablenav-pages"><span class="displaying-num">' . esc_html(sprintf(__('共 %s 项', 'aksrt'), number_format_i18n((int) $data['total']))) . '</span>';
|
||||
echo '<div class="tablenav bottom"><div class="tablenav-pages"><span class="displaying-num">' . esc_html(sprintf(__('共 %s 项', 'jio'), number_format_i18n((int) $data['total']))) . '</span>';
|
||||
if ((int) $data['max_pages'] > 1) {
|
||||
echo paginate_links([
|
||||
'base' => add_query_arg('paged', '%#%', $filter_url), 'format' => '',
|
||||
|
||||
@@ -2,91 +2,91 @@
|
||||
/**
|
||||
* Invite-code generation, import and export dialogs.
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait UsersMenusInviteForms {
|
||||
private function render_invite_codes_add(string $nonce): void {
|
||||
$this->invite_dialog_open('aksrt-inv-add', __('添加邀请码', 'aksrt'), __('批量生成新邀请码,或导入已有的邀请码数据。', 'aksrt'), 'post', 'aksrt_invite_add', $nonce);
|
||||
echo '<fieldset class="aksrt-inv-modes"><legend class="screen-reader-text">' . esc_html__('添加方式', 'aksrt') . '</legend>';
|
||||
foreach (['auto' => __('系统自动生成', 'aksrt'), 'import' => __('导入邀请码', 'aksrt')] as $value => $label) {
|
||||
$this->invite_dialog_open('jio-inv-add', __('添加邀请码', 'jio'), __('批量生成新邀请码,或导入已有的邀请码数据。', 'jio'), 'post', 'jio_invite_add', $nonce);
|
||||
echo '<fieldset class="jio-inv-modes"><legend class="screen-reader-text">' . esc_html__('添加方式', 'jio') . '</legend>';
|
||||
foreach (['auto' => __('系统自动生成', 'jio'), 'import' => __('导入邀请码', 'jio')] as $value => $label) {
|
||||
echo '<label><input type="radio" name="add_type" value="' . esc_attr($value) . '"' . checked($value, 'auto', false) . '><span>' . esc_html($label) . '</span></label>';
|
||||
}
|
||||
echo '</fieldset><fieldset class="aksrt-inv-pane" data-aksrt-inv-dep="add_type=auto"><legend class="screen-reader-text">' . esc_html__('自动生成设置', 'aksrt') . '</legend>';
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('生成数量', 'aksrt') . '</span>'
|
||||
echo '</fieldset><fieldset class="jio-inv-pane" data-jio-inv-dep="add_type=auto"><legend class="screen-reader-text">' . esc_html__('自动生成设置', 'jio') . '</legend>';
|
||||
echo '<div class="jio-native-form-grid">';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('生成数量', 'jio') . '</span>'
|
||||
. '<input type="number" name="auto_num" value="20" min="1" max="1000" step="1" required autofocus>'
|
||||
. '<small class="aksrt-native-field__hint">' . esc_html__('单次可生成 1–1000 个。', 'aksrt') . '</small></label>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('标识', 'aksrt') . '</span>'
|
||||
. '<small class="jio-native-field__hint">' . esc_html__('单次可生成 1–1000 个。', 'jio') . '</small></label>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('标识', 'jio') . '</span>'
|
||||
. '<input type="text" name="auto_remarks" value="' . esc_attr('invita_' . current_time('YmdHis')) . '" maxlength="191">'
|
||||
. '<small class="aksrt-native-field__hint">' . esc_html__('为本批邀请码命名,方便查找与管理。', 'aksrt') . '</small></label></div>';
|
||||
. '<small class="jio-native-field__hint">' . esc_html__('为本批邀请码命名,方便查找与管理。', 'jio') . '</small></label></div>';
|
||||
$this->render_invite_reward_fields();
|
||||
echo '<label class="aksrt-inv-advanced"><input type="checkbox" name="auto_top_s" value="1">' . esc_html__('自定义邀请码位数', 'aksrt') . '</label>';
|
||||
echo '<fieldset class="aksrt-inv-pane aksrt-inv-length" data-aksrt-inv-dep="auto_top_s=1" hidden disabled><legend class="screen-reader-text">' . esc_html__('自定义位数', 'aksrt') . '</legend>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('邀请码位数', 'aksrt') . '</span>'
|
||||
echo '<label class="jio-inv-advanced"><input type="checkbox" name="auto_top_s" value="1">' . esc_html__('自定义邀请码位数', 'jio') . '</label>';
|
||||
echo '<fieldset class="jio-inv-pane jio-inv-length" data-jio-inv-dep="auto_top_s=1" hidden disabled><legend class="screen-reader-text">' . esc_html__('自定义位数', 'jio') . '</legend>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('邀请码位数', 'jio') . '</span>'
|
||||
. '<input type="number" name="auto_rand_password_limit" value="10" min="4" max="50" step="1" required>'
|
||||
. '<small class="aksrt-native-field__hint">' . esc_html__('4–50 位;默认 10 位。位数过短容易重复。', 'aksrt') . '</small></label></fieldset></fieldset>';
|
||||
. '<small class="jio-native-field__hint">' . esc_html__('4–50 位;默认 10 位。位数过短容易重复。', 'jio') . '</small></label></fieldset></fieldset>';
|
||||
|
||||
echo '<fieldset class="aksrt-inv-pane" data-aksrt-inv-dep="add_type=import" hidden disabled><legend class="screen-reader-text">' . esc_html__('导入设置', 'aksrt') . '</legend>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('邀请码数据', 'aksrt') . '</span>'
|
||||
. '<textarea name="import_data" rows="7" class="code" required data-limit-error="' . esc_attr__('单次最多导入 1000 行,请拆分后重试。', 'aksrt') . '" placeholder="93FW0XAY 无 signup P86NPWKI points=10 signup" aria-describedby="aksrt-inv-import-hint"></textarea>'
|
||||
. '<small class="aksrt-native-field__hint" id="aksrt-inv-import-hint">' . esc_html__('每行一个,格式为“邀请码 奖励 标识”,无奖励填“无”。单次最多 1000 条,空行会忽略,重复的邀请码不会导入。', 'aksrt') . '</small></label>';
|
||||
echo '<label class="aksrt-native-field aksrt-inv-separator"><span class="aksrt-native-field__label">' . esc_html__('分隔符', 'aksrt') . '</span>'
|
||||
echo '<fieldset class="jio-inv-pane" data-jio-inv-dep="add_type=import" hidden disabled><legend class="screen-reader-text">' . esc_html__('导入设置', 'jio') . '</legend>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('邀请码数据', 'jio') . '</span>'
|
||||
. '<textarea name="import_data" rows="7" class="code" required data-limit-error="' . esc_attr__('单次最多导入 1000 行,请拆分后重试。', 'jio') . '" placeholder="93FW0XAY 无 signup P86NPWKI points=10 signup" aria-describedby="jio-inv-import-hint"></textarea>'
|
||||
. '<small class="jio-native-field__hint" id="jio-inv-import-hint">' . esc_html__('每行一个,格式为“邀请码 奖励 标识”,无奖励填“无”。单次最多 1000 条,空行会忽略,重复的邀请码不会导入。', 'jio') . '</small></label>';
|
||||
echo '<label class="jio-native-field jio-inv-separator"><span class="jio-native-field__label">' . esc_html__('分隔符', 'jio') . '</span>'
|
||||
. '<input type="text" name="import_division" value=" " maxlength="4">'
|
||||
. '<small class="aksrt-native-field__hint">' . esc_html__('默认为空格,也可填写其他分隔符,例如 |。', 'aksrt') . '</small></label>';
|
||||
echo '<details class="aksrt-inv-help"><summary>' . esc_html__('查看奖励格式与示例', 'aksrt') . '</summary><div>'
|
||||
. '<p>' . esc_html__('奖励参数用 & 连接:level_integral 为经验值,points 为积分,balance 为余额(元),vip 为会员等级(1 或 2),vip_time 为会员天数。', 'aksrt') . '</p>'
|
||||
. '<small class="jio-native-field__hint">' . esc_html__('默认为空格,也可填写其他分隔符,例如 |。', 'jio') . '</small></label>';
|
||||
echo '<details class="jio-inv-help"><summary>' . esc_html__('查看奖励格式与示例', 'jio') . '</summary><div>'
|
||||
. '<p>' . esc_html__('奖励参数用 & 连接:level_integral 为经验值,points 为积分,balance 为余额(元),vip 为会员等级(1 或 2),vip_time 为会员天数。', 'jio') . '</p>'
|
||||
. '<pre>93FW0XAY 无 signup P86NPWKI points=10 signup AUF9D2B4 balance=30&vip=2&vip_time=5 signup</pre>'
|
||||
. '<p>' . esc_html__('第三行示例:奖励 30 元余额和 5 天二级会员。标识可省略;导出的文本文件也可按相同分隔符重新导入。', 'aksrt') . '</p></div></details></fieldset>';
|
||||
$this->invite_dialog_close(__('生成邀请码', 'aksrt'), ' data-aksrt-inv-add-submit data-auto-label="' . esc_attr__('生成邀请码', 'aksrt') . '" data-import-label="' . esc_attr__('导入邀请码', 'aksrt') . '" data-busy-label="' . esc_attr__('正在添加…', 'aksrt') . '"');
|
||||
. '<p>' . esc_html__('第三行示例:奖励 30 元余额和 5 天二级会员。标识可省略;导出的文本文件也可按相同分隔符重新导入。', 'jio') . '</p></div></details></fieldset>';
|
||||
$this->invite_dialog_close(__('生成邀请码', 'jio'), ' data-jio-inv-add-submit data-auto-label="' . esc_attr__('生成邀请码', 'jio') . '" data-import-label="' . esc_attr__('导入邀请码', 'jio') . '" data-busy-label="' . esc_attr__('正在添加…', 'jio') . '"');
|
||||
}
|
||||
|
||||
private function render_invite_reward_fields(): void {
|
||||
echo '<fieldset class="aksrt-inv-rewards"><legend>' . esc_html__('使用奖励', 'aksrt') . '</legend><p class="aksrt-inv-hint">' . esc_html__('用户使用邀请码注册后获得以下奖励;数值为 0 表示不赠送。', 'aksrt') . '</p>';
|
||||
echo '<div class="aksrt-native-form-grid aksrt-inv-reward-grid">';
|
||||
foreach (['level_integral' => __('经验值', 'aksrt'), 'points' => __('积分', 'aksrt'), 'balance' => __('余额(元)', 'aksrt')] as $name => $label) {
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html($label) . '</span><input type="number" name="' . esc_attr($name) . '" value="0" min="0" step="' . ($name === 'balance' ? '0.01' : '1') . '"></label>';
|
||||
echo '<fieldset class="jio-inv-rewards"><legend>' . esc_html__('使用奖励', 'jio') . '</legend><p class="jio-inv-hint">' . esc_html__('用户使用邀请码注册后获得以下奖励;数值为 0 表示不赠送。', 'jio') . '</p>';
|
||||
echo '<div class="jio-native-form-grid jio-inv-reward-grid">';
|
||||
foreach (['level_integral' => __('经验值', 'jio'), 'points' => __('积分', 'jio'), 'balance' => __('余额(元)', 'jio')] as $name => $label) {
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html($label) . '</span><input type="number" name="' . esc_attr($name) . '" value="0" min="0" step="' . ($name === 'balance' ? '0.01' : '1') . '"></label>';
|
||||
}
|
||||
echo '</div><fieldset class="aksrt-inv-vip"><legend>' . esc_html__('VIP 会员', 'aksrt') . '</legend><div class="aksrt-inv-radios">';
|
||||
foreach (['0' => __('不赠送', 'aksrt'), '1' => __('一级会员', 'aksrt'), '2' => __('二级会员', 'aksrt')] as $value => $label) {
|
||||
echo '</div><fieldset class="jio-inv-vip"><legend>' . esc_html__('VIP 会员', 'jio') . '</legend><div class="jio-inv-radios">';
|
||||
foreach (['0' => __('不赠送', 'jio'), '1' => __('一级会员', 'jio'), '2' => __('二级会员', 'jio')] as $value => $label) {
|
||||
echo '<label><input type="radio" name="vip" value="' . esc_attr($value) . '"' . checked((string) $value, '0', false) . '>' . esc_html($label) . '</label>';
|
||||
}
|
||||
echo '</div></fieldset><fieldset class="aksrt-inv-pane aksrt-inv-vip-time" data-aksrt-inv-dep="vip=1|2" hidden disabled><legend class="screen-reader-text">' . esc_html__('会员时长', 'aksrt') . '</legend>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('会员赠送时长(天)', 'aksrt') . '</span><input type="number" name="vip_time" value="30" min="1" step="1" required></label></fieldset></fieldset>';
|
||||
echo '</div></fieldset><fieldset class="jio-inv-pane jio-inv-vip-time" data-jio-inv-dep="vip=1|2" hidden disabled><legend class="screen-reader-text">' . esc_html__('会员时长', 'jio') . '</legend>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('会员赠送时长(天)', 'jio') . '</span><input type="number" name="vip_time" value="30" min="1" step="1" required></label></fieldset></fieldset>';
|
||||
}
|
||||
|
||||
private function render_invite_codes_export(string $nonce): void {
|
||||
$status = in_array(($_GET['status'] ?? 'all'), ['all', 'unused', 'used'], true) ? (string) $_GET['status'] : 'all';
|
||||
$this->invite_dialog_open('aksrt-inv-export', __('导出邀请码', 'aksrt'), __('选择使用状态与文件格式,导出后可在本地保存或分发。', 'aksrt'), 'get', 'aksrt_invite_export', $nonce);
|
||||
echo '<fieldset class="aksrt-inv-options"><legend>' . esc_html__('使用状态', 'aksrt') . '</legend><div class="aksrt-inv-radios">';
|
||||
foreach (['all' => __('全部', 'aksrt'), 'unused' => __('未使用', 'aksrt'), 'used' => __('已使用', 'aksrt')] as $value => $label) {
|
||||
$this->invite_dialog_open('jio-inv-export', __('导出邀请码', 'jio'), __('选择使用状态与文件格式,导出后可在本地保存或分发。', 'jio'), 'get', 'jio_invite_export', $nonce);
|
||||
echo '<fieldset class="jio-inv-options"><legend>' . esc_html__('使用状态', 'jio') . '</legend><div class="jio-inv-radios">';
|
||||
foreach (['all' => __('全部', 'jio'), 'unused' => __('未使用', 'jio'), 'used' => __('已使用', 'jio')] as $value => $label) {
|
||||
echo '<label><input type="radio" name="status" value="' . esc_attr($value) . '"' . checked($value, $status, false) . '>' . esc_html($label) . '</label>';
|
||||
}
|
||||
echo '</div></fieldset><fieldset class="aksrt-inv-options"><legend>' . esc_html__('导出格式', 'aksrt') . '</legend><div class="aksrt-inv-formats">';
|
||||
foreach (['csv' => [__('Excel 表格(.csv)', 'aksrt'), __('包含奖励、时间、使用状态与标识,适合整理统计。', 'aksrt')], 'text' => [__('文本文档(.txt)', 'aksrt'), __('每行一个邀请码,包含奖励与标识,可再次导入。', 'aksrt')]] as $value => [$label, $hint]) {
|
||||
echo '</div></fieldset><fieldset class="jio-inv-options"><legend>' . esc_html__('导出格式', 'jio') . '</legend><div class="jio-inv-formats">';
|
||||
foreach (['csv' => [__('Excel 表格(.csv)', 'jio'), __('包含奖励、时间、使用状态与标识,适合整理统计。', 'jio')], 'text' => [__('文本文档(.txt)', 'jio'), __('每行一个邀请码,包含奖励与标识,可再次导入。', 'jio')]] as $value => [$label, $hint]) {
|
||||
echo '<label><input type="radio" name="export_format" value="' . esc_attr($value) . '"' . checked($value, 'csv', false) . '><span><strong>' . esc_html($label) . '</strong><small>' . esc_html($hint) . '</small></span></label>';
|
||||
}
|
||||
echo '</div></fieldset><fieldset class="aksrt-inv-pane" data-aksrt-inv-dep="export_format=text" hidden disabled><legend class="screen-reader-text">' . esc_html__('文本设置', 'aksrt') . '</legend>';
|
||||
echo '<label class="aksrt-native-field aksrt-inv-separator"><span class="aksrt-native-field__label">' . esc_html__('分隔符', 'aksrt') . '</span><input type="text" name="text_division" value=" " maxlength="4">'
|
||||
. '<small class="aksrt-native-field__hint">' . esc_html__('默认为空格;重新导入时请使用相同分隔符。', 'aksrt') . '</small></label></fieldset>';
|
||||
echo '<p class="aksrt-native-callout aksrt-inv-export-note">' . esc_html__('每次最多导出最新的 10,000 条记录。导出仅按上方使用状态筛选,不受列表关键词、标识或勾选项影响。', 'aksrt') . '</p>';
|
||||
$this->invite_dialog_close(__('导出并下载', 'aksrt'));
|
||||
echo '</div></fieldset><fieldset class="jio-inv-pane" data-jio-inv-dep="export_format=text" hidden disabled><legend class="screen-reader-text">' . esc_html__('文本设置', 'jio') . '</legend>';
|
||||
echo '<label class="jio-native-field jio-inv-separator"><span class="jio-native-field__label">' . esc_html__('分隔符', 'jio') . '</span><input type="text" name="text_division" value=" " maxlength="4">'
|
||||
. '<small class="jio-native-field__hint">' . esc_html__('默认为空格;重新导入时请使用相同分隔符。', 'jio') . '</small></label></fieldset>';
|
||||
echo '<p class="jio-native-callout jio-inv-export-note">' . esc_html__('每次最多导出最新的 10,000 条记录。导出仅按上方使用状态筛选,不受列表关键词、标识或勾选项影响。', 'jio') . '</p>';
|
||||
$this->invite_dialog_close(__('导出并下载', 'jio'));
|
||||
}
|
||||
|
||||
private function invite_dialog_open(string $id, string $title, string $description, string $method, string $action, string $nonce): void {
|
||||
echo '<dialog class="aksrt-inv-dialog" id="' . esc_attr($id) . '" aria-labelledby="' . esc_attr($id) . '-title" aria-describedby="' . esc_attr($id) . '-description">';
|
||||
echo '<form method="' . esc_attr($method) . '" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-inv-dialog__frame">';
|
||||
echo '<input type="hidden" name="action" value="' . esc_attr($action) . '"><input type="hidden" name="aksrt_nonce" value="' . esc_attr($nonce) . '">';
|
||||
echo '<header class="aksrt-inv-dialog__head"><div><h2 id="' . esc_attr($id) . '-title">' . esc_html($title) . '</h2><p id="' . esc_attr($id) . '-description">' . esc_html($description) . '</p></div>';
|
||||
echo '<button type="button" class="aksrt-inv-dialog__close" data-aksrt-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header><div class="aksrt-inv-dialog__body">';
|
||||
echo '<dialog class="jio-inv-dialog" id="' . esc_attr($id) . '" aria-labelledby="' . esc_attr($id) . '-title" aria-describedby="' . esc_attr($id) . '-description">';
|
||||
echo '<form method="' . esc_attr($method) . '" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-inv-dialog__frame">';
|
||||
echo '<input type="hidden" name="action" value="' . esc_attr($action) . '"><input type="hidden" name="jio_nonce" value="' . esc_attr($nonce) . '">';
|
||||
echo '<header class="jio-inv-dialog__head"><div><h2 id="' . esc_attr($id) . '-title">' . esc_html($title) . '</h2><p id="' . esc_attr($id) . '-description">' . esc_html($description) . '</p></div>';
|
||||
echo '<button type="button" class="jio-inv-dialog__close" data-jio-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header><div class="jio-inv-dialog__body">';
|
||||
}
|
||||
|
||||
private function invite_dialog_close(string $label, string $attributes = ''): void {
|
||||
echo '</div><footer class="aksrt-inv-dialog__foot"><button type="button" class="button" data-aksrt-dialog-close>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
echo '</div><footer class="jio-inv-dialog__foot"><button type="button" class="button" data-jio-dialog-close>' . esc_html__('取消', 'jio') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary"' . $attributes . '>' . esc_html($label) . '</button></footer></form></dialog>';
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,13 @@
|
||||
* 消息管理后台页:页面编排、带权限 / nonce 的写操作、筛选与反馈。
|
||||
* 展示由同目录的 MessagesList / MessagesCompose traits 承载;查询与投递下沉消息服务。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Messages\AdminBroadcast;
|
||||
use AKSRT\Messages\NotificationAdminQuery;
|
||||
use Jio\Messages\AdminBroadcast;
|
||||
use Jio\Messages\NotificationAdminQuery;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -18,8 +18,8 @@ trait UsersMenusMessages {
|
||||
use UsersMenusMessagesList;
|
||||
use UsersMenusMessagesCompose;
|
||||
|
||||
private const MESSAGE_PAGE_SLUG = 'aksrt_messages';
|
||||
private const MESSAGE_COMPOSE_ID = 'aksrt-msg-compose';
|
||||
private const MESSAGE_PAGE_SLUG = 'jio_messages';
|
||||
private const MESSAGE_COMPOSE_ID = 'jio-msg-compose';
|
||||
|
||||
public function enqueue_message_assets(string $hook): void {
|
||||
if ($hook !== 'users_page_' . self::MESSAGE_PAGE_SLUG) {
|
||||
@@ -27,11 +27,11 @@ trait UsersMenusMessages {
|
||||
}
|
||||
$css = '/assets/css/admin/messages.css';
|
||||
$js = '/assets/js/admin-messages.js';
|
||||
if (is_file(AKSRT_DIR . $css)) {
|
||||
wp_enqueue_style('aksrt-admin-messages', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
if (is_file(JIO_DIR . $css)) {
|
||||
wp_enqueue_style('jio-admin-messages', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
}
|
||||
if (is_file(AKSRT_DIR . $js)) {
|
||||
wp_enqueue_script('aksrt-admin-messages', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
if (is_file(JIO_DIR . $js)) {
|
||||
wp_enqueue_script('jio-admin-messages', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,21 +40,21 @@ trait UsersMenusMessages {
|
||||
$filters = $this->message_filters();
|
||||
$paged = max(1, absint($_GET['paged'] ?? 1));
|
||||
|
||||
NativeUi::page_open('aksrt-messages-admin');
|
||||
NativeUi::page_open('jio-messages-admin');
|
||||
NativeUi::header(
|
||||
__('消息管理', 'aksrt'),
|
||||
__('消息管理', 'jio'),
|
||||
'',
|
||||
'dashicons-email-alt',
|
||||
[
|
||||
[
|
||||
'label' => __('发送系统消息', 'aksrt'),
|
||||
'label' => __('发送系统消息', 'jio'),
|
||||
'dialog' => self::MESSAGE_COMPOSE_ID,
|
||||
'icon' => 'dashicons-megaphone',
|
||||
'primary' => true,
|
||||
],
|
||||
[
|
||||
'label' => __('通知配置', 'aksrt'),
|
||||
'url' => admin_url('admin.php?page=aksrt-settings#/users/notifications'),
|
||||
'label' => __('通知配置', 'jio'),
|
||||
'url' => admin_url('admin.php?page=jio-settings#/users/notifications'),
|
||||
'icon' => 'dashicons-admin-generic',
|
||||
],
|
||||
]
|
||||
@@ -62,7 +62,7 @@ trait UsersMenusMessages {
|
||||
$this->render_message_notices();
|
||||
|
||||
if (NotificationAdminQuery::table() === '') {
|
||||
echo '<div class="notice notice-warning"><p>' . esc_html__('通知表不可用。', 'aksrt') . '</p></div>';
|
||||
echo '<div class="notice notice-warning"><p>' . esc_html__('通知表不可用。', 'jio') . '</p></div>';
|
||||
NativeUi::page_close();
|
||||
return;
|
||||
}
|
||||
@@ -71,8 +71,8 @@ trait UsersMenusMessages {
|
||||
$result = NotificationAdminQuery::paginate($query, $paged);
|
||||
$this->render_message_overview();
|
||||
|
||||
NativeUi::card_open(__('站内通知', 'aksrt'), '', true, 'aksrt-messages-admin__card');
|
||||
echo '<div class="aksrt-messages-admin__toolbar">';
|
||||
NativeUi::card_open(__('站内通知', 'jio'), '', true, 'jio-messages-admin__card');
|
||||
echo '<div class="jio-messages-admin__toolbar">';
|
||||
echo $this->message_views_html($filters, NotificationAdminQuery::status_counts($query)); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
$this->render_message_filter_form($filters);
|
||||
echo '</div>';
|
||||
@@ -94,7 +94,7 @@ trait UsersMenusMessages {
|
||||
|
||||
public function handle_send_message(): void {
|
||||
$this->ensure_cap();
|
||||
check_admin_referer('aksrt_users_send_message');
|
||||
check_admin_referer('jio_users_send_message');
|
||||
|
||||
$mode = sanitize_key((string) wp_unslash($_POST['recipient_mode'] ?? 'users'));
|
||||
$title = trim(sanitize_text_field((string) wp_unslash($_POST['title'] ?? '')));
|
||||
@@ -102,16 +102,16 @@ trait UsersMenusMessages {
|
||||
$target_url = esc_url_raw(trim((string) wp_unslash($_POST['target_url'] ?? '')));
|
||||
|
||||
if (!in_array($mode, ['users', 'role'], true)) {
|
||||
$this->message_fail(__('请选择有效的收件人模式。', 'aksrt'));
|
||||
$this->message_fail(__('请选择有效的收件人模式。', 'jio'));
|
||||
}
|
||||
if ($content === '') {
|
||||
$this->message_fail(__('请填写消息内容。', 'aksrt'));
|
||||
$this->message_fail(__('请填写消息内容。', 'jio'));
|
||||
}
|
||||
if (mb_strlen($content) > AdminBroadcast::CONTENT_MAX) {
|
||||
$this->message_fail(sprintf(__('消息内容不能超过 %d 字。', 'aksrt'), AdminBroadcast::CONTENT_MAX));
|
||||
$this->message_fail(sprintf(__('消息内容不能超过 %d 字。', 'jio'), AdminBroadcast::CONTENT_MAX));
|
||||
}
|
||||
if (mb_strlen($title) > AdminBroadcast::TITLE_MAX) {
|
||||
$this->message_fail(sprintf(__('消息标题不能超过 %d 字。', 'aksrt'), AdminBroadcast::TITLE_MAX));
|
||||
$this->message_fail(sprintf(__('消息标题不能超过 %d 字。', 'jio'), AdminBroadcast::TITLE_MAX));
|
||||
}
|
||||
$title = mb_substr($title !== '' ? $title : $content, 0, AdminBroadcast::TITLE_MAX);
|
||||
|
||||
@@ -128,28 +128,28 @@ trait UsersMenusMessages {
|
||||
$this->message_fail($ids->get_error_message());
|
||||
}
|
||||
if ($ids === []) {
|
||||
$this->message_fail(__('没有匹配到任何有效收件人。', 'aksrt'));
|
||||
$this->message_fail(__('没有匹配到任何有效收件人。', 'jio'));
|
||||
}
|
||||
// 确认绑定角色与人数;页面加载后人数变化时拒绝投递,避免按过期预览群发。
|
||||
if ($mode === 'role' && (
|
||||
sanitize_key((string) wp_unslash($_POST['confirmed_role'] ?? '')) !== $role
|
||||
|| absint($_POST['confirmed_count'] ?? 0) !== count($ids)
|
||||
)) {
|
||||
$this->message_fail(__('收件人范围尚未确认或人数已变化,请重新确认后发送。', 'aksrt'));
|
||||
$this->message_fail(__('收件人范围尚未确认或人数已变化,请重新确认后发送。', 'jio'));
|
||||
}
|
||||
|
||||
$sent = AdminBroadcast::send($ids, $title, $content, $target_url);
|
||||
$this->message_redirect([
|
||||
'aksrt_msg' => $sent['sent'] > 0 ? 'sent' : 'send_failed',
|
||||
'aksrt_msg_n' => $sent['sent'],
|
||||
'aksrt_msg_f' => $sent['failed'],
|
||||
'aksrt_msg_u' => $unknown,
|
||||
'jio_msg' => $sent['sent'] > 0 ? 'sent' : 'send_failed',
|
||||
'jio_msg_n' => $sent['sent'],
|
||||
'jio_msg_f' => $sent['failed'],
|
||||
'jio_msg_u' => $unknown,
|
||||
]);
|
||||
}
|
||||
|
||||
public function handle_bulk_messages(): void {
|
||||
$this->ensure_cap();
|
||||
check_admin_referer('aksrt_users_bulk_messages');
|
||||
check_admin_referer('jio_users_bulk_messages');
|
||||
|
||||
$op = sanitize_key((string) wp_unslash($_POST['bulk_action'] ?? ''));
|
||||
$single_id = absint($_POST['message_id'] ?? 0);
|
||||
@@ -161,46 +161,46 @@ trait UsersMenusMessages {
|
||||
}
|
||||
|
||||
if (!in_array($op, ['delete', 'mark_read'], true) || array_filter($ids) === []) {
|
||||
$this->message_redirect(array_merge($back, ['aksrt_msg' => 'bulk_none']));
|
||||
$this->message_redirect(array_merge($back, ['jio_msg' => 'bulk_none']));
|
||||
}
|
||||
$count = $op === 'delete' ? NotificationAdminQuery::delete($ids) : NotificationAdminQuery::mark_read($ids);
|
||||
$this->message_redirect(array_merge($back, [
|
||||
'aksrt_msg' => $op === 'delete' ? 'deleted' : 'marked',
|
||||
'aksrt_msg_n' => $count,
|
||||
'jio_msg' => $op === 'delete' ? 'deleted' : 'marked',
|
||||
'jio_msg_n' => $count,
|
||||
]));
|
||||
}
|
||||
|
||||
private function render_message_notices(): void {
|
||||
$code = sanitize_key((string) wp_unslash($_GET['aksrt_msg'] ?? ''));
|
||||
$n = absint($_GET['aksrt_msg_n'] ?? 0);
|
||||
$code = sanitize_key((string) wp_unslash($_GET['jio_msg'] ?? ''));
|
||||
$n = absint($_GET['jio_msg_n'] ?? 0);
|
||||
$error = false;
|
||||
switch ($code) {
|
||||
case 'sent':
|
||||
$text = sprintf(__('已发送 %d 条消息。', 'aksrt'), $n);
|
||||
$failed = absint($_GET['aksrt_msg_f'] ?? 0);
|
||||
$unknown = absint($_GET['aksrt_msg_u'] ?? 0);
|
||||
$text = sprintf(__('已发送 %d 条消息。', 'jio'), $n);
|
||||
$failed = absint($_GET['jio_msg_f'] ?? 0);
|
||||
$unknown = absint($_GET['jio_msg_u'] ?? 0);
|
||||
if ($failed > 0) {
|
||||
$text .= ' ' . sprintf(__('投递失败 %d 条。', 'aksrt'), $failed);
|
||||
$text .= ' ' . sprintf(__('投递失败 %d 条。', 'jio'), $failed);
|
||||
}
|
||||
if ($unknown > 0) {
|
||||
$text .= ' ' . sprintf(__('%d 个收件人未找到,已跳过。', 'aksrt'), $unknown);
|
||||
$text .= ' ' . sprintf(__('%d 个收件人未找到,已跳过。', 'jio'), $unknown);
|
||||
}
|
||||
break;
|
||||
case 'send_failed':
|
||||
$error = true;
|
||||
$text = isset($_GET['aksrt_msg_error'])
|
||||
? sanitize_text_field((string) wp_unslash($_GET['aksrt_msg_error']))
|
||||
: __('消息发送失败。', 'aksrt');
|
||||
$text = isset($_GET['jio_msg_error'])
|
||||
? sanitize_text_field((string) wp_unslash($_GET['jio_msg_error']))
|
||||
: __('消息发送失败。', 'jio');
|
||||
break;
|
||||
case 'deleted':
|
||||
$text = sprintf(__('已删除 %d 条消息。', 'aksrt'), $n);
|
||||
$text = sprintf(__('已删除 %d 条消息。', 'jio'), $n);
|
||||
break;
|
||||
case 'marked':
|
||||
$text = sprintf(__('已将 %d 条消息标记为已读。', 'aksrt'), $n);
|
||||
$text = sprintf(__('已将 %d 条消息标记为已读。', 'jio'), $n);
|
||||
break;
|
||||
case 'bulk_none':
|
||||
$error = true;
|
||||
$text = __('请先勾选消息并选择操作。', 'aksrt');
|
||||
$text = __('请先勾选消息并选择操作。', 'jio');
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@@ -258,7 +258,7 @@ trait UsersMenusMessages {
|
||||
}
|
||||
|
||||
private function message_fail(string $error): void {
|
||||
$this->message_redirect(['aksrt_msg' => 'send_failed', 'aksrt_msg_error' => $error]);
|
||||
$this->message_redirect(['jio_msg' => 'send_failed', 'jio_msg_error' => $error]);
|
||||
}
|
||||
|
||||
private function message_redirect(array $args): void {
|
||||
|
||||
@@ -2,108 +2,108 @@
|
||||
/**
|
||||
* 消息管理弹窗:发送表单、只读详情、删除与角色群发共用的二次确认。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Messages\AdminBroadcast;
|
||||
use Jio\Messages\AdminBroadcast;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait UsersMenusMessagesCompose {
|
||||
|
||||
private function render_message_compose(): void {
|
||||
$open = sanitize_key((string) wp_unslash($_GET['aksrt_msg'] ?? '')) === 'send_failed';
|
||||
$open = sanitize_key((string) wp_unslash($_GET['jio_msg'] ?? '')) === 'send_failed';
|
||||
printf(
|
||||
'<dialog class="aksrt-msg-dialog" id="%s" aria-labelledby="%s-title"%s>',
|
||||
esc_attr(self::MESSAGE_COMPOSE_ID), esc_attr(self::MESSAGE_COMPOSE_ID), $open ? ' data-aksrt-dialog-autoopen' : ''
|
||||
'<dialog class="jio-msg-dialog" id="%s" aria-labelledby="%s-title"%s>',
|
||||
esc_attr(self::MESSAGE_COMPOSE_ID), esc_attr(self::MESSAGE_COMPOSE_ID), $open ? ' data-jio-dialog-autoopen' : ''
|
||||
);
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-msg-dialog__frame aksrt-messages-admin__compose" data-aksrt-msg-compose';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-msg-dialog__frame jio-messages-admin__compose" data-jio-msg-compose';
|
||||
$strings = [
|
||||
'count-error' => __('最多输入 {count} 字。', 'aksrt'),
|
||||
'role-hint' => __('将发送给 {count} 位用户;提交前需再次确认。', 'aksrt'),
|
||||
'role-title' => __('确认按角色群发', 'aksrt'),
|
||||
'role-confirm' => __('将向「{role}」的 {count} 位用户发送系统消息,并按通知配置触发提醒。请确认收件人范围与消息内容。', 'aksrt'),
|
||||
'role-send' => __('确认发送', 'aksrt'),
|
||||
'count-error' => __('最多输入 {count} 字。', 'jio'),
|
||||
'role-hint' => __('将发送给 {count} 位用户;提交前需再次确认。', 'jio'),
|
||||
'role-title' => __('确认按角色群发', 'jio'),
|
||||
'role-confirm' => __('将向「{role}」的 {count} 位用户发送系统消息,并按通知配置触发提醒。请确认收件人范围与消息内容。', 'jio'),
|
||||
'role-send' => __('确认发送', 'jio'),
|
||||
];
|
||||
foreach ($strings as $key => $text) {
|
||||
echo ' data-i18n-' . esc_attr($key) . '="' . esc_attr($text) . '"';
|
||||
}
|
||||
echo '>';
|
||||
echo '<input type="hidden" name="action" value="aksrt_users_send_message" />';
|
||||
echo '<input type="hidden" name="action" value="jio_users_send_message" />';
|
||||
echo '<input type="hidden" name="confirmed_role" value="" /><input type="hidden" name="confirmed_count" value="" />';
|
||||
wp_nonce_field('aksrt_users_send_message');
|
||||
$this->render_message_dialog_head(self::MESSAGE_COMPOSE_ID, __('发送系统消息', 'aksrt'), 'dashicons-megaphone');
|
||||
echo '<div class="aksrt-msg-dialog__body">';
|
||||
wp_nonce_field('jio_users_send_message');
|
||||
$this->render_message_dialog_head(self::MESSAGE_COMPOSE_ID, __('发送系统消息', 'jio'), 'dashicons-megaphone');
|
||||
echo '<div class="jio-msg-dialog__body">';
|
||||
if ($open) {
|
||||
$error = sanitize_text_field((string) wp_unslash($_GET['aksrt_msg_error'] ?? __('消息发送失败,请检查后重试。', 'aksrt')));
|
||||
echo '<p class="aksrt-native-callout is-danger">' . esc_html($error) . '</p>';
|
||||
$error = sanitize_text_field((string) wp_unslash($_GET['jio_msg_error'] ?? __('消息发送失败,请检查后重试。', 'jio')));
|
||||
echo '<p class="jio-native-callout is-danger">' . esc_html($error) . '</p>';
|
||||
}
|
||||
echo '<fieldset class="aksrt-msg-modes"><legend class="aksrt-native-field__label">' . esc_html__('收件人', 'aksrt') . '</legend>';
|
||||
foreach (['users' => [__('指定用户', 'aksrt'), 'dashicons-admin-users'], 'role' => [__('按角色群发', 'aksrt'), 'dashicons-groups']] as $value => [$label, $icon]) {
|
||||
echo '<label class="aksrt-msg-mode"><input type="radio" name="recipient_mode" value="' . esc_attr($value) . '"' . checked($value, 'users', false) . ' />';
|
||||
echo '<fieldset class="jio-msg-modes"><legend class="jio-native-field__label">' . esc_html__('收件人', 'jio') . '</legend>';
|
||||
foreach (['users' => [__('指定用户', 'jio'), 'dashicons-admin-users'], 'role' => [__('按角色群发', 'jio'), 'dashicons-groups']] as $value => [$label, $icon]) {
|
||||
echo '<label class="jio-msg-mode"><input type="radio" name="recipient_mode" value="' . esc_attr($value) . '"' . checked($value, 'users', false) . ' />';
|
||||
echo '<span><span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>' . esc_html($label) . '</span></label>';
|
||||
}
|
||||
echo '</fieldset><div class="aksrt-native-form-grid">';
|
||||
echo '</fieldset><div class="jio-native-form-grid">';
|
||||
printf(
|
||||
'<label class="aksrt-native-field is-wide" data-aksrt-msg-pane="users"><span class="aksrt-native-field__label">%s</span><textarea name="users" rows="2" required placeholder="%s"></textarea><small class="aksrt-native-field__hint">%s</small></label>',
|
||||
esc_html__('用户', 'aksrt'), esc_attr__('用户 ID / 用户名 / 邮箱,逗号或换行分隔', 'aksrt'), esc_html(sprintf(__('单次最多 %d 位。', 'aksrt'), AdminBroadcast::MAX_USERS))
|
||||
'<label class="jio-native-field is-wide" data-jio-msg-pane="users"><span class="jio-native-field__label">%s</span><textarea name="users" rows="2" required placeholder="%s"></textarea><small class="jio-native-field__hint">%s</small></label>',
|
||||
esc_html__('用户', 'jio'), esc_attr__('用户 ID / 用户名 / 邮箱,逗号或换行分隔', 'jio'), esc_html(sprintf(__('单次最多 %d 位。', 'jio'), AdminBroadcast::MAX_USERS))
|
||||
);
|
||||
echo '<label class="aksrt-native-field is-wide" data-aksrt-msg-pane="role" hidden><span class="aksrt-native-field__label">' . esc_html__('角色', 'aksrt') . '</span>';
|
||||
echo '<select name="role" required disabled><option value="" selected disabled>' . esc_html__('请选择角色', 'aksrt') . '</option>';
|
||||
echo '<label class="jio-native-field is-wide" data-jio-msg-pane="role" hidden><span class="jio-native-field__label">' . esc_html__('角色', 'jio') . '</span>';
|
||||
echo '<select name="role" required disabled><option value="" selected disabled>' . esc_html__('请选择角色', 'jio') . '</option>';
|
||||
$counts = count_users('time');
|
||||
foreach (wp_roles()->get_names() as $role => $name) {
|
||||
$count = (int) ($counts['avail_roles'][$role] ?? 0);
|
||||
$label = translate_user_role($name);
|
||||
$disabled = $count === 0 || $count > AdminBroadcast::MAX_ROLE_USERS;
|
||||
$suffix = $count > AdminBroadcast::MAX_ROLE_USERS ? __('(超过发送上限)', 'aksrt') : ($count === 0 ? __('(暂无用户)', 'aksrt') : '');
|
||||
$suffix = $count > AdminBroadcast::MAX_ROLE_USERS ? __('(超过发送上限)', 'jio') : ($count === 0 ? __('(暂无用户)', 'jio') : '');
|
||||
printf(
|
||||
'<option value="%s" data-count="%d" data-label="%s"%s>%s</option>',
|
||||
esc_attr($role), $count, esc_attr($label), $disabled ? ' disabled' : '',
|
||||
esc_html(sprintf(__('%1$s · %2$s 人', 'aksrt'), $label, number_format_i18n($count)) . $suffix)
|
||||
esc_html(sprintf(__('%1$s · %2$s 人', 'jio'), $label, number_format_i18n($count)) . $suffix)
|
||||
);
|
||||
}
|
||||
echo '</select><small class="aksrt-native-field__hint">' . esc_html(sprintf(__('单个角色最多 %d 人;空角色和超限角色不可选。', 'aksrt'), AdminBroadcast::MAX_ROLE_USERS)) . '</small>';
|
||||
echo '<small class="aksrt-native-field__hint" data-aksrt-msg-role-hint aria-live="polite"></small></label>';
|
||||
echo '</select><small class="jio-native-field__hint">' . esc_html(sprintf(__('单个角色最多 %d 人;空角色和超限角色不可选。', 'jio'), AdminBroadcast::MAX_ROLE_USERS)) . '</small>';
|
||||
echo '<small class="jio-native-field__hint" data-jio-msg-role-hint aria-live="polite"></small></label>';
|
||||
printf(
|
||||
'<label class="aksrt-native-field"><span class="aksrt-native-field__label">%s</span><input type="text" name="title" data-count-limit="%d" aria-describedby="aksrt-msg-title-count" placeholder="%s" /><small class="aksrt-msg-counter" id="aksrt-msg-title-count" data-count-for="title"></small></label>',
|
||||
esc_html__('标题', 'aksrt'), AdminBroadcast::TITLE_MAX, esc_attr__('留空则取内容开头', 'aksrt')
|
||||
'<label class="jio-native-field"><span class="jio-native-field__label">%s</span><input type="text" name="title" data-count-limit="%d" aria-describedby="jio-msg-title-count" placeholder="%s" /><small class="jio-msg-counter" id="jio-msg-title-count" data-count-for="title"></small></label>',
|
||||
esc_html__('标题', 'jio'), AdminBroadcast::TITLE_MAX, esc_attr__('留空则取内容开头', 'jio')
|
||||
);
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('跳转链接(可选)', 'aksrt') . '</span><input type="url" name="target_url" placeholder="https://" /></label>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('跳转链接(可选)', 'jio') . '</span><input type="url" name="target_url" placeholder="https://" /></label>';
|
||||
printf(
|
||||
'<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">%s</span><textarea name="content" rows="5" data-count-limit="%d" required aria-describedby="aksrt-msg-content-count"></textarea><small class="aksrt-msg-counter" id="aksrt-msg-content-count" data-count-for="content"></small></label>',
|
||||
esc_html__('内容', 'aksrt'), AdminBroadcast::CONTENT_MAX
|
||||
'<label class="jio-native-field is-wide"><span class="jio-native-field__label">%s</span><textarea name="content" rows="5" data-count-limit="%d" required aria-describedby="jio-msg-content-count"></textarea><small class="jio-msg-counter" id="jio-msg-content-count" data-count-for="content"></small></label>',
|
||||
esc_html__('内容', 'jio'), AdminBroadcast::CONTENT_MAX
|
||||
);
|
||||
echo '</div><p class="aksrt-native-muted aksrt-msg-dialog__note">' . esc_html__('以「系统通知」类型逐人投递;邮件 / 公众号提醒按通知配置同步触发。', 'aksrt') . '</p></div>';
|
||||
echo '<footer class="aksrt-msg-dialog__foot"><button type="button" class="button" data-aksrt-dialog-close>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary" data-aksrt-msg-send>' . esc_html__('发送', 'aksrt') . '</button></footer></form></dialog>';
|
||||
echo '</div><p class="jio-native-muted jio-msg-dialog__note">' . esc_html__('以「系统通知」类型逐人投递;邮件 / 公众号提醒按通知配置同步触发。', 'jio') . '</p></div>';
|
||||
echo '<footer class="jio-msg-dialog__foot"><button type="button" class="button" data-jio-dialog-close>' . esc_html__('取消', 'jio') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary" data-jio-msg-send>' . esc_html__('发送', 'jio') . '</button></footer></form></dialog>';
|
||||
}
|
||||
|
||||
private function render_message_dialogs(): void {
|
||||
echo '<dialog class="aksrt-msg-dialog" id="aksrt-msg-detail" aria-labelledby="aksrt-msg-detail-title"><div class="aksrt-msg-dialog__frame">';
|
||||
$this->render_message_dialog_head('aksrt-msg-detail', __('消息详情', 'aksrt'), 'dashicons-email-alt');
|
||||
echo '<div class="aksrt-msg-dialog__body" data-aksrt-msg-detail-body></div>';
|
||||
echo '<footer class="aksrt-msg-dialog__foot"><button type="button" class="button" data-aksrt-dialog-close>' . esc_html__('关闭', 'aksrt') . '</button></footer></div></dialog>';
|
||||
echo '<dialog class="jio-msg-dialog" id="jio-msg-detail" aria-labelledby="jio-msg-detail-title"><div class="jio-msg-dialog__frame">';
|
||||
$this->render_message_dialog_head('jio-msg-detail', __('消息详情', 'jio'), 'dashicons-email-alt');
|
||||
echo '<div class="jio-msg-dialog__body" data-jio-msg-detail-body></div>';
|
||||
echo '<footer class="jio-msg-dialog__foot"><button type="button" class="button" data-jio-dialog-close>' . esc_html__('关闭', 'jio') . '</button></footer></div></dialog>';
|
||||
|
||||
echo '<dialog class="aksrt-msg-dialog aksrt-msg-dialog--confirm" id="aksrt-msg-confirm" aria-labelledby="aksrt-msg-confirm-title" aria-describedby="aksrt-msg-confirm-text"';
|
||||
echo '<dialog class="jio-msg-dialog jio-msg-dialog--confirm" id="jio-msg-confirm" aria-labelledby="jio-msg-confirm-title" aria-describedby="jio-msg-confirm-text"';
|
||||
foreach ([
|
||||
'delete-title' => __('删除消息', 'aksrt'),
|
||||
'delete-text' => __('确定删除 {count} 条消息?删除后用户收件箱中也将不再显示,此操作不可撤销。', 'aksrt'),
|
||||
'delete-send' => __('确认删除', 'aksrt'),
|
||||
'delete-title' => __('删除消息', 'jio'),
|
||||
'delete-text' => __('确定删除 {count} 条消息?删除后用户收件箱中也将不再显示,此操作不可撤销。', 'jio'),
|
||||
'delete-send' => __('确认删除', 'jio'),
|
||||
] as $key => $text) {
|
||||
echo ' data-i18n-' . esc_attr($key) . '="' . esc_attr($text) . '"';
|
||||
}
|
||||
echo '><div class="aksrt-msg-dialog__frame">';
|
||||
$this->render_message_dialog_head('aksrt-msg-confirm', __('确认操作', 'aksrt'), 'dashicons-warning');
|
||||
echo '<div class="aksrt-msg-dialog__body"><p id="aksrt-msg-confirm-text"></p></div>';
|
||||
echo '<footer class="aksrt-msg-dialog__foot"><button type="button" class="button" data-aksrt-dialog-close autofocus>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
echo '<button type="button" class="button button-primary" data-aksrt-msg-confirm>' . esc_html__('确认', 'aksrt') . '</button></footer></div></dialog>';
|
||||
echo '><div class="jio-msg-dialog__frame">';
|
||||
$this->render_message_dialog_head('jio-msg-confirm', __('确认操作', 'jio'), 'dashicons-warning');
|
||||
echo '<div class="jio-msg-dialog__body"><p id="jio-msg-confirm-text"></p></div>';
|
||||
echo '<footer class="jio-msg-dialog__foot"><button type="button" class="button" data-jio-dialog-close autofocus>' . esc_html__('取消', 'jio') . '</button>';
|
||||
echo '<button type="button" class="button button-primary" data-jio-msg-confirm>' . esc_html__('确认', 'jio') . '</button></footer></div></dialog>';
|
||||
}
|
||||
|
||||
private function render_message_dialog_head(string $id, string $title, string $icon): void {
|
||||
echo '<header class="aksrt-msg-dialog__head"><h2 id="' . esc_attr($id) . '-title" class="aksrt-msg-dialog__title">';
|
||||
echo '<span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span><span data-aksrt-dialog-title>' . esc_html($title) . '</span></h2>';
|
||||
echo '<button type="button" class="aksrt-msg-dialog__close" data-aksrt-dialog-close aria-label="' . esc_attr__('关闭', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
echo '<header class="jio-msg-dialog__head"><h2 id="' . esc_attr($id) . '-title" class="jio-msg-dialog__title">';
|
||||
echo '<span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span><span data-jio-dialog-title>' . esc_html($title) . '</span></h2>';
|
||||
echo '<button type="button" class="jio-msg-dialog__close" data-jio-dialog-close aria-label="' . esc_attr__('关闭', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,13 @@
|
||||
/**
|
||||
* 消息管理展示:概览、筛选、列表与按行预渲染的只读详情。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Messages\MessageTypes;
|
||||
use AKSRT\Messages\NotificationAdminQuery;
|
||||
use Jio\Messages\MessageTypes;
|
||||
use Jio\Messages\NotificationAdminQuery;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -17,22 +17,22 @@ trait UsersMenusMessagesList {
|
||||
private function render_message_overview(): void {
|
||||
$stats = NotificationAdminQuery::overview();
|
||||
NativeUi::stats([
|
||||
['label' => __('消息总数', 'aksrt'), 'value' => number_format_i18n($stats['total']), 'icon' => 'dashicons-email-alt', 'tone' => 'info', 'url' => $this->message_page_url()],
|
||||
['label' => __('未读', 'aksrt'), 'value' => number_format_i18n($stats['unread']), 'icon' => 'dashicons-marker', 'tone' => 'danger', 'url' => $this->message_page_url(['status' => 'unread'])],
|
||||
['label' => __('今日新增', 'aksrt'), 'value' => number_format_i18n($stats['today']), 'icon' => 'dashicons-calendar-alt', 'tone' => 'success', 'url' => $this->message_page_url(['period' => 'today'])],
|
||||
['label' => __('涉及用户', 'aksrt'), 'value' => number_format_i18n($stats['recipients']), 'icon' => 'dashicons-groups', 'tone' => 'primary', 'url' => admin_url('users.php'), 'hint' => __('前往用户管理', 'aksrt')],
|
||||
['label' => __('消息总数', 'jio'), 'value' => number_format_i18n($stats['total']), 'icon' => 'dashicons-email-alt', 'tone' => 'info', 'url' => $this->message_page_url()],
|
||||
['label' => __('未读', 'jio'), 'value' => number_format_i18n($stats['unread']), 'icon' => 'dashicons-marker', 'tone' => 'danger', 'url' => $this->message_page_url(['status' => 'unread'])],
|
||||
['label' => __('今日新增', 'jio'), 'value' => number_format_i18n($stats['today']), 'icon' => 'dashicons-calendar-alt', 'tone' => 'success', 'url' => $this->message_page_url(['period' => 'today'])],
|
||||
['label' => __('涉及用户', 'jio'), 'value' => number_format_i18n($stats['recipients']), 'icon' => 'dashicons-groups', 'tone' => 'primary', 'url' => admin_url('users.php'), 'hint' => __('前往用户管理', 'jio')],
|
||||
]);
|
||||
}
|
||||
|
||||
private function message_views_html(array $filters, array $counts): string {
|
||||
$views = [
|
||||
'' => [__('全部', 'aksrt'), $counts['all']],
|
||||
'unread' => [__('未读', 'aksrt'), $counts['unread']],
|
||||
'read' => [__('已读', 'aksrt'), $counts['read']],
|
||||
'' => [__('全部', 'jio'), $counts['all']],
|
||||
'unread' => [__('未读', 'jio'), $counts['unread']],
|
||||
'read' => [__('已读', 'jio'), $counts['read']],
|
||||
];
|
||||
$args = $this->message_filter_args($filters);
|
||||
unset($args['status']);
|
||||
$html = '<ul class="subsubsub" aria-label="' . esc_attr__('消息状态', 'aksrt') . '">';
|
||||
$html = '<ul class="subsubsub" aria-label="' . esc_attr__('消息状态', 'jio') . '">';
|
||||
foreach ($views as $key => [$label, $count]) {
|
||||
$html .= sprintf(
|
||||
'<li><a href="%s"%s>%s <span class="count">(%s)</span></a></li>',
|
||||
@@ -46,33 +46,33 @@ trait UsersMenusMessagesList {
|
||||
}
|
||||
|
||||
private function render_message_filter_form(array $filters): void {
|
||||
echo '<form method="get" action="' . esc_url(admin_url('users.php')) . '" class="aksrt-native-filters">';
|
||||
echo '<form method="get" action="' . esc_url(admin_url('users.php')) . '" class="jio-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="' . esc_attr(self::MESSAGE_PAGE_SLUG) . '" />';
|
||||
if ($filters['status'] !== '') {
|
||||
echo '<input type="hidden" name="status" value="' . esc_attr($filters['status']) . '" />';
|
||||
}
|
||||
echo '<label class="screen-reader-text" for="aksrt-msg-filter-type">' . esc_html__('按类型筛选', 'aksrt') . '</label>';
|
||||
echo '<select name="type" id="aksrt-msg-filter-type"><option value="">' . esc_html__('全部类型', 'aksrt') . '</option>';
|
||||
echo '<label class="screen-reader-text" for="jio-msg-filter-type">' . esc_html__('按类型筛选', 'jio') . '</label>';
|
||||
echo '<select name="type" id="jio-msg-filter-type"><option value="">' . esc_html__('全部类型', 'jio') . '</option>';
|
||||
foreach (NotificationAdminQuery::types() as $type) {
|
||||
printf('<option value="%s"%s>%s</option>', esc_attr($type), selected($filters['type'], $type, false), esc_html(MessageTypes::label($type)));
|
||||
}
|
||||
echo '</select>';
|
||||
echo '<label class="screen-reader-text" for="aksrt-msg-filter-period">' . esc_html__('按发送时间筛选', 'aksrt') . '</label>';
|
||||
echo '<select name="period" id="aksrt-msg-filter-period"><option value="">' . esc_html__('全部时间', 'aksrt') . '</option>';
|
||||
echo '<option value="today"' . selected($filters['period'], 'today', false) . '>' . esc_html__('今日新增', 'aksrt') . '</option></select>';
|
||||
echo '<label class="screen-reader-text" for="jio-msg-filter-period">' . esc_html__('按发送时间筛选', 'jio') . '</label>';
|
||||
echo '<select name="period" id="jio-msg-filter-period"><option value="">' . esc_html__('全部时间', 'jio') . '</option>';
|
||||
echo '<option value="today"' . selected($filters['period'], 'today', false) . '>' . esc_html__('今日新增', 'jio') . '</option></select>';
|
||||
printf(
|
||||
'<label class="screen-reader-text" for="aksrt-msg-filter-user">%s</label><input type="text" id="aksrt-msg-filter-user" name="user" value="%s" placeholder="%s" />',
|
||||
esc_html__('按收件人筛选', 'aksrt'), esc_attr($filters['user']), esc_attr__('收件人 ID / 用户名 / 邮箱', 'aksrt')
|
||||
'<label class="screen-reader-text" for="jio-msg-filter-user">%s</label><input type="text" id="jio-msg-filter-user" name="user" value="%s" placeholder="%s" />',
|
||||
esc_html__('按收件人筛选', 'jio'), esc_attr($filters['user']), esc_attr__('收件人 ID / 用户名 / 邮箱', 'jio')
|
||||
);
|
||||
printf(
|
||||
'<label class="screen-reader-text" for="aksrt-msg-filter-s">%s</label><input type="search" id="aksrt-msg-filter-s" name="s" value="%s" placeholder="%s" />',
|
||||
esc_html__('搜索消息', 'aksrt'), esc_attr($filters['s']), esc_attr__('标题 / 内容', 'aksrt')
|
||||
'<label class="screen-reader-text" for="jio-msg-filter-s">%s</label><input type="search" id="jio-msg-filter-s" name="s" value="%s" placeholder="%s" />',
|
||||
esc_html__('搜索消息', 'jio'), esc_attr($filters['s']), esc_attr__('标题 / 内容', 'jio')
|
||||
);
|
||||
submit_button(__('筛选', 'aksrt'), '', '', false);
|
||||
submit_button(__('筛选', 'jio'), '', '', false);
|
||||
$other = $filters;
|
||||
unset($other['status']);
|
||||
if ($this->message_filter_args($other) !== []) {
|
||||
echo '<a class="button-link" href="' . esc_url($this->message_page_url($filters['status'] === '' ? [] : ['status' => $filters['status']])) . '">' . esc_html__('清除筛选', 'aksrt') . '</a>';
|
||||
echo '<a class="button-link" href="' . esc_url($this->message_page_url($filters['status'] === '' ? [] : ['status' => $filters['status']])) . '">' . esc_html__('清除筛选', 'jio') . '</a>';
|
||||
}
|
||||
echo '</form>';
|
||||
}
|
||||
@@ -82,29 +82,29 @@ trait UsersMenusMessagesList {
|
||||
if ($user_ids !== []) {
|
||||
cache_users($user_ids);
|
||||
}
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-messages-admin__list" data-aksrt-msg-list>';
|
||||
echo '<input type="hidden" name="action" value="aksrt_users_bulk_messages" />';
|
||||
wp_nonce_field('aksrt_users_bulk_messages');
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-messages-admin__list" data-jio-msg-list>';
|
||||
echo '<input type="hidden" name="action" value="jio_users_bulk_messages" />';
|
||||
wp_nonce_field('jio_users_bulk_messages');
|
||||
foreach ($this->message_filter_args($filters) as $key => $value) {
|
||||
printf('<input type="hidden" name="%s" value="%s" />', esc_attr($key), esc_attr((string) $value));
|
||||
}
|
||||
echo '<input type="hidden" name="paged" value="' . esc_attr((string) $page) . '" />';
|
||||
echo '<input type="hidden" name="bulk_action" value="" /><input type="hidden" name="message_id" value="" />';
|
||||
echo '<div class="aksrt-messages-admin__bulk">';
|
||||
echo '<span class="aksrt-messages-admin__selection" data-aksrt-msg-selection role="status" aria-live="polite" data-label="' . esc_attr__('已选 %d 条', 'aksrt') . '"></span>';
|
||||
echo '<button type="button" class="button" data-aksrt-msg-action="mark_read" disabled>' . esc_html__('标记已读', 'aksrt') . '</button>';
|
||||
echo '<button type="button" class="button aksrt-messages-admin__danger" data-aksrt-msg-action="delete" disabled>' . esc_html__('删除', 'aksrt') . '</button>';
|
||||
echo '</div><div class="aksrt-native-table-wrap">';
|
||||
echo '<table class="wp-list-table widefat fixed striped aksrt-messages-admin__table"><thead><tr>';
|
||||
echo '<td class="manage-column column-cb check-column"><label class="screen-reader-text" for="aksrt-msg-check-all">' . esc_html__('全选本页消息', 'aksrt') . '</label><input id="aksrt-msg-check-all" type="checkbox" data-aksrt-msg-check-all /></td>';
|
||||
foreach (['title' => __('消息', 'aksrt'), 'type' => __('类型', 'aksrt'), 'user' => __('收件人', 'aksrt'), 'read' => __('状态', 'aksrt'), 'time' => __('发送时间', 'aksrt'), 'actions' => __('操作', 'aksrt')] as $column => $label) {
|
||||
echo '<div class="jio-messages-admin__bulk">';
|
||||
echo '<span class="jio-messages-admin__selection" data-jio-msg-selection role="status" aria-live="polite" data-label="' . esc_attr__('已选 %d 条', 'jio') . '"></span>';
|
||||
echo '<button type="button" class="button" data-jio-msg-action="mark_read" disabled>' . esc_html__('标记已读', 'jio') . '</button>';
|
||||
echo '<button type="button" class="button jio-messages-admin__danger" data-jio-msg-action="delete" disabled>' . esc_html__('删除', 'jio') . '</button>';
|
||||
echo '</div><div class="jio-native-table-wrap">';
|
||||
echo '<table class="wp-list-table widefat fixed striped jio-messages-admin__table"><thead><tr>';
|
||||
echo '<td class="manage-column column-cb check-column"><label class="screen-reader-text" for="jio-msg-check-all">' . esc_html__('全选本页消息', 'jio') . '</label><input id="jio-msg-check-all" type="checkbox" data-jio-msg-check-all /></td>';
|
||||
foreach (['title' => __('消息', 'jio'), 'type' => __('类型', 'jio'), 'user' => __('收件人', 'jio'), 'read' => __('状态', 'jio'), 'time' => __('发送时间', 'jio'), 'actions' => __('操作', 'jio')] as $column => $label) {
|
||||
echo '<th scope="col" class="column-' . esc_attr($column) . '">' . esc_html($label) . '</th>';
|
||||
}
|
||||
echo '</tr></thead><tbody>';
|
||||
if ($items === []) {
|
||||
$hint = $user_missing ? __('没有找到该收件人。', 'aksrt')
|
||||
: ($this->message_filter_args($filters) === [] ? __('站内还没有任何通知。', 'aksrt') : __('当前筛选条件下没有消息。', 'aksrt'));
|
||||
echo NativeUi::empty_row(7, __('暂无消息', 'aksrt'), $hint); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
$hint = $user_missing ? __('没有找到该收件人。', 'jio')
|
||||
: ($this->message_filter_args($filters) === [] ? __('站内还没有任何通知。', 'jio') : __('当前筛选条件下没有消息。', 'jio'));
|
||||
echo NativeUi::empty_row(7, __('暂无消息', 'jio'), $hint); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
}
|
||||
foreach ($items as $row) {
|
||||
$this->render_message_row($row, $filters);
|
||||
@@ -120,77 +120,77 @@ trait UsersMenusMessagesList {
|
||||
$unread = empty($row['is_read']);
|
||||
echo '<tr class="' . ($unread ? 'is-unread' : 'is-read') . '">';
|
||||
printf(
|
||||
'<th scope="row" class="check-column"><label class="screen-reader-text" for="aksrt-msg-%1$d">%2$s</label><input id="aksrt-msg-%1$d" type="checkbox" name="ids[]" value="%1$d" data-aksrt-msg-check /></th>',
|
||||
$id, esc_html(sprintf(__('选择消息 #%d', 'aksrt'), $id))
|
||||
'<th scope="row" class="check-column"><label class="screen-reader-text" for="jio-msg-%1$d">%2$s</label><input id="jio-msg-%1$d" type="checkbox" name="ids[]" value="%1$d" data-jio-msg-check /></th>',
|
||||
$id, esc_html(sprintf(__('选择消息 #%d', 'jio'), $id))
|
||||
);
|
||||
echo '<td class="column-title"><strong class="row-title">' . esc_html($title) . '</strong>';
|
||||
if ($desc !== '' && $desc !== $title) {
|
||||
echo '<div class="aksrt-messages-admin__desc">' . esc_html(mb_strimwidth($desc, 0, 160, '…')) . '</div>';
|
||||
echo '<div class="jio-messages-admin__desc">' . esc_html(mb_strimwidth($desc, 0, 160, '…')) . '</div>';
|
||||
}
|
||||
echo '<div class="aksrt-messages-admin__sub"><code>#' . esc_html((string) $id) . '</code></div>';
|
||||
echo '<div class="jio-messages-admin__sub"><code>#' . esc_html((string) $id) . '</code></div>';
|
||||
$this->render_message_detail_template($row);
|
||||
echo '</td>';
|
||||
printf(
|
||||
'<td class="column-type"><a class="aksrt-msg-badge is-%s" href="%s">%s</a></td>',
|
||||
'<td class="column-type"><a class="jio-msg-badge is-%s" href="%s">%s</a></td>',
|
||||
esc_attr(MessageTypes::tone($type)), esc_url($this->message_page_url(array_merge($this->message_filter_args($filters), ['type' => $type]))), esc_html(MessageTypes::label($type))
|
||||
);
|
||||
echo '<td class="column-user">' . $this->message_user_cell((int) $row['user_id'], $filters) . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
$read_at = (string) ($row['read_at'] ?? '');
|
||||
printf(
|
||||
'<td class="column-read"><span class="aksrt-msg-state %s"%s>%s</span></td>',
|
||||
'<td class="column-read"><span class="jio-msg-state %s"%s>%s</span></td>',
|
||||
$unread ? 'is-unread' : 'is-read',
|
||||
!$unread && $read_at !== '' ? ' title="' . esc_attr(sprintf(__('阅读于 %s', 'aksrt'), $this->message_time($read_at))) . '"' : '',
|
||||
$unread ? esc_html__('未读', 'aksrt') : esc_html__('已读', 'aksrt')
|
||||
!$unread && $read_at !== '' ? ' title="' . esc_attr(sprintf(__('阅读于 %s', 'jio'), $this->message_time($read_at))) . '"' : '',
|
||||
$unread ? esc_html__('未读', 'jio') : esc_html__('已读', 'jio')
|
||||
);
|
||||
$created = (string) $row['created_at'];
|
||||
$ts = $created !== '' ? strtotime($created . ' UTC') : false;
|
||||
printf(
|
||||
'<td class="column-time"><time datetime="%s">%s</time>%s</td>',
|
||||
esc_attr($ts ? gmdate('c', $ts) : ''), esc_html($this->message_time($created)),
|
||||
$ts ? '<div class="aksrt-messages-admin__sub">' . esc_html(sprintf(__('%s前', 'aksrt'), human_time_diff($ts))) . '</div>' : ''
|
||||
$ts ? '<div class="jio-messages-admin__sub">' . esc_html(sprintf(__('%s前', 'jio'), human_time_diff($ts))) . '</div>' : ''
|
||||
);
|
||||
echo '<td class="column-actions"><div class="aksrt-msg-row-actions">';
|
||||
printf('<button type="button" class="button-link" data-aksrt-msg-view="%d" aria-haspopup="dialog" aria-controls="aksrt-msg-detail">%s</button>', $id, esc_html__('查看', 'aksrt'));
|
||||
echo '<td class="column-actions"><div class="jio-msg-row-actions">';
|
||||
printf('<button type="button" class="button-link" data-jio-msg-view="%d" aria-haspopup="dialog" aria-controls="jio-msg-detail">%s</button>', $id, esc_html__('查看', 'jio'));
|
||||
if ($unread) {
|
||||
printf('<button type="button" class="button-link" data-aksrt-msg-action="mark_read" data-id="%d">%s</button>', $id, esc_html__('标记已读', 'aksrt'));
|
||||
printf('<button type="button" class="button-link" data-jio-msg-action="mark_read" data-id="%d">%s</button>', $id, esc_html__('标记已读', 'jio'));
|
||||
}
|
||||
printf('<button type="button" class="button-link button-link-delete" data-aksrt-msg-action="delete" data-id="%d">%s</button>', $id, esc_html__('删除', 'aksrt'));
|
||||
printf('<button type="button" class="button-link button-link-delete" data-jio-msg-action="delete" data-id="%d">%s</button>', $id, esc_html__('删除', 'jio'));
|
||||
echo '</div></td></tr>';
|
||||
}
|
||||
|
||||
private function render_message_detail_template(array $row): void {
|
||||
$user = get_userdata((int) $row['user_id']);
|
||||
$recipient = $user ? $user->display_name : __('已删除用户', 'aksrt');
|
||||
echo '<template id="aksrt-msg-detail-' . esc_attr((string) $row['id']) . '">';
|
||||
echo '<h3 class="aksrt-msg-detail__title">' . esc_html((string) $row['title']) . '</h3><dl class="aksrt-msg-detail__meta">';
|
||||
$recipient = $user ? $user->display_name : __('已删除用户', 'jio');
|
||||
echo '<template id="jio-msg-detail-' . esc_attr((string) $row['id']) . '">';
|
||||
echo '<h3 class="jio-msg-detail__title">' . esc_html((string) $row['title']) . '</h3><dl class="jio-msg-detail__meta">';
|
||||
$meta = [
|
||||
__('消息编号', 'aksrt') => '#' . $row['id'],
|
||||
__('收件人', 'aksrt') => $recipient . ' · #' . $row['user_id'],
|
||||
__('消息类型', 'aksrt') => MessageTypes::label((string) $row['type']),
|
||||
__('发送时间', 'aksrt') => $this->message_time((string) $row['created_at']),
|
||||
__('阅读状态', 'aksrt') => empty($row['is_read']) ? __('未读', 'aksrt') : __('已读', 'aksrt'),
|
||||
__('阅读时间', 'aksrt') => empty($row['is_read']) || empty($row['read_at']) ? '—' : $this->message_time((string) $row['read_at']),
|
||||
__('消息编号', 'jio') => '#' . $row['id'],
|
||||
__('收件人', 'jio') => $recipient . ' · #' . $row['user_id'],
|
||||
__('消息类型', 'jio') => MessageTypes::label((string) $row['type']),
|
||||
__('发送时间', 'jio') => $this->message_time((string) $row['created_at']),
|
||||
__('阅读状态', 'jio') => empty($row['is_read']) ? __('未读', 'jio') : __('已读', 'jio'),
|
||||
__('阅读时间', 'jio') => empty($row['is_read']) || empty($row['read_at']) ? '—' : $this->message_time((string) $row['read_at']),
|
||||
];
|
||||
foreach ($meta as $label => $value) {
|
||||
echo '<div><dt>' . esc_html($label) . '</dt><dd>' . esc_html($value) . '</dd></div>';
|
||||
}
|
||||
echo '</dl><h4>' . esc_html__('完整内容', 'aksrt') . '</h4><div class="aksrt-msg-detail__content">' . esc_html((string) $row['description']) . '</div>';
|
||||
echo '</dl><h4>' . esc_html__('完整内容', 'jio') . '</h4><div class="jio-msg-detail__content">' . esc_html((string) $row['description']) . '</div>';
|
||||
if (!empty($row['target_url'])) {
|
||||
printf('<a class="aksrt-msg-detail__link" href="%s" target="_blank" rel="noopener noreferrer">%s<span class="screen-reader-text">%s</span></a>', esc_url((string) $row['target_url']), esc_html((string) $row['target_url']), esc_html__('(在新窗口打开)', 'aksrt'));
|
||||
printf('<a class="jio-msg-detail__link" href="%s" target="_blank" rel="noopener noreferrer">%s<span class="screen-reader-text">%s</span></a>', esc_url((string) $row['target_url']), esc_html((string) $row['target_url']), esc_html__('(在新窗口打开)', 'jio'));
|
||||
}
|
||||
echo '<p class="aksrt-native-field__hint">' . esc_html__('查看详情不会改变收件人的已读状态。', 'aksrt') . '</p></template>';
|
||||
echo '<p class="jio-native-field__hint">' . esc_html__('查看详情不会改变收件人的已读状态。', 'jio') . '</p></template>';
|
||||
}
|
||||
|
||||
private function message_user_cell(int $user_id, array $filters): string {
|
||||
$user = $user_id > 0 ? get_userdata($user_id) : false;
|
||||
if (!$user) {
|
||||
return '<span class="aksrt-messages-admin__muted">' . esc_html(sprintf(__('已删除用户 #%d', 'aksrt'), $user_id)) . '</span>';
|
||||
return '<span class="jio-messages-admin__muted">' . esc_html(sprintf(__('已删除用户 #%d', 'jio'), $user_id)) . '</span>';
|
||||
}
|
||||
$avatar = function_exists('aksrt_get_avatar_url') ? aksrt_get_avatar_url($user_id, 48) : get_avatar_url($user_id, ['size' => 48]);
|
||||
$avatar = function_exists('jio_get_avatar_url') ? jio_get_avatar_url($user_id, 48) : get_avatar_url($user_id, ['size' => 48]);
|
||||
$filter_url = $this->message_page_url(array_merge($this->message_filter_args($filters), ['user' => (string) $user_id]));
|
||||
return '<a class="aksrt-msg-user" href="' . esc_url($filter_url) . '" title="' . esc_attr__('只看该用户', 'aksrt') . '">'
|
||||
return '<a class="jio-msg-user" href="' . esc_url($filter_url) . '" title="' . esc_attr__('只看该用户', 'jio') . '">'
|
||||
. '<img src="' . esc_url($avatar) . '" width="24" height="24" alt="" />'
|
||||
. '<span>' . esc_html($user->display_name) . '</span></a>'
|
||||
. '<div class="aksrt-messages-admin__sub">#' . esc_html((string) $user_id) . ' · <a href="' . esc_url(get_edit_user_link($user_id)) . '">' . esc_html__('编辑', 'aksrt') . '</a></div>';
|
||||
. '<div class="jio-messages-admin__sub">#' . esc_html((string) $user_id) . ' · <a href="' . esc_url(get_edit_user_link($user_id)) . '">' . esc_html__('编辑', 'jio') . '</a></div>';
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,13 @@
|
||||
* 工单管理:列表(含代客提交弹窗)与处理工作区(?id=)两个页内视图。
|
||||
* 写操作统一使用 TicketRestAdmin;此处只负责服务端渲染。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Ticket\TicketRepository;
|
||||
use AKSRT\Ticket\TicketStatusService;
|
||||
use Jio\Ticket\TicketRepository;
|
||||
use Jio\Ticket\TicketStatusService;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -19,7 +19,7 @@ trait UsersMenusTicketsCenter {
|
||||
use UsersMenusTicketsDetail;
|
||||
use UsersMenusTicketsCreate;
|
||||
|
||||
private const TICKET_PAGE_SLUG = 'aksrt_tickets';
|
||||
private const TICKET_PAGE_SLUG = 'jio_tickets';
|
||||
private const TICKET_PER_PAGE = 20;
|
||||
|
||||
private function count_pending_tickets(): int {
|
||||
@@ -35,18 +35,18 @@ trait UsersMenusTicketsCenter {
|
||||
}
|
||||
$css = '/assets/css/admin/tickets.css';
|
||||
$js = '/assets/js/admin-tickets.js';
|
||||
wp_enqueue_style('aksrt-admin-tickets', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-tickets', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_localize_script('aksrt-admin-tickets', 'AKSRTAdminTickets', [
|
||||
wp_enqueue_style('jio-admin-tickets', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-tickets', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
wp_localize_script('jio-admin-tickets', 'JioAdminTickets', [
|
||||
'restNonce' => wp_create_nonce('wp_rest'),
|
||||
'currentUser' => get_current_user_id(),
|
||||
'strings' => [
|
||||
'saving' => __('正在保存…', 'aksrt'),
|
||||
'saveFailed' => __('保存失败,请检查填写内容后重试。', 'aksrt'),
|
||||
'uncertain' => __('连接中断,提交结果尚未确认。请先重新加载核对,避免重复提交。', 'aksrt'),
|
||||
'required' => __('请填写内容,不能只包含空格。', 'aksrt'),
|
||||
'tooLong' => __('插入后将超过字数限制,请先精简回复内容。', 'aksrt'),
|
||||
'discard' => __('有尚未保存的内容。关闭后将丢弃,是否继续?', 'aksrt'),
|
||||
'saving' => __('正在保存…', 'jio'),
|
||||
'saveFailed' => __('保存失败,请检查填写内容后重试。', 'jio'),
|
||||
'uncertain' => __('连接中断,提交结果尚未确认。请先重新加载核对,避免重复提交。', 'jio'),
|
||||
'required' => __('请填写内容,不能只包含空格。', 'jio'),
|
||||
'tooLong' => __('插入后将超过字数限制,请先精简回复内容。', 'jio'),
|
||||
'discard' => __('有尚未保存的内容。关闭后将丢弃,是否继续?', 'jio'),
|
||||
],
|
||||
]);
|
||||
}
|
||||
@@ -56,7 +56,7 @@ trait UsersMenusTicketsCenter {
|
||||
$filters = $this->ticket_filters($_GET);
|
||||
$paged = max(1, absint($_GET['paged'] ?? 1));
|
||||
$id = absint($_GET['id'] ?? 0);
|
||||
NativeUi::page_open('aksrt-tickets-admin');
|
||||
NativeUi::page_open('jio-tickets-admin');
|
||||
if ($id > 0) {
|
||||
$this->render_ticket_detail_page($id, $this->ticket_context($filters, $paged));
|
||||
} else {
|
||||
@@ -85,12 +85,12 @@ trait UsersMenusTicketsCenter {
|
||||
|
||||
/** 写操作成功后脚本跳转回工作区并带上结果码,这里转成原生通知。 */
|
||||
private function render_ticket_notice(): void {
|
||||
$code = isset($_GET['aksrt_ticket_msg']) && is_scalar($_GET['aksrt_ticket_msg']) ? sanitize_key((string) wp_unslash($_GET['aksrt_ticket_msg'])) : '';
|
||||
$code = isset($_GET['jio_ticket_msg']) && is_scalar($_GET['jio_ticket_msg']) ? sanitize_key((string) wp_unslash($_GET['jio_ticket_msg'])) : '';
|
||||
$map = [
|
||||
'created' => __('工单已创建,用户将收到通知。', 'aksrt'),
|
||||
'reply' => __('回复已发送,用户将收到通知。', 'aksrt'),
|
||||
'status' => __('工单信息已保存。', 'aksrt'),
|
||||
'note' => __('内部备注已添加,仅管理员可见。', 'aksrt'),
|
||||
'created' => __('工单已创建,用户将收到通知。', 'jio'),
|
||||
'reply' => __('回复已发送,用户将收到通知。', 'jio'),
|
||||
'status' => __('工单信息已保存。', 'jio'),
|
||||
'note' => __('内部备注已添加,仅管理员可见。', 'jio'),
|
||||
];
|
||||
if (isset($map[$code])) {
|
||||
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html($map[$code]) . '</p></div>';
|
||||
@@ -99,21 +99,21 @@ trait UsersMenusTicketsCenter {
|
||||
|
||||
/** 提交中与失败提示;失败且结果不确定时显示「重新加载」。 */
|
||||
private function ticket_feedback(): string {
|
||||
return '<div class="aksrt-ticket-feedback" hidden data-aksrt-ticket-feedback><p role="status" aria-live="polite" aria-atomic="true"></p>'
|
||||
. '<button type="button" class="button" data-aksrt-ticket-reload hidden>' . esc_html__('重新加载', 'aksrt') . '</button></div>';
|
||||
return '<div class="jio-ticket-feedback" hidden data-jio-ticket-feedback><p role="status" aria-live="polite" aria-atomic="true"></p>'
|
||||
. '<button type="button" class="button" data-jio-ticket-reload hidden>' . esc_html__('重新加载', 'jio') . '</button></div>';
|
||||
}
|
||||
|
||||
/** 代客提交与内部备注弹窗共用的头部:标题、说明与关闭按钮。 */
|
||||
private function ticket_dialog_head(string $key, string $title, string $description): string {
|
||||
return '<header class="aksrt-ticket-dialog__head"><div><h2 id="aksrt-ticket-' . esc_attr($key) . '-title" tabindex="-1">' . esc_html($title) . '</h2>'
|
||||
. '<p id="aksrt-ticket-' . esc_attr($key) . '-description">' . esc_html($description) . '</p></div>'
|
||||
. '<button type="button" class="aksrt-ticket-dialog__close" data-aksrt-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
return '<header class="jio-ticket-dialog__head"><div><h2 id="jio-ticket-' . esc_attr($key) . '-title" tabindex="-1">' . esc_html($title) . '</h2>'
|
||||
. '<p id="jio-ticket-' . esc_attr($key) . '-description">' . esc_html($description) . '</p></div>'
|
||||
. '<button type="button" class="jio-ticket-dialog__close" data-jio-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
}
|
||||
|
||||
/** 所有表单只提交到现有 REST 写路由,不保留 admin_post 分支。 */
|
||||
private function ticket_form_open(string $action, int $id, string $class = ''): string {
|
||||
$path = $action === 'create' ? '' : '/' . $id . '/' . ($action === 'note' ? 'notes' : $action);
|
||||
return '<form method="post" action="' . esc_url(rest_url('aksrt/v1/admin/tickets' . $path)) . '"'
|
||||
. ' class="' . esc_attr($class) . '" data-aksrt-ticket-form="' . esc_attr($action) . '">';
|
||||
return '<form method="post" action="' . esc_url(rest_url('jio/v1/admin/tickets' . $path)) . '"'
|
||||
. ' class="' . esc_attr($class) . '" data-jio-ticket-form="' . esc_attr($action) . '">';
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,48 @@
|
||||
<?php
|
||||
/** 代客提交工单:列表页上的原生 <dialog>,创建成功后跳转到新工单的工作区。@package AKSRT */
|
||||
/** 代客提交工单:列表页上的原生 <dialog>,创建成功后跳转到新工单的工作区。@package Jio */
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Ticket\TicketSettings;
|
||||
use AKSRT\Ticket\TicketTypeService;
|
||||
use Jio\Ticket\TicketSettings;
|
||||
use Jio\Ticket\TicketTypeService;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait UsersMenusTicketsCreate {
|
||||
|
||||
private const TICKET_CREATE_DIALOG = 'aksrt-ticket-create-dialog';
|
||||
private const TICKET_CREATE_DIALOG = 'jio-ticket-create-dialog';
|
||||
|
||||
private function render_ticket_create_dialog(): void {
|
||||
$types = TicketTypeService::all();
|
||||
echo '<dialog id="' . esc_attr(self::TICKET_CREATE_DIALOG) . '" class="aksrt-ticket-dialog" aria-labelledby="aksrt-ticket-create-title" aria-describedby="aksrt-ticket-create-description">';
|
||||
echo $types === [] ? '<div class="aksrt-ticket-dialog__frame">' : $this->ticket_form_open('create', 0, 'aksrt-ticket-dialog__frame'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->ticket_dialog_head('create', __('代客提交工单', 'aksrt'), __('为指定用户创建工单,创建后由你跟进,用户会收到通知。', 'aksrt')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<div class="aksrt-ticket-dialog__body">' . $this->ticket_feedback(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<dialog id="' . esc_attr(self::TICKET_CREATE_DIALOG) . '" class="jio-ticket-dialog" aria-labelledby="jio-ticket-create-title" aria-describedby="jio-ticket-create-description">';
|
||||
echo $types === [] ? '<div class="jio-ticket-dialog__frame">' : $this->ticket_form_open('create', 0, 'jio-ticket-dialog__frame'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->ticket_dialog_head('create', __('代客提交工单', 'jio'), __('为指定用户创建工单,创建后由你跟进,用户会收到通知。', 'jio')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<div class="jio-ticket-dialog__body">' . $this->ticket_feedback(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
if ($types === []) {
|
||||
echo NativeUi::empty_state(__('尚未配置工单部门', 'aksrt'), __('请先添加部门,再为用户创建工单。', 'aksrt')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<p><a class="button" href="' . esc_url(admin_url('admin.php?page=aksrt-settings#/users/ticket')) . '">' . esc_html__('前往工单配置', 'aksrt') . '</a></p>';
|
||||
echo NativeUi::empty_state(__('尚未配置工单部门', 'jio'), __('请先添加部门,再为用户创建工单。', 'jio')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<p><a class="button" href="' . esc_url(admin_url('admin.php?page=jio-settings#/users/ticket')) . '">' . esc_html__('前往工单配置', 'jio') . '</a></p>';
|
||||
} else {
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('用户 ID', 'aksrt') . '</span><input type="number" name="user_id" min="1" step="1" required placeholder="' . esc_attr__('填写工单归属用户的 ID', 'aksrt') . '"></label>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('工单部门', 'aksrt') . '</span><select name="ticket_type_id" required><option value="">' . esc_html__('请选择部门', 'aksrt') . '</option>';
|
||||
echo '<div class="jio-native-form-grid">';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('用户 ID', 'jio') . '</span><input type="number" name="user_id" min="1" step="1" required placeholder="' . esc_attr__('填写工单归属用户的 ID', 'jio') . '"></label>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('工单部门', 'jio') . '</span><select name="ticket_type_id" required><option value="">' . esc_html__('请选择部门', 'jio') . '</option>';
|
||||
foreach ($types as $type) {
|
||||
echo '<option value="' . (int) $type['id'] . '">' . esc_html((string) $type['name']) . '</option>';
|
||||
}
|
||||
echo '</select></label>';
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('工单标题', 'aksrt') . '</span><input type="text" name="title" maxlength="' . TicketSettings::title_max() . '" required placeholder="' . esc_attr__('简要说明用户遇到的问题', 'aksrt') . '"></label>';
|
||||
echo '<label class="jio-native-field is-wide"><span class="jio-native-field__label">' . esc_html__('工单标题', 'jio') . '</span><input type="text" name="title" maxlength="' . TicketSettings::title_max() . '" required placeholder="' . esc_attr__('简要说明用户遇到的问题', 'jio') . '"></label>';
|
||||
if (TicketSettings::order_link_enabled()) {
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('关联订单(可选)', 'aksrt') . '</span><input type="text" name="order_number" placeholder="' . esc_attr__('填写订单号', 'aksrt') . '"></label>';
|
||||
echo '<label class="jio-native-field is-wide"><span class="jio-native-field__label">' . esc_html__('关联订单(可选)', 'jio') . '</span><input type="text" name="order_number" placeholder="' . esc_attr__('填写订单号', 'jio') . '"></label>';
|
||||
}
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('问题描述', 'aksrt') . '</span><textarea id="aksrt-ticket-create-content" name="content" rows="7" maxlength="' . TicketSettings::content_max() . '" required data-aksrt-ticket-editor aria-describedby="aksrt-ticket-create-count" placeholder="' . esc_attr__('描述问题、发生时间和已尝试的处理方式', 'aksrt') . '"></textarea>';
|
||||
echo '<span id="aksrt-ticket-create-count" class="aksrt-ticket-counter" data-aksrt-ticket-counter="aksrt-ticket-create-content">0 / ' . TicketSettings::content_max() . '</span></label></div>';
|
||||
echo '<label class="jio-native-field is-wide"><span class="jio-native-field__label">' . esc_html__('问题描述', 'jio') . '</span><textarea id="jio-ticket-create-content" name="content" rows="7" maxlength="' . TicketSettings::content_max() . '" required data-jio-ticket-editor aria-describedby="jio-ticket-create-count" placeholder="' . esc_attr__('描述问题、发生时间和已尝试的处理方式', 'jio') . '"></textarea>';
|
||||
echo '<span id="jio-ticket-create-count" class="jio-ticket-counter" data-jio-ticket-counter="jio-ticket-create-content">0 / ' . TicketSettings::content_max() . '</span></label></div>';
|
||||
}
|
||||
echo '</div><footer class="aksrt-ticket-dialog__foot">';
|
||||
echo '</div><footer class="jio-ticket-dialog__foot">';
|
||||
if ($types !== []) {
|
||||
echo '<span class="aksrt-ticket-hint">' . esc_html__('新工单状态为「已回复」', 'aksrt') . '</span>';
|
||||
echo '<span class="jio-ticket-hint">' . esc_html__('新工单状态为「已回复」', 'jio') . '</span>';
|
||||
}
|
||||
echo '<button type="button" class="button" data-aksrt-dialog-close>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
echo '<button type="button" class="button" data-jio-dialog-close>' . esc_html__('取消', 'jio') . '</button>';
|
||||
if ($types !== []) {
|
||||
echo '<button type="submit" class="button button-primary">' . esc_html__('创建工单并通知用户', 'aksrt') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary">' . esc_html__('创建工单并通知用户', 'jio') . '</button>';
|
||||
}
|
||||
echo '</footer>' . ($types === [] ? '</div>' : '</form>') . '</dialog>';
|
||||
}
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
<?php
|
||||
/** 工单处理工作区(?id=):左侧会话面板(含底部回复框),右侧处理信息与内部备注(弹窗添加)。@package AKSRT */
|
||||
/** 工单处理工作区(?id=):左侧会话面板(含底部回复框),右侧处理信息与内部备注(弹窗添加)。@package Jio */
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Ticket\TicketNoteService;
|
||||
use AKSRT\Ticket\TicketPrereplyService;
|
||||
use AKSRT\Ticket\TicketRepository;
|
||||
use AKSRT\Ticket\TicketSettings;
|
||||
use AKSRT\Ticket\TicketStatusService;
|
||||
use AKSRT\Ticket\TicketTypeService;
|
||||
use Jio\Ticket\TicketNoteService;
|
||||
use Jio\Ticket\TicketPrereplyService;
|
||||
use Jio\Ticket\TicketRepository;
|
||||
use Jio\Ticket\TicketSettings;
|
||||
use Jio\Ticket\TicketStatusService;
|
||||
use Jio\Ticket\TicketTypeService;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait UsersMenusTicketsDetail {
|
||||
|
||||
private const TICKET_NOTE_DIALOG = 'aksrt-ticket-note-dialog';
|
||||
private const TICKET_NOTE_DIALOG = 'jio-ticket-note-dialog';
|
||||
|
||||
/** 详情按 ID 查询,不受列表筛选影响;$context 只用于「返回工单列表」。 */
|
||||
private function render_ticket_detail_page(int $id, array $context): void {
|
||||
$back = NativeUi::button_link(__('返回工单列表', 'aksrt'), $this->ticket_page_url($context), false, 'dashicons-arrow-left-alt');
|
||||
$back = NativeUi::button_link(__('返回工单列表', 'jio'), $this->ticket_page_url($context), false, 'dashicons-arrow-left-alt');
|
||||
$ticket = TicketRepository::get_instance()->find($id);
|
||||
echo '<div class="aksrt-native-header aksrt-tickets-admin__detail-header"><div class="aksrt-native-header__main"><h1 class="aksrt-native-header__title">';
|
||||
echo '<div class="jio-native-header jio-tickets-admin__detail-header"><div class="jio-native-header__main"><h1 class="jio-native-header__title">';
|
||||
if ($ticket === null) {
|
||||
echo esc_html__('工单详情', 'aksrt') . '</h1></div><div class="aksrt-native-header__actions">' . $back . '</div></div><hr class="wp-header-end">'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo NativeUi::empty_state(__('工单不存在', 'aksrt'), __('工单可能已被删除,或链接有误。', 'aksrt'), 'dashicons-search'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo esc_html__('工单详情', 'jio') . '</h1></div><div class="jio-native-header__actions">' . $back . '</div></div><hr class="wp-header-end">'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo NativeUi::empty_state(__('工单不存在', 'jio'), __('工单可能已被删除,或链接有误。', 'jio'), 'dashicons-search'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
return;
|
||||
}
|
||||
echo '<span class="aksrt-native-header__icon dashicons dashicons-tickets-alt" aria-hidden="true"></span>' . esc_html((string) $ticket['title']) . '</h1>';
|
||||
echo '<p class="aksrt-native-header__desc"><span>' . esc_html((string) $ticket['ticket_num']) . '</span> ' . $this->ticket_status_badge($ticket) . $this->ticket_urge_badge($ticket) . '</p>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '</div><div class="aksrt-native-header__actions">' . $back . '</div></div><hr class="wp-header-end">'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<span class="jio-native-header__icon dashicons dashicons-tickets-alt" aria-hidden="true"></span>' . esc_html((string) $ticket['title']) . '</h1>';
|
||||
echo '<p class="jio-native-header__desc"><span>' . esc_html((string) $ticket['ticket_num']) . '</span> ' . $this->ticket_status_badge($ticket) . $this->ticket_urge_badge($ticket) . '</p>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '</div><div class="jio-native-header__actions">' . $back . '</div></div><hr class="wp-header-end">'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
$this->render_ticket_notice();
|
||||
echo $this->ticket_feedback(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->render_ticket_detail_panel($ticket); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
@@ -38,11 +38,11 @@ trait UsersMenusTicketsDetail {
|
||||
private function render_ticket_detail_panel(array $ticket): string {
|
||||
$id = (int) $ticket['id'];
|
||||
$footer = !empty($ticket['is_closed'])
|
||||
? '<p class="aksrt-ticket-detail__closed"><span class="dashicons dashicons-lock" aria-hidden="true"></span>' . esc_html__('工单已关闭,不能继续回复或变更处理信息;仍可添加内部备注。', 'aksrt') . '</p>'
|
||||
? '<p class="jio-ticket-detail__closed"><span class="dashicons dashicons-lock" aria-hidden="true"></span>' . esc_html__('工单已关闭,不能继续回复或变更处理信息;仍可添加内部备注。', 'jio') . '</p>'
|
||||
: $this->ticket_reply_form($id);
|
||||
$html = '<div class="aksrt-ticket-detail" data-aksrt-ticket-detail="' . $id . '"><div class="aksrt-ticket-detail__main">';
|
||||
$html = '<div class="jio-ticket-detail" data-jio-ticket-detail="' . $id . '"><div class="jio-ticket-detail__main">';
|
||||
$html .= $this->ticket_conversation($ticket, TicketRepository::get_instance()->replies($id), $footer);
|
||||
$html .= '</div><aside class="aksrt-ticket-detail__side" aria-label="' . esc_attr__('工单信息与内部备注', 'aksrt') . '">';
|
||||
$html .= '</div><aside class="jio-ticket-detail__side" aria-label="' . esc_attr__('工单信息与内部备注', 'jio') . '">';
|
||||
return $html . $this->ticket_info_card($ticket) . $this->ticket_notes_card($id) . '</aside></div>';
|
||||
}
|
||||
|
||||
@@ -52,48 +52,48 @@ trait UsersMenusTicketsDetail {
|
||||
* 原始问题与回复同属一条时间线:用户在左、客服在右、系统事件居中。
|
||||
*/
|
||||
private function ticket_conversation(array $ticket, array $replies, string $footer): string {
|
||||
$html = '<section class="aksrt-ticket-conversation" aria-label="' . esc_attr__('问题与沟通记录', 'aksrt') . '">';
|
||||
$html .= '<div class="aksrt-ticket-section-head"><h3>' . esc_html__('沟通记录', 'aksrt') . ' <span class="aksrt-ticket-pill">' . esc_html(sprintf(__('%d 条记录', 'aksrt'), count($replies))) . '</span></h3>';
|
||||
$html = '<section class="jio-ticket-conversation" aria-label="' . esc_attr__('问题与沟通记录', 'jio') . '">';
|
||||
$html .= '<div class="jio-ticket-section-head"><h3>' . esc_html__('沟通记录', 'jio') . ' <span class="jio-ticket-pill">' . esc_html(sprintf(__('%d 条记录', 'jio'), count($replies))) . '</span></h3>';
|
||||
if ($replies !== []) {
|
||||
$html .= '<button type="button" class="button-link" data-aksrt-ticket-latest hidden>' . esc_html__('查看最新记录', 'aksrt') . '</button>';
|
||||
$html .= '<button type="button" class="button-link" data-jio-ticket-latest hidden>' . esc_html__('查看最新记录', 'jio') . '</button>';
|
||||
}
|
||||
$html .= '</div><div class="aksrt-ticket-conversation__scroll" tabindex="0" role="region" aria-label="' . esc_attr__('可滚动的沟通记录', 'aksrt') . '"><ol class="aksrt-ticket-detail__thread">';
|
||||
$html .= $this->ticket_message('origin', (int) $ticket['user_id'], (string) $ticket['user_name'], !empty($ticket['post_admin_id']) ? __('代客提交', 'aksrt') : __('提交了工单', 'aksrt'), (string) $ticket['post_time'], (string) $ticket['content'], (array) $ticket['attachment']);
|
||||
$html .= '</div><div class="jio-ticket-conversation__scroll" tabindex="0" role="region" aria-label="' . esc_attr__('可滚动的沟通记录', 'jio') . '"><ol class="jio-ticket-detail__thread">';
|
||||
$html .= $this->ticket_message('origin', (int) $ticket['user_id'], (string) $ticket['user_name'], !empty($ticket['post_admin_id']) ? __('代客提交', 'jio') : __('提交了工单', 'jio'), (string) $ticket['post_time'], (string) $ticket['content'], (array) $ticket['attachment']);
|
||||
if ($replies === []) {
|
||||
$html .= '<li class="aksrt-ticket-detail__empty">' . esc_html__('暂无回复,请核对问题后联系用户。', 'aksrt') . '</li>';
|
||||
$html .= '<li class="jio-ticket-detail__empty">' . esc_html__('暂无回复,请核对问题后联系用户。', 'jio') . '</li>';
|
||||
}
|
||||
foreach ($replies as $reply) {
|
||||
if ((string) $reply['author_type'] === 'system') {
|
||||
$html .= '<li class="aksrt-ticket-detail__event"><span>' . esc_html((string) $reply['content']) . '</span><time>' . esc_html((string) $reply['created_at']) . '</time></li>';
|
||||
$html .= '<li class="jio-ticket-detail__event"><span>' . esc_html((string) $reply['content']) . '</span><time>' . esc_html((string) $reply['created_at']) . '</time></li>';
|
||||
continue;
|
||||
}
|
||||
$admin = (string) $reply['author_type'] === 'admin';
|
||||
$quote_html = '';
|
||||
if (is_array($reply['quote'] ?? null)) {
|
||||
$quote = $reply['quote'];
|
||||
$quote_html = '<blockquote class="aksrt-ticket-detail__quote">' . esc_html(!empty($quote['is_deleted']) ? __('原回复已删除', 'aksrt') : (string) $quote['content']) . '</blockquote>';
|
||||
$quote_html = '<blockquote class="jio-ticket-detail__quote">' . esc_html(!empty($quote['is_deleted']) ? __('原回复已删除', 'jio') : (string) $quote['content']) . '</blockquote>';
|
||||
}
|
||||
$html .= $this->ticket_message($admin ? 'admin' : 'user', (int) $reply['author_id'], (string) $reply['author_name'], $admin ? __('客服', 'aksrt') : __('用户', 'aksrt'), (string) $reply['created_at'], (string) $reply['content'], (array) $reply['attachment'], $quote_html);
|
||||
$html .= $this->ticket_message($admin ? 'admin' : 'user', (int) $reply['author_id'], (string) $reply['author_name'], $admin ? __('客服', 'jio') : __('用户', 'jio'), (string) $reply['created_at'], (string) $reply['content'], (array) $reply['attachment'], $quote_html);
|
||||
}
|
||||
return $html . '</ol></div>' . $footer . '</section>';
|
||||
}
|
||||
|
||||
private function ticket_message(string $variant, int $user_id, string $name, string $role, string $time, string $content, array $attachments, string $quote_html = ''): string {
|
||||
$avatar = function_exists('aksrt_get_avatar_url') ? aksrt_get_avatar_url($user_id, 64) : '';
|
||||
return '<li class="aksrt-ticket-detail__message is-' . esc_attr($variant) . '">'
|
||||
. ($avatar !== '' ? '<img class="aksrt-ticket-detail__avatar" src="' . esc_url($avatar) . '" alt="" width="32" height="32">' : '<span class="aksrt-ticket-detail__avatar" aria-hidden="true"></span>')
|
||||
. '<div class="aksrt-ticket-detail__bubble"><header class="aksrt-ticket-detail__meta"><strong>' . esc_html($name !== '' ? $name : '#' . $user_id) . '</strong>'
|
||||
. '<span class="aksrt-ticket-detail__role">' . esc_html($role) . '</span><time>' . esc_html($time) . '</time></header>'
|
||||
. $quote_html . '<div class="aksrt-ticket-detail__body">' . wpautop(esc_html($content)) . '</div>'
|
||||
$avatar = function_exists('jio_get_avatar_url') ? jio_get_avatar_url($user_id, 64) : '';
|
||||
return '<li class="jio-ticket-detail__message is-' . esc_attr($variant) . '">'
|
||||
. ($avatar !== '' ? '<img class="jio-ticket-detail__avatar" src="' . esc_url($avatar) . '" alt="" width="32" height="32">' : '<span class="jio-ticket-detail__avatar" aria-hidden="true"></span>')
|
||||
. '<div class="jio-ticket-detail__bubble"><header class="jio-ticket-detail__meta"><strong>' . esc_html($name !== '' ? $name : '#' . $user_id) . '</strong>'
|
||||
. '<span class="jio-ticket-detail__role">' . esc_html($role) . '</span><time>' . esc_html($time) . '</time></header>'
|
||||
. $quote_html . '<div class="jio-ticket-detail__body">' . wpautop(esc_html($content)) . '</div>'
|
||||
. $this->ticket_attachments_html($attachments) . '</div></li>';
|
||||
}
|
||||
|
||||
private function ticket_reply_form(int $id): string {
|
||||
$field = 'aksrt-ticket-reply-' . $id;
|
||||
$field = 'jio-ticket-reply-' . $id;
|
||||
$tools = '';
|
||||
$prereplies = TicketPrereplyService::all();
|
||||
if ($prereplies !== []) {
|
||||
$tools = '<label class="screen-reader-text" for="' . $field . '-pre">' . esc_html__('插入预设回复', 'aksrt') . '</label><select id="' . $field . '-pre" data-aksrt-ticket-prereply="' . $field . '"><option value="">' . esc_html__('插入预设回复…', 'aksrt') . '</option>';
|
||||
$tools = '<label class="screen-reader-text" for="' . $field . '-pre">' . esc_html__('插入预设回复', 'jio') . '</label><select id="' . $field . '-pre" data-jio-ticket-prereply="' . $field . '"><option value="">' . esc_html__('插入预设回复…', 'jio') . '</option>';
|
||||
foreach ($prereplies as $row) {
|
||||
$tools .= '<option value="' . esc_attr((string) $row['content']) . '">' . esc_html(mb_strimwidth((string) $row['content'], 0, 40, '…')) . '</option>';
|
||||
}
|
||||
@@ -101,13 +101,13 @@ trait UsersMenusTicketsDetail {
|
||||
}
|
||||
$max = TicketSettings::reply_max();
|
||||
// 输入框与工具栏合为一个整体:预设回复、字数与发送按钮同排;Ctrl/⌘ + Enter 发送。
|
||||
return $this->ticket_form_open('reply', $id, 'aksrt-ticket-composer')
|
||||
. '<label class="screen-reader-text" for="' . esc_attr($field) . '">' . esc_html__('回复用户', 'aksrt') . '</label>'
|
||||
. '<div class="aksrt-ticket-composer__box"><textarea id="' . esc_attr($field) . '" name="content" rows="3" maxlength="' . $max . '" required data-aksrt-ticket-editor data-aksrt-ticket-send-shortcut aria-describedby="' . esc_attr($field) . '-count ' . esc_attr($field) . '-hint" placeholder="' . esc_attr__('回复用户,发送后用户将收到通知…', 'aksrt') . '"></textarea>'
|
||||
. '<div class="aksrt-ticket-composer__bar">' . $tools
|
||||
. '<span id="' . esc_attr($field) . '-hint" class="aksrt-ticket-hint aksrt-ticket-composer__shortcut">' . esc_html__('Ctrl + Enter 发送', 'aksrt') . '</span>'
|
||||
. '<span id="' . esc_attr($field) . '-count" class="aksrt-ticket-counter" data-aksrt-ticket-counter="' . esc_attr($field) . '">0 / ' . $max . '</span>'
|
||||
. '<button type="submit" class="button button-primary"><span class="dashicons dashicons-email-alt" aria-hidden="true"></span>' . esc_html__('发送回复', 'aksrt') . '</button></div></div></form>';
|
||||
return $this->ticket_form_open('reply', $id, 'jio-ticket-composer')
|
||||
. '<label class="screen-reader-text" for="' . esc_attr($field) . '">' . esc_html__('回复用户', 'jio') . '</label>'
|
||||
. '<div class="jio-ticket-composer__box"><textarea id="' . esc_attr($field) . '" name="content" rows="3" maxlength="' . $max . '" required data-jio-ticket-editor data-jio-ticket-send-shortcut aria-describedby="' . esc_attr($field) . '-count ' . esc_attr($field) . '-hint" placeholder="' . esc_attr__('回复用户,发送后用户将收到通知…', 'jio') . '"></textarea>'
|
||||
. '<div class="jio-ticket-composer__bar">' . $tools
|
||||
. '<span id="' . esc_attr($field) . '-hint" class="jio-ticket-hint jio-ticket-composer__shortcut">' . esc_html__('Ctrl + Enter 发送', 'jio') . '</span>'
|
||||
. '<span id="' . esc_attr($field) . '-count" class="jio-ticket-counter" data-jio-ticket-counter="' . esc_attr($field) . '">0 / ' . $max . '</span>'
|
||||
. '<button type="submit" class="button button-primary"><span class="dashicons dashicons-email-alt" aria-hidden="true"></span>' . esc_html__('发送回复', 'jio') . '</button></div></div></form>';
|
||||
}
|
||||
|
||||
private function ticket_info_card(array $ticket): string {
|
||||
@@ -115,40 +115,40 @@ trait UsersMenusTicketsDetail {
|
||||
$editable = empty($ticket['is_closed']);
|
||||
$controls = $editable ? $this->ticket_info_controls($ticket) : [];
|
||||
$rows = [
|
||||
[__('工单号', 'aksrt'), esc_html((string) $ticket['ticket_num'])],
|
||||
[__('状态', 'aksrt'), $controls['status'] ?? $this->ticket_status_badge($ticket), 'status'],
|
||||
[__('用户', 'aksrt'), $this->ticket_user_chip((int) $ticket['user_id'], (string) $ticket['user_name'])],
|
||||
[__('部门', 'aksrt'), $controls['ticket_type_id'] ?? esc_html((string) $ticket['ticket_type_name'] !== '' ? (string) $ticket['ticket_type_name'] : '—'), 'ticket_type_id'],
|
||||
[__('跟进人', 'aksrt'), $controls['admin_id'] ?? esc_html((int) $ticket['admin_id'] > 0 ? (string) $ticket['admin_name'] : __('未指派', 'aksrt')), 'admin_id'],
|
||||
[__('工单号', 'jio'), esc_html((string) $ticket['ticket_num'])],
|
||||
[__('状态', 'jio'), $controls['status'] ?? $this->ticket_status_badge($ticket), 'status'],
|
||||
[__('用户', 'jio'), $this->ticket_user_chip((int) $ticket['user_id'], (string) $ticket['user_name'])],
|
||||
[__('部门', 'jio'), $controls['ticket_type_id'] ?? esc_html((string) $ticket['ticket_type_name'] !== '' ? (string) $ticket['ticket_type_name'] : '—'), 'ticket_type_id'],
|
||||
[__('跟进人', 'jio'), $controls['admin_id'] ?? esc_html((int) $ticket['admin_id'] > 0 ? (string) $ticket['admin_name'] : __('未指派', 'jio')), 'admin_id'],
|
||||
];
|
||||
if ((string) $ticket['order_number'] !== '') {
|
||||
$rows[] = [__('关联订单', 'aksrt'), esc_html((string) $ticket['order_number'])];
|
||||
$rows[] = [__('关联订单', 'jio'), esc_html((string) $ticket['order_number'])];
|
||||
}
|
||||
$rows[] = [__('提交时间', 'aksrt'), esc_html((string) $ticket['post_time'])];
|
||||
$rows[] = [__('提交时间', 'jio'), esc_html((string) $ticket['post_time'])];
|
||||
if ((int) $ticket['urge_count'] > 0) {
|
||||
$rows[] = [__('催单', 'aksrt'), '<span class="aksrt-ticket-urge">' . esc_html(sprintf(__('%1$d 次,最近 %2$s', 'aksrt'), (int) $ticket['urge_count'], (string) $ticket['last_urge_time'])) . '</span>'];
|
||||
$rows[] = [__('催单', 'jio'), '<span class="jio-ticket-urge">' . esc_html(sprintf(__('%1$d 次,最近 %2$s', 'jio'), (int) $ticket['urge_count'], (string) $ticket['last_urge_time'])) . '</span>'];
|
||||
}
|
||||
$html = '<section class="aksrt-ticket-detail__card"><div class="aksrt-ticket-section-head"><h3>' . esc_html__('工单信息', 'aksrt') . '</h3>';
|
||||
$html = '<section class="jio-ticket-detail__card"><div class="jio-ticket-section-head"><h3>' . esc_html__('工单信息', 'jio') . '</h3>';
|
||||
if ($editable) {
|
||||
$html .= '<button type="button" class="button-link" data-aksrt-ticket-assign>' . esc_html__('由我跟进', 'aksrt') . '</button>';
|
||||
$html .= '<button type="button" class="button-link" data-jio-ticket-assign>' . esc_html__('由我跟进', 'jio') . '</button>';
|
||||
}
|
||||
$html .= '</div>';
|
||||
if ($editable) {
|
||||
$html .= $this->ticket_form_open('status', $id, 'aksrt-ticket-detail__status-form');
|
||||
$html .= $this->ticket_form_open('status', $id, 'jio-ticket-detail__status-form');
|
||||
}
|
||||
$html .= '<dl class="aksrt-ticket-detail__info">';
|
||||
$html .= '<dl class="jio-ticket-detail__info">';
|
||||
foreach ($rows as $row) {
|
||||
[$label, $value] = $row;
|
||||
$label_html = esc_html($label);
|
||||
if ($editable && isset($row[2])) {
|
||||
$label_html = '<label for="aksrt-ticket-' . esc_attr($row[2]) . '-' . $id . '">' . $label_html . '</label>';
|
||||
$label_html = '<label for="jio-ticket-' . esc_attr($row[2]) . '-' . $id . '">' . $label_html . '</label>';
|
||||
}
|
||||
$html .= '<dt>' . $label_html . '</dt><dd>' . $value . '</dd>';
|
||||
}
|
||||
$html .= '</dl>';
|
||||
if ($editable) {
|
||||
$html .= '<div class="aksrt-ticket-close-warning" data-aksrt-ticket-close-warning hidden><label><input type="checkbox" data-aksrt-ticket-close-confirm> <span>' . esc_html__('确认关闭工单:关闭后无法再次回复,尚未发送的回复不会保存。', 'aksrt') . '</span></label></div>';
|
||||
$html .= '<button type="submit" class="button">' . esc_html__('保存工单信息', 'aksrt') . '</button></form>';
|
||||
$html .= '<div class="jio-ticket-close-warning" data-jio-ticket-close-warning hidden><label><input type="checkbox" data-jio-ticket-close-confirm> <span>' . esc_html__('确认关闭工单:关闭后无法再次回复,尚未发送的回复不会保存。', 'jio') . '</span></label></div>';
|
||||
$html .= '<button type="submit" class="button">' . esc_html__('保存工单信息', 'jio') . '</button></form>';
|
||||
}
|
||||
return $html . '</section>';
|
||||
}
|
||||
@@ -157,7 +157,7 @@ trait UsersMenusTicketsDetail {
|
||||
private function ticket_info_controls(array $ticket): array {
|
||||
$id = (int) $ticket['id'];
|
||||
$from = (string) $ticket['status'];
|
||||
$controls = ['status' => '<select id="aksrt-ticket-status-' . $id . '" name="status" data-aksrt-ticket-status>'];
|
||||
$controls = ['status' => '<select id="jio-ticket-status-' . $id . '" name="status" data-jio-ticket-status>'];
|
||||
foreach (TicketStatusService::map() as $row) {
|
||||
$key = (string) $row['status_key'];
|
||||
if (TicketStatusService::can_transition($from, $key)) {
|
||||
@@ -165,19 +165,19 @@ trait UsersMenusTicketsDetail {
|
||||
}
|
||||
}
|
||||
$controls['status'] .= '</select>';
|
||||
$controls['ticket_type_id'] = '<select id="aksrt-ticket-ticket_type_id-' . $id . '" name="ticket_type_id" required>';
|
||||
$controls['ticket_type_id'] = '<select id="jio-ticket-ticket_type_id-' . $id . '" name="ticket_type_id" required>';
|
||||
foreach (TicketTypeService::all() as $row) {
|
||||
$controls['ticket_type_id'] .= '<option value="' . (int) $row['id'] . '"' . selected((int) $ticket['ticket_type_id'], (int) $row['id'], false) . '>' . esc_html((string) $row['name']) . '</option>';
|
||||
}
|
||||
$controls['ticket_type_id'] .= '</select>';
|
||||
$controls['admin_id'] = '<select id="aksrt-ticket-admin_id-' . $id . '" name="admin_id"><option value="0">' . esc_html__('未指派', 'aksrt') . '</option>';
|
||||
$controls['admin_id'] = '<select id="jio-ticket-admin_id-' . $id . '" name="admin_id"><option value="0">' . esc_html__('未指派', 'jio') . '</option>';
|
||||
$current = (int) $ticket['admin_id'];
|
||||
$admins = $this->ticket_admin_options();
|
||||
if ($current > 0 && !isset($admins[$current])) {
|
||||
$admins[$current] = (string) $ticket['admin_name'];
|
||||
}
|
||||
foreach ($admins as $admin_id => $name) {
|
||||
$controls['admin_id'] .= '<option value="' . $admin_id . '"' . selected($current, $admin_id, false) . '>' . esc_html($admin_id === get_current_user_id() ? sprintf(__('%s(我)', 'aksrt'), $name) : $name) . '</option>';
|
||||
$controls['admin_id'] .= '<option value="' . $admin_id . '"' . selected($current, $admin_id, false) . '>' . esc_html($admin_id === get_current_user_id() ? sprintf(__('%s(我)', 'jio'), $name) : $name) . '</option>';
|
||||
}
|
||||
$controls['admin_id'] .= '</select>';
|
||||
return $controls;
|
||||
@@ -203,42 +203,42 @@ trait UsersMenusTicketsDetail {
|
||||
private function ticket_notes_card(int $id): string {
|
||||
$notes = TicketNoteService::for_ticket($id);
|
||||
$visible = 3;
|
||||
$html = '<section class="aksrt-ticket-detail__card aksrt-ticket-detail__private"><div class="aksrt-ticket-section-head"><h3>' . esc_html__('内部备注', 'aksrt');
|
||||
$html = '<section class="jio-ticket-detail__card jio-ticket-detail__private"><div class="jio-ticket-section-head"><h3>' . esc_html__('内部备注', 'jio');
|
||||
if ($notes !== []) {
|
||||
$html .= ' <span class="aksrt-ticket-pill">' . count($notes) . '</span>';
|
||||
$html .= ' <span class="jio-ticket-pill">' . count($notes) . '</span>';
|
||||
}
|
||||
$html .= '</h3><button type="button" class="button button-small" data-aksrt-dialog-open="' . esc_attr(self::TICKET_NOTE_DIALOG) . '" aria-haspopup="dialog" aria-controls="' . esc_attr(self::TICKET_NOTE_DIALOG) . '">'
|
||||
. '<span class="dashicons dashicons-plus-alt2" aria-hidden="true"></span>' . esc_html__('添加备注', 'aksrt') . '</button></div>';
|
||||
$html .= '<p class="aksrt-ticket-hint">' . esc_html__('仅管理员可见,不会发送给用户。', 'aksrt') . '</p>';
|
||||
$html .= '</h3><button type="button" class="button button-small" data-jio-dialog-open="' . esc_attr(self::TICKET_NOTE_DIALOG) . '" aria-haspopup="dialog" aria-controls="' . esc_attr(self::TICKET_NOTE_DIALOG) . '">'
|
||||
. '<span class="dashicons dashicons-plus-alt2" aria-hidden="true"></span>' . esc_html__('添加备注', 'jio') . '</button></div>';
|
||||
$html .= '<p class="jio-ticket-hint">' . esc_html__('仅管理员可见,不会发送给用户。', 'jio') . '</p>';
|
||||
if ($notes === []) {
|
||||
return $html . '<p class="aksrt-ticket-detail__notes-empty">' . esc_html__('暂无备注', 'aksrt') . '</p></section>';
|
||||
return $html . '<p class="jio-ticket-detail__notes-empty">' . esc_html__('暂无备注', 'jio') . '</p></section>';
|
||||
}
|
||||
$older = array_slice($notes, 0, -$visible);
|
||||
$html .= '<div class="aksrt-ticket-detail__notes">';
|
||||
$html .= '<div class="jio-ticket-detail__notes">';
|
||||
if ($older !== []) {
|
||||
$html .= '<details class="aksrt-ticket-detail__notes-older"><summary>' . esc_html(sprintf(__('更早的 %d 条备注', 'aksrt'), count($older))) . '</summary>' . $this->ticket_notes_list($older) . '</details>';
|
||||
$html .= '<details class="jio-ticket-detail__notes-older"><summary>' . esc_html(sprintf(__('更早的 %d 条备注', 'jio'), count($older))) . '</summary>' . $this->ticket_notes_list($older) . '</details>';
|
||||
}
|
||||
return $html . $this->ticket_notes_list(array_slice($notes, -$visible)) . '</div></section>';
|
||||
}
|
||||
|
||||
private function ticket_note_dialog(int $id): string {
|
||||
$field = 'aksrt-ticket-note-' . $id;
|
||||
$field = 'jio-ticket-note-' . $id;
|
||||
$max = TicketSettings::reply_max();
|
||||
return '<dialog id="' . esc_attr(self::TICKET_NOTE_DIALOG) . '" class="aksrt-ticket-dialog aksrt-ticket-dialog--note" aria-labelledby="aksrt-ticket-note-title" aria-describedby="aksrt-ticket-note-description">'
|
||||
. $this->ticket_form_open('note', $id, 'aksrt-ticket-dialog__frame')
|
||||
. $this->ticket_dialog_head('note', __('添加内部备注', 'aksrt'), __('仅管理员可见,不会发送给用户。', 'aksrt'))
|
||||
. '<div class="aksrt-ticket-dialog__body">' . $this->ticket_feedback()
|
||||
. '<label class="screen-reader-text" for="' . esc_attr($field) . '">' . esc_html__('备注内容', 'aksrt') . '</label>'
|
||||
. '<textarea id="' . esc_attr($field) . '" name="content" rows="6" maxlength="' . $max . '" required autofocus data-aksrt-ticket-editor aria-describedby="' . esc_attr($field) . '-count" placeholder="' . esc_attr__('记录处理进展、排查结论…', 'aksrt') . '"></textarea>'
|
||||
. '<span id="' . esc_attr($field) . '-count" class="aksrt-ticket-counter aksrt-ticket-dialog__counter" data-aksrt-ticket-counter="' . esc_attr($field) . '">0 / ' . $max . '</span></div>'
|
||||
. '<footer class="aksrt-ticket-dialog__foot"><button type="button" class="button" data-aksrt-dialog-close>' . esc_html__('取消', 'aksrt') . '</button>'
|
||||
. '<button type="submit" class="button button-primary">' . esc_html__('保存备注', 'aksrt') . '</button></footer></form></dialog>';
|
||||
return '<dialog id="' . esc_attr(self::TICKET_NOTE_DIALOG) . '" class="jio-ticket-dialog jio-ticket-dialog--note" aria-labelledby="jio-ticket-note-title" aria-describedby="jio-ticket-note-description">'
|
||||
. $this->ticket_form_open('note', $id, 'jio-ticket-dialog__frame')
|
||||
. $this->ticket_dialog_head('note', __('添加内部备注', 'jio'), __('仅管理员可见,不会发送给用户。', 'jio'))
|
||||
. '<div class="jio-ticket-dialog__body">' . $this->ticket_feedback()
|
||||
. '<label class="screen-reader-text" for="' . esc_attr($field) . '">' . esc_html__('备注内容', 'jio') . '</label>'
|
||||
. '<textarea id="' . esc_attr($field) . '" name="content" rows="6" maxlength="' . $max . '" required autofocus data-jio-ticket-editor aria-describedby="' . esc_attr($field) . '-count" placeholder="' . esc_attr__('记录处理进展、排查结论…', 'jio') . '"></textarea>'
|
||||
. '<span id="' . esc_attr($field) . '-count" class="jio-ticket-counter jio-ticket-dialog__counter" data-jio-ticket-counter="' . esc_attr($field) . '">0 / ' . $max . '</span></div>'
|
||||
. '<footer class="jio-ticket-dialog__foot"><button type="button" class="button" data-jio-dialog-close>' . esc_html__('取消', 'jio') . '</button>'
|
||||
. '<button type="submit" class="button button-primary">' . esc_html__('保存备注', 'jio') . '</button></footer></form></dialog>';
|
||||
}
|
||||
|
||||
private function ticket_notes_list(array $notes): string {
|
||||
$html = '<ol>';
|
||||
foreach ($notes as $note) {
|
||||
$html .= '<li><div class="aksrt-ticket-detail__note-head"><strong>' . esc_html((string) $note['admin_name']) . '</strong><time>' . esc_html((string) $note['created_at']) . '</time></div><div>' . nl2br(esc_html((string) $note['content'])) . '</div></li>';
|
||||
$html .= '<li><div class="jio-ticket-detail__note-head"><strong>' . esc_html((string) $note['admin_name']) . '</strong><time>' . esc_html((string) $note['created_at']) . '</time></div><div>' . nl2br(esc_html((string) $note['content'])) . '</div></li>';
|
||||
}
|
||||
return $html . '</ol>';
|
||||
}
|
||||
@@ -247,9 +247,9 @@ trait UsersMenusTicketsDetail {
|
||||
if ($urls === []) {
|
||||
return '';
|
||||
}
|
||||
$html = '<ul class="aksrt-ticket-detail__attachments">';
|
||||
$html = '<ul class="jio-ticket-detail__attachments">';
|
||||
foreach ($urls as $index => $url) {
|
||||
$html .= '<li><a href="' . esc_url((string) $url) . '" target="_blank" rel="noopener noreferrer"><img src="' . esc_url((string) $url) . '" alt="' . esc_attr(sprintf(__('查看附件 %d(新窗口)', 'aksrt'), $index + 1)) . '"></a></li>';
|
||||
$html .= '<li><a href="' . esc_url((string) $url) . '" target="_blank" rel="noopener noreferrer"><img src="' . esc_url((string) $url) . '" alt="' . esc_attr(sprintf(__('查看附件 %d(新窗口)', 'jio'), $index + 1)) . '"></a></li>';
|
||||
}
|
||||
return $html . '</ul>';
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?php
|
||||
/** 工单筛选与列表;标题与操作直接链接到处理工作区(?id=)。@package AKSRT */
|
||||
/** 工单筛选与列表;标题与操作直接链接到处理工作区(?id=)。@package Jio */
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Ticket\TicketRepository;
|
||||
use AKSRT\Ticket\TicketStatusService;
|
||||
use AKSRT\Ticket\TicketTypeService;
|
||||
use Jio\Ticket\TicketRepository;
|
||||
use Jio\Ticket\TicketStatusService;
|
||||
use Jio\Ticket\TicketTypeService;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -15,19 +15,19 @@ trait UsersMenusTicketsList {
|
||||
// 先查询并夹取页码,创建成功后跳转的工作区链接才能带上实际页码。
|
||||
[$result, $paged] = $this->ticket_list_result($filters, $paged);
|
||||
NativeUi::header(
|
||||
__('工单管理', 'aksrt'),
|
||||
__('查看用户问题、跟进沟通并记录处理结果。', 'aksrt'),
|
||||
__('工单管理', 'jio'),
|
||||
__('查看用户问题、跟进沟通并记录处理结果。', 'jio'),
|
||||
'dashicons-tickets-alt',
|
||||
[
|
||||
[
|
||||
'label' => __('代客提交工单', 'aksrt'),
|
||||
'label' => __('代客提交工单', 'jio'),
|
||||
'dialog' => self::TICKET_CREATE_DIALOG,
|
||||
'primary' => true,
|
||||
'icon' => 'dashicons-plus-alt2',
|
||||
],
|
||||
[
|
||||
'label' => __('工单配置', 'aksrt'),
|
||||
'url' => admin_url('admin.php?page=aksrt-settings#/users/ticket'),
|
||||
'label' => __('工单配置', 'jio'),
|
||||
'url' => admin_url('admin.php?page=jio-settings#/users/ticket'),
|
||||
'icon' => 'dashicons-admin-generic',
|
||||
],
|
||||
]
|
||||
@@ -57,27 +57,27 @@ trait UsersMenusTicketsList {
|
||||
}
|
||||
|
||||
private function render_ticket_list(array $filters, int $paged, array $result): void {
|
||||
NativeUi::card_open('', '', true, 'aksrt-tickets-admin__list-card');
|
||||
NativeUi::card_open('', '', true, 'jio-tickets-admin__list-card');
|
||||
$this->render_ticket_toolbar($filters);
|
||||
echo '<div class="aksrt-native-table-wrap"><table class="widefat striped aksrt-tickets-admin__table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('工单处理列表', 'aksrt') . '</caption><thead><tr>';
|
||||
echo '<div class="jio-native-table-wrap"><table class="widefat striped jio-tickets-admin__table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('工单处理列表', 'jio') . '</caption><thead><tr>';
|
||||
foreach ([
|
||||
'title' => __('工单 / 问题', 'aksrt'),
|
||||
'status' => __('状态', 'aksrt'),
|
||||
'type' => __('部门', 'aksrt'),
|
||||
'user' => __('用户', 'aksrt'),
|
||||
'admin' => __('跟进人', 'aksrt'),
|
||||
'time' => __('最后活动', 'aksrt'),
|
||||
'actions' => __('操作', 'aksrt'),
|
||||
'title' => __('工单 / 问题', 'jio'),
|
||||
'status' => __('状态', 'jio'),
|
||||
'type' => __('部门', 'jio'),
|
||||
'user' => __('用户', 'jio'),
|
||||
'admin' => __('跟进人', 'jio'),
|
||||
'time' => __('最后活动', 'jio'),
|
||||
'actions' => __('操作', 'jio'),
|
||||
] as $key => $label) {
|
||||
echo '<th scope="col" class="column-' . esc_attr($key) . '">' . esc_html($label) . '</th>';
|
||||
}
|
||||
echo '</tr></thead><tbody>';
|
||||
if ($result['items'] === []) {
|
||||
$filtered = $filters['status'] !== '' || $filters['ticket_type_id'] > 0 || $filters['s'] !== '';
|
||||
echo NativeUi::empty_row(7, __('暂无工单', 'aksrt'), $filtered // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
? __('请调整筛选条件,或使用标题右侧的「代客提交工单」。', 'aksrt')
|
||||
: __('暂无未关闭工单。可切换状态查看历史记录,或代用户创建工单。', 'aksrt'));
|
||||
echo NativeUi::empty_row(7, __('暂无工单', 'jio'), $filtered // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
? __('请调整筛选条件,或使用标题右侧的「代客提交工单」。', 'jio')
|
||||
: __('暂无未关闭工单。可切换状态查看历史记录,或代用户创建工单。', 'jio'));
|
||||
}
|
||||
$context = $this->ticket_context($filters, $paged);
|
||||
foreach ($result['items'] as $ticket) {
|
||||
@@ -106,13 +106,13 @@ trait UsersMenusTicketsList {
|
||||
}
|
||||
$views[] = [$key, (string) $row['name'], $count, (string) $row['color']];
|
||||
}
|
||||
array_unshift($views, ['', __('未关闭', 'aksrt'), $open_total, '']);
|
||||
echo '<div class="aksrt-tickets-admin__toolbar"><h2 class="aksrt-native-card__title">' . esc_html__('工单列表', 'aksrt') . '</h2>';
|
||||
echo '<nav aria-label="' . esc_attr__('按工单状态筛选', 'aksrt') . '"><ul class="subsubsub">';
|
||||
array_unshift($views, ['', __('未关闭', 'jio'), $open_total, '']);
|
||||
echo '<div class="jio-tickets-admin__toolbar"><h2 class="jio-native-card__title">' . esc_html__('工单列表', 'jio') . '</h2>';
|
||||
echo '<nav aria-label="' . esc_attr__('按工单状态筛选', 'jio') . '"><ul class="subsubsub">';
|
||||
foreach ($views as [$key, $label, $count, $color]) {
|
||||
echo '<li><a href="' . esc_url($this->ticket_page_url(array_merge($filters, ['status' => $key]))) . '"' . ($filters['status'] === $key ? ' class="current" aria-current="page"' : '') . '>';
|
||||
if ($color !== '') {
|
||||
echo '<span class="aksrt-ticket-dot" aria-hidden="true" style="--aksrt-ticket-color:' . esc_attr($color) . '"></span>';
|
||||
echo '<span class="jio-ticket-dot" aria-hidden="true" style="--jio-ticket-color:' . esc_attr($color) . '"></span>';
|
||||
}
|
||||
echo esc_html($label) . ' <span class="count">' . esc_html(number_format_i18n($count)) . '</span></a></li>';
|
||||
}
|
||||
@@ -122,17 +122,17 @@ trait UsersMenusTicketsList {
|
||||
}
|
||||
|
||||
private function render_ticket_filter_form(array $filters): void {
|
||||
echo '<form method="get" action="' . esc_url(admin_url('users.php')) . '" class="aksrt-native-filters">';
|
||||
echo '<form method="get" action="' . esc_url(admin_url('users.php')) . '" class="jio-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="' . esc_attr(self::TICKET_PAGE_SLUG) . '"><input type="hidden" name="status" value="' . esc_attr($filters['status']) . '">';
|
||||
echo '<label class="screen-reader-text" for="aksrt-ticket-filter-type">' . esc_html__('按部门筛选', 'aksrt') . '</label><select name="ticket_type_id" id="aksrt-ticket-filter-type"><option value="">' . esc_html__('全部部门', 'aksrt') . '</option>';
|
||||
echo '<label class="screen-reader-text" for="jio-ticket-filter-type">' . esc_html__('按部门筛选', 'jio') . '</label><select name="ticket_type_id" id="jio-ticket-filter-type"><option value="">' . esc_html__('全部部门', 'jio') . '</option>';
|
||||
foreach (TicketTypeService::all() as $row) {
|
||||
echo '<option value="' . (int) $row['id'] . '"' . selected($filters['ticket_type_id'], (int) $row['id'], false) . '>' . esc_html((string) $row['name']) . '</option>';
|
||||
}
|
||||
echo '</select><label class="screen-reader-text" for="aksrt-ticket-filter-s">' . esc_html__('搜索工单', 'aksrt') . '</label>';
|
||||
echo '<input type="search" id="aksrt-ticket-filter-s" name="s" value="' . esc_attr($filters['s']) . '" placeholder="' . esc_attr__('工单号 / 标题', 'aksrt') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('筛选', 'aksrt') . '</button>';
|
||||
echo '</select><label class="screen-reader-text" for="jio-ticket-filter-s">' . esc_html__('搜索工单', 'jio') . '</label>';
|
||||
echo '<input type="search" id="jio-ticket-filter-s" name="s" value="' . esc_attr($filters['s']) . '" placeholder="' . esc_attr__('工单号 / 标题', 'jio') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('筛选', 'jio') . '</button>';
|
||||
if ($filters['ticket_type_id'] > 0 || $filters['s'] !== '') {
|
||||
echo '<a class="button-link" href="' . esc_url($this->ticket_page_url(['status' => $filters['status']])) . '">' . esc_html__('清除筛选', 'aksrt') . '</a>';
|
||||
echo '<a class="button-link" href="' . esc_url($this->ticket_page_url(['status' => $filters['status']])) . '">' . esc_html__('清除筛选', 'jio') . '</a>';
|
||||
}
|
||||
echo '</form>';
|
||||
}
|
||||
@@ -142,22 +142,22 @@ trait UsersMenusTicketsList {
|
||||
$url = esc_url($this->ticket_page_url(['id' => $id] + $context));
|
||||
$last = (string) $ticket['last_reply_time'] !== '' ? (string) $ticket['last_reply_time'] : (string) $ticket['post_time'];
|
||||
$awaiting = in_array((string) $ticket['status'], [TicketStatusService::KEY_PENDING, TicketStatusService::KEY_USER_REPLIED], true);
|
||||
echo '<tr class="aksrt-tickets-admin__row' . ($awaiting ? ' is-awaiting' : '') . '">';
|
||||
echo '<tr class="jio-tickets-admin__row' . ($awaiting ? ' is-awaiting' : '') . '">';
|
||||
echo '<td class="column-title"><a class="row-title" href="' . $url . '">' . esc_html((string) $ticket['title']) . '</a>' . $this->ticket_urge_badge($ticket); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<div class="aksrt-tickets-admin__sub">' . esc_html((string) $ticket['ticket_num']) . '</div>';
|
||||
echo '<div class="jio-tickets-admin__sub">' . esc_html((string) $ticket['ticket_num']) . '</div>';
|
||||
if ((string) $ticket['order_number'] !== '') {
|
||||
echo '<div class="aksrt-tickets-admin__sub">' . esc_html__('订单:', 'aksrt') . esc_html((string) $ticket['order_number']) . '</div>';
|
||||
echo '<div class="jio-tickets-admin__sub">' . esc_html__('订单:', 'jio') . esc_html((string) $ticket['order_number']) . '</div>';
|
||||
}
|
||||
echo '</td><td class="column-status">' . $this->ticket_status_badge($ticket) . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<td class="column-type">' . esc_html((string) $ticket['ticket_type_name'] !== '' ? (string) $ticket['ticket_type_name'] : '—') . '</td>';
|
||||
echo '<td class="column-user">' . $this->ticket_user_chip((int) $ticket['user_id'], (string) $ticket['user_name']) . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<td class="column-admin">' . ((int) $ticket['admin_id'] > 0 ? esc_html((string) $ticket['admin_name']) : '<span class="aksrt-ticket-hint">' . esc_html__('未指派', 'aksrt') . '</span>') . '</td>';
|
||||
echo '<td class="column-time"><time>' . esc_html($last) . '</time><div class="aksrt-tickets-admin__sub">' . esc_html(sprintf(__('提交于 %s', 'aksrt'), (string) $ticket['post_time'])) . '</div></td>';
|
||||
echo '<td class="column-actions"><a class="button' . ($awaiting ? ' button-primary' : '') . '" href="' . $url . '">' . esc_html(!empty($ticket['is_closed']) ? __('查看记录', 'aksrt') : __('处理工单', 'aksrt')) . '</a></td></tr>';
|
||||
echo '<td class="column-admin">' . ((int) $ticket['admin_id'] > 0 ? esc_html((string) $ticket['admin_name']) : '<span class="jio-ticket-hint">' . esc_html__('未指派', 'jio') . '</span>') . '</td>';
|
||||
echo '<td class="column-time"><time>' . esc_html($last) . '</time><div class="jio-tickets-admin__sub">' . esc_html(sprintf(__('提交于 %s', 'jio'), (string) $ticket['post_time'])) . '</div></td>';
|
||||
echo '<td class="column-actions"><a class="button' . ($awaiting ? ' button-primary' : '') . '" href="' . $url . '">' . esc_html(!empty($ticket['is_closed']) ? __('查看记录', 'jio') : __('处理工单', 'jio')) . '</a></td></tr>';
|
||||
}
|
||||
|
||||
private function ticket_status_badge(array $ticket): string {
|
||||
return '<span class="aksrt-ticket-badge" style="--aksrt-ticket-color:' . esc_attr((string) $ticket['status_color']) . '">' . esc_html((string) $ticket['status_label']) . '</span>';
|
||||
return '<span class="jio-ticket-badge" style="--jio-ticket-color:' . esc_attr((string) $ticket['status_color']) . '">' . esc_html((string) $ticket['status_label']) . '</span>';
|
||||
}
|
||||
|
||||
private function ticket_urge_badge(array $ticket): string {
|
||||
@@ -165,13 +165,13 @@ trait UsersMenusTicketsList {
|
||||
if ($count <= 0 || !empty($ticket['is_closed'])) {
|
||||
return '';
|
||||
}
|
||||
return ' <span class="aksrt-ticket-urge" title="' . esc_attr(sprintf(__('最近催单:%s', 'aksrt'), (string) $ticket['last_urge_time'])) . '">' . esc_html(sprintf(__('催单 ×%d', 'aksrt'), $count)) . '</span>';
|
||||
return ' <span class="jio-ticket-urge" title="' . esc_attr(sprintf(__('最近催单:%s', 'jio'), (string) $ticket['last_urge_time'])) . '">' . esc_html(sprintf(__('催单 ×%d', 'jio'), $count)) . '</span>';
|
||||
}
|
||||
|
||||
private function ticket_user_chip(int $user_id, string $name): string {
|
||||
$avatar = function_exists('aksrt_get_avatar_url') ? aksrt_get_avatar_url($user_id, 48) : '';
|
||||
$avatar = function_exists('jio_get_avatar_url') ? jio_get_avatar_url($user_id, 48) : '';
|
||||
$label = $name !== '' ? $name : '#' . $user_id;
|
||||
return '<a class="aksrt-ticket-user" href="' . esc_url(get_edit_user_link($user_id)) . '" target="_blank" rel="noopener noreferrer" aria-label="' . esc_attr(sprintf(__('%s 的用户资料(新窗口)', 'aksrt'), $label)) . '">'
|
||||
return '<a class="jio-ticket-user" href="' . esc_url(get_edit_user_link($user_id)) . '" target="_blank" rel="noopener noreferrer" aria-label="' . esc_attr(sprintf(__('%s 的用户资料(新窗口)', 'jio'), $label)) . '">'
|
||||
. ($avatar !== '' ? '<img src="' . esc_url($avatar) . '" alt="" width="24" height="24">' : '')
|
||||
. '<span>' . esc_html($label) . '</span></a>';
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?php
|
||||
/** Native WordPress identity application review. @package AKSRT */
|
||||
/** Native WordPress identity application review. @package Jio */
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Identity\ApplicationRepository;
|
||||
use AKSRT\Identity\EvidenceService;
|
||||
use AKSRT\Identity\EventRepository;
|
||||
use AKSRT\User\AuthApply;
|
||||
use Jio\Identity\ApplicationRepository;
|
||||
use Jio\Identity\EvidenceService;
|
||||
use Jio\Identity\EventRepository;
|
||||
use Jio\User\AuthApply;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -17,16 +17,16 @@ trait UsersMenusVerify {
|
||||
private const VERIFY_STATUSES = ['pending', 'approved', 'rejected', ''];
|
||||
|
||||
public function enqueue_verify_assets(string $hook): void {
|
||||
if ($hook !== 'users_page_aksrt_user_auth') return;
|
||||
if ($hook !== 'users_page_jio_user_auth') return;
|
||||
$css = '/assets/css/admin/user-auth.css';
|
||||
$js = '/assets/js/admin-user-auth.js';
|
||||
wp_enqueue_style('aksrt-admin-user-auth', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-user-auth', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-user-auth', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-user-auth', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
private function count_verify_status(string $status): int {
|
||||
global $wpdb;
|
||||
$table = \AKSRT\Core\DBMigrator::table('identity_application');
|
||||
$table = \Jio\Core\DBMigrator::table('identity_application');
|
||||
return (int) $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$table} WHERE type = %s AND channel = %s AND status = %s",
|
||||
'general', 'manual', $status === 'pending' ? 'manual_pending' : $status
|
||||
@@ -38,23 +38,23 @@ trait UsersMenusVerify {
|
||||
$filters = $this->get_verify_filters($_GET);
|
||||
$data = $this->query_verify_requests($filters, max(1, (int) ($_GET['paged'] ?? 1)));
|
||||
$counts = $this->get_verify_status_counts($filters);
|
||||
NativeUi::page_open('aksrt-verify-page');
|
||||
NativeUi::page_open('jio-verify-page');
|
||||
NativeUi::header(
|
||||
__('身份认证', 'aksrt'),
|
||||
__('查看申请资料与证明材料,在审核弹窗中确认处理结果。', 'aksrt'),
|
||||
__('身份认证', 'jio'),
|
||||
__('查看申请资料与证明材料,在审核弹窗中确认处理结果。', 'jio'),
|
||||
'dashicons-id',
|
||||
[['label' => __('认证设置', 'aksrt'), 'url' => admin_url('admin.php?page=aksrt-settings#/users/verify'), 'icon' => 'dashicons-admin-settings']]
|
||||
[['label' => __('认证设置', 'jio'), 'url' => admin_url('admin.php?page=jio-settings#/users/verify'), 'icon' => 'dashicons-admin-settings']]
|
||||
);
|
||||
$this->flash();
|
||||
NativeUi::card_open('', '', true, 'aksrt-verify-list-card');
|
||||
NativeUi::card_open('', '', true, 'jio-verify-list-card');
|
||||
$this->render_verify_filters($filters, $counts);
|
||||
echo '<div class="aksrt-native-table-wrap">';
|
||||
echo '<div class="jio-native-table-wrap">';
|
||||
$this->render_verify_table($data['items'], $filters, $data['page']);
|
||||
NativeUi::table_wrap_close();
|
||||
AdminListPagination::render('aksrt_user_auth', $data['total'], $data['page'], self::VERIFY_PER_PAGE, $filters, 'users.php');
|
||||
AdminListPagination::render('jio_user_auth', $data['total'], $data['page'], self::VERIFY_PER_PAGE, $filters, 'users.php');
|
||||
NativeUi::card_close();
|
||||
echo '<dialog id="aksrt-verify-dialog" class="aksrt-verify-dialog" aria-labelledby="aksrt-verify-dialog-title" aria-describedby="aksrt-verify-dialog-description"></dialog>';
|
||||
echo '<noscript><p class="aksrt-native-callout">' . esc_html__('请启用 JavaScript 后查看申请资料和处理审核。', 'aksrt') . '</p></noscript>';
|
||||
echo '<dialog id="jio-verify-dialog" class="jio-verify-dialog" aria-labelledby="jio-verify-dialog-title" aria-describedby="jio-verify-dialog-description"></dialog>';
|
||||
echo '<noscript><p class="jio-native-callout">' . esc_html__('请启用 JavaScript 后查看申请资料和处理审核。', 'jio') . '</p></noscript>';
|
||||
NativeUi::page_close();
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ trait UsersMenusVerify {
|
||||
/** @return array{0:string,1:array} */
|
||||
private function verify_query_base(array $filters): array {
|
||||
global $wpdb;
|
||||
$table = \AKSRT\Core\DBMigrator::table('identity_application');
|
||||
$table = \Jio\Core\DBMigrator::table('identity_application');
|
||||
$where = ['a.type = %s', 'a.channel = %s'];
|
||||
$params = ['general', 'manual'];
|
||||
if ($filters['status'] !== '') {
|
||||
@@ -128,38 +128,38 @@ trait UsersMenusVerify {
|
||||
}
|
||||
|
||||
private function verify_page_url(array $args = []): string {
|
||||
return add_query_arg(array_merge(['page' => 'aksrt_user_auth'], $args), admin_url('users.php'));
|
||||
return add_query_arg(array_merge(['page' => 'jio_user_auth'], $args), admin_url('users.php'));
|
||||
}
|
||||
|
||||
private function render_verify_filters(array $filters, array $counts): void {
|
||||
echo '<div class="aksrt-verify-toolbar"><div class="aksrt-verify-toolbar__views">';
|
||||
echo '<h2 class="aksrt-native-card__title">' . esc_html__('认证申请', 'aksrt') . '</h2>';
|
||||
echo '<ul class="subsubsub" aria-label="' . esc_attr__('认证状态筛选', 'aksrt') . '">';
|
||||
foreach (['pending' => __('待审核', 'aksrt'), 'approved' => __('已通过', 'aksrt'), 'rejected' => __('已拒绝', 'aksrt'), '' => __('全部', 'aksrt')] as $key => $label) {
|
||||
echo '<div class="jio-verify-toolbar"><div class="jio-verify-toolbar__views">';
|
||||
echo '<h2 class="jio-native-card__title">' . esc_html__('认证申请', 'jio') . '</h2>';
|
||||
echo '<ul class="subsubsub" aria-label="' . esc_attr__('认证状态筛选', 'jio') . '">';
|
||||
foreach (['pending' => __('待审核', 'jio'), 'approved' => __('已通过', 'jio'), 'rejected' => __('已拒绝', 'jio'), '' => __('全部', 'jio')] as $key => $label) {
|
||||
$url = $this->verify_page_url(['status' => $key, 'keyword' => $filters['keyword']]);
|
||||
echo '<li><a href="' . esc_url($url) . '"' . ($key === $filters['status'] ? ' class="current" aria-current="page"' : '') . '>' . esc_html($label)
|
||||
. ' <span class="count">' . esc_html(number_format_i18n($counts[$key === '' ? 'all' : $key])) . '</span></a></li>';
|
||||
}
|
||||
echo '</ul></div><form method="get" action="' . esc_url(admin_url('users.php')) . '" class="aksrt-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="aksrt_user_auth"><input type="hidden" name="status" value="' . esc_attr($filters['status']) . '">';
|
||||
echo '<label class="screen-reader-text" for="aksrt-verify-search">' . esc_html__('搜索用户或认证资料', 'aksrt') . '</label>';
|
||||
echo '<input type="search" id="aksrt-verify-search" name="keyword" value="' . esc_attr($filters['keyword']) . '" placeholder="' . esc_attr__('用户 ID / 昵称 / 邮箱 / 认证名称', 'aksrt') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('搜索', 'aksrt') . '</button>';
|
||||
echo '</ul></div><form method="get" action="' . esc_url(admin_url('users.php')) . '" class="jio-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="jio_user_auth"><input type="hidden" name="status" value="' . esc_attr($filters['status']) . '">';
|
||||
echo '<label class="screen-reader-text" for="jio-verify-search">' . esc_html__('搜索用户或认证资料', 'jio') . '</label>';
|
||||
echo '<input type="search" id="jio-verify-search" name="keyword" value="' . esc_attr($filters['keyword']) . '" placeholder="' . esc_attr__('用户 ID / 昵称 / 邮箱 / 认证名称', 'jio') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('搜索', 'jio') . '</button>';
|
||||
if ($filters['keyword'] !== '') {
|
||||
echo '<a class="button-link" href="' . esc_url($this->verify_page_url(['status' => $filters['status']])) . '">' . esc_html__('清除搜索', 'aksrt') . '</a>';
|
||||
echo '<a class="button-link" href="' . esc_url($this->verify_page_url(['status' => $filters['status']])) . '">' . esc_html__('清除搜索', 'jio') . '</a>';
|
||||
}
|
||||
echo '</form></div>';
|
||||
}
|
||||
|
||||
private function render_verify_table(array $items, array $filters, int $page): void {
|
||||
echo '<table class="widefat fixed striped aksrt-verify-table"><thead><tr>';
|
||||
foreach (['user' => __('申请用户', 'aksrt'), 'profile' => __('认证资料', 'aksrt'), 'evidence' => __('证明材料', 'aksrt'), 'status' => __('状态', 'aksrt'), 'time' => __('申请时间', 'aksrt'), 'actions' => __('操作', 'aksrt')] as $key => $label) {
|
||||
echo '<table class="widefat fixed striped jio-verify-table"><thead><tr>';
|
||||
foreach (['user' => __('申请用户', 'jio'), 'profile' => __('认证资料', 'jio'), 'evidence' => __('证明材料', 'jio'), 'status' => __('状态', 'jio'), 'time' => __('申请时间', 'jio'), 'actions' => __('操作', 'jio')] as $key => $label) {
|
||||
echo '<th scope="col" class="column-' . esc_attr($key) . '">' . esc_html($label) . '</th>';
|
||||
}
|
||||
echo '</tr></thead><tbody>';
|
||||
if ($items === []) {
|
||||
$title = $filters['status'] === 'pending' && $filters['keyword'] === '' ? __('暂无待审核申请', 'aksrt') : __('没有符合条件的认证申请', 'aksrt');
|
||||
$hint = $filters['keyword'] !== '' ? __('请调整搜索关键词或切换状态。', 'aksrt') : __('可切换状态查看其他申请,新申请将在这里显示。', 'aksrt');
|
||||
$title = $filters['status'] === 'pending' && $filters['keyword'] === '' ? __('暂无待审核申请', 'jio') : __('没有符合条件的认证申请', 'jio');
|
||||
$hint = $filters['keyword'] !== '' ? __('请调整搜索关键词或切换状态。', 'jio') : __('可切换状态查看其他申请,新申请将在这里显示。', 'jio');
|
||||
echo NativeUi::empty_row(6, $title, $hint); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
}
|
||||
foreach ($items as $row) $this->render_verify_row($row, $filters, $page);
|
||||
@@ -171,23 +171,23 @@ trait UsersMenusVerify {
|
||||
$pending = $row['status'] === 'manual_pending';
|
||||
$evidence = (new EvidenceService())->items($row, true);
|
||||
echo '<tr><td class="column-user">' . NativeUi::user_cell((int) $row['user_id'], (string) $row['display_name']); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<div class="aksrt-verify-sub">' . esc_html((string) $row['user_email']) . '</div></td>';
|
||||
echo '<td class="column-profile"><strong>' . esc_html((string) $row['public_name']) . '</strong><div class="aksrt-verify-summary">' . esc_html((string) $row['description']) . '</div>';
|
||||
echo '<div class="jio-verify-sub">' . esc_html((string) $row['user_email']) . '</div></td>';
|
||||
echo '<td class="column-profile"><strong>' . esc_html((string) $row['public_name']) . '</strong><div class="jio-verify-summary">' . esc_html((string) $row['description']) . '</div>';
|
||||
$this->render_verify_detail_template($row, $evidence, $filters, $page);
|
||||
$this->render_verify_delete_template($row, $filters, $page);
|
||||
echo '</td><td class="column-evidence">' . esc_html($evidence === [] ? __('未提供材料', 'aksrt') : sprintf(__('%d 份材料', 'aksrt'), count($evidence))) . '</td>';
|
||||
echo '</td><td class="column-evidence">' . esc_html($evidence === [] ? __('未提供材料', 'jio') : sprintf(__('%d 份材料', 'jio'), count($evidence))) . '</td>';
|
||||
echo '<td class="column-status">' . $this->verify_status_badge((string) $row['status']) . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<td class="column-time">' . esc_html($this->verify_time((string) $row['created_at'])) . '</td>';
|
||||
echo '<td class="column-actions"><div class="aksrt-verify-row-actions">';
|
||||
echo '<button type="button" class="button' . ($pending ? ' button-primary' : '') . '" data-aksrt-verify-view="' . $id . '" aria-haspopup="dialog" aria-controls="aksrt-verify-dialog">' . esc_html($pending ? __('审核', 'aksrt') : __('查看详情', 'aksrt')) . '</button>';
|
||||
echo '<button type="button" class="button-link button-link-delete" data-aksrt-verify-delete="' . $id . '" aria-haspopup="dialog" aria-controls="aksrt-verify-dialog">' . esc_html__('删除记录', 'aksrt') . '</button></div></td></tr>';
|
||||
echo '<td class="column-actions"><div class="jio-verify-row-actions">';
|
||||
echo '<button type="button" class="button' . ($pending ? ' button-primary' : '') . '" data-jio-verify-view="' . $id . '" aria-haspopup="dialog" aria-controls="jio-verify-dialog">' . esc_html($pending ? __('审核', 'jio') : __('查看详情', 'jio')) . '</button>';
|
||||
echo '<button type="button" class="button-link button-link-delete" data-jio-verify-delete="' . $id . '" aria-haspopup="dialog" aria-controls="jio-verify-dialog">' . esc_html__('删除记录', 'jio') . '</button></div></td></tr>';
|
||||
}
|
||||
|
||||
private function verify_status_badge(string $status): string {
|
||||
return NativeUi::status_badge($status, [
|
||||
'manual_pending' => [__('待审核', 'aksrt'), 'warning'],
|
||||
'verified' => [__('已通过', 'aksrt'), 'success'],
|
||||
'rejected' => [__('已拒绝', 'aksrt'), 'danger'],
|
||||
'manual_pending' => [__('待审核', 'jio'), 'warning'],
|
||||
'verified' => [__('已通过', 'jio'), 'success'],
|
||||
'rejected' => [__('已拒绝', 'jio'), 'danger'],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -203,9 +203,9 @@ trait UsersMenusVerify {
|
||||
}
|
||||
|
||||
public function handle_review_verify(): void {
|
||||
if (!current_user_can('aksrt_review_identity') && !current_user_can(self::CAPABILITY)) wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
if (!current_user_can('jio_review_identity') && !current_user_can(self::CAPABILITY)) wp_die(esc_html__('权限不足', 'jio'));
|
||||
$uuid = sanitize_text_field((string) wp_unslash($_POST['application_uuid'] ?? ''));
|
||||
check_admin_referer('aksrt_users_review_verify_' . $uuid);
|
||||
check_admin_referer('jio_users_review_verify_' . $uuid);
|
||||
$user_id = absint($_POST['user_id'] ?? 0);
|
||||
$status = sanitize_key((string) wp_unslash($_POST['status'] ?? ''));
|
||||
$note = sanitize_textarea_field((string) wp_unslash($_POST['note'] ?? ''));
|
||||
@@ -215,27 +215,27 @@ trait UsersMenusVerify {
|
||||
];
|
||||
$result = AuthApply::review($user_id, $uuid, $status, $note, get_current_user_id(), $fields);
|
||||
$extra = $this->verify_return_args();
|
||||
if (is_wp_error($result)) $extra['aksrt_verify_error'] = $result->get_error_message();
|
||||
$this->redirect('aksrt_user_auth', is_wp_error($result) ? 'verify_failed' : 'verify_updated', $extra);
|
||||
if (is_wp_error($result)) $extra['jio_verify_error'] = $result->get_error_message();
|
||||
$this->redirect('jio_user_auth', is_wp_error($result) ? 'verify_failed' : 'verify_updated', $extra);
|
||||
}
|
||||
|
||||
public function handle_delete_verify(): void {
|
||||
if (!current_user_can('aksrt_review_identity') && !current_user_can(self::CAPABILITY)) wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
if (!current_user_can('jio_review_identity') && !current_user_can(self::CAPABILITY)) wp_die(esc_html__('权限不足', 'jio'));
|
||||
$uuid = sanitize_text_field((string) wp_unslash($_POST['application_uuid'] ?? ''));
|
||||
check_admin_referer('aksrt_users_delete_verify_' . $uuid);
|
||||
check_admin_referer('jio_users_delete_verify_' . $uuid);
|
||||
$extra = $this->verify_return_args();
|
||||
$repo = new ApplicationRepository();
|
||||
$application = $repo->find_by_uuid($uuid);
|
||||
if (!$application) {
|
||||
$this->redirect('aksrt_user_auth', 'verify_failed', array_merge($extra, ['aksrt_verify_error' => __('认证记录不存在', 'aksrt')]));
|
||||
$this->redirect('jio_user_auth', 'verify_failed', array_merge($extra, ['jio_verify_error' => __('认证记录不存在', 'jio')]));
|
||||
}
|
||||
if (!$repo->delete((int) $application['id'])) {
|
||||
$this->redirect('aksrt_user_auth', 'verify_failed', array_merge($extra, ['aksrt_verify_error' => __('认证记录删除失败', 'aksrt')]));
|
||||
$this->redirect('jio_user_auth', 'verify_failed', array_merge($extra, ['jio_verify_error' => __('认证记录删除失败', 'jio')]));
|
||||
}
|
||||
(new EvidenceService())->delete((array) $application['evidence_ids']);
|
||||
if (!(new EventRepository())->delete_for_application((int) $application['id'])) {
|
||||
$this->redirect('aksrt_user_auth', 'verify_failed', array_merge($extra, ['aksrt_verify_error' => __('认证申请记录已删除,但审核事件清理失败。', 'aksrt')]));
|
||||
$this->redirect('jio_user_auth', 'verify_failed', array_merge($extra, ['jio_verify_error' => __('认证申请记录已删除,但审核事件清理失败。', 'jio')]));
|
||||
}
|
||||
$this->redirect('aksrt_user_auth', 'verify_deleted', $extra);
|
||||
$this->redirect('jio_user_auth', 'verify_deleted', $extra);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
<?php
|
||||
/** Identity application detail, review and deletion dialogs. @package AKSRT */
|
||||
/** Identity application detail, review and deletion dialogs. @package Jio */
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait UsersMenusVerifyDialogs {
|
||||
|
||||
private function render_verify_dialog_head(string $title, string $description): void {
|
||||
echo '<div class="aksrt-verify-dialog__head"><div><h2 id="aksrt-verify-dialog-title">' . esc_html($title) . '</h2>';
|
||||
echo '<p id="aksrt-verify-dialog-description">' . esc_html($description) . '</p></div>';
|
||||
echo '<button type="button" class="aksrt-verify-dialog__close" data-aksrt-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></div>';
|
||||
echo '<div class="jio-verify-dialog__head"><div><h2 id="jio-verify-dialog-title">' . esc_html($title) . '</h2>';
|
||||
echo '<p id="jio-verify-dialog-description">' . esc_html($description) . '</p></div>';
|
||||
echo '<button type="button" class="jio-verify-dialog__close" data-jio-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></div>';
|
||||
}
|
||||
|
||||
private function render_verify_context(array $row, array $filters, int $page, string $action): void {
|
||||
@@ -24,70 +24,70 @@ trait UsersMenusVerifyDialogs {
|
||||
|
||||
private function render_verify_detail_template(array $row, array $evidence, array $filters, int $page): void {
|
||||
$pending = $row['status'] === 'manual_pending';
|
||||
echo '<template id="aksrt-verify-detail-' . (int) $row['id'] . '">';
|
||||
echo '<template id="jio-verify-detail-' . (int) $row['id'] . '">';
|
||||
if ($pending) {
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-verify-dialog__frame" data-aksrt-verify-review>';
|
||||
$this->render_verify_context($row, $filters, $page, 'aksrt_users_review_verify');
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-verify-dialog__frame" data-jio-verify-review>';
|
||||
$this->render_verify_context($row, $filters, $page, 'jio_users_review_verify');
|
||||
echo '<input type="hidden" name="user_id" value="' . (int) $row['user_id'] . '">';
|
||||
} else {
|
||||
echo '<div class="aksrt-verify-dialog__frame">';
|
||||
echo '<div class="jio-verify-dialog__frame">';
|
||||
}
|
||||
$this->render_verify_dialog_head(
|
||||
$pending ? __('审核认证申请', 'aksrt') : __('认证申请详情', 'aksrt'),
|
||||
$pending ? __('先核对申请资料与证明材料,再选择审核结果。', 'aksrt') : __('该申请已处理,以下为申请资料与审核结果。', 'aksrt')
|
||||
$pending ? __('审核认证申请', 'jio') : __('认证申请详情', 'jio'),
|
||||
$pending ? __('先核对申请资料与证明材料,再选择审核结果。', 'jio') : __('该申请已处理,以下为申请资料与审核结果。', 'jio')
|
||||
);
|
||||
echo '<div class="aksrt-verify-dialog__body">';
|
||||
echo '<div class="aksrt-verify-applicant">' . NativeUi::user_cell((int) $row['user_id'], (string) $row['display_name']); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<div class="jio-verify-dialog__body">';
|
||||
echo '<div class="jio-verify-applicant">' . NativeUi::user_cell((int) $row['user_id'], (string) $row['display_name']); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo $this->verify_status_badge((string) $row['status']) . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
$meta = [
|
||||
__('用户账号', 'aksrt') => (string) $row['user_login'],
|
||||
__('邮箱', 'aksrt') => (string) $row['user_email'],
|
||||
__('申请时间', 'aksrt') => $this->verify_time((string) $row['created_at']),
|
||||
__('用户账号', 'jio') => (string) $row['user_login'],
|
||||
__('邮箱', 'jio') => (string) $row['user_email'],
|
||||
__('申请时间', 'jio') => $this->verify_time((string) $row['created_at']),
|
||||
];
|
||||
if (!$pending) {
|
||||
$reviewer = get_userdata((int) $row['reviewer_id']);
|
||||
$meta[__('审核人', 'aksrt')] = $reviewer ? (string) $reviewer->display_name : '—';
|
||||
$meta[__('审核时间', 'aksrt')] = $this->verify_time((string) ($row['completed_at'] ?? ''));
|
||||
$meta[__('审核人', 'jio')] = $reviewer ? (string) $reviewer->display_name : '—';
|
||||
$meta[__('审核时间', 'jio')] = $this->verify_time((string) ($row['completed_at'] ?? ''));
|
||||
}
|
||||
echo '<dl class="aksrt-verify-meta">';
|
||||
echo '<dl class="jio-verify-meta">';
|
||||
foreach ($meta as $label => $value) {
|
||||
echo '<div><dt>' . esc_html($label) . '</dt><dd>' . esc_html($value !== '' ? $value : '—') . '</dd></div>';
|
||||
}
|
||||
echo '</dl>';
|
||||
$this->render_verify_text_section(__('认证名称', 'aksrt'), (string) $row['public_name']);
|
||||
$this->render_verify_text_section(__('认证简介', 'aksrt'), (string) $row['description']);
|
||||
$this->render_verify_text_section(__('申请说明', 'aksrt'), (string) $row['manual_reason']);
|
||||
$this->render_verify_text_section(__('认证名称', 'jio'), (string) $row['public_name']);
|
||||
$this->render_verify_text_section(__('认证简介', 'jio'), (string) $row['description']);
|
||||
$this->render_verify_text_section(__('申请说明', 'jio'), (string) $row['manual_reason']);
|
||||
$this->render_verify_evidence($evidence);
|
||||
if ($pending) {
|
||||
$this->render_verify_review_fields($row);
|
||||
} else {
|
||||
$this->render_verify_text_section($row['status'] === 'rejected' ? __('拒绝原因', 'aksrt') : __('处理留言', 'aksrt'), (string) $row['review_note']);
|
||||
$this->render_verify_text_section($row['status'] === 'rejected' ? __('拒绝原因', 'jio') : __('处理留言', 'jio'), (string) $row['review_note']);
|
||||
}
|
||||
echo '</div><div class="aksrt-verify-dialog__foot">';
|
||||
echo '<button type="button" class="button" data-aksrt-dialog-close>' . esc_html($pending ? __('取消', 'aksrt') : __('关闭', 'aksrt')) . '</button>';
|
||||
echo '</div><div class="jio-verify-dialog__foot">';
|
||||
echo '<button type="button" class="button" data-jio-dialog-close>' . esc_html($pending ? __('取消', 'jio') : __('关闭', 'jio')) . '</button>';
|
||||
if ($pending) {
|
||||
echo '<button type="submit" class="button button-primary" data-aksrt-verify-submit disabled'
|
||||
. ' data-idle-label="' . esc_attr__('请选择审核结果', 'aksrt') . '"'
|
||||
. ' data-approve-label="' . esc_attr__('确认通过', 'aksrt') . '"'
|
||||
. ' data-reject-label="' . esc_attr__('确认拒绝', 'aksrt') . '"'
|
||||
. ' data-busy-label="' . esc_attr__('正在提交…', 'aksrt') . '">' . esc_html__('请选择审核结果', 'aksrt') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary" data-jio-verify-submit disabled'
|
||||
. ' data-idle-label="' . esc_attr__('请选择审核结果', 'jio') . '"'
|
||||
. ' data-approve-label="' . esc_attr__('确认通过', 'jio') . '"'
|
||||
. ' data-reject-label="' . esc_attr__('确认拒绝', 'jio') . '"'
|
||||
. ' data-busy-label="' . esc_attr__('正在提交…', 'jio') . '">' . esc_html__('请选择审核结果', 'jio') . '</button>';
|
||||
}
|
||||
echo '</div>' . ($pending ? '</form>' : '</div>') . '</template>';
|
||||
}
|
||||
|
||||
private function render_verify_text_section(string $title, string $text): void {
|
||||
echo '<section class="aksrt-verify-section"><h3>' . esc_html($title) . '</h3><div class="aksrt-verify-copy">' . esc_html($text !== '' ? $text : __('未填写', 'aksrt')) . '</div></section>';
|
||||
echo '<section class="jio-verify-section"><h3>' . esc_html($title) . '</h3><div class="jio-verify-copy">' . esc_html($text !== '' ? $text : __('未填写', 'jio')) . '</div></section>';
|
||||
}
|
||||
|
||||
private function render_verify_evidence(array $evidence): void {
|
||||
echo '<section class="aksrt-verify-section"><h3>' . esc_html__('证明材料', 'aksrt') . '</h3>';
|
||||
echo '<section class="jio-verify-section"><h3>' . esc_html__('证明材料', 'jio') . '</h3>';
|
||||
if ($evidence === []) {
|
||||
echo '<p class="aksrt-verify-evidence-empty">' . esc_html__('未提供可查看的证明材料,请结合申请说明判断。', 'aksrt') . '</p>';
|
||||
echo '<p class="jio-verify-evidence-empty">' . esc_html__('未提供可查看的证明材料,请结合申请说明判断。', 'jio') . '</p>';
|
||||
} else {
|
||||
echo '<ul class="aksrt-verify-evidence">';
|
||||
echo '<ul class="jio-verify-evidence">';
|
||||
foreach ($evidence as $index => $item) {
|
||||
$label = sprintf(__('材料 %d(新窗口查看原图)', 'aksrt'), $index + 1);
|
||||
echo '<li><a href="' . esc_url($item['url']) . '" target="_blank" rel="noopener noreferrer"><img src="' . esc_url($item['thumbnail_url']) . '" alt="' . esc_attr(sprintf(__('证明材料 %d', 'aksrt'), $index + 1)) . '"><span>' . esc_html($label) . '</span></a></li>';
|
||||
$label = sprintf(__('材料 %d(新窗口查看原图)', 'jio'), $index + 1);
|
||||
echo '<li><a href="' . esc_url($item['url']) . '" target="_blank" rel="noopener noreferrer"><img src="' . esc_url($item['thumbnail_url']) . '" alt="' . esc_attr(sprintf(__('证明材料 %d', 'jio'), $index + 1)) . '"><span>' . esc_html($label) . '</span></a></li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
}
|
||||
@@ -95,30 +95,30 @@ trait UsersMenusVerifyDialogs {
|
||||
}
|
||||
|
||||
private function render_verify_review_fields(array $row): void {
|
||||
echo '<section class="aksrt-verify-review"><h3>' . esc_html__('审核处理', 'aksrt') . '</h3>';
|
||||
echo '<fieldset class="aksrt-verify-decisions"><legend>' . esc_html__('审核结果(必选)', 'aksrt') . '</legend>';
|
||||
foreach (['verified' => __('通过申请', 'aksrt'), 'rejected' => __('拒绝申请', 'aksrt')] as $status => $label) {
|
||||
echo '<section class="jio-verify-review"><h3>' . esc_html__('审核处理', 'jio') . '</h3>';
|
||||
echo '<fieldset class="jio-verify-decisions"><legend>' . esc_html__('审核结果(必选)', 'jio') . '</legend>';
|
||||
foreach (['verified' => __('通过申请', 'jio'), 'rejected' => __('拒绝申请', 'jio')] as $status => $label) {
|
||||
echo '<label><input type="radio" name="status" value="' . esc_attr($status) . '" required><span>' . esc_html($label) . '</span></label>';
|
||||
}
|
||||
echo '</fieldset><fieldset class="aksrt-verify-profile" data-aksrt-verify-profile hidden disabled><legend>' . esc_html__('通过后公开展示的认证资料', 'aksrt') . '</legend>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('认证名称(必填)', 'aksrt') . '</span>';
|
||||
echo '<input type="text" name="name" maxlength="64" value="' . esc_attr((string) $row['public_name']) . '" required data-required-error="' . esc_attr__('请填写认证名称', 'aksrt') . '"></label>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('认证简介', 'aksrt') . '</span><textarea name="description" rows="3">' . esc_textarea((string) $row['description']) . '</textarea></label>';
|
||||
echo '<p class="aksrt-native-field__hint">' . esc_html__('通过审核前可修订名称与简介,原申请说明和证明材料不会被修改。', 'aksrt') . '</p></fieldset>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label" data-aksrt-verify-note-label data-optional="' . esc_attr__('处理留言(选填)', 'aksrt') . '" data-required="' . esc_attr__('拒绝原因(必填)', 'aksrt') . '">' . esc_html__('处理留言(选填)', 'aksrt') . '</span>';
|
||||
echo '<textarea name="note" rows="3" data-required-error="' . esc_attr__('请填写拒绝原因', 'aksrt') . '" aria-describedby="aksrt-verify-note-hint"></textarea></label>';
|
||||
echo '<p id="aksrt-verify-note-hint" class="aksrt-native-field__hint">' . esc_html__('审核结果会通知申请用户;拒绝时请说明原因,便于用户修改后重新申请。', 'aksrt') . '</p></section>';
|
||||
echo '</fieldset><fieldset class="jio-verify-profile" data-jio-verify-profile hidden disabled><legend>' . esc_html__('通过后公开展示的认证资料', 'jio') . '</legend>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('认证名称(必填)', 'jio') . '</span>';
|
||||
echo '<input type="text" name="name" maxlength="64" value="' . esc_attr((string) $row['public_name']) . '" required data-required-error="' . esc_attr__('请填写认证名称', 'jio') . '"></label>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('认证简介', 'jio') . '</span><textarea name="description" rows="3">' . esc_textarea((string) $row['description']) . '</textarea></label>';
|
||||
echo '<p class="jio-native-field__hint">' . esc_html__('通过审核前可修订名称与简介,原申请说明和证明材料不会被修改。', 'jio') . '</p></fieldset>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label" data-jio-verify-note-label data-optional="' . esc_attr__('处理留言(选填)', 'jio') . '" data-required="' . esc_attr__('拒绝原因(必填)', 'jio') . '">' . esc_html__('处理留言(选填)', 'jio') . '</span>';
|
||||
echo '<textarea name="note" rows="3" data-required-error="' . esc_attr__('请填写拒绝原因', 'jio') . '" aria-describedby="jio-verify-note-hint"></textarea></label>';
|
||||
echo '<p id="jio-verify-note-hint" class="jio-native-field__hint">' . esc_html__('审核结果会通知申请用户;拒绝时请说明原因,便于用户修改后重新申请。', 'jio') . '</p></section>';
|
||||
}
|
||||
|
||||
private function render_verify_delete_template(array $row, array $filters, int $page): void {
|
||||
echo '<template id="aksrt-verify-delete-' . (int) $row['id'] . '"><form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-verify-dialog__frame" data-aksrt-verify-delete-form>';
|
||||
$this->render_verify_context($row, $filters, $page, 'aksrt_users_delete_verify');
|
||||
$this->render_verify_dialog_head(__('删除认证申请记录', 'aksrt'), __('请核对申请信息。此操作不可撤销。', 'aksrt'));
|
||||
echo '<div class="aksrt-verify-dialog__body"><div class="aksrt-verify-applicant">' . NativeUi::user_cell((int) $row['user_id'], (string) $row['display_name']); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<template id="jio-verify-delete-' . (int) $row['id'] . '"><form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-verify-dialog__frame" data-jio-verify-delete-form>';
|
||||
$this->render_verify_context($row, $filters, $page, 'jio_users_delete_verify');
|
||||
$this->render_verify_dialog_head(__('删除认证申请记录', 'jio'), __('请核对申请信息。此操作不可撤销。', 'jio'));
|
||||
echo '<div class="jio-verify-dialog__body"><div class="jio-verify-applicant">' . NativeUi::user_cell((int) $row['user_id'], (string) $row['display_name']); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo $this->verify_status_badge((string) $row['status']) . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
$this->render_verify_text_section(__('认证名称', 'aksrt'), (string) $row['public_name']);
|
||||
echo '<p class="aksrt-native-callout is-danger">' . esc_html__('将永久删除该申请记录、证明材料及审核事件。删除记录不会撤销用户已获得的认证。', 'aksrt') . '</p>';
|
||||
echo '</div><div class="aksrt-verify-dialog__foot"><button type="button" class="button" data-aksrt-dialog-close autofocus>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
echo '<button type="submit" class="button is-danger" data-aksrt-verify-submit data-idle-label="' . esc_attr__('确认删除', 'aksrt') . '" data-busy-label="' . esc_attr__('正在提交…', 'aksrt') . '">' . esc_html__('确认删除', 'aksrt') . '</button></div></form></template>';
|
||||
$this->render_verify_text_section(__('认证名称', 'jio'), (string) $row['public_name']);
|
||||
echo '<p class="jio-native-callout is-danger">' . esc_html__('将永久删除该申请记录、证明材料及审核事件。删除记录不会撤销用户已获得的认证。', 'jio') . '</p>';
|
||||
echo '</div><div class="jio-verify-dialog__foot"><button type="button" class="button" data-jio-dialog-close autofocus>' . esc_html__('取消', 'jio') . '</button>';
|
||||
echo '<button type="submit" class="button is-danger" data-jio-verify-submit data-idle-label="' . esc_attr__('确认删除', 'jio') . '" data-busy-label="' . esc_attr__('正在提交…', 'jio') . '">' . esc_html__('确认删除', 'jio') . '</button></div></form></template>';
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* 用户会员后台页(users.php → aksrt_user_vip):页面编排、写操作与查询。
|
||||
* 用户会员后台页(users.php → jio_user_vip):页面编排、写操作与查询。
|
||||
*
|
||||
* 列表展示由 UsersMenusVipView 输出;所有会员变更都通过同一个弹窗表单提交到
|
||||
* admin_post_aksrt_users_mutate_vip(单人调整与批量调整共用),交互脚本见
|
||||
* admin_post_jio_users_mutate_vip(单人调整与批量调整共用),交互脚本见
|
||||
* assets/js/admin-vip.js。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -17,7 +17,7 @@ trait UsersMenusVip {
|
||||
|
||||
use UsersMenusVipView;
|
||||
|
||||
private const VIP_PAGE_SLUG = 'aksrt_user_vip';
|
||||
private const VIP_PAGE_SLUG = 'jio_user_vip';
|
||||
private const VIP_PER_PAGE = 20;
|
||||
private const VIP_FILTER_KEYS = ['user_id', 'level', 'state', 'date_from', 'date_to', 'orderby', 'order'];
|
||||
|
||||
@@ -27,11 +27,11 @@ trait UsersMenusVip {
|
||||
}
|
||||
$css = '/assets/css/admin/vip.css';
|
||||
$js = '/assets/js/admin-vip.js';
|
||||
if (is_file(AKSRT_DIR . $css)) {
|
||||
wp_enqueue_style('aksrt-admin-vip', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
if (is_file(JIO_DIR . $css)) {
|
||||
wp_enqueue_style('jio-admin-vip', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
}
|
||||
if (is_file(AKSRT_DIR . $js)) {
|
||||
wp_enqueue_script('aksrt-admin-vip', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
if (is_file(JIO_DIR . $js)) {
|
||||
wp_enqueue_script('jio-admin-vip', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,14 +39,14 @@ trait UsersMenusVip {
|
||||
$this->ensure_cap();
|
||||
$tab = $this->vip_tab();
|
||||
$page = max(1, absint($_GET['paged'] ?? 1));
|
||||
NativeUi::page_open('aksrt-vip');
|
||||
NativeUi::page_open('jio-vip');
|
||||
NativeUi::header(
|
||||
__('用户会员', 'aksrt'),
|
||||
__('查看会员档位与有效期,单人或批量调整会员;每次变更都会记录原因与结果。', 'aksrt'),
|
||||
__('用户会员', 'jio'),
|
||||
__('查看会员档位与有效期,单人或批量调整会员;每次变更都会记录原因与结果。', 'jio'),
|
||||
'dashicons-awards',
|
||||
[[
|
||||
'label' => __('会员配置', 'aksrt'),
|
||||
'url' => admin_url('admin.php?page=aksrt-settings#/payment/vip'),
|
||||
'label' => __('会员配置', 'jio'),
|
||||
'url' => admin_url('admin.php?page=jio-settings#/payment/vip'),
|
||||
'icon' => 'dashicons-admin-generic',
|
||||
]]
|
||||
);
|
||||
@@ -67,18 +67,18 @@ trait UsersMenusVip {
|
||||
|
||||
public function handle_mutate_vip(): void {
|
||||
$this->ensure_cap();
|
||||
check_admin_referer('aksrt_users_mutate_vip');
|
||||
check_admin_referer('jio_users_mutate_vip');
|
||||
$ids = isset($_POST['user_ids']) ? wp_unslash($_POST['user_ids']) : [];
|
||||
$ids = is_array($ids) ? array_values(array_unique(array_filter(array_map('absint', $ids)))) : [];
|
||||
if ($ids === [] || count($ids) > 100) {
|
||||
$this->redirect_vip_result('vip_failed', ['aksrt_vip_error' => __('请选择 1 至 100 名用户。', 'aksrt')]);
|
||||
$this->redirect_vip_result('vip_failed', ['jio_vip_error' => __('请选择 1 至 100 名用户。', 'jio')]);
|
||||
}
|
||||
|
||||
$operation = sanitize_key((string) wp_unslash($_POST['vip_operation'] ?? ''));
|
||||
$reason = sanitize_text_field((string) wp_unslash($_POST['reason'] ?? ''));
|
||||
$payload = $this->vip_mutation_payload($operation);
|
||||
if (is_wp_error($payload)) {
|
||||
$this->redirect_vip_result('vip_failed', ['aksrt_vip_error' => $payload->get_error_message()]);
|
||||
$this->redirect_vip_result('vip_failed', ['jio_vip_error' => $payload->get_error_message()]);
|
||||
}
|
||||
|
||||
$batch = count($ids) > 1 ? wp_generate_uuid4() : '';
|
||||
@@ -89,14 +89,14 @@ trait UsersMenusVip {
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
$result = class_exists('\AKSRT\Content\VipService')
|
||||
? \AKSRT\Content\VipService::get_instance()->mutate_membership($user_id, $operation, $payload, [
|
||||
$result = class_exists('\Jio\Content\VipService')
|
||||
? \Jio\Content\VipService::get_instance()->mutate_membership($user_id, $operation, $payload, [
|
||||
'actor_user_id' => get_current_user_id(),
|
||||
'source' => 'admin',
|
||||
'reason' => $reason,
|
||||
'batch_uuid' => $batch,
|
||||
])
|
||||
: new \WP_Error('aksrt_vip_unavailable', __('会员服务不可用。', 'aksrt'));
|
||||
: new \WP_Error('jio_vip_unavailable', __('会员服务不可用。', 'jio'));
|
||||
if (is_wp_error($result)) {
|
||||
$failed++;
|
||||
$last_error = $result->get_error_message();
|
||||
@@ -105,12 +105,12 @@ trait UsersMenusVip {
|
||||
}
|
||||
}
|
||||
if ($ok === 0) {
|
||||
$this->redirect_vip_result('vip_failed', $last_error !== '' ? ['aksrt_vip_error' => $last_error] : []);
|
||||
$this->redirect_vip_result('vip_failed', $last_error !== '' ? ['jio_vip_error' => $last_error] : []);
|
||||
}
|
||||
$this->redirect_vip_result($failed > 0 || $skipped > 0 ? 'vip_partial' : 'vip_updated', [
|
||||
'aksrt_vip_ok' => $ok,
|
||||
'aksrt_vip_failed_count' => $failed,
|
||||
'aksrt_vip_skipped' => $skipped,
|
||||
'jio_vip_ok' => $ok,
|
||||
'jio_vip_failed_count' => $failed,
|
||||
'jio_vip_skipped' => $skipped,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -164,13 +164,13 @@ trait UsersMenusVip {
|
||||
private function vip_sql_parts(): array {
|
||||
global $wpdb;
|
||||
$meta = "(SELECT user_id,
|
||||
MAX(CASE WHEN meta_key = 'aksrt_vip_level' THEN meta_value END) level,
|
||||
MAX(CASE WHEN meta_key = 'aksrt_vip_expires' THEN meta_value END) expires,
|
||||
MAX(CASE WHEN meta_key = 'aksrt_vip_base_level' THEN meta_value END) base_level,
|
||||
MAX(CASE WHEN meta_key = 'aksrt_vip_base_expires' THEN meta_value END) base_expires,
|
||||
MAX(CASE WHEN meta_key = 'aksrt_vip_level_expired' THEN meta_value END) expired_level
|
||||
MAX(CASE WHEN meta_key = 'jio_vip_level' THEN meta_value END) level,
|
||||
MAX(CASE WHEN meta_key = 'jio_vip_expires' THEN meta_value END) expires,
|
||||
MAX(CASE WHEN meta_key = 'jio_vip_base_level' THEN meta_value END) base_level,
|
||||
MAX(CASE WHEN meta_key = 'jio_vip_base_expires' THEN meta_value END) base_expires,
|
||||
MAX(CASE WHEN meta_key = 'jio_vip_level_expired' THEN meta_value END) expired_level
|
||||
FROM {$wpdb->usermeta}
|
||||
WHERE meta_key IN ('aksrt_vip_level', 'aksrt_vip_expires', 'aksrt_vip_base_level', 'aksrt_vip_base_expires', 'aksrt_vip_level_expired')
|
||||
WHERE meta_key IN ('jio_vip_level', 'jio_vip_expires', 'jio_vip_base_level', 'jio_vip_base_expires', 'jio_vip_level_expired')
|
||||
GROUP BY user_id) m";
|
||||
$raw_level = 'CAST(COALESCE(m.level, 0) AS UNSIGNED)';
|
||||
$raw_expires = 'CAST(COALESCE(m.expires, 0) AS UNSIGNED)';
|
||||
@@ -274,7 +274,7 @@ trait UsersMenusVip {
|
||||
/** @return array<string,mixed>|\WP_Error */
|
||||
private function vip_mutation_payload(string $operation): array|\WP_Error {
|
||||
if (!in_array($operation, ['set', 'extend', 'disable', 'restore'], true)) {
|
||||
return new \WP_Error('aksrt_vip_operation_invalid', __('会员操作无效。', 'aksrt'));
|
||||
return new \WP_Error('jio_vip_operation_invalid', __('会员操作无效。', 'jio'));
|
||||
}
|
||||
if ($operation === 'disable' || $operation === 'restore') {
|
||||
return [];
|
||||
@@ -290,7 +290,7 @@ trait UsersMenusVip {
|
||||
}
|
||||
$date = $this->vip_date($_POST['expires_date'] ?? '');
|
||||
if ($date === '') {
|
||||
return new \WP_Error('aksrt_vip_expiry_required', __('请设置永久会员或有效的到期日期。', 'aksrt'));
|
||||
return new \WP_Error('jio_vip_expiry_required', __('请设置永久会员或有效的到期日期。', 'jio'));
|
||||
}
|
||||
$expires = (new \DateTimeImmutable($date . ' 23:59:59', wp_timezone()))->getTimestamp();
|
||||
return ['level' => absint($_POST['level'] ?? 0), 'expires' => $expires];
|
||||
@@ -298,10 +298,10 @@ trait UsersMenusVip {
|
||||
|
||||
/** @return array<int,string> */
|
||||
private function vip_level_names(): array {
|
||||
$levels = class_exists('\AKSRT\Settings\Settings') ? (array) (\AKSRT\Settings\Settings::get_payment_vip_settings()['levels'] ?? []) : [];
|
||||
$levels = class_exists('\Jio\Settings\Settings') ? (array) (\Jio\Settings\Settings::get_payment_vip_settings()['levels'] ?? []) : [];
|
||||
return [
|
||||
1 => (string) ($levels[1]['name'] ?? __('一级会员', 'aksrt')),
|
||||
2 => (string) ($levels[2]['name'] ?? __('二级会员', 'aksrt')),
|
||||
1 => (string) ($levels[1]['name'] ?? __('一级会员', 'jio')),
|
||||
2 => (string) ($levels[2]['name'] ?? __('二级会员', 'jio')),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
* 弹窗是页面上唯一的写表单:行内「调整」与工具栏「批量调整」都只负责把目标用户
|
||||
* 填进弹窗(assets/js/admin-vip.js),提交由 UsersMenusVip::handle_mutate_vip() 处理。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -21,29 +21,29 @@ trait UsersMenusVipView {
|
||||
$base = admin_url('users.php?page=' . self::VIP_PAGE_SLUG);
|
||||
NativeUi::stats([
|
||||
[
|
||||
'label' => __('限时会员', 'aksrt'),
|
||||
'label' => __('限时会员', 'jio'),
|
||||
'value' => $stats['active'],
|
||||
'url' => add_query_arg('state', 'active', $base),
|
||||
'icon' => 'dashicons-clock',
|
||||
'hint' => __('有效期内', 'aksrt'),
|
||||
'hint' => __('有效期内', 'jio'),
|
||||
],
|
||||
[
|
||||
'label' => __('永久会员', 'aksrt'),
|
||||
'label' => __('永久会员', 'jio'),
|
||||
'value' => $stats['permanent'],
|
||||
'url' => add_query_arg('state', 'permanent', $base),
|
||||
'tone' => 'primary',
|
||||
'icon' => 'dashicons-awards',
|
||||
],
|
||||
[
|
||||
'label' => __('7 天内到期', 'aksrt'),
|
||||
'label' => __('7 天内到期', 'jio'),
|
||||
'value' => $stats['expiring'],
|
||||
'url' => add_query_arg(['state' => 'active', 'date_from' => $window['from'], 'date_to' => $window['to'], 'orderby' => 'expires', 'order' => 'ASC'], $base),
|
||||
'tone' => $stats['expiring'] > 0 ? 'warning' : 'neutral',
|
||||
'icon' => 'dashicons-warning',
|
||||
'hint' => __('建议提醒续费', 'aksrt'),
|
||||
'hint' => __('建议提醒续费', 'jio'),
|
||||
],
|
||||
[
|
||||
'label' => __('已过期', 'aksrt'),
|
||||
'label' => __('已过期', 'jio'),
|
||||
'value' => $stats['expired'],
|
||||
'url' => add_query_arg('state', 'expired', $base),
|
||||
'tone' => 'neutral',
|
||||
@@ -54,14 +54,14 @@ trait UsersMenusVipView {
|
||||
'value' => $stats['level_1'],
|
||||
'tone' => 'info',
|
||||
'icon' => 'dashicons-star-half',
|
||||
'hint' => __('当前有效人数', 'aksrt'),
|
||||
'hint' => __('当前有效人数', 'jio'),
|
||||
],
|
||||
[
|
||||
'label' => $names[2],
|
||||
'value' => $stats['level_2'],
|
||||
'tone' => 'info',
|
||||
'icon' => 'dashicons-star-filled',
|
||||
'hint' => __('当前有效人数', 'aksrt'),
|
||||
'hint' => __('当前有效人数', 'jio'),
|
||||
],
|
||||
]);
|
||||
}
|
||||
@@ -69,13 +69,13 @@ trait UsersMenusVipView {
|
||||
private function render_vip_tabs(string $tab): void {
|
||||
$base = admin_url('users.php?page=' . self::VIP_PAGE_SLUG);
|
||||
$tabs = [
|
||||
'members' => [__('会员列表', 'aksrt'), $base, 'dashicons-groups'],
|
||||
'history' => [__('变更记录', 'aksrt'), add_query_arg('tab', 'history', $base), 'dashicons-backup'],
|
||||
'members' => [__('会员列表', 'jio'), $base, 'dashicons-groups'],
|
||||
'history' => [__('变更记录', 'jio'), add_query_arg('tab', 'history', $base), 'dashicons-backup'],
|
||||
];
|
||||
echo '<nav class="aksrt-native-tabs" aria-label="' . esc_attr__('会员视图', 'aksrt') . '">';
|
||||
echo '<nav class="jio-native-tabs" aria-label="' . esc_attr__('会员视图', 'jio') . '">';
|
||||
foreach ($tabs as $key => [$label, $url, $icon]) {
|
||||
$current = $key === $tab;
|
||||
echo '<a class="aksrt-native-tabs__link' . ($current ? ' is-active' : '') . '" href="' . esc_url($url) . '"' . ($current ? ' aria-current="page"' : '') . '>'
|
||||
echo '<a class="jio-native-tabs__link' . ($current ? ' is-active' : '') . '" href="' . esc_url($url) . '"' . ($current ? ' aria-current="page"' : '') . '>'
|
||||
. '<span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>' . esc_html($label) . '</a>';
|
||||
}
|
||||
echo '</nav>';
|
||||
@@ -83,25 +83,25 @@ trait UsersMenusVipView {
|
||||
|
||||
private function render_vip_filters(array $filters): void {
|
||||
$names = $this->vip_level_names();
|
||||
NativeUi::card_open('', '', false, 'aksrt-native-toolbar aksrt-vip-filters');
|
||||
echo '<form method="get" class="aksrt-native-filters">';
|
||||
NativeUi::card_open('', '', false, 'jio-native-toolbar jio-vip-filters');
|
||||
echo '<form method="get" class="jio-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="' . esc_attr(self::VIP_PAGE_SLUG) . '">';
|
||||
echo '<input type="hidden" name="orderby" value="' . esc_attr($filters['orderby']) . '"><input type="hidden" name="order" value="' . esc_attr($filters['order']) . '">';
|
||||
echo '<label>UID<input type="number" min="1" name="user_id" class="small-text" value="' . esc_attr($filters['user_id'] > 0 ? (string) $filters['user_id'] : '') . '" placeholder="' . esc_attr__('全部', 'aksrt') . '"></label>';
|
||||
echo '<label>' . esc_html__('档位', 'aksrt') . '<select name="level">';
|
||||
foreach (['all' => __('全部', 'aksrt'), '0' => __('普通用户', 'aksrt'), '1' => $names[1], '2' => $names[2]] as $key => $label) {
|
||||
echo '<label>UID<input type="number" min="1" name="user_id" class="small-text" value="' . esc_attr($filters['user_id'] > 0 ? (string) $filters['user_id'] : '') . '" placeholder="' . esc_attr__('全部', 'jio') . '"></label>';
|
||||
echo '<label>' . esc_html__('档位', 'jio') . '<select name="level">';
|
||||
foreach (['all' => __('全部', 'jio'), '0' => __('普通用户', 'jio'), '1' => $names[1], '2' => $names[2]] as $key => $label) {
|
||||
printf('<option value="%s"%s>%s</option>', esc_attr((string) $key), selected($filters['level'], (string) $key, false), esc_html($label));
|
||||
}
|
||||
echo '</select></label><label>' . esc_html__('状态', 'aksrt') . '<select name="state">';
|
||||
echo '</select></label><label>' . esc_html__('状态', 'jio') . '<select name="state">';
|
||||
foreach ($this->vip_state_labels() as $key => $label) {
|
||||
printf('<option value="%s"%s>%s</option>', esc_attr($key), selected($filters['state'], $key, false), esc_html($label));
|
||||
}
|
||||
echo '</select></label>';
|
||||
echo '<label>' . esc_html__('到期日', 'aksrt') . '<input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '" aria-label="' . esc_attr__('到期日起', 'aksrt') . '">'
|
||||
. '<span aria-hidden="true">—</span><input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '" aria-label="' . esc_attr__('到期日止', 'aksrt') . '"></label>';
|
||||
echo '<button type="submit" class="button button-primary">' . esc_html__('筛选', 'aksrt') . '</button>';
|
||||
echo '<label>' . esc_html__('到期日', 'jio') . '<input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '" aria-label="' . esc_attr__('到期日起', 'jio') . '">'
|
||||
. '<span aria-hidden="true">—</span><input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '" aria-label="' . esc_attr__('到期日止', 'jio') . '"></label>';
|
||||
echo '<button type="submit" class="button button-primary">' . esc_html__('筛选', 'jio') . '</button>';
|
||||
if ($this->vip_filters_active($filters)) {
|
||||
echo '<a class="button" href="' . esc_url(admin_url('users.php?page=' . self::VIP_PAGE_SLUG)) . '">' . esc_html__('重置', 'aksrt') . '</a>';
|
||||
echo '<a class="button" href="' . esc_url(admin_url('users.php?page=' . self::VIP_PAGE_SLUG)) . '">' . esc_html__('重置', 'jio') . '</a>';
|
||||
}
|
||||
echo '</form>';
|
||||
NativeUi::card_close();
|
||||
@@ -110,28 +110,28 @@ trait UsersMenusVipView {
|
||||
/** @return array<string,string> */
|
||||
private function vip_state_labels(): array {
|
||||
return [
|
||||
'all' => __('全部', 'aksrt'),
|
||||
'active' => __('限时有效', 'aksrt'),
|
||||
'permanent' => __('永久', 'aksrt'),
|
||||
'expired' => __('已过期', 'aksrt'),
|
||||
'none' => __('无会员', 'aksrt'),
|
||||
'all' => __('全部', 'jio'),
|
||||
'active' => __('限时有效', 'jio'),
|
||||
'permanent' => __('永久', 'jio'),
|
||||
'expired' => __('已过期', 'jio'),
|
||||
'none' => __('无会员', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
private function render_vip_table(array $items, array $filters, int $pages, int $page): void {
|
||||
$actions = '<span class="aksrt-vip-selection" data-aksrt-vip-selection hidden></span>'
|
||||
. '<button type="button" class="button button-primary" data-aksrt-vip-bulk disabled>'
|
||||
. '<span class="dashicons dashicons-edit" aria-hidden="true"></span>' . esc_html__('批量调整', 'aksrt') . '</button>';
|
||||
NativeUi::table_card_open(__('会员列表', 'aksrt'), $actions);
|
||||
echo '<table class="wp-list-table widefat fixed striped aksrt-vip-table"><thead><tr>';
|
||||
echo '<td class="manage-column column-cb check-column"><input type="checkbox" data-aksrt-vip-check-all aria-label="' . esc_attr__('选择本页全部用户', 'aksrt') . '"></td>';
|
||||
echo '<th class="column-user">' . esc_html__('用户', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('当前会员', 'aksrt') . '</th>';
|
||||
$actions = '<span class="jio-vip-selection" data-jio-vip-selection hidden></span>'
|
||||
. '<button type="button" class="button button-primary" data-jio-vip-bulk disabled>'
|
||||
. '<span class="dashicons dashicons-edit" aria-hidden="true"></span>' . esc_html__('批量调整', 'jio') . '</button>';
|
||||
NativeUi::table_card_open(__('会员列表', 'jio'), $actions);
|
||||
echo '<table class="wp-list-table widefat fixed striped jio-vip-table"><thead><tr>';
|
||||
echo '<td class="manage-column column-cb check-column"><input type="checkbox" data-jio-vip-check-all aria-label="' . esc_attr__('选择本页全部用户', 'jio') . '"></td>';
|
||||
echo '<th class="column-user">' . esc_html__('用户', 'jio') . '</th>';
|
||||
echo '<th>' . esc_html__('当前会员', 'jio') . '</th>';
|
||||
echo '<th>' . $this->vip_sort_link($filters) . '</th>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<th class="column-actions">' . esc_html__('操作', 'aksrt') . '</th></tr></thead><tbody>';
|
||||
echo '<th class="column-actions">' . esc_html__('操作', 'jio') . '</th></tr></thead><tbody>';
|
||||
if ($items === []) {
|
||||
$hint = $this->vip_filters_active($filters) ? __('试试调整或重置筛选条件。', 'aksrt') : '';
|
||||
echo NativeUi::empty_row(5, __('没有符合条件的用户', 'aksrt'), $hint); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
$hint = $this->vip_filters_active($filters) ? __('试试调整或重置筛选条件。', 'jio') : '';
|
||||
echo NativeUi::empty_row(5, __('没有符合条件的用户', 'jio'), $hint); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
}
|
||||
foreach ($items as $row) {
|
||||
$this->render_vip_row($row);
|
||||
@@ -146,8 +146,8 @@ trait UsersMenusVipView {
|
||||
$active = $filters['orderby'] === 'expires';
|
||||
$next = $active && $filters['order'] === 'ASC' ? 'DESC' : 'ASC';
|
||||
$state = $active ? ($filters['order'] === 'ASC' ? ' is-asc' : ' is-desc') : '';
|
||||
return '<a class="aksrt-vip-sort' . $state . '" href="' . esc_url($this->vip_list_url($filters, ['orderby' => 'expires', 'order' => $next])) . '">'
|
||||
. esc_html__('到期时间', 'aksrt') . '<span class="aksrt-vip-sort__icon" aria-hidden="true"><i></i><i></i></span></a>';
|
||||
return '<a class="jio-vip-sort' . $state . '" href="' . esc_url($this->vip_list_url($filters, ['orderby' => 'expires', 'order' => $next])) . '">'
|
||||
. esc_html__('到期时间', 'jio') . '<span class="jio-vip-sort__icon" aria-hidden="true"><i></i><i></i></span></a>';
|
||||
}
|
||||
|
||||
private function render_vip_row(array $row): void {
|
||||
@@ -160,27 +160,27 @@ trait UsersMenusVipView {
|
||||
$fell_back = $level > 0 && ((int) $row['raw_level'] !== $level || (int) $row['raw_expires'] !== $expires);
|
||||
$base_level = (int) $row['base_level'];
|
||||
$base_expires = (int) $row['base_expires'];
|
||||
$label = $level > 0 ? ($names[$level] ?? ('VIP' . $level)) : __('普通用户', 'aksrt');
|
||||
$label = $level > 0 ? ($names[$level] ?? ('VIP' . $level)) : __('普通用户', 'jio');
|
||||
|
||||
if ($expired) {
|
||||
$lapsed = max((int) $row['raw_level'], (int) $row['expired_level']);
|
||||
$badge = NativeUi::badge(sprintf(__('%s · 已过期', 'aksrt'), $names[$lapsed] ?? __('会员', 'aksrt')), 'neutral');
|
||||
$badge = NativeUi::badge(sprintf(__('%s · 已过期', 'jio'), $names[$lapsed] ?? __('会员', 'jio')), 'neutral');
|
||||
} else {
|
||||
$badge = NativeUi::badge($label, $level > 0 ? ($fell_back ? 'warning' : ($expires === 0 ? 'primary' : 'success')) : 'neutral', $level > 0);
|
||||
}
|
||||
$note = '';
|
||||
if ($fell_back) {
|
||||
$note = __('限时档位已到期,按基础档位计', 'aksrt');
|
||||
$note = __('限时档位已到期,按基础档位计', 'jio');
|
||||
} elseif ($level > 0 && $base_level > 0) {
|
||||
$note = sprintf(__('到期后回退:%s', 'aksrt'), $names[$base_level] ?? ('VIP' . $base_level));
|
||||
$note = sprintf(__('到期后回退:%s', 'jio'), $names[$base_level] ?? ('VIP' . $base_level));
|
||||
}
|
||||
|
||||
$expiry_label = $this->vip_expiry_label($level, $expires, $expired);
|
||||
$expiry_html = esc_html($expiry_label);
|
||||
if ($level > 0 && $expires > 0 && !$expired) {
|
||||
$days = (int) ceil(($expires - $now) / DAY_IN_SECONDS);
|
||||
$expiry_html .= '<span class="aksrt-vip-remaining ' . ($days <= 7 ? 'aksrt-native-text--warning' : 'aksrt-native-muted') . '">'
|
||||
. esc_html(sprintf(__('剩余 %s', 'aksrt'), human_time_diff($now, $expires))) . '</span>';
|
||||
$expiry_html .= '<span class="jio-vip-remaining ' . ($days <= 7 ? 'jio-native-text--warning' : 'jio-native-muted') . '">'
|
||||
. esc_html(sprintf(__('剩余 %s', 'jio'), human_time_diff($now, $expires))) . '</span>';
|
||||
}
|
||||
|
||||
$can_extend = $level > 0 && $expires > 0 && !$expired;
|
||||
@@ -189,7 +189,7 @@ trait UsersMenusVipView {
|
||||
$data = [
|
||||
'user-id' => (string) $id,
|
||||
'user-name' => $user_name,
|
||||
'current' => $expired ? __('已过期', 'aksrt') : $label . ($level > 0 ? ' · ' . $expiry_label : ''),
|
||||
'current' => $expired ? __('已过期', 'jio') : $label . ($level > 0 ? ' · ' . $expiry_label : ''),
|
||||
'level' => (string) max(1, $level),
|
||||
'can-extend' => $can_extend ? '1' : '0',
|
||||
'can-restore' => $can_restore ? '1' : '0',
|
||||
@@ -200,130 +200,130 @@ trait UsersMenusVipView {
|
||||
}
|
||||
|
||||
echo '<tr>';
|
||||
echo '<th scope="row" class="check-column"><input type="checkbox" class="aksrt-vip-check" value="' . esc_attr((string) $id) . '"' . $attrs . ' aria-label="' . esc_attr(sprintf(__('选择 %s', 'aksrt'), $user_name)) . '"></th>'; // phpcs:ignore WordPress.Security.EscapeOutput -- attrs escaped above.
|
||||
echo '<td class="column-user">' . NativeUi::user_cell($id, $user_name) . '<span class="aksrt-native-muted aksrt-vip-login">@' . esc_html((string) $row['user_login']) . '</span></td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<td>' . $badge . ($note !== '' ? '<span class="aksrt-vip-note aksrt-native-muted">' . esc_html($note) . '</span>' : '') . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<td class="aksrt-vip-expiry">' . $expiry_html . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- escaped above.
|
||||
echo '<td class="column-actions"><button type="button" class="button button-small" data-aksrt-vip-edit' . $attrs . '>' // phpcs:ignore WordPress.Security.EscapeOutput -- attrs escaped above.
|
||||
. '<span class="dashicons dashicons-edit" aria-hidden="true"></span>' . esc_html__('调整', 'aksrt') . '</button></td>';
|
||||
echo '<th scope="row" class="check-column"><input type="checkbox" class="jio-vip-check" value="' . esc_attr((string) $id) . '"' . $attrs . ' aria-label="' . esc_attr(sprintf(__('选择 %s', 'jio'), $user_name)) . '"></th>'; // phpcs:ignore WordPress.Security.EscapeOutput -- attrs escaped above.
|
||||
echo '<td class="column-user">' . NativeUi::user_cell($id, $user_name) . '<span class="jio-native-muted jio-vip-login">@' . esc_html((string) $row['user_login']) . '</span></td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<td>' . $badge . ($note !== '' ? '<span class="jio-vip-note jio-native-muted">' . esc_html($note) . '</span>' : '') . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<td class="jio-vip-expiry">' . $expiry_html . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- escaped above.
|
||||
echo '<td class="column-actions"><button type="button" class="button button-small" data-jio-vip-edit' . $attrs . '>' // phpcs:ignore WordPress.Security.EscapeOutput -- attrs escaped above.
|
||||
. '<span class="dashicons dashicons-edit" aria-hidden="true"></span>' . esc_html__('调整', 'jio') . '</button></td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
|
||||
private function vip_expiry_label(int $level, int $expires, bool $expired): string {
|
||||
if ($expired) {
|
||||
return __('已过期', 'aksrt');
|
||||
return __('已过期', 'jio');
|
||||
}
|
||||
if ($level <= 0) {
|
||||
return '—';
|
||||
}
|
||||
return $expires === 0 ? __('永久有效', 'aksrt') : wp_date('Y-m-d H:i', $expires, wp_timezone());
|
||||
return $expires === 0 ? __('永久有效', 'jio') : wp_date('Y-m-d H:i', $expires, wp_timezone());
|
||||
}
|
||||
|
||||
private function render_vip_dialog(array $filters): void {
|
||||
$names = $this->vip_level_names();
|
||||
$today = (new \DateTimeImmutable('today', wp_timezone()))->format('Y-m-d');
|
||||
$strings = [
|
||||
'single' => __('调整会员:%s', 'aksrt'),
|
||||
'bulk' => __('批量调整会员(%d 人)', 'aksrt'),
|
||||
'selected' => __('已选 %d 人', 'aksrt'),
|
||||
'bulk-note' => __('不满足条件的用户会被跳过并计入失败,结果会在提交后汇总显示。', 'aksrt'),
|
||||
'extend-off' => __('该用户不是限时有效会员,无法延长。', 'aksrt'),
|
||||
'restore-off' => __('该用户没有可恢复的回退会员。', 'aksrt'),
|
||||
'single' => __('调整会员:%s', 'jio'),
|
||||
'bulk' => __('批量调整会员(%d 人)', 'jio'),
|
||||
'selected' => __('已选 %d 人', 'jio'),
|
||||
'bulk-note' => __('不满足条件的用户会被跳过并计入失败,结果会在提交后汇总显示。', 'jio'),
|
||||
'extend-off' => __('该用户不是限时有效会员,无法延长。', 'jio'),
|
||||
'restore-off' => __('该用户没有可恢复的回退会员。', 'jio'),
|
||||
];
|
||||
$attrs = '';
|
||||
foreach ($strings as $key => $value) {
|
||||
$attrs .= ' data-i18n-' . $key . '="' . esc_attr($value) . '"';
|
||||
}
|
||||
echo '<dialog class="aksrt-vip-dialog" id="aksrt-vip-dialog" aria-labelledby="aksrt-vip-dialog-title"' . $attrs . '>'; // phpcs:ignore WordPress.Security.EscapeOutput -- attrs escaped above.
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-vip-dialog__form">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_users_mutate_vip">';
|
||||
wp_nonce_field('aksrt_users_mutate_vip');
|
||||
echo '<dialog class="jio-vip-dialog" id="jio-vip-dialog" aria-labelledby="jio-vip-dialog-title"' . $attrs . '>'; // phpcs:ignore WordPress.Security.EscapeOutput -- attrs escaped above.
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-vip-dialog__form">';
|
||||
echo '<input type="hidden" name="action" value="jio_users_mutate_vip">';
|
||||
wp_nonce_field('jio_users_mutate_vip');
|
||||
$this->render_vip_return_fields($filters);
|
||||
echo '<div data-aksrt-vip-ids></div>';
|
||||
echo '<div data-jio-vip-ids></div>';
|
||||
|
||||
echo '<header class="aksrt-vip-dialog__head"><h2 id="aksrt-vip-dialog-title" class="aksrt-vip-dialog__title" data-aksrt-vip-title></h2>'
|
||||
. '<button type="button" class="aksrt-vip-dialog__close" data-aksrt-vip-close aria-label="' . esc_attr__('关闭', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
echo '<div class="aksrt-vip-dialog__body">';
|
||||
echo '<p class="aksrt-vip-dialog__target" data-aksrt-vip-target></p>';
|
||||
echo '<header class="jio-vip-dialog__head"><h2 id="jio-vip-dialog-title" class="jio-vip-dialog__title" data-jio-vip-title></h2>'
|
||||
. '<button type="button" class="jio-vip-dialog__close" data-jio-vip-close aria-label="' . esc_attr__('关闭', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
echo '<div class="jio-vip-dialog__body">';
|
||||
echo '<p class="jio-vip-dialog__target" data-jio-vip-target></p>';
|
||||
|
||||
echo '<fieldset class="aksrt-vip-ops"><legend class="aksrt-native-field__label">' . esc_html__('操作', 'aksrt') . '</legend>';
|
||||
echo '<fieldset class="jio-vip-ops"><legend class="jio-native-field__label">' . esc_html__('操作', 'jio') . '</legend>';
|
||||
$ops = [
|
||||
'set' => [__('设置会员', 'aksrt'), 'dashicons-awards'],
|
||||
'extend' => [__('延长有效期', 'aksrt'), 'dashicons-clock'],
|
||||
'restore' => [__('恢复回退会员', 'aksrt'), 'dashicons-undo'],
|
||||
'disable' => [__('停用会员', 'aksrt'), 'dashicons-dismiss'],
|
||||
'set' => [__('设置会员', 'jio'), 'dashicons-awards'],
|
||||
'extend' => [__('延长有效期', 'jio'), 'dashicons-clock'],
|
||||
'restore' => [__('恢复回退会员', 'jio'), 'dashicons-undo'],
|
||||
'disable' => [__('停用会员', 'jio'), 'dashicons-dismiss'],
|
||||
];
|
||||
foreach ($ops as $key => [$label, $icon]) {
|
||||
echo '<label class="aksrt-vip-op' . ($key === 'disable' ? ' is-danger' : '') . '"><input type="radio" name="vip_operation" value="' . esc_attr($key) . '"' . checked($key, 'set', false) . '>'
|
||||
echo '<label class="jio-vip-op' . ($key === 'disable' ? ' is-danger' : '') . '"><input type="radio" name="vip_operation" value="' . esc_attr($key) . '"' . checked($key, 'set', false) . '>'
|
||||
. '<span><span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>' . esc_html($label) . '</span></label>';
|
||||
}
|
||||
echo '</fieldset>';
|
||||
echo '<p class="aksrt-vip-op-hint aksrt-native-muted" data-aksrt-vip-op-hint hidden></p>';
|
||||
echo '<p class="jio-vip-op-hint jio-native-muted" data-jio-vip-op-hint hidden></p>';
|
||||
|
||||
echo '<div class="aksrt-native-form-grid aksrt-vip-pane" data-aksrt-vip-pane="set">';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('会员档位', 'aksrt') . '</span><select name="level" data-aksrt-vip-level>';
|
||||
echo '<div class="jio-native-form-grid jio-vip-pane" data-jio-vip-pane="set">';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('会员档位', 'jio') . '</span><select name="level" data-jio-vip-level>';
|
||||
foreach ($names as $value => $label) {
|
||||
echo '<option value="' . esc_attr((string) $value) . '">' . esc_html($label) . '</option>';
|
||||
}
|
||||
echo '</select></label>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('到期日', 'aksrt') . '</span><input type="date" name="expires_date" min="' . esc_attr($today) . '" required data-aksrt-vip-date>'
|
||||
. '<span class="aksrt-native-field__hint">' . esc_html__('当天 23:59:59 到期', 'aksrt') . '</span></label>';
|
||||
echo '<label class="aksrt-vip-switch is-wide"><input type="checkbox" name="permanent" value="1" data-aksrt-vip-permanent>' . esc_html__('永久会员(不设到期日)', 'aksrt') . '</label>';
|
||||
echo '<p class="aksrt-native-field__hint is-wide">' . esc_html__('设置会员会直接覆盖当前档位与到期时间,并清除回退会员。', 'aksrt') . '</p>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('到期日', 'jio') . '</span><input type="date" name="expires_date" min="' . esc_attr($today) . '" required data-jio-vip-date>'
|
||||
. '<span class="jio-native-field__hint">' . esc_html__('当天 23:59:59 到期', 'jio') . '</span></label>';
|
||||
echo '<label class="jio-vip-switch is-wide"><input type="checkbox" name="permanent" value="1" data-jio-vip-permanent>' . esc_html__('永久会员(不设到期日)', 'jio') . '</label>';
|
||||
echo '<p class="jio-native-field__hint is-wide">' . esc_html__('设置会员会直接覆盖当前档位与到期时间,并清除回退会员。', 'jio') . '</p>';
|
||||
echo '</div>';
|
||||
|
||||
echo '<div class="aksrt-native-form-grid aksrt-vip-pane" data-aksrt-vip-pane="extend" hidden>';
|
||||
echo '<div class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('延长时长', 'aksrt') . '</span><div class="aksrt-vip-duration">'
|
||||
. '<input type="number" name="amount" min="1" max="3650" value="30" required aria-label="' . esc_attr__('延长数量', 'aksrt') . '">'
|
||||
. '<select name="unit" aria-label="' . esc_attr__('延长单位', 'aksrt') . '"><option value="day">' . esc_html__('天', 'aksrt') . '</option><option value="month">' . esc_html__('月', 'aksrt') . '</option></select></div>'
|
||||
. '<span class="aksrt-native-field__hint">' . esc_html__('从当前到期时间起顺延;仅对限时有效会员生效。', 'aksrt') . '</span></div>';
|
||||
echo '<div class="jio-native-form-grid jio-vip-pane" data-jio-vip-pane="extend" hidden>';
|
||||
echo '<div class="jio-native-field is-wide"><span class="jio-native-field__label">' . esc_html__('延长时长', 'jio') . '</span><div class="jio-vip-duration">'
|
||||
. '<input type="number" name="amount" min="1" max="3650" value="30" required aria-label="' . esc_attr__('延长数量', 'jio') . '">'
|
||||
. '<select name="unit" aria-label="' . esc_attr__('延长单位', 'jio') . '"><option value="day">' . esc_html__('天', 'jio') . '</option><option value="month">' . esc_html__('月', 'jio') . '</option></select></div>'
|
||||
. '<span class="jio-native-field__hint">' . esc_html__('从当前到期时间起顺延;仅对限时有效会员生效。', 'jio') . '</span></div>';
|
||||
echo '</div>';
|
||||
|
||||
echo '<div class="aksrt-native-callout is-warning aksrt-vip-pane" data-aksrt-vip-pane="restore" hidden>' . esc_html__('将取消当前限时升级,恢复为回退会员(档位与到期时间按升级前保存的记录)。', 'aksrt') . '</div>';
|
||||
echo '<div class="aksrt-native-callout is-danger aksrt-vip-pane" data-aksrt-vip-pane="disable" hidden>' . esc_html__('停用后立即撤销前端会员权益,并清除回退会员记录。此操作无法撤销,只能重新设置会员。', 'aksrt') . '</div>';
|
||||
echo '<div class="jio-native-callout is-warning jio-vip-pane" data-jio-vip-pane="restore" hidden>' . esc_html__('将取消当前限时升级,恢复为回退会员(档位与到期时间按升级前保存的记录)。', 'jio') . '</div>';
|
||||
echo '<div class="jio-native-callout is-danger jio-vip-pane" data-jio-vip-pane="disable" hidden>' . esc_html__('停用后立即撤销前端会员权益,并清除回退会员记录。此操作无法撤销,只能重新设置会员。', 'jio') . '</div>';
|
||||
|
||||
echo '<label class="aksrt-native-field aksrt-vip-reason"><span class="aksrt-native-field__label">' . esc_html__('变更原因', 'aksrt') . ' <span class="aksrt-native-text--danger" aria-hidden="true">*</span></span>'
|
||||
. '<input type="text" name="reason" required maxlength="255" placeholder="' . esc_attr__('例如:活动赠送、客服补偿、退款撤销', 'aksrt') . '">'
|
||||
. '<span class="aksrt-native-field__hint">' . esc_html__('写入变更记录,便于后续追溯。', 'aksrt') . '</span></label>';
|
||||
echo '<p class="aksrt-native-muted aksrt-vip-bulk-note" data-aksrt-vip-bulk-note hidden></p>';
|
||||
echo '<label class="jio-native-field jio-vip-reason"><span class="jio-native-field__label">' . esc_html__('变更原因', 'jio') . ' <span class="jio-native-text--danger" aria-hidden="true">*</span></span>'
|
||||
. '<input type="text" name="reason" required maxlength="255" placeholder="' . esc_attr__('例如:活动赠送、客服补偿、退款撤销', 'jio') . '">'
|
||||
. '<span class="jio-native-field__hint">' . esc_html__('写入变更记录,便于后续追溯。', 'jio') . '</span></label>';
|
||||
echo '<p class="jio-native-muted jio-vip-bulk-note" data-jio-vip-bulk-note hidden></p>';
|
||||
echo '</div>';
|
||||
|
||||
echo '<footer class="aksrt-vip-dialog__foot"><button type="button" class="button" data-aksrt-vip-close>' . esc_html__('取消', 'aksrt') . '</button>'
|
||||
. '<button type="submit" class="button button-primary" data-aksrt-vip-submit>' . esc_html__('确认变更', 'aksrt') . '</button></footer>';
|
||||
echo '<footer class="jio-vip-dialog__foot"><button type="button" class="button" data-jio-vip-close>' . esc_html__('取消', 'jio') . '</button>'
|
||||
. '<button type="submit" class="button button-primary" data-jio-vip-submit>' . esc_html__('确认变更', 'jio') . '</button></footer>';
|
||||
echo '</form></dialog>';
|
||||
}
|
||||
|
||||
private function render_vip_history(int $page): void {
|
||||
$result = (new \AKSRT\Content\VipMembershipEventRepository())->query($page, self::VIP_PER_PAGE);
|
||||
$result = (new \Jio\Content\VipMembershipEventRepository())->query($page, self::VIP_PER_PAGE);
|
||||
$ops = [
|
||||
'set' => [__('设置会员', 'aksrt'), 'success'],
|
||||
'extend' => [__('延长有效期', 'aksrt'), 'info'],
|
||||
'restore' => [__('恢复回退会员', 'aksrt'), 'warning'],
|
||||
'disable' => [__('停用会员', 'aksrt'), 'danger'],
|
||||
'expire_recover' => [__('到期自动回退', 'aksrt'), 'neutral'],
|
||||
'set' => [__('设置会员', 'jio'), 'success'],
|
||||
'extend' => [__('延长有效期', 'jio'), 'info'],
|
||||
'restore' => [__('恢复回退会员', 'jio'), 'warning'],
|
||||
'disable' => [__('停用会员', 'jio'), 'danger'],
|
||||
'expire_recover' => [__('到期自动回退', 'jio'), 'neutral'],
|
||||
];
|
||||
$statuses = [
|
||||
'succeeded' => [__('成功', 'aksrt'), 'success'],
|
||||
'failed' => [__('失败', 'aksrt'), 'danger'],
|
||||
'pending' => [__('处理中', 'aksrt'), 'warning'],
|
||||
'succeeded' => [__('成功', 'jio'), 'success'],
|
||||
'failed' => [__('失败', 'jio'), 'danger'],
|
||||
'pending' => [__('处理中', 'jio'), 'warning'],
|
||||
];
|
||||
NativeUi::table_card_open(__('变更记录', 'aksrt'), '<span class="aksrt-native-muted">' . esc_html(sprintf(__('共 %d 条', 'aksrt'), (int) $result['total'])) . '</span>');
|
||||
echo '<table class="wp-list-table widefat fixed striped aksrt-vip-history"><thead><tr><th class="column-time">' . esc_html__('时间', 'aksrt') . '</th><th class="column-user">' . esc_html__('用户', 'aksrt') . '</th><th>' . esc_html__('操作', 'aksrt') . '</th><th>' . esc_html__('操作人', 'aksrt') . '</th><th class="column-reason">' . esc_html__('原因', 'aksrt') . '</th><th>' . esc_html__('结果', 'aksrt') . '</th></tr></thead><tbody>';
|
||||
NativeUi::table_card_open(__('变更记录', 'jio'), '<span class="jio-native-muted">' . esc_html(sprintf(__('共 %d 条', 'jio'), (int) $result['total'])) . '</span>');
|
||||
echo '<table class="wp-list-table widefat fixed striped jio-vip-history"><thead><tr><th class="column-time">' . esc_html__('时间', 'jio') . '</th><th class="column-user">' . esc_html__('用户', 'jio') . '</th><th>' . esc_html__('操作', 'jio') . '</th><th>' . esc_html__('操作人', 'jio') . '</th><th class="column-reason">' . esc_html__('原因', 'jio') . '</th><th>' . esc_html__('结果', 'jio') . '</th></tr></thead><tbody>';
|
||||
if ($result['items'] === []) {
|
||||
echo NativeUi::empty_row(6, __('暂无变更记录', 'aksrt'), __('在会员列表中调整会员后会出现在这里。', 'aksrt')); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo NativeUi::empty_row(6, __('暂无变更记录', 'jio'), __('在会员列表中调整会员后会出现在这里。', 'jio')); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
}
|
||||
foreach ($result['items'] as $event) {
|
||||
$created = (string) $event['created_at'];
|
||||
$time = $created !== '' ? get_date_from_gmt($created, 'Y-m-d H:i') : '—';
|
||||
$actor = (string) ($event['actor_name'] ?? '');
|
||||
if ($actor === '') {
|
||||
$actor = ((string) ($event['source'] ?? '') === 'admin') ? '—' : __('系统', 'aksrt');
|
||||
$actor = ((string) ($event['source'] ?? '') === 'admin') ? '—' : __('系统', 'jio');
|
||||
}
|
||||
$result_html = NativeUi::status_badge((string) $event['status'], $statuses);
|
||||
if ((string) $event['status'] === 'failed' && (string) ($event['error_message'] ?? '') !== '') {
|
||||
$result_html .= '<span class="aksrt-vip-note aksrt-native-text--danger">' . esc_html((string) $event['error_message']) . '</span>';
|
||||
$result_html .= '<span class="jio-vip-note jio-native-text--danger">' . esc_html((string) $event['error_message']) . '</span>';
|
||||
}
|
||||
echo '<tr><td class="column-time">' . esc_html($time) . (!empty($event['batch_uuid']) ? '<span class="aksrt-vip-note aksrt-native-muted">' . esc_html__('批量', 'aksrt') . '</span>' : '') . '</td>'
|
||||
echo '<tr><td class="column-time">' . esc_html($time) . (!empty($event['batch_uuid']) ? '<span class="jio-vip-note jio-native-muted">' . esc_html__('批量', 'jio') . '</span>' : '') . '</td>'
|
||||
. '<td class="column-user">' . NativeUi::user_cell((int) $event['user_id'], (string) ($event['display_name'] ?: $event['user_login'] ?: '')) . '</td>' // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
. '<td>' . NativeUi::status_badge((string) $event['operation'], $ops) . '</td>' // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
. '<td>' . esc_html($actor) . '</td>'
|
||||
|
||||
Reference in new issue
Block a user