feat(widgets): reviews + highlights carousel cards; plate-aware board list
Add zibll reviews/highlights marketing widgets with CSS, and make the legacy forum board-list widget prefer aksrt_forum_plate CPT.
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Batch3c: reviews + highlights.
|
||||
*/
|
||||
|
||||
.aksrt-reviews {
|
||||
margin-bottom: 16px;
|
||||
--aksrt-rev-pc: 3;
|
||||
--aksrt-rev-m: 1.2;
|
||||
}
|
||||
.aksrt-reviews__track {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: calc((100% - (var(--aksrt-rev-pc) - 1) * 12px) / var(--aksrt-rev-pc));
|
||||
gap: 12px;
|
||||
overflow-x: auto;
|
||||
scroll-snap-type: x mandatory;
|
||||
padding-bottom: 6px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
@media (max-width: 767.98px) {
|
||||
.aksrt-reviews__track {
|
||||
grid-auto-columns: calc((100% - (var(--aksrt-rev-m) - 1) * 12px) / var(--aksrt-rev-m));
|
||||
}
|
||||
}
|
||||
.aksrt-reviews__slide { scroll-snap-align: start; min-width: 0; }
|
||||
.aksrt-reviews__card {
|
||||
position: relative;
|
||||
border: 1px solid var(--color-line, #e5e7eb);
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
background: var(--surface-1, #fff);
|
||||
min-height: 100%;
|
||||
}
|
||||
.aksrt-reviews__head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
|
||||
.aksrt-reviews__avatar {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
background: var(--surface-2, #eee);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #999;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.aksrt-reviews__name { font-weight: 700; font-size: 14px; }
|
||||
.aksrt-reviews__name a { color: inherit; text-decoration: none; }
|
||||
.aksrt-reviews__role { font-size: 12px; color: var(--muted-2-color, #888); }
|
||||
.aksrt-reviews__stars { margin-bottom: 8px; font-size: 12px; }
|
||||
.aksrt-reviews__stars .is-on { color: #f5a623; }
|
||||
.aksrt-reviews__stars .is-off { color: #ddd; }
|
||||
.aksrt-reviews__content {
|
||||
font-size: 13px;
|
||||
line-height: 1.55;
|
||||
color: var(--color-ink-2, #444);
|
||||
margin: 0;
|
||||
}
|
||||
.aksrt-reviews__quote {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
bottom: 10px;
|
||||
opacity: .12;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.aksrt-highlights { margin-bottom: 16px; display: flex; flex-direction: column; gap: 12px; }
|
||||
.aksrt-highlights__row {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
.aksrt-highlights__row.is-large { grid-template-columns: 1fr; }
|
||||
.aksrt-highlights__row.is-medium { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.aksrt-highlights__row.is-small { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||
@media (max-width: 767.98px) {
|
||||
.aksrt-highlights__row.is-medium,
|
||||
.aksrt-highlights__row.is-small { grid-template-columns: 1fr; }
|
||||
}
|
||||
.aksrt-hl-card {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--color-line, #e5e7eb);
|
||||
background: var(--surface-1, #fff);
|
||||
padding: 14px;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
overflow: hidden;
|
||||
min-height: 100%;
|
||||
}
|
||||
.aksrt-hl-card.color-blue { --hl: #3b82f6; }
|
||||
.aksrt-hl-card.color-pink { --hl: #ec4899; }
|
||||
.aksrt-hl-card.color-purple { --hl: #a855f7; }
|
||||
.aksrt-hl-card.color-green { --hl: #22c55e; }
|
||||
.aksrt-hl-card.color-orange { --hl: #f97316; }
|
||||
.aksrt-hl-card.color-cyan { --hl: #06b6d4; }
|
||||
.aksrt-hl-card.size-large { min-height: 140px; }
|
||||
.aksrt-hl-card__media {
|
||||
width: 96px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
background: var(--surface-2, #f3f4f6);
|
||||
}
|
||||
.aksrt-hl-card.size-large .aksrt-hl-card__media { width: 160px; }
|
||||
.aksrt-hl-card__media img { width: 100%; height: 100%; object-fit: cover; display: block; min-height: 80px; }
|
||||
.aksrt-hl-card__body { min-width: 0; flex: 1; display: flex; flex-direction: column; }
|
||||
.aksrt-hl-card__icon { color: var(--hl, #3b82f6); font-size: 22px; margin-bottom: 8px; }
|
||||
.aksrt-hl-card__title { font-weight: 700; font-size: 15px; margin-bottom: 4px; }
|
||||
.aksrt-hl-card__desc { font-size: 12px; color: var(--muted-2-color, #888); line-height: 1.5; }
|
||||
@@ -4,3 +4,4 @@
|
||||
@import url("widgets/part-4.css");
|
||||
@import url("widget-slider.css");
|
||||
@import url("widget-batch3.css");
|
||||
@import url("widget-batch3c.css");
|
||||
|
||||
@@ -66,6 +66,8 @@ function aksrt_register_widgets() {
|
||||
register_widget('\AKSRT\Widgets\WidgetPricingPlans');
|
||||
register_widget('\AKSRT\Widgets\WidgetFaq');
|
||||
register_widget('\AKSRT\Widgets\WidgetStats');
|
||||
register_widget('\AKSRT\Widgets\WidgetReviews');
|
||||
register_widget('\AKSRT\Widgets\WidgetHighlights');
|
||||
|
||||
|
||||
// Forum widgets
|
||||
|
||||
@@ -26,31 +26,60 @@ class WidgetForumBoardList extends \WP_Widget {
|
||||
echo $args['before_widget'];
|
||||
echo $args['before_title'] . esc_html($title) . $args['after_title'];
|
||||
|
||||
$boards = get_terms([
|
||||
'taxonomy' => 'aksrt_forum_board',
|
||||
'hide_empty' => true,
|
||||
'orderby' => 'meta_value_num',
|
||||
'meta_key' => '_aksrt_sort_order',
|
||||
'order' => 'ASC',
|
||||
]);
|
||||
|
||||
if (!is_wp_error($boards) && !empty($boards)) {
|
||||
echo '<ul class="aksrt-forum-widget__boards">';
|
||||
foreach ($boards as $board) {
|
||||
$icon = get_term_meta($board->term_id, '_aksrt_icon', true);
|
||||
echo '<li class="aksrt-forum-widget__board">';
|
||||
echo '<a href="' . esc_url(get_term_link($board)) . '">';
|
||||
if ($icon) {
|
||||
echo '<i class="' . esc_attr($icon) . '"></i> ';
|
||||
$rendered = false;
|
||||
if (post_type_exists('aksrt_forum_plate')) {
|
||||
$plates = get_posts([
|
||||
'post_type' => 'aksrt_forum_plate',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => 30,
|
||||
'orderby' => 'menu_order title',
|
||||
'order' => 'ASC',
|
||||
]);
|
||||
if ($plates) {
|
||||
$rendered = true;
|
||||
echo '<ul class="aksrt-forum-widget__boards">';
|
||||
foreach ($plates as $plate) {
|
||||
$icon = (string) get_post_meta($plate->ID, '_aksrt_icon', true);
|
||||
$count = (int) get_post_meta($plate->ID, '_aksrt_posts_count', true);
|
||||
echo '<li class="aksrt-forum-widget__board">';
|
||||
echo '<a href="' . esc_url(get_permalink($plate)) . '">';
|
||||
if ($icon !== '') {
|
||||
echo '<i class="' . esc_attr($icon) . '"></i> ';
|
||||
}
|
||||
echo esc_html(get_the_title($plate));
|
||||
echo '<span class="aksrt-forum-widget__count">' . $count . '</span>';
|
||||
echo '</a>';
|
||||
echo '</li>';
|
||||
}
|
||||
echo esc_html($board->name);
|
||||
echo '<span class="aksrt-forum-widget__count">' . (int) $board->count . '</span>';
|
||||
echo '</a>';
|
||||
echo '</li>';
|
||||
echo '</ul>';
|
||||
}
|
||||
}
|
||||
if (!$rendered) {
|
||||
$boards = get_terms([
|
||||
'taxonomy' => 'aksrt_forum_board',
|
||||
'hide_empty' => true,
|
||||
'orderby' => 'meta_value_num',
|
||||
'meta_key' => '_aksrt_sort_order',
|
||||
'order' => 'ASC',
|
||||
]);
|
||||
if (!is_wp_error($boards) && !empty($boards)) {
|
||||
echo '<ul class="aksrt-forum-widget__boards">';
|
||||
foreach ($boards as $board) {
|
||||
$icon = get_term_meta($board->term_id, '_aksrt_icon', true);
|
||||
echo '<li class="aksrt-forum-widget__board">';
|
||||
echo '<a href="' . esc_url(get_term_link($board)) . '">';
|
||||
if ($icon) {
|
||||
echo '<i class="' . esc_attr($icon) . '"></i> ';
|
||||
}
|
||||
echo esc_html($board->name);
|
||||
echo '<span class="aksrt-forum-widget__count">' . (int) $board->count . '</span>';
|
||||
echo '</a>';
|
||||
echo '</li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
} else {
|
||||
echo '<p class="aksrt-forum-widget__empty">' . esc_html__('暂无版块', 'aksrt') . '</p>';
|
||||
}
|
||||
echo '</ul>';
|
||||
} else {
|
||||
echo '<p class="aksrt-forum-widget__empty">' . esc_html__('暂无版块', 'aksrt') . '</p>';
|
||||
}
|
||||
|
||||
echo $args['after_widget'];
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
/**
|
||||
* Highlights cards (zibll zib_widget_highlights lite).
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Widgets;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class WidgetHighlights extends \WP_Widget {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_highlights',
|
||||
esc_html__('AKSRT 亮点卡片', 'aksrt'),
|
||||
[
|
||||
'description' => esc_html__('大中小尺寸亮点/卖点卡片栅格。', 'aksrt'),
|
||||
'classname' => 'widget_aksrt_highlights',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function widget($args, $instance): void {
|
||||
$cards = $this->normalize($instance['cards'] ?? []);
|
||||
if (!$cards) {
|
||||
return;
|
||||
}
|
||||
$go_link = !empty($instance['go_link']);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
$large = array_values(array_filter($cards, static fn($c) => ($c['size'] ?? 'medium') === 'large'));
|
||||
$medium = array_values(array_filter($cards, static fn($c) => ($c['size'] ?? 'medium') === 'medium'));
|
||||
$small = array_values(array_filter($cards, static fn($c) => ($c['size'] ?? 'medium') === 'small'));
|
||||
// Fallback: if no size tags, treat all as medium.
|
||||
if (!$large && !$medium && !$small) {
|
||||
$medium = $cards;
|
||||
}
|
||||
|
||||
echo '<div class="aksrt-highlights">';
|
||||
if ($large) {
|
||||
echo '<div class="aksrt-highlights__row is-large">';
|
||||
foreach ($large as $c) {
|
||||
echo $this->card($c, 'large', $go_link); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
if ($medium) {
|
||||
echo '<div class="aksrt-highlights__row is-medium">';
|
||||
foreach ($medium as $c) {
|
||||
echo $this->card($c, 'medium', $go_link); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
if ($small) {
|
||||
echo '<div class="aksrt-highlights__row is-small">';
|
||||
foreach ($small as $c) {
|
||||
echo $this->card($c, 'small', $go_link); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $c
|
||||
*/
|
||||
private function card(array $c, string $size, bool $go_link): string {
|
||||
$color = sanitize_key((string) ($c['color'] ?? 'blue'));
|
||||
if (!in_array($color, ['blue', 'pink', 'purple', 'green', 'orange', 'cyan'], true)) {
|
||||
$color = 'blue';
|
||||
}
|
||||
$url = (string) ($c['url'] ?? '');
|
||||
if ($url !== '' && $go_link && function_exists('aksrt_go_link')) {
|
||||
$url = aksrt_go_link($url, true);
|
||||
}
|
||||
$tag = $url !== '' ? 'a' : 'div';
|
||||
$href = $url !== '' ? ' href="' . esc_url($url) . '"' : '';
|
||||
$img = (string) ($c['image'] ?? '');
|
||||
$html = '<' . $tag . ' class="aksrt-hl-card size-' . esc_attr($size) . ' color-' . esc_attr($color) . '"' . $href . '>';
|
||||
if ($img !== '') {
|
||||
$html .= '<div class="aksrt-hl-card__media"><img src="' . esc_url($img) . '" alt="" loading="lazy" /></div>';
|
||||
}
|
||||
$html .= '<div class="aksrt-hl-card__body">';
|
||||
if (!empty($c['icon'])) {
|
||||
$html .= '<div class="aksrt-hl-card__icon"><i class="' . esc_attr((string) $c['icon']) . '" aria-hidden="true"></i></div>';
|
||||
}
|
||||
if ($c['title'] !== '') {
|
||||
$html .= '<div class="aksrt-hl-card__title">' . esc_html($c['title']) . '</div>';
|
||||
}
|
||||
if ($c['desc'] !== '') {
|
||||
$html .= '<div class="aksrt-hl-card__desc">' . esc_html($c['desc']) . '</div>';
|
||||
}
|
||||
$html .= '</div></' . $tag . '>';
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $raw
|
||||
* @return array<int, array<string,mixed>>
|
||||
*/
|
||||
private function normalize($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'] ?? ''));
|
||||
$desc = sanitize_text_field((string) ($row['desc'] ?? ''));
|
||||
if ($title === '' && $desc === '') {
|
||||
continue;
|
||||
}
|
||||
$size = sanitize_key((string) ($row['size'] ?? 'medium'));
|
||||
if (!in_array($size, ['large', 'medium', 'small'], true)) {
|
||||
$size = 'medium';
|
||||
}
|
||||
$out[] = [
|
||||
'title' => $title,
|
||||
'desc' => $desc,
|
||||
'icon' => sanitize_text_field((string) ($row['icon'] ?? '')),
|
||||
'image' => esc_url_raw(trim((string) ($row['image'] ?? ''))),
|
||||
'url' => esc_url_raw(trim((string) ($row['url'] ?? ''))),
|
||||
'color' => sanitize_key((string) ($row['color'] ?? 'blue')),
|
||||
'size' => $size,
|
||||
];
|
||||
if (count($out) >= 12) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function form($instance): void {
|
||||
$json = is_string($instance['cards'] ?? null)
|
||||
? (string) $instance['cards']
|
||||
: wp_json_encode($this->normalize($instance['cards'] ?? []), 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('卡片 JSON', 'aksrt'); ?>
|
||||
<textarea class="widefat" rows="8" name="<?php echo esc_attr($this->get_field_name('cards')); ?>" placeholder='[{"title":"高速","desc":"…","size":"large","color":"blue","icon":"fas fa-bolt"}]'><?php echo esc_textarea((string) $json); ?></textarea>
|
||||
</label>
|
||||
<small><?php esc_html_e('size: large|medium|small;color: blue/pink/…', 'aksrt'); ?></small>
|
||||
</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'] ?? '')),
|
||||
'cards' => $this->normalize($new_instance['cards'] ?? []),
|
||||
'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
/**
|
||||
* Testimonials / virtual reviews carousel (zibll zib_widget_reviews lite).
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Widgets;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class WidgetReviews extends \WP_Widget {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_reviews',
|
||||
esc_html__('AKSRT 用户评价', 'aksrt'),
|
||||
[
|
||||
'description' => esc_html__('虚拟用户评价卡片横向滚动。', 'aksrt'),
|
||||
'classname' => 'widget_aksrt_reviews',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function widget($args, $instance): void {
|
||||
$reviews = $this->normalize($instance['reviews'] ?? []);
|
||||
if (!$reviews) {
|
||||
return;
|
||||
}
|
||||
$pc = max(1, min(6, (float) ($instance['desktop_per_view'] ?? 3)));
|
||||
$m = max(1, min(3, (float) ($instance['mobile_per_view'] ?? 1.2)));
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
$style = sprintf('--aksrt-rev-pc:%s;--aksrt-rev-m:%s;', rtrim(rtrim(number_format($pc, 2, '.', ''), '0'), '.'), rtrim(rtrim(number_format($m, 2, '.', ''), '0'), '.'));
|
||||
echo '<div class="aksrt-reviews" style="' . esc_attr($style) . '">';
|
||||
echo '<div class="aksrt-reviews__track">';
|
||||
foreach ($reviews as $r) {
|
||||
echo '<div class="aksrt-reviews__slide"><div class="aksrt-reviews__card">';
|
||||
echo '<div class="aksrt-reviews__head">';
|
||||
if ($r['avatar'] !== '') {
|
||||
echo '<img class="aksrt-reviews__avatar" src="' . esc_url($r['avatar']) . '" alt="" loading="lazy" width="44" height="44" />';
|
||||
} else {
|
||||
echo '<span class="aksrt-reviews__avatar is-placeholder"><i class="fa fa-user" aria-hidden="true"></i></span>';
|
||||
}
|
||||
echo '<div class="aksrt-reviews__meta">';
|
||||
if ($r['name'] !== '') {
|
||||
$name_html = esc_html($r['name']);
|
||||
if ($r['url'] !== '') {
|
||||
$name_html = '<a href="' . esc_url($r['url']) . '" target="_blank" rel="noopener noreferrer">' . $name_html . '</a>';
|
||||
}
|
||||
echo '<div class="aksrt-reviews__name">' . $name_html . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
if ($r['role'] !== '') {
|
||||
echo '<div class="aksrt-reviews__role">' . esc_html($r['role']) . '</div>';
|
||||
}
|
||||
echo '</div></div>';
|
||||
echo '<div class="aksrt-reviews__stars" aria-label="' . esc_attr(sprintf(__('%d 星', 'aksrt'), $r['rating'])) . '">';
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
echo '<i class="fa fa-star' . ($i <= $r['rating'] ? ' is-on' : ' is-off') . '" aria-hidden="true"></i>';
|
||||
}
|
||||
echo '</div>';
|
||||
if ($r['content'] !== '') {
|
||||
echo '<p class="aksrt-reviews__content">' . nl2br(esc_html($r['content'])) . '</p>';
|
||||
}
|
||||
echo '<i class="fa fa-quote-right aksrt-reviews__quote" aria-hidden="true"></i>';
|
||||
echo '</div></div>';
|
||||
}
|
||||
echo '</div></div>';
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $raw
|
||||
* @return array<int, array{name:string,role:string,avatar:string,content:string,rating:int,url:string}>
|
||||
*/
|
||||
private function normalize($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'] ?? ''));
|
||||
$content = sanitize_textarea_field((string) ($row['content'] ?? ''));
|
||||
if ($name === '' && $content === '') {
|
||||
continue;
|
||||
}
|
||||
$out[] = [
|
||||
'name' => $name,
|
||||
'role' => sanitize_text_field((string) ($row['role'] ?? '')),
|
||||
'avatar' => esc_url_raw(trim((string) ($row['avatar'] ?? ''))),
|
||||
'content' => $content,
|
||||
'rating' => max(0, min(5, (int) ($row['rating'] ?? 5))),
|
||||
'url' => esc_url_raw(trim((string) ($row['url'] ?? ''))),
|
||||
];
|
||||
if (count($out) >= 20) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function form($instance): void {
|
||||
$json = is_string($instance['reviews'] ?? null)
|
||||
? (string) $instance['reviews']
|
||||
: wp_json_encode($this->normalize($instance['reviews'] ?? []), 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('评价 JSON', 'aksrt'); ?>
|
||||
<textarea class="widefat" rows="8" name="<?php echo esc_attr($this->get_field_name('reviews')); ?>" placeholder='[{"name":"张三","role":"站长","rating":5,"content":"很好用","avatar":""}]'><?php echo esc_textarea((string) $json); ?></textarea>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('PC 每屏', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="number" min="1" max="6" step="0.1" name="<?php echo esc_attr($this->get_field_name('desktop_per_view')); ?>" value="<?php echo esc_attr((string) ($instance['desktop_per_view'] ?? '3')); ?>">
|
||||
</label>
|
||||
<label><?php esc_html_e('移动每屏', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="number" min="1" max="3" step="0.1" name="<?php echo esc_attr($this->get_field_name('mobile_per_view')); ?>" value="<?php echo esc_attr((string) ($instance['mobile_per_view'] ?? '1.2')); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function update($new_instance, $old_instance): array {
|
||||
return [
|
||||
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
|
||||
'reviews' => $this->normalize($new_instance['reviews'] ?? []),
|
||||
'desktop_per_view' => max(1, min(6, (float) ($new_instance['desktop_per_view'] ?? 3))),
|
||||
'mobile_per_view' => max(1, min(3, (float) ($new_instance['mobile_per_view'] ?? 1.2))),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user