feat(header): user-box density 1:1 (check-in, wallet, VIP, signout)

Add payvip/check-in/wallet/badges strips, guest OAuth strip, confirm
signout modal + REST check-in wiring for zibll header parity.
This commit is contained in:
2026-07-18 21:28:32 +08:00
parent cd04a03329
commit c32738267f
6 changed files with 426 additions and 1 deletions
@@ -0,0 +1,96 @@
/**
* Header user-box density (check-in / wallet / payvip / signout modal).
*
* @package AKSRT
*/
.aksrt-header-wallet {
display: flex;
gap: 10px;
justify-content: space-between;
padding: 8px 10px;
border-radius: 8px;
background: var(--surface-2, rgba(0, 0, 0, 0.04));
color: inherit;
text-decoration: none;
font-size: 12px;
}
.aksrt-header-wallet__item b {
margin-left: 2px;
}
.aksrt-header-checkin {
width: 100%;
border: 0;
border-radius: 6px;
padding: 8px 10px;
cursor: pointer;
background: rgba(255, 193, 7, 0.18);
color: #b78103;
}
.aksrt-header-checkin.is-done {
opacity: 0.75;
cursor: default;
}
.aksrt-header-payvip .payvip-icon,
.aksrt-header-payvip .but {
display: block;
text-align: center;
text-decoration: none;
padding: 8px 10px;
border-radius: 6px;
background: linear-gradient(90deg, #ff6b6b, #f04494);
color: #fff;
}
.aksrt-header-badges {
flex-wrap: wrap;
gap: 4px;
}
#modal_signout.modal {
position: fixed;
inset: 0;
z-index: 1100;
display: none;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.45);
padding: 16px;
}
#modal_signout.modal.is-open,
#modal_signout.modal.show {
display: flex;
}
#modal_signout .modal-content {
background: var(--main-bg-color, #fff);
border-radius: 10px;
min-width: 280px;
max-width: 92vw;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.18);
}
#modal_signout .but.c-red {
background: #e74c3c;
color: #fff;
text-decoration: none;
padding: 8px 14px;
border-radius: 6px;
}
#modal_signout .but {
border: 0;
background: var(--surface-2, #f0f0f0);
padding: 8px 14px;
border-radius: 6px;
cursor: pointer;
}
.user-dropdown .social_loginbar {
padding: 0 10px 12px;
}
+113
View File
@@ -0,0 +1,113 @@
/**
* Header user-box: check-in REST + signout modal.
*
* @package AKSRT
*/
(function () {
'use strict';
function restBase() {
if (window.aksrtData && window.aksrtData.restUrl) return window.aksrtData.restUrl.replace(/\/?$/, '/');
if (window.aksrtData && window.aksrtData.siteUrl) return String(window.aksrtData.siteUrl).replace(/\/?$/, '/') + 'wp-json/';
return '/wp-json/';
}
function restNonce() {
return (window.aksrtData && window.aksrtData.restNonce) || '';
}
function toast(msg, type) {
if (window.aksrt && typeof window.aksrt.toast === 'function') {
window.aksrt.toast(msg, type || 'info');
return;
}
// eslint-disable-next-line no-alert
window.alert(msg);
}
function openModal(id) {
var modal = document.getElementById(id);
if (!modal) return;
modal.classList.add('is-open', 'show');
modal.setAttribute('aria-hidden', 'false');
}
function closeModal(id) {
var modal = document.getElementById(id);
if (!modal) return;
modal.classList.remove('is-open', 'show');
modal.setAttribute('aria-hidden', 'true');
}
function initSignoutModal() {
document.addEventListener('click', function (e) {
var openBtn = e.target.closest('[data-target="#modal_signout"], [href="#modal_signout"]');
if (openBtn) {
e.preventDefault();
openModal('modal_signout');
return;
}
var dismiss = e.target.closest('#modal_signout [data-dismiss="modal"]');
if (dismiss) {
e.preventDefault();
closeModal('modal_signout');
return;
}
if (e.target && e.target.id === 'modal_signout') {
closeModal('modal_signout');
}
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') closeModal('modal_signout');
});
}
function initCheckin() {
document.addEventListener('click', function (e) {
var btn = e.target.closest('[data-aksrt-checkin="1"]');
if (!btn || btn.disabled) return;
e.preventDefault();
btn.disabled = true;
fetch(restBase() + 'aksrt/v1/signin', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': restNonce(),
},
body: '{}',
})
.then(function (r) {
return r.json().then(function (data) {
return { ok: r.ok, data: data };
});
})
.then(function (res) {
if (!res.ok) {
var msg = (res.data && (res.data.message || res.data.msg)) || '签到失败';
toast(msg, 'danger');
btn.disabled = false;
return;
}
toast((res.data && res.data.message) || '签到成功', 'success');
btn.classList.add('is-done');
btn.innerHTML = '<i class="fa fa-calendar-check-o" aria-hidden="true"></i> 今日已签到';
})
.catch(function () {
toast('网络错误', 'danger');
btn.disabled = false;
});
});
}
function init() {
initSignoutModal();
initCheckin();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
+34 -1
View File
@@ -303,11 +303,32 @@ if (!empty($aksrt_bg_styles)) {
</a> </a>
</div> </div>
<?php
if (function_exists('aksrt_header_payvip_bar')) {
echo aksrt_header_payvip_bar(get_current_user_id()); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
if (function_exists('aksrt_header_user_checkin_btn')) {
$checkin = aksrt_header_user_checkin_btn(get_current_user_id());
if ($checkin !== '') {
echo '<div class="mt6 em09" style="padding:2px;">' . $checkin . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
if (function_exists('aksrt_header_user_wallet_mini')) {
echo '<div class="mt6" style="padding:2px;">' . aksrt_header_user_wallet_mini(get_current_user_id()) . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
?>
<div class="user-dropdown__credits"> <div class="user-dropdown__credits">
<i class="fas fa-coins" aria-hidden="true"></i> <i class="fas fa-coins" aria-hidden="true"></i>
<span class="user-dropdown__credits-label"><?php echo esc_html($aksrt_currency_name); ?></span> <span class="user-dropdown__credits-label"><?php echo esc_html($aksrt_currency_name); ?></span>
<span class="user-dropdown__credits-value"><?php echo esc_html(number_format_i18n($aksrt_user_credit)); ?></span> <span class="user-dropdown__credits-value"><?php echo esc_html(number_format_i18n($aksrt_user_credit)); ?></span>
</div> </div>
<?php
}
if (function_exists('aksrt_header_user_badges_row')) {
echo aksrt_header_user_badges_row(get_current_user_id()); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
?>
<div class="user-dropdown__sep"></div> <div class="user-dropdown__sep"></div>
@@ -326,7 +347,7 @@ if (!empty($aksrt_bg_styles)) {
<span><?php esc_html_e('发布内容', 'aksrt'); ?></span> <span><?php esc_html_e('发布内容', 'aksrt'); ?></span>
</a> </a>
<?php endif; ?> <?php endif; ?>
<a class="user-dropdown__action is-danger" href="<?php echo esc_url(wp_logout_url(home_url('/'))); ?>"> <a class="user-dropdown__action is-danger" href="javascript:;" data-toggle="modal" data-target="#modal_signout">
<span class="user-dropdown__action-icon is-red"><i class="fas fa-sign-out-alt" aria-hidden="true"></i></span> <span class="user-dropdown__action-icon is-red"><i class="fas fa-sign-out-alt" aria-hidden="true"></i></span>
<span><?php esc_html_e('退出登录', 'aksrt'); ?></span> <span><?php esc_html_e('退出登录', 'aksrt'); ?></span>
</a> </a>
@@ -370,6 +391,16 @@ if (!empty($aksrt_bg_styles)) {
<span><?php esc_html_e('找回密码', 'aksrt'); ?></span> <span><?php esc_html_e('找回密码', 'aksrt'); ?></span>
</a> </a>
</div> </div>
<?php
if (class_exists('\AKSRT\Auth\OAuth') && method_exists(\AKSRT\Auth\OAuth::get_instance(), 'render_social_buttons')) {
$oauth_html = (string) \AKSRT\Auth\OAuth::get_instance()->render_social_buttons(__('快速登录', 'aksrt'));
if ($oauth_html !== '') {
echo '<div class="social_loginbar mt10">' . $oauth_html . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
?>
</div> </div>
</div> </div>
<?php endif; ?> <?php endif; ?>
@@ -385,6 +416,8 @@ if (!empty($aksrt_bg_styles)) {
</div> </div>
</nav> </nav>
</header> </header>
<?php if (function_exists('aksrt_render_signout_modal')) { aksrt_render_signout_modal(); } ?>
<?php if (!empty($aksrt_header_settings['mobile_drawer'])) : ?> <?php if (!empty($aksrt_header_settings['mobile_drawer'])) : ?>
<div class="mobile-header nav-drawer-backdrop" id="nav-drawer-backdrop" aria-hidden="true"></div> <div class="mobile-header nav-drawer-backdrop" id="nav-drawer-backdrop" aria-hidden="true"></div>
+1
View File
@@ -44,6 +44,7 @@ require_once AKSRT_DIR . '/inc/helpers/aksrt-credits.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-null.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-null.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-ajax-list.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-ajax-list.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-links.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-links.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-header-userbox.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-identity.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-identity.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-rewards.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-rewards.php';
require_once AKSRT_DIR . '/inc/helpers/menu-decoration.php'; require_once AKSRT_DIR . '/inc/helpers/menu-decoration.php';
+161
View File
@@ -0,0 +1,161 @@
<?php
/**
* Header user-box density helpers (zibll zib_header_user_box subset).
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!function_exists('aksrt_header_user_checkin_btn')) :
/**
* Check-in CTA for header dropdown (uses REST /aksrt/v1/signin).
*/
function aksrt_header_user_checkin_btn(int $user_id = 0): string {
$user_id = $user_id > 0 ? $user_id : get_current_user_id();
if ($user_id <= 0 || !class_exists('\AKSRT\Credits\Signin')) {
return '';
}
$cfg = class_exists('\AKSRT\Settings\Settings')
? \AKSRT\Settings\Settings::get_signin_reward_settings()
: ['enabled' => false];
if (empty($cfg['enabled'])) {
return '';
}
// Lightweight today status via private API if available.
$signed = false;
try {
$signin = \AKSRT\Credits\Signin::get_instance();
// Prefer public method if present.
if (method_exists($signin, 'is_signed_today')) {
$signed = (bool) $signin->is_signed_today($user_id);
} else {
// Fallback: direct meta/table probe via rest_today logic is heavy; use option day key.
$day = wp_date('Y-m-d');
$signed = (bool) get_user_meta($user_id, 'aksrt_signin_' . $day, true);
}
} catch (\Throwable $e) {
$signed = false;
}
if ($signed) {
return '<button type="button" class="but block c-yellow aksrt-header-checkin is-done" disabled>'
. '<i class="fa fa-calendar-check-o" aria-hidden="true"></i> '
. esc_html__('今日已签到', 'aksrt')
. '</button>';
}
return '<button type="button" class="but block c-yellow aksrt-header-checkin" data-aksrt-checkin="1">'
. '<i class="fa fa-calendar-check-o" aria-hidden="true"></i> '
. esc_html__('签到领取今日奖励', 'aksrt')
. '</button>';
}
endif;
if (!function_exists('aksrt_header_user_wallet_mini')) :
/**
* Mini wallet strip: credits + cash balance.
*/
function aksrt_header_user_wallet_mini(int $user_id = 0): string {
$user_id = $user_id > 0 ? $user_id : get_current_user_id();
if ($user_id <= 0) {
return '';
}
$credits = function_exists('aksrt_user_credits') ? (int) aksrt_user_credits($user_id) : 0;
$currency = class_exists('\AKSRT\Settings\Settings')
? \AKSRT\Settings\Settings::get_currency_name()
: __('积分', 'aksrt');
$wallet = 0.0;
if (class_exists('\AKSRT\Credits\Balance')) {
$wallet = (float) \AKSRT\Credits\Balance::balance($user_id);
}
$center = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url('assets') : '#';
$html = '<a class="aksrt-header-wallet flex ac jsb" href="' . esc_url($center) . '">';
$html .= '<span class="aksrt-header-wallet__item"><i class="fas fa-coins" aria-hidden="true"></i> '
. esc_html($currency) . ' <b>' . esc_html(number_format_i18n($credits)) . '</b></span>';
$html .= '<span class="aksrt-header-wallet__item"><i class="fas fa-wallet" aria-hidden="true"></i> '
. esc_html__('余额', 'aksrt') . ' <b>' . esc_html(number_format_i18n($wallet, 2)) . '</b></span>';
$html .= '</a>';
return $html;
}
endif;
if (!function_exists('aksrt_header_user_badges_row')) :
/**
* Identity / medal chips under profile.
*/
function aksrt_header_user_badges_row(int $user_id = 0): string {
$user_id = $user_id > 0 ? $user_id : get_current_user_id();
if ($user_id <= 0 || !function_exists('aksrt_render_user_identity_chips')) {
return '';
}
$chips = aksrt_render_user_identity_chips($user_id, 'card');
if ($chips === '') {
return '';
}
return '<div class="em09 author-tag mb10 mt6 flex jc aksrt-header-badges">' . $chips . '</div>';
}
endif;
if (!function_exists('aksrt_header_payvip_bar')) :
/**
* VIP status / open CTA bar.
*/
function aksrt_header_payvip_bar(int $user_id = 0): string {
$user_id = $user_id > 0 ? $user_id : get_current_user_id();
if ($user_id <= 0) {
return '';
}
$badge = function_exists('aksrt_render_vip_badge') ? aksrt_render_vip_badge($user_id, 'md') : '';
if ($badge !== '') {
return '<div class="mt10 em09 aksrt-header-payvip">' . $badge . '</div>';
}
// Guest-style open VIP for non-members if VIP feature on.
$vip_url = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url('vip') : '#';
$enabled = true;
if (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_vip_settings')) {
$vs = \AKSRT\Settings\Settings::get_vip_settings();
$enabled = !empty($vs['enabled']) || !empty($vs['vip1_enabled']) || !empty($vs['vip2_enabled']);
}
if (!$enabled) {
return '';
}
return '<div class="mt10 em09 aksrt-header-payvip">'
. '<a class="but jb-red radius4 payvip-icon btn-block" href="' . esc_url($vip_url) . '">'
. '<i class="fa fa-diamond mr6" aria-hidden="true"></i>'
. esc_html__('开通会员 尊享会员权益', 'aksrt')
. '</a></div>';
}
endif;
if (!function_exists('aksrt_render_signout_modal')) :
/**
* Confirm logout modal markup (once per page).
*/
function aksrt_render_signout_modal(): void {
static $done = false;
if ($done || !is_user_logged_in()) {
return;
}
$done = true;
$logout = wp_logout_url(home_url('/'));
?>
<div class="modal fade" id="modal_signout" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-mini" role="document">
<div class="modal-content">
<div class="modal-body text-center" style="padding:24px;">
<div class="em2x mb10"><i class="fa fa-sign-out" aria-hidden="true"></i></div>
<div class="mb15"><?php esc_html_e('确认退出当前账号?', 'aksrt'); ?></div>
<div class="but-average flex jc" style="gap:10px;">
<button type="button" class="but" data-dismiss="modal"><?php esc_html_e('取消', 'aksrt'); ?></button>
<a class="but c-red" href="<?php echo esc_url($logout); ?>"><?php esc_html_e('确认退出', 'aksrt'); ?></a>
</div>
</div>
</div>
</div>
</div>
<?php
}
endif;
+21
View File
@@ -129,6 +129,27 @@ trait ScriptsPart1b {
} }
} }
// Header user-box density (check-in / wallet / signout modal)
$hub_css = get_template_directory() . '/assets/css/frontend/nav/header-userbox.css';
if (file_exists($hub_css)) {
wp_enqueue_style(
'aksrt-header-userbox',
$base_uri . '/assets/css/frontend/nav/header-userbox.css',
[$core_dep],
(string) filemtime($hub_css)
);
}
$hub_js = get_template_directory() . '/assets/js/header-userbox.js';
if (file_exists($hub_js)) {
wp_enqueue_script(
'aksrt-header-userbox',
$base_uri . '/assets/js/header-userbox.js',
[],
(string) filemtime($hub_js),
true
);
}
// Print stylesheet — only load on singular pages // Print stylesheet — only load on singular pages
if (is_singular()) { if (is_singular()) {
wp_enqueue_style( wp_enqueue_style(