fix(vip): 在 /user/vip/ 档位购买卡下方补充积分兑换会员入口

参考主题 zibpay_user_page_tab_content_vip:非会员 + 积分兑换会员开启 +
有可兑换套餐时,在两张档位购买卡下方输出全宽入口横条(points-color 图标
+ 标题 + 立即兑换 badg),点击经 data-aksrt-vip-points-open 打开 wp_footer
静态兑换弹窗,与会员购买弹窗内入口共用同一弹窗。

- aksrt-vip.php 新增 aksrt_get_vip_points_exchange_entry_html(),门控与
  弹窗壳同源(取服务端 catalog),已是会员不展示(对齐参考主题契约)
- vip-tab.php 在卡片区之后输出入口;credits.css 补 .uc-vip-points-entry
  表面样式(与档位卡同一变量体系,暗色自适应)
- pay-vip.js 改文档级委托承接 data-aksrt-vip-points-open,一处绑定覆盖
  静态页面横条与弹窗内按钮;移除 bindModalUi 内的重复分支
- 契约测试更新为双入口(11 项),node --test 420 项通过,php -l 通过

未写数据迁移;未做真实视觉效果验收。
This commit is contained in:
kate522 committed 2026-09-23 14:30:54 +08:00
1 parent b798c7a45d
commit 2801a89c1f
7 files changed
+164 -22

No files matched your search

+56 -4
View File
@@ -427,6 +427,55 @@ if (!function_exists('aksrt_get_vip_purchase_url')) {
}
}
/**
* 用户中心 /user/vip/ 页面的积分兑换会员入口(参考主题 zibpay_user_page_tab_content_vip 1:1)。
*
* 参考主题在「我的会员」摘要与两张档位购买卡之后输出全宽入口横条:
* .c-yellow.zib-widget.flex.ac.jsb(zib-widget → 本主题 aksrt-panel 表面)
* ├─ .flex1.em12:points-color 图标 +「积分兑换会员」
* └─ .badg.radius.c-yellow:「立即兑换」+ fa-chevron-circle-right
* 门控对齐参考主题 zibpay_get_vip_points_exchange_link:已是会员不展示
* ($vip_level ? '' : link),且要求积分兑换会员开启、至少一个会员等级
* 开启并存在可兑换套餐(catalog.points_exchange 已内含 credits 门控)。
*
* 入口点击经 data-aksrt-vip-points-open 由 pay-vip.js 打开 wp_footer 输出的
* 静态兑换弹窗(aksrt_render_vip_points_exchange_modal),与会员购买弹窗内
* 的入口共用同一弹窗与开合开关。
*
* @param int $user_id 当前用户 ID(<=0 返回空串)。
* @param int $vip_level 用户当前会员等级(>0 时返回空串;缺省 -1 表示自行解析)。
* @return string 空串表示不满足展示条件。
*/
if (!function_exists('aksrt_get_vip_points_exchange_entry_html')) :
function aksrt_get_vip_points_exchange_entry_html(int $user_id, int $vip_level = -1): string {
if ($user_id <= 0 || !class_exists('\AKSRT\Content\VipService')) {
return '';
}
if ($vip_level < 0) {
$vip_level = function_exists('aksrt_get_user_vip_level') ? aksrt_get_user_vip_level($user_id) : 0;
}
// 参考主题契约:已经是会员就不再展示积分兑换入口。
if ($vip_level > 0) {
return '';
}
$catalog = \AKSRT\Content\VipService::get_instance()->get_catalog($user_id);
$points_exchange = is_array($catalog['points_exchange'] ?? null) ? $catalog['points_exchange'] : [];
$packages = is_array($points_exchange['packages'] ?? null) ? $points_exchange['packages'] : [];
if (empty($points_exchange['enabled']) || $packages === []) {
return '';
}
return '<a class="uc-vip-points-entry flex ac jsb" href="javascript:;" data-aksrt-vip-points-open>'
. '<span class="flex1 em12 c-yellow">'
. '<svg class="icon mr6 em12" aria-hidden="true"><use xlink:href="#icon-points-color"></use></svg>'
. esc_html__('积分兑换会员', 'aksrt')
. '</span>'
. '<span class="badg radius c-yellow">' . esc_html__('立即兑换', 'aksrt')
. '<i class="fa fas fa-chevron-circle-right ml6" aria-hidden="true"></i></span></a>';
}
endif;
/**
* Format VIP equity HTML for front-end display.
*
@@ -507,10 +556,13 @@ if (!function_exists('aksrt_format_vip_equity_html')) {
/**
* 全局积分兑换会员弹窗(参考主题 refresh_modal 同构静态壳)。
*
* 入口位于会员购买弹窗(.pay-vip → assets/js/pay-vip.js)内,而 .pay-vip
* 触发点全站可见(页头、浮动按钮、用户中心、付费卡),因此弹窗壳在满足
* 条件时随 wp_footer 全局输出一次;显隐由 pay-vip.js 自包含开关驱动,
* 静止态由 vip-modal.css 的 .modal[data-aksrt-vip-points-modal] 显式隐藏。
* 入口有两处:会员购买弹窗(.pay-vip → assets/js/pay-vip.js)内的「积分兑换
* 会员」按钮,以及 /user/vip/ 档位购买卡下方的入口横条
* (aksrt_get_vip_points_exchange_entry_html);二者均带
* data-aksrt-vip-points-open,由 pay-vip.js 文档级委托打开本弹窗。.pay-vip
* 触发点全站可见,因此弹窗壳在满足条件时随 wp_footer 全局输出一次;显隐由
* pay-vip.js 自包含开关驱动,静止态由 vip-modal.css 的
* .modal[data-aksrt-vip-points-modal] 显式隐藏。
*/
if (!function_exists('aksrt_render_vip_points_exchange_modal')) :
function aksrt_render_vip_points_exchange_modal(): void {
@@ -164,6 +164,12 @@ foreach ([1, 2] as $lv) {
<?php echo $cards; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<?php
// 积分兑换会员入口(参考主题 zibpay_user_page_tab_content_vip 1:1:档位购买卡
// 下方的全宽横条;仅非会员 + 积分兑换开启 + 有可兑换套餐时输出)。
echo aksrt_get_vip_points_exchange_entry_html($user_id, $level); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- 各字段已在生成时转义。
?>
<?php if (!empty($vip_settings['more'])) : ?>
<div class="muted-2-color em09 muted-box padding-10 mini-radius mt15">
<?php echo $vip_settings['more']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>