fix(user-center): load revenue tabs without refresh
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.uc-mobile-tabs button {
|
||||
.uc-mobile-tabs a {
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
padding: 8px 14px;
|
||||
@@ -35,16 +35,13 @@
|
||||
color: var(--color-ink-3);
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.uc-mobile-tabs button:hover {
|
||||
background: var(--surface-2);
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
.uc-mobile-tabs button.is-active {
|
||||
.uc-mobile-tabs a:hover,
|
||||
.uc-mobile-tabs a.is-active {
|
||||
background: var(--color-primary-soft);
|
||||
color: var(--color-primary);
|
||||
font-weight: 600;
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var root = document.querySelector('[data-aksrt-revenue-page]');
|
||||
if (!root) return;
|
||||
function init(root) {
|
||||
var data = window.aksrtData || {};
|
||||
if (!root || !data) return;
|
||||
|
||||
var data = window.aksrtData || {};
|
||||
var rest = data.restUrl || data.apiUrl || ((data.siteUrl || '').replace(/\/$/, '') + '/wp-json/aksrt/v1');
|
||||
var rest = data.restUrl || data.apiUrl || ((data.siteUrl || '').replace(/\/$/, '') + '/wp-json/aksrt/v1');
|
||||
if (!rest) return;
|
||||
rest = rest.replace(/\/?$/, '');
|
||||
|
||||
@@ -218,8 +218,44 @@
|
||||
}).then(function (result) {
|
||||
if (!result.ok || !result.json || !result.json.success) { setWithdrawBusy(false); setWithdrawTip((result.json && result.json.message) || '提现申请失败', true); return; }
|
||||
setWithdrawTip('提现申请已提交', false);
|
||||
window.setTimeout(function () { window.location.reload(); }, 700);
|
||||
if (result.json.data && typeof result.json.data.wallet_balance !== 'undefined') {
|
||||
var balanceEl = root.querySelector('[data-aksrt-revenue-balance]');
|
||||
if (balanceEl) balanceEl.textContent = Number(result.json.data.wallet_balance).toFixed(2);
|
||||
}
|
||||
withdrawForm.reset();
|
||||
setWithdrawBusy(false);
|
||||
}).catch(function () { setWithdrawBusy(false); setWithdrawTip('网络错误,请稍后重试', true); });
|
||||
});
|
||||
var rewardTabs = root.querySelectorAll('.uc-reward-tabs a, .uc-reward-pagination a');
|
||||
rewardTabs.forEach(function (link) {
|
||||
link.addEventListener('click', function (event) {
|
||||
event.preventDefault();
|
||||
var targetUrl = new URL(link.href, window.location.href);
|
||||
var panelUrl = rest + '/user-center/panel/revenue?' + targetUrl.searchParams.toString();
|
||||
fetch(panelUrl, {
|
||||
credentials: 'same-origin',
|
||||
headers: { 'X-WP-Nonce': data.restNonce || data.nonce || '' }
|
||||
}).then(function (response) {
|
||||
return response.json().then(function (payload) {
|
||||
if (!response.ok || !payload.success || !payload.data || !payload.data.html) throw new Error('revenue-load-failed');
|
||||
var current = document.querySelector('[data-aksrt-revenue-page]');
|
||||
if (!current) return;
|
||||
var wrapper = document.createElement('div');
|
||||
wrapper.innerHTML = payload.data.html;
|
||||
var next = wrapper.firstElementChild;
|
||||
if (!next) throw new Error('revenue-html-empty');
|
||||
current.replaceWith(next);
|
||||
window.history.pushState({}, '', targetUrl);
|
||||
init(next);
|
||||
});
|
||||
}).catch(function () {
|
||||
if (typeof showToast === 'function') showToast('加载失败,请重试');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
window.AKSRTRevenueCenterInit = init;
|
||||
init(document.querySelector('[data-aksrt-revenue-page]'));
|
||||
})();
|
||||
|
||||
@@ -56,11 +56,6 @@
|
||||
|
||||
function switchTo(name) {
|
||||
if (!panels[name]) name = defaultTab;
|
||||
if (panels[name].panel.dataset.panelLoaded === 'false') {
|
||||
window.location.assign(tabUrls[name] || panels[name].panel.dataset.tabUrl || ('?tab=' + encodeURIComponent(name)));
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(panels).forEach(function(key) {
|
||||
var active = key === name;
|
||||
panels[key].tab.classList.toggle('is-active', active);
|
||||
@@ -68,14 +63,48 @@
|
||||
panels[key].panel.classList.toggle('is-active', active);
|
||||
panels[key].panel.hidden = !active;
|
||||
});
|
||||
|
||||
sidebarBtns.forEach(function(btn) {
|
||||
btn.classList.toggle('is-active', btn.getAttribute('data-tab') === name);
|
||||
});
|
||||
|
||||
closeDrawer();
|
||||
}
|
||||
|
||||
function loadPanel(name) {
|
||||
if (!panels[name]) return Promise.reject(new Error('panel-not-found'));
|
||||
var target = panels[name].panel;
|
||||
if (target.dataset.panelLoaded === 'true') return Promise.resolve();
|
||||
if (target._aksrtPanelRequest) return target._aksrtPanelRequest;
|
||||
var restUrl = (window.aksrtData && (window.aksrtData.restUrl || window.aksrtData.apiUrl)) || '';
|
||||
if (!restUrl) return Promise.reject(new Error('rest-unavailable'));
|
||||
target.classList.add('is-loading');
|
||||
var url = restUrl.replace(/\/$/, '') + '/user-center/panel/' + encodeURIComponent(name);
|
||||
target._aksrtPanelRequest = fetch(url, {
|
||||
credentials: 'same-origin',
|
||||
headers: { 'X-WP-Nonce': (window.aksrtData && (window.aksrtData.restNonce || window.aksrtData.nonce)) || '' }
|
||||
}).then(function(response) {
|
||||
return response.json().then(function(payload) {
|
||||
if (!response.ok || !payload || !payload.success || !payload.data || typeof payload.data.html !== 'string') throw new Error('panel-load-failed');
|
||||
target.innerHTML = payload.data.html;
|
||||
target.dataset.panelLoaded = 'true';
|
||||
});
|
||||
}).finally(function() {
|
||||
target._aksrtPanelRequest = null;
|
||||
target.classList.remove('is-loading');
|
||||
});
|
||||
return target._aksrtPanelRequest;
|
||||
}
|
||||
|
||||
function activate(name, updateHistory) {
|
||||
if (!panels[name]) name = defaultTab;
|
||||
return loadPanel(name).then(function() {
|
||||
switchTo(name);
|
||||
if (updateHistory) updateUrl(name);
|
||||
if (typeof window.AKSRTRevenueCenterInit === 'function') window.AKSRTRevenueCenterInit(panels[name].panel);
|
||||
}).catch(function() {
|
||||
if (typeof showToast === 'function') showToast(i18n.networkError || '加载失败,请重试');
|
||||
});
|
||||
}
|
||||
|
||||
function tabFromUrl() {
|
||||
var m = window.location.search.match(/[?&]tab=([^&]+)/);
|
||||
var name = m ? decodeURIComponent(m[1]) : (cfg.activeTab || defaultTab);
|
||||
@@ -85,38 +114,27 @@
|
||||
function updateUrl(name) {
|
||||
if (!window.history.replaceState) return;
|
||||
var u = new URL(window.location);
|
||||
if (name === defaultTab) {
|
||||
u.searchParams.delete('tab');
|
||||
} else {
|
||||
u.searchParams.set('tab', name);
|
||||
}
|
||||
if (name === defaultTab) u.searchParams.delete('tab');
|
||||
else u.searchParams.set('tab', name);
|
||||
window.history.replaceState({}, '', u);
|
||||
}
|
||||
|
||||
tabs.forEach(function(tab) {
|
||||
tab.addEventListener('click', function(e) {
|
||||
var name = this.getAttribute('data-tab');
|
||||
if (!panels[name] || panels[name].panel.dataset.panelLoaded === 'false') return;
|
||||
e.preventDefault();
|
||||
switchTo(name);
|
||||
updateUrl(name);
|
||||
activate(this.getAttribute('data-tab'), true);
|
||||
});
|
||||
});
|
||||
|
||||
sidebarBtns.forEach(function(btn) {
|
||||
btn.addEventListener('click', function(e) {
|
||||
var name = this.getAttribute('data-tab');
|
||||
if (!panels[name] || panels[name].panel.dataset.panelLoaded === 'false') return;
|
||||
e.preventDefault();
|
||||
switchTo(name);
|
||||
updateUrl(name);
|
||||
activate(this.getAttribute('data-tab'), true);
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('popstate', function() { activate(tabFromUrl(), false); });
|
||||
if (drawerTrigger) drawerTrigger.addEventListener('click', openDrawer);
|
||||
if (drawerOverlay) drawerOverlay.addEventListener('click', closeDrawer);
|
||||
|
||||
switchTo(tabFromUrl());
|
||||
activate(tabFromUrl(), false);
|
||||
}
|
||||
|
||||
/* ==================== 个人信息保存 ==================== */
|
||||
|
||||
@@ -342,7 +342,9 @@ if (!function_exists('aksrt_user_center_runtime_config')) {
|
||||
'publicNonce' => wp_create_nonce('aksrt_public_nonce'),
|
||||
'settingsNonce' => wp_create_nonce('aksrt_account_settings'),
|
||||
'userId' => $user_id,
|
||||
'defaultTab' => $default_tab,
|
||||
'restUrl' => rest_url('aksrt/v1'),
|
||||
'restNonce' => wp_create_nonce('wp_rest'),
|
||||
'defaultTab' => $default_tab,
|
||||
'activeTab' => $active_tab,
|
||||
'tabAliases' => [
|
||||
'signin' => 'credits',
|
||||
|
||||
@@ -51,7 +51,8 @@ if ($details_text === '') {
|
||||
$details_text = trim((string) ($affiliate['note'] ?? '')) ?: __('好友通过推广链接完成符合条件的订单后,系统会按当前佣金比例记录返佣,结算后进入可提现余额。', 'aksrt');
|
||||
}
|
||||
|
||||
$active_reward_tab = sanitize_key((string) ($_GET['reward_tab'] ?? 'detail'));
|
||||
$revenue_request = is_array($context['revenue_request'] ?? null) ? $context['revenue_request'] : [];
|
||||
$active_reward_tab = sanitize_key((string) ($revenue_request['reward_tab'] ?? ($_GET['reward_tab'] ?? 'detail')));
|
||||
if (!in_array($active_reward_tab, ['detail', 'orders', 'users'], true)) {
|
||||
$active_reward_tab = 'detail';
|
||||
}
|
||||
@@ -65,7 +66,7 @@ $format_amount = static function ($amount): string {
|
||||
return number_format_i18n((float) $amount, 2);
|
||||
};
|
||||
|
||||
$commission_page = max(1, (int) ($_GET['commission_page'] ?? 1));
|
||||
$commission_page = max(1, (int) ($revenue_request['commission_page'] ?? ($_GET['commission_page'] ?? 1)));
|
||||
$commission_view = class_exists('\AKSRT\Payment\Rebate')
|
||||
? \AKSRT\Payment\Rebate::get_instance()->get_list([
|
||||
'user_id' => $user_id,
|
||||
@@ -107,7 +108,7 @@ $status_labels = [
|
||||
<div class="uc-commission-card__body">
|
||||
<span><?php esc_html_e('我的佣金', 'aksrt'); ?></span>
|
||||
<strong><?php echo esc_html($format_amount($available_commission)); ?></strong>
|
||||
<p><?php printf(esc_html__('累计佣金 %s,待结算 %s', 'aksrt'), esc_html($format_amount($commission_stats['total_amount'] ?? 0)), esc_html($format_amount($commission_stats['pending_amount'] ?? 0))); ?></p>
|
||||
<span><?php printf(esc_html__('累计佣金 %s,待结算 %s', 'aksrt'), esc_html($format_amount($commission_stats['total_amount'] ?? 0)), esc_html($format_amount($commission_stats['pending_amount'] ?? 0))); ?></span>
|
||||
</div>
|
||||
<?php if ($withdraw_on) : ?>
|
||||
<button type="button" class="uc-commission-card__action" data-aksrt-commission-withdraw-open><i class="fas fa-arrow-right" aria-hidden="true"></i><?php esc_html_e('立即提现', 'aksrt'); ?></button>
|
||||
|
||||
@@ -199,7 +199,7 @@ class Rebate {
|
||||
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending,
|
||||
SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) as pending_amount,
|
||||
SUM(CASE WHEN status = 'settled' THEN 1 ELSE 0 END) as settled,
|
||||
SUM(amount) as total_amount,
|
||||
SUM(CASE WHEN status IN ('pending', 'settled') THEN amount ELSE 0 END) as total_amount,
|
||||
SUM(CASE WHEN status = 'settled' THEN amount ELSE 0 END) as settled_amount
|
||||
FROM {$table} {$where}";
|
||||
$row = $params
|
||||
|
||||
@@ -25,6 +25,80 @@ class Center {
|
||||
|
||||
public function init(): void {
|
||||
$this->register_default_panels();
|
||||
add_action('rest_api_init', [$this, 'register_rest_routes']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register user-center fragment routes.
|
||||
*/
|
||||
public function register_rest_routes(): void {
|
||||
register_rest_route('aksrt/v1', '/user-center/panel/(?P<tab>[a-z0-9_-]+)', [
|
||||
'methods' => \WP_REST_Server::READABLE,
|
||||
'callback' => [$this, 'rest_panel'],
|
||||
'permission_callback' => static function (): bool {
|
||||
return is_user_logged_in();
|
||||
},
|
||||
'args' => [
|
||||
'tab' => [
|
||||
'required' => true,
|
||||
'sanitize_callback' => 'sanitize_key',
|
||||
'validate_callback' => static function ($value): bool {
|
||||
return preg_match('/^[a-z0-9_-]+$/', (string) $value) === 1;
|
||||
},
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a user-center panel or a revenue reward fragment.
|
||||
*/
|
||||
public function rest_panel(\WP_REST_Request $request) {
|
||||
$tab = sanitize_key((string) $request->get_param('tab'));
|
||||
$tabs = $this->get_dashboard_tabs();
|
||||
if (!isset($tabs[$tab])) {
|
||||
return new \WP_Error('aksrt_user_center_panel', __('用户中心面板不存在', 'aksrt'), ['status' => 404]);
|
||||
}
|
||||
|
||||
$user = wp_get_current_user();
|
||||
$context = function_exists('aksrt_get_user_center_render_context')
|
||||
? aksrt_get_user_center_render_context($user)
|
||||
: [];
|
||||
if (!is_array($context) || !$context) {
|
||||
return new \WP_Error('aksrt_user_center_context', __('用户中心数据加载失败', 'aksrt'), ['status' => 500]);
|
||||
}
|
||||
|
||||
$revenue_request = [];
|
||||
if ($tab === 'revenue') {
|
||||
$reward_tab = sanitize_key((string) $request->get_param('reward_tab'));
|
||||
if (!in_array($reward_tab, ['detail', 'orders', 'users'], true)) {
|
||||
$reward_tab = 'detail';
|
||||
}
|
||||
$revenue_request = [
|
||||
'reward_tab' => $reward_tab,
|
||||
'commission_page' => max(1, min(100, (int) $request->get_param('commission_page'))),
|
||||
'invite_page' => max(1, min(100, (int) $request->get_param('invite_page'))),
|
||||
];
|
||||
$context['revenue_request'] = $revenue_request;
|
||||
}
|
||||
|
||||
$html = function_exists('aksrt_capture_user_center_panel')
|
||||
? aksrt_capture_user_center_panel($tab, $tabs[$tab], $context)
|
||||
: '';
|
||||
if ($html === '') {
|
||||
return new \WP_Error('aksrt_user_center_panel_empty', __('用户中心面板暂不可用', 'aksrt'), ['status' => 500]);
|
||||
}
|
||||
|
||||
return rest_ensure_response([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'tab' => $tab,
|
||||
'html' => $html,
|
||||
'reward_tab' => $revenue_request['reward_tab'] ?? '',
|
||||
'commission_page' => $revenue_request['commission_page'] ?? 1,
|
||||
'invite_page' => $revenue_request['invite_page'] ?? 1,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user