Files
wordpress-theme-aksrt/src/AKSRT/Widgets/WidgetPricingPlans.php
T
kate522 93ab4650dd feat(widgets): reviews swiper-tilt, feature countup, tilt/pricing motion
Reviews use swiper-scroll + 3D tilt cards; feature intro stats count-up
and tab video pause; tilt showcase hooks effects.js; pricing cards animate.
2026-07-21 09:46:17 +08:00

240 lines
8.8 KiB
PHP

<?php
/**
* Pricing plan cards (zibll zib_widget_pricing_plans lite).
*
* @package AKSRT
*/
namespace AKSRT\Widgets;
defined('ABSPATH') || exit;
class WidgetPricingPlans extends \WP_Widget {
public function __construct() {
parent::__construct(
'aksrt_pricing_plans',
esc_html__('AKSRT 价格方案', 'aksrt'),
[
'description' => esc_html__('多列定价卡片 + 信任条(落地页/VIP 介绍)。', 'aksrt'),
'classname' => 'widget_aksrt_pricing_plans',
]
);
}
public function widget($args, $instance): void {
$plans = $this->normalize_plans($instance['lists'] ?? $instance['plans'] ?? []);
if (!$plans) {
return;
}
$trust = $this->normalize_trust($instance['trust_items'] ?? []);
$currency = sanitize_text_field((string) ($instance['currency'] ?? '¥'));
if ($currency === '') {
$currency = '¥';
}
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
if ($title !== '') {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
echo '<div class="aksrt-pricing">';
echo '<div class="aksrt-pricing__cards">';
$pi = 0;
foreach ($plans as $plan) {
$accent = sanitize_key((string) ($plan['color'] ?? 'blue'));
if (!in_array($accent, ['pink', 'purple', 'blue', 'green', 'yellow', 'cyan'], true)) {
$accent = 'blue';
}
$featured = !empty($plan['is_featured']);
$pack = function_exists('aksrt_obs_animate_attrs') ? aksrt_obs_animate_attrs('slideup', false, $pi * 0.12) : ['class' => '', 'attr' => ''];
$class = 'aksrt-pricing__card accent-' . $accent . ($featured ? ' is-featured' : '') . $pack['class'];
echo '<div class="' . esc_attr(trim($class)) . '"' . $pack['attr'] . '>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$pi++;
if ($featured || !empty($plan['tag'])) {
$badge = (string) ($plan['tag'] !== '' ? $plan['tag'] : __('推荐', 'aksrt'));
echo '<span class="aksrt-pricing__badge">' . esc_html($badge) . '</span>';
}
echo '<h3 class="aksrt-pricing__name">' . esc_html($plan['name']) . '</h3>';
if ($plan['desc'] !== '') {
echo '<p class="aksrt-pricing__desc">' . esc_html($plan['desc']) . '</p>';
}
$price = (float) $plan['price'];
$show = (float) $plan['show_price'];
echo '<div class="aksrt-pricing__price">';
echo '<span class="aksrt-pricing__main"><span class="aksrt-pricing__mark">' . esc_html($currency) . '</span>' . esc_html($this->fmt($price)) . '</span>';
if ($show > $price && $show > 0) {
echo '<span class="aksrt-pricing__original"><span class="aksrt-pricing__mark">' . esc_html($currency) . '</span>' . esc_html($this->fmt($show)) . '</span>';
}
echo '</div>';
if ($plan['features']) {
echo '<ul class="aksrt-pricing__features">';
foreach ($plan['features'] as $ft) {
echo '<li><i class="fa fa-check" aria-hidden="true"></i><span>' . esc_html($ft) . '</span></li>';
}
echo '</ul>';
}
if ($plan['btn_text'] !== '' && $plan['btn_url'] !== '') {
$url = $plan['btn_url'];
if (!empty($instance['go_link']) && function_exists('aksrt_go_link')) {
$url = aksrt_go_link($url, true);
}
echo '<div class="aksrt-pricing__action"><a class="aksrt-pricing__btn" href="' . esc_url($url) . '">' . esc_html($plan['btn_text']) . '</a></div>';
}
echo '</div>';
}
echo '</div>';
if ($trust) {
echo '<div class="aksrt-pricing__trust">';
foreach ($trust as $ti) {
echo '<div class="aksrt-pricing__trust-item">';
if ($ti['icon'] !== '') {
echo '<i class="' . esc_attr($ti['icon']) . '" aria-hidden="true"></i>';
}
echo '<div><strong>' . esc_html($ti['title']) . '</strong>';
if ($ti['desc'] !== '') {
echo '<span>' . esc_html($ti['desc']) . '</span>';
}
echo '</div></div>';
}
echo '</div>';
}
echo '</div>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
private function fmt(float $n): string {
if (abs($n - round($n)) < 0.001) {
return (string) (int) round($n);
}
return number_format($n, 2, '.', '');
}
/**
* @param mixed $raw
* @return array<int, array<string,mixed>>
*/
private function normalize_plans($raw): array {
if (is_string($raw)) {
$decoded = json_decode($raw, true);
$raw = is_array($decoded) ? $decoded : [];
}
if (!is_array($raw)) {
return [];
}
$out = [];
foreach ($raw as $row) {
if (!is_array($row)) {
continue;
}
$name = sanitize_text_field((string) ($row['name'] ?? $row['title'] ?? ''));
if ($name === '') {
continue;
}
$intro = (string) ($row['intro'] ?? $row['features'] ?? '');
if (is_array($row['features'] ?? null)) {
$features = array_values(array_filter(array_map('sanitize_text_field', $row['features'])));
} else {
$features = array_values(array_filter(array_map('sanitize_text_field', preg_split('/\r\n|\r|\n/', $intro) ?: [])));
}
$btn = is_array($row['btn'] ?? null) ? $row['btn'] : [];
$out[] = [
'name' => $name,
'desc' => sanitize_text_field((string) ($row['desc'] ?? '')),
'price' => (float) ($row['price'] ?? 0),
'show_price' => (float) ($row['show_price'] ?? 0),
'color' => sanitize_key((string) ($row['color'] ?? 'blue')),
'is_featured' => !empty($row['is_featured']) ? 1 : 0,
'tag' => sanitize_text_field((string) ($row['tag'] ?? '')),
'features' => $features,
'btn_text' => sanitize_text_field((string) ($row['btn_text'] ?? $btn['text'] ?? '')),
'btn_url' => esc_url_raw(trim((string) ($row['btn_url'] ?? $btn['url'] ?? ''))),
];
if (count($out) >= 6) {
break;
}
}
return $out;
}
/**
* @param mixed $raw
* @return array<int, array{title:string,desc:string,icon:string}>
*/
private function normalize_trust($raw): array {
if (is_string($raw)) {
$decoded = json_decode($raw, true);
$raw = is_array($decoded) ? $decoded : [];
}
if (!is_array($raw)) {
return [];
}
$out = [];
foreach ($raw as $row) {
if (!is_array($row)) {
continue;
}
$title = sanitize_text_field((string) ($row['title'] ?? ''));
if ($title === '') {
continue;
}
$out[] = [
'title' => $title,
'desc' => sanitize_text_field((string) ($row['desc'] ?? '')),
'icon' => sanitize_text_field((string) ($row['icon'] ?? 'fa fa-check-circle')),
];
if (count($out) >= 6) {
break;
}
}
return $out;
}
public function form($instance): void {
$plans = is_string($instance['lists'] ?? null)
? (string) $instance['lists']
: wp_json_encode($this->normalize_plans($instance['lists'] ?? []), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$trust = is_string($instance['trust_items'] ?? null)
? (string) $instance['trust_items']
: wp_json_encode($this->normalize_trust($instance['trust_items'] ?? []), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
?>
<p>
<label><?php esc_html_e('标题', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('title')); ?>" value="<?php echo esc_attr((string) ($instance['title'] ?? '')); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('货币符号', 'aksrt'); ?>
<input class="tiny-text" type="text" name="<?php echo esc_attr($this->get_field_name('currency')); ?>" value="<?php echo esc_attr((string) ($instance['currency'] ?? '¥')); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('方案 JSON', 'aksrt'); ?>
<textarea class="widefat" rows="8" name="<?php echo esc_attr($this->get_field_name('lists')); ?>" placeholder='[{"name":"Pro","price":99,"show_price":199,"desc":"适合创作者","features":["无限下载","优先支持"],"btn_text":"立即开通","btn_url":"#","is_featured":1,"color":"pink"}]'><?php echo esc_textarea((string) $plans); ?></textarea>
</label>
</p>
<p>
<label><?php esc_html_e('信任条 JSON', 'aksrt'); ?>
<textarea class="widefat" rows="3" name="<?php echo esc_attr($this->get_field_name('trust_items')); ?>"><?php echo esc_textarea((string) $trust); ?></textarea>
</label>
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('go_link')); ?>" value="1" <?php checked(!empty($instance['go_link'])); ?>> <?php esc_html_e('外链跳转页', 'aksrt'); ?></label>
</p>
<?php
}
public function update($new_instance, $old_instance): array {
return [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'currency' => sanitize_text_field((string) ($new_instance['currency'] ?? '¥')),
'lists' => $this->normalize_plans($new_instance['lists'] ?? []),
'trust_items' => $this->normalize_trust($new_instance['trust_items'] ?? []),
'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
];
}
}