feat(widgets): singular post pay purchase sidebar box

Add WidgetPostPayBox (zibll posts_pay parity) with theme skins,
paid/unpaid CTAs, sales badge, and registration.
This commit is contained in:
2026-07-21 12:42:57 +08:00
parent fd1137804e
commit 6a78b553d6
3 changed files with 220 additions and 0 deletions
+38
View File
@@ -2479,3 +2479,41 @@ a.main-color > .icon-cover-card:hover,
.widget-tab-posts__skeleton .placeholder.k1 { width: 80%; }
.widget-tab-posts__skeleton .placeholder.s1 { width: 48%; }
.posts-mini-lists.aksrt-main-posts { /* zibll mini outer */ }
/* batch37-39 residual */
.aksrt-post-pay-box { position: relative; overflow: hidden; }
.aksrt-post-pay-box .pay-tag.abs-center {
position: absolute; top: 12px; left: 50%; transform: translateX(-50%);
z-index: 2; padding: 2px 10px; border-radius: 999px; background: rgba(0,0,0,.35); color: #fff; font-size: 12px;
}
.aksrt-post-pay-box__hero { position: relative; overflow: hidden; min-height: 120px; color: #fff; }
.aksrt-post-pay-box__blob { opacity: .55; pointer-events: none; }
.aksrt-post-pay-box__blob--a { height: 200px; left: 75%; width: 200px; top: -34%; border-radius: 100%; }
.aksrt-post-pay-box__blob--b { height: 305px; width: 337px; left: -229px; border-radius: 100%; opacity: .7; }
.aksrt-post-pay-box .box-body { position: relative; z-index: 1; }
.aksrt-post-pay-box .img-badge.hot { position: absolute; top: 8px; right: 8px; z-index: 2; }
.aksrt-post-pay-box .but.btn-block { width: 100%; justify-content: center; }
.aksrt-main-posts__skeleton .placeholder-row,
.aksrt-product-tab__skeleton .placeholder-row {
display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px;
}
.aksrt-main-posts__skeleton .posts-mini.placeholder-row { flex-direction: row; align-items: center; gap: 10px; }
.aksrt-main-posts__skeleton .thumb-placeholder,
.aksrt-product-tab__skeleton .placeholder.thumb {
width: 72px; height: 54px; border-radius: 8px; background: var(--surface-2, #eee);
}
.aksrt-product-tab__skeleton .placeholder.thumb { width: 100%; height: 100px; }
.aksrt-main-posts__skeleton .placeholder.k1,
.aksrt-main-posts__skeleton .placeholder.t1,
.aksrt-main-posts__skeleton .placeholder.s1,
.aksrt-product-tab__skeleton .placeholder.k1,
.aksrt-product-tab__skeleton .placeholder.s1 {
height: 12px; border-radius: 4px; background: var(--surface-2, #eee);
}
.aksrt-main-posts__skeleton .placeholder.k1,
.aksrt-product-tab__skeleton .placeholder.k1 { width: 80%; }
.aksrt-main-posts__skeleton .placeholder.s1,
.aksrt-product-tab__skeleton .placeholder.s1 { width: 48%; }
.aksrt-main-posts[data-load-mode="ajax"].is-ajax-loading { opacity: .85; }
.widget-search-box.is-affix { position: sticky; top: 72px; }
+1
View File
@@ -81,6 +81,7 @@ function aksrt_register_widgets() {
register_widget('\AKSRT\Widgets\WidgetFeatureIntro');
register_widget('\AKSRT\Widgets\WidgetTermCards');
register_widget('\AKSRT\Widgets\WidgetPaidPosts');
register_widget('\AKSRT\Widgets\WidgetPostPayBox');
register_widget('\AKSRT\Widgets\WidgetAuthorCard');
register_widget('\AKSRT\Widgets\WidgetOnelinePosts');
register_widget('\AKSRT\Widgets\WidgetTiltCases');
+181
View File
@@ -0,0 +1,181 @@
<?php
/**
* Singular post pay purchase sidebar card (zibll zib_widget_ui_posts_pay).
*
* @package AKSRT
*/
namespace AKSRT\Widgets;
defined('ABSPATH') || exit;
class WidgetPostPayBox extends \WP_Widget {
public function __construct() {
parent::__construct(
'aksrt_post_pay_box',
esc_html__('AKSRT 付费购买', 'aksrt'),
[
'description' => esc_html__('显示当前文章/帖子的付费购买模块,推荐放在详情侧栏。', 'aksrt'),
'classname' => 'widget_aksrt_post_pay_box',
]
);
}
/**
* @param array<string,mixed> $instance
*/
public static function render_html(array $instance = []): string {
if (!is_singular(['post', 'page', 'aksrt_forum_topic', 'aksrt_product'])) {
return '';
}
$post = get_queried_object();
if (!$post instanceof \WP_Post) {
return '';
}
if (!function_exists('aksrt_get_pay_meta') || !function_exists('aksrt_is_paid_post')) {
return '';
}
if (!aksrt_is_paid_post($post)) {
return '';
}
$meta = aksrt_get_pay_meta($post);
$pay_type = (string) ($meta['pay_type'] ?? 'no');
if ($pay_type === '' || $pay_type === 'no') {
return '';
}
$theme = sanitize_html_class((string) ($instance['theme'] ?? 'jb-red'));
if ($theme === '') {
$theme = 'jb-red';
}
$has_access = function_exists('aksrt_has_paid_access') ? aksrt_has_paid_access($post) : false;
$priced = function_exists('aksrt_get_user_pay_price') ? aksrt_get_user_pay_price($post) : null;
$pay_modo = is_array($priced) && (($priced['modo'] ?? '') === 'points') ? 'points' : '0';
$amount = is_array($priced) ? (float) ($priced['amount'] ?? 0) : (float) ($meta['price'] ?? 0);
$currency = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_currency_name() : __('积分', 'aksrt');
$cash_symbol = '¥';
if (class_exists('\AKSRT\Settings\Settings')) {
$paid_s = \AKSRT\Settings\Settings::get_paid_content_settings();
$cash_symbol = (string) ($paid_s['display']['currency_symbol'] ?? '¥');
}
$price_text = $pay_modo === 'points'
? (function_exists('aksrt_format_price') ? aksrt_format_price($amount) : (string) $amount) . ' ' . $currency
: $cash_symbol . (function_exists('aksrt_format_price') ? aksrt_format_price($amount) : number_format($amount, 2, '.', ''));
$type_labels = [
'reading' => __('付费阅读', 'aksrt'),
'download' => __('付费下载', 'aksrt'),
'gallery' => __('付费图库', 'aksrt'),
'video' => __('付费视频', 'aksrt'),
'vip' => __('会员专属', 'aksrt'),
];
$type_label = $type_labels[$pay_type] ?? __('付费内容', 'aksrt');
$cta_map = [
'reading' => __('查看内容', 'aksrt'),
'download' => __('获取下载', 'aksrt'),
'gallery' => __('查看图片', 'aksrt'),
'video' => __('查看视频', 'aksrt'),
'vip' => __('查看内容', 'aksrt'),
];
$cta_label = $cta_map[$pay_type] ?? __('查看内容', 'aksrt');
$sales = 0;
if (function_exists('aksrt_get_download_box_meta')) {
$box = aksrt_get_download_box_meta($post);
$sales = (int) ($box['sales_count'] ?? 0);
}
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink($post));
$is_logged_in = is_user_logged_in();
$demo = trim((string) ($meta['demo_url'] ?? ''));
ob_start();
echo '<div class="zib-widget pay-box pay-widget aksrt-post-pay-box order-type-' . esc_attr($pay_type) . ($has_access ? ' is-paid' : ' is-unpaid') . '" style="padding:0;">';
echo '<badge class="pay-tag abs-center">' . esc_html($type_label) . '</badge>';
echo '<div class="relative-h ' . esc_attr($theme) . ' aksrt-post-pay-box__hero">';
echo '<div class="absolute radius ' . esc_attr($theme) . ' aksrt-post-pay-box__blob aksrt-post-pay-box__blob--a"></div>';
echo '<div class="absolute radius ' . esc_attr($theme) . ' aksrt-post-pay-box__blob aksrt-post-pay-box__blob--b"></div>';
echo '<div class="relative box-body text-center">';
if ($has_access) {
echo '<p class="mt10 mb6"><i class="fa fa-shopping-bag fa-3x" aria-hidden="true"></i></p>';
echo '<b class="em14">' . esc_html__('已解锁', 'aksrt') . '</b>';
} else {
echo '<div class="text-center mt10 aksrt-post-pay-box__price">';
if ($amount > 0) {
echo '<div class="em14 font-bold">' . esc_html($price_text) . '</div>';
} else {
echo '<div class="em14 font-bold">' . esc_html__('免费', 'aksrt') . '</div>';
}
echo '</div>';
}
echo '</div></div>';
echo '<div class="box-body">';
if ($has_access) {
echo '<div class="mt10"><a href="#posts-pay" class="but padding-lg btn-block ' . esc_attr($theme) . '"><i class="fa fa-dot-circle-o fa-fw" aria-hidden="true"></i> ' . esc_html($cta_label) . '</a></div>';
} elseif (!$is_logged_in) {
echo '<div class="mt10"><a href="' . esc_url($login_url) . '" class="but padding-lg btn-block jb-blue"><i class="fa fa-sign-in" aria-hidden="true"></i> ' . esc_html__('登录后购买', 'aksrt') . '</a></div>';
if ($amount > 0 && $pay_modo !== 'points' && function_exists('aksrt_allow_guest_content_pay') && aksrt_allow_guest_content_pay($post)) {
echo '<div class="mt10"><button type="button" class="but padding-lg btn-block ' . esc_attr($theme) . '" data-aksrt-pay-content="' . esc_attr((string) $post->ID) . '"><i class="fa fa-shopping-cart fa-fw" aria-hidden="true"></i> ' . esc_html(sprintf(/* translators: price */ __('游客购买(%s)', 'aksrt'), $price_text)) . '</button></div>';
}
} else {
echo '<div class="mt10"><button type="button" class="but padding-lg btn-block ' . esc_attr($theme) . ' cashier-link" data-aksrt-pay-content="' . esc_attr((string) $post->ID) . '"><i class="fa fa-shopping-cart fa-fw" aria-hidden="true"></i> ' . esc_html(sprintf(/* translators: price */ __('立即购买(%s)', 'aksrt'), $price_text)) . '</button></div>';
}
if ($demo !== '') {
echo '<div class="mt10"><a class="but hollow btn-block" href="' . esc_url($demo) . '" target="_blank" rel="noopener noreferrer"><i class="fa fa-external-link" aria-hidden="true"></i> ' . esc_html__('演示地址', 'aksrt') . '</a></div>';
}
echo '</div>';
if ($sales > 0) {
echo '<badge class="img-badge hot jb-blue px12">' . esc_html(sprintf(/* translators: sales count */ __('已售 %s', 'aksrt'), (string) $sales)) . '</badge>';
}
echo '</div>';
return (string) ob_get_clean();
}
public function widget($args, $instance): void {
$html = self::render_html($instance);
if ($html === '') {
return;
}
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
public function form($instance): void {
$theme = sanitize_html_class((string) ($instance['theme'] ?? 'jb-red'));
$opts = [
'jb-red' => __('红', 'aksrt'),
'jb-yellow' => __('橙', 'aksrt'),
'jb-blue' => __('蓝', 'aksrt'),
'jb-green' => __('绿', 'aksrt'),
'jb-purple' => __('紫', 'aksrt'),
'jb-vip1' => __('会员金', 'aksrt'),
'jb-vip2' => __('会员灰', 'aksrt'),
];
?>
<p class="description"><?php esc_html_e('仅在文章/帖子详情侧栏显示当前内容的付费购买卡。', 'aksrt'); ?></p>
<p>
<label><?php esc_html_e('色彩主题', 'aksrt'); ?>
<select class="widefat" name="<?php echo esc_attr($this->get_field_name('theme')); ?>">
<?php foreach ($opts as $k => $label) : ?>
<option value="<?php echo esc_attr($k); ?>" <?php selected($theme, $k); ?>><?php echo esc_html($label); ?></option>
<?php endforeach; ?>
</select>
</label>
</p>
<?php
}
public function update($new_instance, $old_instance): array {
$theme = sanitize_html_class((string) ($new_instance['theme'] ?? 'jb-red'));
$allowed = ['jb-red', 'jb-yellow', 'jb-blue', 'jb-green', 'jb-purple', 'jb-vip1', 'jb-vip2'];
if (!in_array($theme, $allowed, true)) {
$theme = 'jb-red';
}
return ['theme' => $theme];
}
}