Files
wordpress-theme-aksrt/src/AKSRT/Widgets/WidgetSlider.php
T
kate522 7a476a220d feat(widgets): product lazy fallback and admin empty states
Lazyload product list fallback thumbs; surface widget_null admin empties
for notice/stats/faq/slider; stats cards gain data-aksrt-animate.
2026-07-21 10:55:32 +08:00

240 lines
11 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Simple image slider widget (zibll widget_ui_slider → aksrt_render_slider / new-swiper).
*
* @package AKSRT
*/
namespace AKSRT\Widgets;
defined('ABSPATH') || exit;
class WidgetSlider extends \WP_Widget {
public function __construct() {
parent::__construct(
'aksrt_slider',
esc_html__('AKSRT 幻灯片', 'aksrt'),
[
'description' => esc_html__('多图轮播(共享 new-swiper / Swiper 栈),支持链接与标题。', 'aksrt'),
'classname' => 'widget_aksrt_slider slide-widget',
]
);
}
public function widget($args, $instance): void {
$slides = $this->normalize_slides($instance['slides'] ?? []);
if (!$slides) {
if (current_user_can('manage_options')) {
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo function_exists('aksrt_get_widget_null')
? aksrt_get_widget_null(__('当前配置下没有可显示的内容', 'aksrt')) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
: '<div class="c-red muted-box mb20"><b>' . esc_html__('幻灯片模块:', 'aksrt') . '</b>' . esc_html__('当前配置下没有可显示的内容,请正确配置或移出该模块', 'aksrt') . '</div>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
return;
}
$interval = max(2, min(20, (int) ($instance['interval'] ?? 4)));
$loop = !isset($instance['loop']) || !empty($instance['loop']);
$dots = !isset($instance['pagination']) || !empty($instance['pagination']);
$nav = !isset($instance['button']) || !empty($instance['button']);
$blank = !empty($instance['blank']);
$height = max(80, min(800, (int) ($instance['height'] ?? 220)));
$m_height = max(60, min(800, (int) ($instance['m_height'] ?? $height)));
$effect = sanitize_key((string) ($instance['effect'] ?? 'slide'));
if (!in_array($effect, ['slide', 'fade', 'cube', 'coverflow', 'flip'], true)) {
$effect = 'slide';
}
$slide_payload = [];
foreach ($slides as $slide) {
$slide_payload[] = [
'image' => $slide['image'],
'link' => $slide['href'],
'title' => $slide['title'],
'desc' => $slide['desc'],
'alt' => $slide['title'],
'target' => $blank ? '_blank' : '_self',
];
}
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
}
if (function_exists('aksrt_render_slider')) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo aksrt_render_slider([
'slides' => $slide_payload,
'effect' => $effect,
'loop' => $loop,
'autoplay' => true,
'interval' => $interval * 1000,
'pc_height' => $height . 'px',
'm_height' => $m_height . 'px',
'show_nav' => $nav,
'show_pagination' => $dots,
'slider_class' => 'slide-widget',
'wrapper_class' => 'aksrt-widget-slider-wrap',
]);
} else {
// Minimal fallback without helper.
echo '<div class="aksrt-widget-slider" style="--aksrt-slider-h:' . (int) $height . 'px">';
foreach ($slide_payload as $i => $slide) {
$active = $i === 0 ? ' is-active' : '';
echo '<div class="aksrt-widget-slider__slide' . esc_attr($active) . '">';
$img = '<img src="' . esc_url($slide['image']) . '" alt="' . esc_attr($slide['title']) . '" loading="lazy" />';
if ($slide['link'] !== '') {
echo '<a href="' . esc_url($slide['link']) . '"' . ($blank ? ' target="_blank" rel="noopener noreferrer"' : '') . '>' . $img . '</a>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
echo $img; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
echo '</div>';
}
echo '</div>';
}
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
public function form($instance): void {
$title = (string) ($instance['title'] ?? '');
$interval = (int) ($instance['interval'] ?? 4);
$height = (int) ($instance['height'] ?? 220);
$m_height = (int) ($instance['m_height'] ?? $height);
$effect = sanitize_key((string) ($instance['effect'] ?? 'slide'));
$slides = $this->normalize_slides($instance['slides'] ?? []);
if (!$slides) {
$slides = [
['image' => '', 'href' => '', 'title' => '', 'desc' => ''],
['image' => '', 'href' => '', 'title' => '', 'desc' => ''],
];
}
?>
<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($title); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('切换间隔(秒)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="2" max="20" name="<?php echo esc_attr($this->get_field_name('interval')); ?>" value="<?php echo esc_attr((string) $interval); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('PC 高度(px)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="80" max="800" name="<?php echo esc_attr($this->get_field_name('height')); ?>" value="<?php echo esc_attr((string) $height); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('移动端高度(px)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="60" max="800" name="<?php echo esc_attr($this->get_field_name('m_height')); ?>" value="<?php echo esc_attr((string) $m_height); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('切换效果', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('effect')); ?>">
<option value="slide" <?php selected($effect, 'slide'); ?>><?php esc_html_e('滑动', 'aksrt'); ?></option>
<option value="fade" <?php selected($effect, 'fade'); ?>><?php esc_html_e('淡入', 'aksrt'); ?></option>
<option value="coverflow" <?php selected($effect, 'coverflow'); ?>><?php esc_html_e('Coverflow', 'aksrt'); ?></option>
<option value="cube" <?php selected($effect, 'cube'); ?>><?php esc_html_e('Cube', 'aksrt'); ?></option>
<option value="flip" <?php selected($effect, 'flip'); ?>><?php esc_html_e('Flip', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('loop')); ?>" value="1" <?php checked(!empty($instance['loop']) || !isset($instance['loop'])); ?>> <?php esc_html_e('循环播放', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('button')); ?>" value="1" <?php checked(!empty($instance['button']) || !isset($instance['button'])); ?>> <?php esc_html_e('翻页按钮', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('pagination')); ?>" value="1" <?php checked(!empty($instance['pagination']) || !isset($instance['pagination'])); ?>> <?php esc_html_e('指示点', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('blank')); ?>" value="1" <?php checked(!empty($instance['blank'])); ?>> <?php esc_html_e('新窗口打开', 'aksrt'); ?></label>
</p>
<?php foreach ($slides as $i => $slide) : ?>
<p style="border-top:1px solid #ddd;padding-top:8px;">
<strong><?php echo esc_html(sprintf(__('幻灯片 %d', 'aksrt'), $i + 1)); ?></strong><br>
<label><?php esc_html_e('图片 URL', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('slides') . "[{$i}][image]"); ?>" value="<?php echo esc_attr($slide['image']); ?>">
</label>
<label><?php esc_html_e('链接', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('slides') . "[{$i}][href]"); ?>" value="<?php echo esc_attr($slide['href']); ?>">
</label>
<label><?php esc_html_e('标题', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('slides') . "[{$i}][title]"); ?>" value="<?php echo esc_attr($slide['title']); ?>">
</label>
<label><?php esc_html_e('简介', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('slides') . "[{$i}][desc]"); ?>" value="<?php echo esc_attr($slide['desc']); ?>">
</label>
</p>
<?php endforeach; ?>
<?php
$extra = max(0, 4 - count($slides));
for ($j = 0; $j < $extra; $j++) {
$i = count($slides) + $j;
?>
<p style="border-top:1px dashed #eee;padding-top:8px;">
<strong><?php echo esc_html(sprintf(__('空槽 %d', 'aksrt'), $i + 1)); ?></strong>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('slides') . "[{$i}][image]"); ?>" placeholder="<?php esc_attr_e('图片 URL', 'aksrt'); ?>">
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('slides') . "[{$i}][href]"); ?>" placeholder="<?php esc_attr_e('链接', 'aksrt'); ?>">
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('slides') . "[{$i}][title]"); ?>" placeholder="<?php esc_attr_e('标题', 'aksrt'); ?>">
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('slides') . "[{$i}][desc]"); ?>" placeholder="<?php esc_attr_e('简介', 'aksrt'); ?>">
</p>
<?php
}
?>
<p class="description"><?php esc_html_e('使用主题共享 Swiper(.new-swiper)。图片 URL 必填;仅 1 张时不滑动。', 'aksrt'); ?></p>
<?php
}
public function update($new_instance, $old_instance): array {
$slides = $this->normalize_slides($new_instance['slides'] ?? []);
$effect = sanitize_key((string) ($new_instance['effect'] ?? 'slide'));
if (!in_array($effect, ['slide', 'fade', 'cube', 'coverflow', 'flip'], true)) {
$effect = 'slide';
}
return [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'interval' => max(2, min(20, (int) ($new_instance['interval'] ?? 4))),
'height' => max(80, min(800, (int) ($new_instance['height'] ?? 220))),
'm_height' => max(60, min(800, (int) ($new_instance['m_height'] ?? ($new_instance['height'] ?? 220)))),
'effect' => $effect,
'loop' => !empty($new_instance['loop']) ? 1 : 0,
'button' => !empty($new_instance['button']) ? 1 : 0,
'pagination' => !empty($new_instance['pagination']) ? 1 : 0,
'blank' => !empty($new_instance['blank']) ? 1 : 0,
'slides' => $slides,
];
}
/**
* @param mixed $raw
* @return array<int, array{image:string,href:string,title:string,desc:string}>
*/
private function normalize_slides($raw): array {
if (!is_array($raw)) {
return [];
}
$out = [];
foreach ($raw as $row) {
if (!is_array($row)) {
continue;
}
$image = esc_url_raw(trim((string) ($row['image'] ?? $row['link'] ?? '')));
if ($image === '') {
continue;
}
$out[] = [
'image' => $image,
'href' => esc_url_raw(trim((string) ($row['href'] ?? ''))),
'title' => sanitize_text_field((string) ($row['title'] ?? '')),
'desc' => sanitize_text_field((string) ($row['desc'] ?? $row['dec'] ?? '')),
];
if (count($out) >= 8) {
break;
}
}
return $out;
}
}