diff --git a/assets/css/main.css b/assets/css/main.css index 77578970..95fd638b 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -3932,11 +3932,11 @@ body:has(.modal.in) .float-btn, } /**------小工具:复合信息卡片模块--------**/ -.aksrt-combo-info-card { +.zib-combo-info-card { --zci-radius: 12px; --zci-band-h: 80px; } -.aksrt-combo-info-card .zci-card { +.zib-combo-info-card .zci-card { position: relative; height: 100%; min-height: 180px; @@ -3946,11 +3946,11 @@ body:has(.modal.in) .float-btn, transition: transform 0.25s ease, box-shadow 0.25s ease; box-shadow: 0 1px 3px var(--main-shadow); } -.aksrt-combo-info-card .zci-card:hover { +.zib-combo-info-card .zci-card:hover { transform: translateY(-3px); box-shadow: 0 12px 32px var(--main-shadow); } -.aksrt-combo-info-card .zci-card::before { +.zib-combo-info-card .zci-card::before { content: ''; position: absolute; left: 0; @@ -3961,33 +3961,33 @@ body:has(.modal.in) .float-btn, transition: opacity 0.3s ease; pointer-events: none; } -.aksrt-combo-info-card.zci-has-band .zci-card::before { +.zib-combo-info-card.zci-has-band .zci-card::before { opacity: 1; } /* 色带配色(仅保留关键的渐变,无法用主题变量简写) */ -.aksrt-combo-info-card .zci-card.zci-band-blue::before { +.zib-combo-info-card .zci-card.zci-band-blue::before { background: linear-gradient(170deg, rgba(114, 160, 255, 0.3), rgba(196, 174, 255, 0.2) 15%, transparent 60%); } -.aksrt-combo-info-card .zci-card.zci-band-cyan::before { +.zib-combo-info-card .zci-card.zci-band-cyan::before { background: linear-gradient(170deg, rgba(82, 196, 214, 0.3), rgba(130, 200, 255, 0.18) 15%, transparent 60%); } -.aksrt-combo-info-card .zci-card.zci-band-green::before { +.zib-combo-info-card .zci-card.zci-band-green::before { background: linear-gradient(170deg, rgba(92, 209, 158, 0.3), rgba(180, 230, 170, 0.2) 15%, transparent 60%); } -.aksrt-combo-info-card .zci-card.zci-band-yellow::before { +.zib-combo-info-card .zci-card.zci-band-yellow::before { background: linear-gradient(170deg, rgba(255, 190, 102, 0.3), rgba(255, 220, 132, 0.22) 15%, transparent 60%); } -.aksrt-combo-info-card .zci-card.zci-band-pink::before { +.zib-combo-info-card .zci-card.zci-band-pink::before { background: linear-gradient(170deg, rgba(255, 146, 181, 0.3), rgba(255, 190, 220, 0.2) 15%, transparent 60%); } -.aksrt-combo-info-card .zci-card.zci-band-purple::before { +.zib-combo-info-card .zci-card.zci-band-purple::before { background: linear-gradient(170deg, rgba(170, 140, 255, 0.36), rgba(220, 180, 255, 0.22) 15%, transparent 60%); } -.aksrt-combo-info-card .zci-card.zci-band-gray::before { +.zib-combo-info-card .zci-card.zci-band-gray::before { background: linear-gradient(170deg, rgba(140, 150, 170, 0.22), rgba(190, 195, 210, 0.14) 15%, transparent 60%); } /* 内容区:只控 padding 和层级,flex 布局由 HTML class 完成 */ -.aksrt-combo-info-card .zci-body { +.zib-combo-info-card .zci-body { position: relative; z-index: 1; padding: 20px; @@ -4001,7 +4001,7 @@ body:has(.modal.in) .float-btn, } /* 图标盒子:尺寸与圆角,背景沿用卡片主色自动适配暗色 */ -.aksrt-combo-info-card .zci-icon { +.zib-combo-info-card .zci-icon { width: 44px; height: 44px; border-radius: 8px; @@ -4009,65 +4009,65 @@ body:has(.modal.in) .float-btn, font-size: 42px; } -.aksrt-combo-info-card .zci-icon img { +.zib-combo-info-card .zci-icon img { width: 100%; height: 100%; object-fit: cover; display: block; } /* 标题字号(font-bold / text-ellipsis-2 / mb10 已走工具类) */ -.aksrt-combo-info-card .zci-title { +.zib-combo-info-card .zci-title { font-size: 16px; } /* CTA 行动链接:仅保留颜色 + hover 行为 + 箭头动效 */ -.aksrt-combo-info-card .zci-cta { +.zib-combo-info-card .zci-cta { margin-top: 20px; color: var(--this-color, --key-color); font-weight: 500; transition: 0.2s; } -.aksrt-combo-info-card .zci-cta:hover { +.zib-combo-info-card .zci-cta:hover { gap: 10px; color: var(--focus-color); text-decoration: none; } -.aksrt-combo-info-card .zci-cta .zci-arrow { +.zib-combo-info-card .zci-cta .zci-arrow { transform: rotate(-45deg); font-size: 11px; transition: transform 0.25s ease; } -.aksrt-combo-info-card .zci-cta:hover .zci-arrow { +.zib-combo-info-card .zci-cta:hover .zci-arrow { transform: rotate(-45deg) translateX(2px); } @media (max-width: 767px) { - .aksrt-combo-info-card .zci-card { + .zib-combo-info-card .zci-card { min-height: 160px; } - .aksrt-combo-info-card .zci-body { + .zib-combo-info-card .zci-body { padding: 14px; } .col-xs-12 .zci-card .zci-body { padding: 20px; } - .aksrt-combo-info-card .zci-title { + .zib-combo-info-card .zci-title { font-size: 15px; } } -.sidebar .aksrt-combo-info-card > * { +.sidebar .zib-combo-info-card > * { width: 100% !important; } -.sidebar .aksrt-combo-info-card .zci-card { +.sidebar .zib-combo-info-card .zci-card { min-height: 160px; } -.sidebar .aksrt-combo-info-card .zci-body { +.sidebar .zib-combo-info-card .zci-body { padding: 14px; } .sidebar .col-xs-12 .zci-card .zci-body { padding: 20px; } -.sidebar .aksrt-combo-info-card .zci-title { +.sidebar .zib-combo-info-card .zci-title { font-size: 15px; } @@ -4397,7 +4397,7 @@ body:has(.modal.in) .float-btn, } /**------小工具:横向滚动合作伙伴模块--------**/ -.aksrt-partners-scroll { +.zib-partners-scroll { --zps-card-w: 220px; --zps-card-h: 90px; --zps-card-radius: 10px; @@ -4407,42 +4407,42 @@ body:has(.modal.in) .float-btn, width: 100%; overflow: hidden; } -.aksrt-partners-scroll .zps-row { +.zib-partners-scroll .zps-row { position: relative; overflow: hidden; margin-bottom: var(--zps-gap); } -.aksrt-partners-scroll .zps-row:last-child { +.zib-partners-scroll .zps-row:last-child { margin-bottom: 0; } -.aksrt-partners-scroll .zps-track { +.zib-partners-scroll .zps-track { display: flex; width: max-content; will-change: transform; animation: zps-scroll-left var(--zps-duration, 30s) linear infinite; animation-play-state: paused; } -.aksrt-partners-scroll .zps-dir-right .zps-track { +.zib-partners-scroll .zps-dir-right .zps-track { animation-name: zps-scroll-right; } -.aksrt-partners-scroll .zps-track[data-zps-ready] { +.zib-partners-scroll .zps-track[data-zps-ready] { animation-play-state: running; } -.aksrt-partners-scroll .zps-group { +.zib-partners-scroll .zps-group { gap: var(--zps-gap); padding-right: var(--zps-gap); } -.aksrt-partners-scroll.zps-pause-hover .zps-row:hover .zps-track { +.zib-partners-scroll.zps-pause-hover .zps-row:hover .zps-track { animation-play-state: paused; } -.aksrt-partners-scroll .zps-item { +.zib-partners-scroll .zps-item { width: var(--zps-card-w); height: var(--zps-card-h); overflow: hidden; padding: 8px 10px; color: inherit; } -.aksrt-partners-scroll .zps-img { +.zib-partners-scroll .zps-img { max-width: 100%; max-height: 100%; width: auto; @@ -4450,34 +4450,34 @@ body:has(.modal.in) .float-btn, object-fit: contain; display: block; } -.aksrt-partners-scroll.zps-card-bg .zps-item { +.zib-partners-scroll.zps-card-bg .zps-item { background: var(--main-bg-color); border-radius: var(--zps-card-radius); } -.aksrt-panel .aksrt-partners-scroll.zps-card-bg .zps-item { +.aksrt-panel .zib-partners-scroll.zps-card-bg .zps-item { background: var(--muted-border-color); } /* 图文卡片模式 */ -.aksrt-partners-scroll.zps-style-card .zps-card-icon { +.zib-partners-scroll.zps-style-card .zps-card-icon { width: calc(var(--zps-card-h) - 30px); height: calc(var(--zps-card-h) - 30px); min-width: 40px; min-height: 40px; overflow: hidden; } -.aksrt-partners-scroll.zps-style-card .zps-img { +.zib-partners-scroll.zps-style-card .zps-img { object-fit: cover; border-radius: calc(var(--zps-card-radius) - 4px); width: 100%; } /* 纯 Logo 模式:高度固定、宽度自适应,图片保持原始比例 */ -.aksrt-partners-scroll.zps-style-logo .zps-item { +.zib-partners-scroll.zps-style-logo .zps-item { width: auto; min-width: 0; justify-content: center; padding: 0 20px; } -.aksrt-partners-scroll.zps-style-logo .zps-img { +.zib-partners-scroll.zps-style-logo .zps-img { height: calc(var(--zps-card-h) - 24px); max-height: calc(var(--zps-card-h) - 24px); width: auto; @@ -4485,28 +4485,28 @@ body:has(.modal.in) .float-btn, filter: grayscale(40%); transition: filter 0.25s ease, transform 0.25s ease; } -.aksrt-partners-scroll.zps-style-logo .zps-item:hover .zps-img { +.zib-partners-scroll.zps-style-logo .zps-item:hover .zps-img { filter: grayscale(0); transform: scale(1.04); } /* 精简胶囊模式 */ -.aksrt-partners-scroll.zps-style-chip .zps-item { +.zib-partners-scroll.zps-style-chip .zps-item { min-height: 40px; width: auto; height: auto; padding: 8px 14px; } -.aksrt-partners-scroll.zps-card-bg.zps-style-chip .zps-item { +.zib-partners-scroll.zps-card-bg.zps-style-chip .zps-item { padding: 8px 16px 8px 10px; } -.aksrt-partners-scroll.zps-style-chip .zps-chip-icon .zps-img { +.zib-partners-scroll.zps-style-chip .zps-chip-icon .zps-img { height: 22px; border-radius: 4px; } -.aksrt-partners-scroll.zps-card-bg.zps-style-chip .zps-chip-icon .zps-img { +.zib-partners-scroll.zps-card-bg.zps-style-chip .zps-chip-icon .zps-img { border-radius: calc(var(--zps-card-radius) - 4px); } -.aksrt-partners-scroll.zps-style-chip .zps-chip-title { +.zib-partners-scroll.zps-style-chip .zps-chip-title { max-width: 180px; } .zps-end-fade { @@ -4551,11 +4551,11 @@ body:has(.modal.in) .float-btn, } } @media (max-width: 767px) { - .aksrt-partners-scroll { + .zib-partners-scroll { --zps-card-w: 160px; --zps-card-h: 70px; } - .aksrt-partners-scroll.zps-style-logo .zps-item { + .zib-partners-scroll.zps-style-logo .zps-item { padding: 0 12px; } .zps-end-fade::after { @@ -4563,7 +4563,7 @@ body:has(.modal.in) .float-btn, } } @media (prefers-reduced-motion: reduce) { - .aksrt-partners-scroll .zps-track { + .zib-partners-scroll .zps-track { animation: none !important; } } diff --git a/assets/js/runtime/main.js b/assets/js/runtime/main.js index b6a0196f..7a67c59b 100644 --- a/assets/js/runtime/main.js +++ b/assets/js/runtime/main.js @@ -4032,7 +4032,7 @@ function partners_scroll_init() { } function initAll() { - document.querySelectorAll('.aksrt-partners-scroll').forEach(init); + document.querySelectorAll('.zib-partners-scroll').forEach(init); } initAll(); diff --git a/assets/js/section-navs.js b/assets/js/section-navs.js index 45c788e8..c02ca5c9 100644 --- a/assets/js/section-navs.js +++ b/assets/js/section-navs.js @@ -9,7 +9,7 @@ 'use strict'; var HEADING_SEL = 'h1, h2, h3, h4'; - var MIN_HEADINGS = 2; + var MIN_HEADINGS = 3; var NAVBOX = '.posts-nav-box'; var NAV_CLASS = 'posts-nav-lists'; @@ -19,7 +19,7 @@ function ensureId(el, index) { if (!el.id) { - el.id = 'section-nav-' + index; + el.id = 'wznav_' + index; } return el.id; } @@ -54,7 +54,7 @@ box.insertAdjacentHTML( 'beforeend', titleHtml + - '
'; } private static function render_quote(array $i): string { @@ -112,44 +117,305 @@ final class GeneralRenderer { } private static function render_media_cards(array $i): string { - return self::media_grid((array) $i['cards']); - } - - private static function media_grid(array $items, string $class = ''): string { - $html = '
'; - foreach ($items as $item) { - if (is_array($item)) { - $html .= RendererHelpers::card_media($item); + $pc_row = max(1, (int) ($i['pc_row'] ?? 4)); + $m_row = max(1, (int) ($i['m_row'] ?? 2)); + $row_class = 'col-sm-' . (int) (12 / $pc_row); + $row_class .= $m_row > 1 ? ' col-xs-' . (int) (12 / $m_row) : ''; + $covers = (array) ($i['covers'] ?? []); + $is_row = count($covers) > 1; + $is_mobile = function_exists('wp_is_mobile') && wp_is_mobile(); + $style = ''; + $font_pc = (int) ($i['font_size_pc'] ?? 18); + $font_m = (int) ($i['font_size_m'] ?? 14); + if ($font_pc > 0 && $font_pc !== 14) $style .= '--font-size:' . $font_pc . 'px;'; + if ($font_m > 0 && $font_m !== 14) $style .= '--font-size-sm:' . $font_m . 'px;'; + if (!empty($i['font_bold'])) $style .= '--font-weight:bold;--font-weight-sm:bold;'; + $font_color = (string) ($i['font_color'] ?? ''); + if ($font_color !== '') $style .= '--color:' . esc_attr($font_color) . ';--color-sm:' . esc_attr($font_color) . ';'; + $animation = (string) ($i['obs_animation'] ?? ''); + $html = ''; + foreach ($covers as $cover) { + if (!is_array($cover)) { + continue; } + $hide = (string) ($cover['hide'] ?? ''); + if (($is_mobile && $hide === 'm') || (!$is_mobile && $hide === 'pc')) { + continue; + } + $title = (string) ($cover['title'] ?? ''); + $link = RendererHelpers::link((array) ($cover['link'] ?? [])); + $card = aksrt_graphic_card([ + 'class' => 'noshadow mb10', + 'img' => (string) ($cover['image'] ?? ''), + 'video' => (string) ($cover['video'] ?? ''), + 'alt' => wp_strip_all_tags($title), + 'link' => ['url' => $link['url'], 'target' => $link['target']], + 'more' => trim($title) !== '' ? '
' . wp_kses_post($title) . '
' : '', + 'height_scale' => (int) ($i['height_scale'] ?? 30), + 'mask_opacity' => (int) ($i['mask_opacity'] ?? 10), + ]); + if ($card === '') { + continue; + } + $attr = ''; + $col_class = ''; + if ($animation !== '') { + $col_class .= ' obs-animate ani-' . sanitize_html_class($animation); + $attr .= ' data-animation="' . esc_attr($animation) . '"'; + if (!empty($i['animation_repeat'])) { + $attr .= ' data-animation-repeat="true"'; + } + } + if ($is_row) { + $col_class .= ' ' . $row_class; + } + $html .= '
' . $card . '
'; } - return $html . '
'; + if ($html === '') { + return ''; + } + return '
' . ($is_row ? '
' : '') . $html . ($is_row ? '
' : '') . '
'; } private static function render_partners(array $i): string { - $items = ''; - foreach ((array) $i['items'] as $item) { - if (is_array($item)) { - $items .= RendererHelpers::card_media($item, 'aksrt-partner'); + $defaults = [ + 'rows' => 1, 'm_rows' => 1, 'style_type' => 'card', 'card_width' => 220, 'card_height' => 90, + 'speed' => 3, 'direction' => 'alternate', 'pause_hover' => true, 'show_widget_bg' => true, + 'card_bg' => true, 'card_radius' => 10, 'partners' => [], 'link_type' => 'link', + 'link_orderby' => 'name', 'link_order' => 'ASC', 'link_cats' => [], 'link_limit' => 50, + 'go_link' => true, 'end_fade' => true, + ]; + $i = array_merge($defaults, $i); + $is_mobile = function_exists('wp_is_mobile') && wp_is_mobile(); + $rows = max(1, (int) ($is_mobile ? $i['m_rows'] : $i['rows'])); + + if ((string) $i['link_type'] === 'link') { + $args = [ + 'orderby' => (string) ($i['link_orderby'] ?: 'name'), + 'order' => (string) ($i['page_links_order'] ?: 'ASC'), + 'limit' => (int) $i['link_limit'] > 0 ? (int) $i['link_limit'] : -1, + ]; + $cats = array_values(array_filter(array_map('absint', (array) $i['link_cats']))); + if ($cats) { + $args['category'] = $cats; } + $partners = []; + foreach ((array) get_bookmarks($args) as $bookmark) { + $partners[] = [ + 'title' => (string) $bookmark->link_name, + 'desc' => (string) $bookmark->link_description, + 'image' => (string) $bookmark->link_image, + 'link' => ['url' => (string) $bookmark->link_url, 'target' => (string) $bookmark->link_target], + ]; + } + $i['partners'] = $partners; } - return '
' . $items . $items . '
'; + + $partners = array_values(array_filter((array) $i['partners'], 'is_array')); + if (!$partners) { + return function_exists('is_super_admin') && is_super_admin() + ? '
' . esc_html__('[合作伙伴]横向滚动合作伙伴模块:', 'aksrt') . '' . esc_html__('当前配置下没有可显示的内容,请正确配置或移出该模块', 'aksrt') . '
' + : ''; + } + + $rows_data = array_fill(0, $rows, []); + foreach ($partners as $index => $partner) { + $rows_data[$index % $rows][] = $partner; + } + $rows_data = array_values(array_filter($rows_data, static fn(array $row): bool => $row !== [])); + + $style_vars = [ + '--zps-card-w:' . (int) $i['card_width'] . 'px', + '--zps-card-h:' . (int) $i['card_height'] . 'px', + '--zps-card-radius:' . (int) $i['card_radius'] . 'px', + '--zps-speed:' . max(0.1, (float) $i['speed']), + ]; + $style_attr = ' style="' . esc_attr(implode(';', $style_vars)) . '"'; + + $wrap_class = 'zib-partners-scroll'; + $wrap_class .= ' zps-style-' . sanitize_html_class((string) $i['style_type']); + $wrap_class .= !empty($i['pause_hover']) ? ' zps-pause-hover' : ''; + $wrap_class .= !empty($i['card_bg']) ? ' zps-card-bg' : ''; + $wrap_class .= !empty($i['end_fade']) ? ' zps-end-fade' : ''; + + $html = ''; + foreach ($rows_data as $row_index => $row_partners) { + $direction = (string) $i['direction']; + if ($direction === 'left' || $direction === 'right') { + $dir = $direction; + } else { + $dir = $row_index % 2 === 0 ? 'left' : 'right'; + } + $html .= '
'; + foreach ($row_partners as $partner) { + $html .= self::partners_card($partner, (string) $i['style_type'], !empty($i['go_link'])); + } + $html .= '
'; + } + if (trim($html) === '') { + return ''; + } + return '
' . $html . '
'; + } + + private static function partners_card(array $partner, string $style_type, bool $go_link): string { + $partner = wp_parse_args($partner, ['image' => '', 'link' => [], 'title' => '', 'desc' => '']); + $title = trim((string) $partner['title']); + $desc = trim((string) $partner['desc']); + $image = esc_url((string) $partner['image']); + if ($image === '' && $title === '') { + return ''; + } + $img_tag = $image !== '' ? '' . esc_attr($title !== '' ? $title : __('合作伙伴', 'aksrt')) . '' : ''; + + $inner = ''; + if ($style_type === 'logo') { + if ($image === '') { + return ''; + } + $inner .= $img_tag; + } elseif ($style_type === 'chip') { + if ($img_tag !== '') { + $inner .= '' . $img_tag . ''; + } + $inner .= '' . esc_html($title) . ''; + } else { + if ($img_tag !== '') { + $inner .= '
' . $img_tag . '
'; + } + $inner .= '
'; + if ($title !== '') { + $inner .= '
' . esc_html($title) . '
'; + } + if ($desc !== '') { + $inner .= '
' . esc_html($desc) . '
'; + } + $inner .= '
'; + } + + $link = RendererHelpers::link((array) ($partner['link'] ?? [])); + $url = $link['url'] !== '' ? $link['url'] : 'javascript:void(0)'; + if ($go_link && $url !== 'javascript:void(0)' && function_exists('aksrt_go_link')) { + $url = esc_url(aksrt_go_link($link['url'], true)); + } + $target = $link['target'] === '_blank' ? ' target="_blank" rel="noopener noreferrer"' : ''; + $base_class = 'zps-item flex ac shrink0'; + if ($url !== 'javascript:void(0)') { + return '' . $inner . ''; + } + return '
' . $inner . '
'; } private static function render_combo_info(array $i): string { - $html = '
'; - foreach ((array) $i['items'] as $item) { - if (!is_array($item)) { + $i = array_merge([ + 'pc_row' => 4, 'm_row' => 1, 'card_radius' => 12, 'top_band' => true, 'top_band_height' => 70, + 'show_widget_bg' => false, 'cta_text' => '', 'cards' => [], 'obs_animation' => '', 'animation_repeat' => false, + 'pc_gap' => 10, 'm_gap' => 5, 'go_link' => true, 'cta_color' => '', + ], $i); + $is_mobile = function_exists('wp_is_mobile') && wp_is_mobile(); + $pc_row = max(1, (int) $i['pc_row']); + $m_row = max(1, (int) $i['m_row']); + $col_class = 'col-sm-' . max(1, (int) (12 / $pc_row)) . ' col-xs-' . max(1, (int) (12 / $m_row)); + + $style_vars = '--zci-radius:' . (int) $i['card_radius'] . 'px;--zci-band-h:' . (int) $i['top_band_height'] . 'px;'; + $wrap_class = 'zib-combo-info-card row gutters-' . (int) ($is_mobile ? $i['m_gap'] : $i['pc_gap']); + $wrap_class .= !empty($i['top_band']) ? ' zci-has-band' : ''; + + $default_cta = (string) ($i['cta_text'] !== '' ? $i['cta_text'] : __('立即体验', 'aksrt')); + $default_cta_color = (string) ($i['cta_color'] ?? ''); + $auto_palette = ['blue', 'cyan', 'green', 'yellow', 'pink', 'purple']; + $auto_idx = 0; + + $animation = (string) ($i['obs_animation'] ?? ''); + $html = ''; + foreach ((array) $i['cards'] as $key => $card) { + if (!is_array($card)) { continue; } - $tags = ''; - foreach (preg_split('/\s*[,,]\s*/', (string) ($item['tags'] ?? '')) ?: [] as $tag) { - if ($tag !== '') { - $tags .= '' . esc_html($tag) . ''; + $card_html = self::combo_card_item($card, $default_cta, $default_cta_color, $auto_palette, $auto_idx, !empty($i['go_link'])); + if ($card_html === '') { + continue; + } + $attr = ''; + $item_class = $col_class; + if ($animation !== '') { + $item_class .= ' obs-animate ani-' . sanitize_html_class($animation); + $attr .= ' data-animation="' . esc_attr($animation) . '"'; + $attr .= ' style=" --delay: ' . esc_attr((string) ($key * 0.15)) . 's; "'; + if (!empty($i['animation_repeat'])) { + $attr .= ' data-animation-repeat="true"'; } } - $html .= '
' . RendererHelpers::card_media($item) . ($tags ? '
' . $tags . '
' : '') . '
'; + $html .= '
' . $card_html . '
'; } - return $html . '
'; + if ($html === '') { + return ''; + } + return '
' . $html . '
'; + } + + private static function combo_card_item(array $card, string $default_cta, string $default_cta_color, array $auto_palette, int &$auto_idx, bool $go_link): string { + $card = wp_parse_args($card, [ + 'icon_image' => '', 'icon' => '', 'title' => '', 'tags' => [], 'desc' => '', + 'link' => [], 'cta_text' => '', 'cta_color' => '', 'band_color' => 'auto', + ]); + $title = trim(wp_strip_all_tags((string) $card['title'])); + $desc = trim((string) $card['desc']); + if ($title === '' && $desc === '') { + return ''; + } + + $band = (string) $card['band_color']; + if ($band === '' || $band === 'auto') { + $band = $auto_palette[$auto_idx % count($auto_palette)]; + $auto_idx++; + } + + $icon_html = ''; + $icon_image = esc_url((string) $card['icon_image']); + if ($icon_image !== '') { + $icon_html = '
' . esc_attr($title !== '' ? $title : __('图标', 'aksrt')) . '
'; + } elseif (!empty($card['icon'])) { + $icon_html = '
' . self::icon_markup((string) $card['icon']) . '
'; + } + + $tags_html = ''; + $tags = (array) ($card['tags'] ?? []); + if ($tags) { + $tags_html .= '
'; + foreach ($tags as $tag) { + if (!is_array($tag)) { + continue; + } + $tag_text = trim((string) ($tag['text'] ?? '')); + if ($tag_text === '') { + continue; + } + $tags_html .= '' . esc_html($tag_text) . ''; + } + $tags_html .= '
'; + } + + $desc_html = $desc !== '' ? '
' . wp_kses_post($desc) . '
' : ''; + + $cta_text = trim((string) ($card['cta_text'] ?? '')) !== '' ? trim((string) $card['cta_text']) : $default_cta; + $cta_color = trim((string) ($card['cta_color'] ?? '')) !== '' ? trim((string) $card['cta_color']) : $default_cta_color; + $link = RendererHelpers::link((array) ($card['link'] ?? [])); + $url = $link['url']; + if ($url !== '' && $go_link && function_exists('aksrt_go_link')) { + $url = esc_url(aksrt_go_link($link['url'], true)); + } + $target = $link['target'] === '_blank' ? ' target="_blank" rel="noopener noreferrer"' : ''; + $cta_html = $cta_text !== '' && $url !== '' + ? '' . esc_html($cta_text) . '' + : ''; + + $inner = '
' . $icon_html; + if ($title !== '') { + $inner .= '
' . esc_html($title) . '
'; + } + $inner .= $tags_html . $desc_html . $cta_html . '
'; + return '
' . $inner . '
'; } private static function render_text_title(array $i): string { @@ -216,15 +482,11 @@ final class GeneralRenderer { } private static function render_recent_comments(array $i): string { - $comments = get_comments(['status' => 'approve', 'number' => (int) $i['count'], 'user__not_in' => RendererHelpers::ids((string) $i['exclude_users']), 'post__not_in' => RendererHelpers::ids((string) $i['exclude_posts'])]); - if (!$comments) { + $list = function_exists('aksrt_widget_comments_list') ? aksrt_widget_comments_list($i) : ''; + if ($list === '') { return RendererHelpers::empty_state(__('暂无评论', 'aksrt')); } - $html = ''; + return '
' . $list . '
'; } private static function render_links(array $i): string { diff --git a/src/WidgetSystem/Renderers/SliderRenderer.php b/src/WidgetSystem/Renderers/SliderRenderer.php index 14b30875..54d437a6 100644 --- a/src/WidgetSystem/Renderers/SliderRenderer.php +++ b/src/WidgetSystem/Renderers/SliderRenderer.php @@ -51,44 +51,59 @@ final class SliderRenderer { $option = (array) ($instance['option'] ?? []); $option['direction'] = 'horizontal'; $option['scale_height'] = true; $option['scale'] = (int) ($instance['slide_scale'] ?? 50); $slide_list = (array) ($instance['slides'] ?? []); $slider = !empty($slide_list[0]) ? self::slider($slide_list, $option) : ''; - $covers = ''; $is_mobile = function_exists('wp_is_mobile') && wp_is_mobile(); $cover_list = array_values((array) ($instance['covers'] ?? [])); $is_row = count($cover_list) > 1; - $pc_row = max(1, min(3, (int) ($instance['pc_row'] ?? 2))); - $m_row = max(1, min(3, (int) ($instance['m_row'] ?? 2))); + $layout_type = (string) ($instance['m_layout_type'] ?? 'vertical'); + $pc_row = max(1, min(4, (int) ($instance['pc_row'] ?? 2))); + $m_row = $layout_type === 'default' ? $pc_row : max(1, min(6, (int) ($instance['m_row'] ?? 2))); $row_class = 'col-sm-' . (int) (12 / $pc_row) . ($m_row > 1 ? ' col-xs-' . (int) (12 / $m_row) : ''); + $m_cover_ratio = max(0, (int) ($instance['m_cover_ratio'] ?? 45)); + $mask = max(0, min(90, (int) ($instance['mask_opacity'] ?? 0))) / 100; + $cover_style_vars = '--font-size:' . (int) ($instance['font_size_pc'] ?? 16) . 'px;--font-size-sm:' . (int) ($instance['font_size_m'] ?? 14) . 'px;'; + if (!empty($instance['font_bold'])) $cover_style_vars .= '--font-weight:bold;--font-weight-sm:bold;'; + $font_color = (string) ($instance['font_color'] ?? ''); + if ($font_color !== '') $cover_style_vars .= '--color:' . $font_color . ';--color-sm:' . $font_color . ';'; + $covers = ''; foreach ($cover_list as $cover) { if (!is_array($cover)) continue; $hide = (string) ($cover['hide'] ?? ''); if (($is_mobile && $hide === 'm') || (!$is_mobile && $hide === 'pc')) continue; $image = esc_url((string) ($cover['image'] ?? '')); $video = esc_url((string) ($cover['video'] ?? '')); - if ($image === '' && ($video === '' || $is_mobile)) continue; - $media = $video !== '' && !$is_mobile ? '' : '' . esc_attr(wp_strip_all_tags((string) ($cover['title'] ?? ''))) . ''; - $mask = max(0, min(90, (int) ($instance['mask_opacity'] ?? 0))) / 100; - $card = $media . ($mask ? '' : '') . (!empty($cover['title']) ? '
' . wp_kses_post((string) $cover['title']) . '
' : ''); + $title = (string) ($cover['title'] ?? ''); $link = RendererHelpers::link((array) ($cover['link'] ?? [])); - $card = $link['url'] !== '' ? '' . $card . '' : '' . $card . ''; - $covers .= '
' . $card . '
'; + $card = aksrt_graphic_card([ + 'class' => 'noshadow mb10', + 'img' => $image, + 'video' => $video, + 'alt' => wp_strip_all_tags($title), + 'link' => ['url' => $link['url'], 'target' => $link['target']], + 'more' => trim($title) !== '' ? '
' . wp_kses_post($title) . '
' : '', + 'height_scale' => 0, + 'mask_opacity' => (int) ($instance['mask_opacity'] ?? 0), + ]); + if ($card === '') continue; + $covers .= '
' . $card . '
'; } if ($covers !== '') { - $cover_style = '--font-size:' . (int) ($instance['font_size_pc'] ?? 16) . 'px;--font-size-sm:' . (int) ($instance['font_size_m'] ?? 14) . 'px;'; - if (!empty($instance['font_bold'])) $cover_style .= '--font-weight:bold;--font-weight-sm:bold;'; - if (!empty($instance['font_color'])) $cover_style .= '--color:' . $instance['font_color'] . ';--color-sm:' . $instance['font_color'] . ';'; - $covers = '
' . ($is_row ? '
' : '') . $covers . ($is_row ? '
' : '') . '
'; + $covers = '
' . ($is_row ? '
' : '') . $covers . ($is_row ? '
' : '') . '
'; } if ($slider === '' && $covers === '') return RendererHelpers::empty_state(__('请配置幻灯片或卡片', 'aksrt')); $layout = max(0, min(100, (int) ($instance['layout_ratio'] ?? 60))); $slide_scale = max(0, (int) ($instance['slide_scale'] ?? 50)); - $rows = max(1, (int) ($instance['pc_row'] ?? 2)); + $rows = $pc_row; $columns = max(1, (int) ($instance['pc_col'] ?? 2)); $base = $layout < 100 ? (($layout * $slide_scale) / (100 - $layout)) : 0; - $cover_ratio = (($base - (($columns - 1) * 3)) / $columns) * $rows; - $style = '--layout-ratio:' . $layout . '%;--cover-ratio:' . $cover_ratio . '%;--m-cover-ratio:' . (int) ($instance['m_cover_ratio'] ?? 45) . '%;--cover-cols:' . $rows . ';'; - $class = 'flex widget-slider-graphic m-' . sanitize_html_class((string) ($instance['m_layout_type'] ?? 'vertical')); + $cover_ratio = $columns > 0 ? ((($base - (($columns - 1) * 3)) / $columns) * $rows) : 0; + $style = '--layout-ratio:' . $layout . '%;--cover-ratio:' . $cover_ratio . '%;--m-cover-ratio:' . $m_cover_ratio . '%;'; + $animation = ''; + if (!empty($instance['obs_animation'])) { + $animation = ' obs-animate ani-' . sanitize_html_class((string) $instance['obs_animation']) . (!empty($instance['animation_repeat']) ? ' obs-animate-repeat' : ''); + } + $class = 'flex widget-slider-graphic m-' . sanitize_html_class($layout_type); $html = '
'; - if ($slider !== '') $html .= '
' . $slider . '
'; + if ($slider !== '') $html .= '
' . $slider . '
'; if ($covers !== '') $html .= '
' . $covers . '
'; return $html . '
'; } diff --git a/src/WidgetSystem/WidgetCatalog.php b/src/WidgetSystem/WidgetCatalog.php index 7f4dc0e2..9677245a 100644 --- a/src/WidgetSystem/WidgetCatalog.php +++ b/src/WidgetSystem/WidgetCatalog.php @@ -30,13 +30,13 @@ final class WidgetCatalog { ['notice', '上下滚动公告', '自动滚动的公告信息列表', 'wide'], ], 'content' => [ - ['term_cards', '分类图文卡片', '将分类或标签显示为图文卡片', 'wide'], - ['term_aggregation', '分类与标签聚合卡片', '聚合分类、标签与文章内容', 'wide'], - ['hot_posts', '热榜文章', '按浏览、评论、收藏等维度排行', 'sidebar'], + ['term_cards', '分类图文卡片', '将分类显示为图文卡片', 'wide'], + ['term_aggregation', '分类聚合卡片', '将分类显示为图文聚合卡片,并附带最新文章列表', 'wide'], + ['hot_posts', '热榜文章', '按浏览、点赞、评论、收藏等维度生成榜单', 'sidebar'], ['post_pay', '付费购买', '显示当前文章的知识付费购买模块', 'sidebar'], - ['posts', '文章列表', '多条件筛选和多样式文章列表', 'wide'], + ['posts', '文章列表', '通过各种筛选、排序显示文章列表,支持多种显示模式', 'wide'], ['tab_posts', '多栏目文章(新)', '通过多个栏目切换文章列表', 'wide'], - ['oneline_posts', '横向滚动文章', '单行横向滚动文章列表', 'wide'], + ['oneline_posts', '横向滚动文章', '显示文章列表,只显示一行,左右滚动', 'wide'], ], 'knowledge_product' => [ ['pricing_plans', '知识产品方案卡片', '课程与会员方案价格、权益和购买入口', 'wide'], @@ -49,7 +49,7 @@ final class WidgetCatalog { ], 'slider' => [ ['slider', '幻灯片(新)', '支持图片、视频、文字和视差图层', 'wide'], - ['slider_media_combo', '幻灯片加图文视频卡片', '幻灯片与图文视频卡片组合布局', 'wide'], + ['slider_media_combo', '幻灯片加图文视频卡片', '左侧幻灯片与右侧图文视频封面卡片横向组合展示', 'wide'], ], 'user' => [ ['user_card', '用户个人信息', '未登录时候显示登录注册按钮,登录后显示登录用户的个人信息', 'sidebar'], @@ -70,9 +70,9 @@ final class WidgetCatalog { 'renderer' => $slug, 'size' => $size, 'common' => [ - 'title' => !in_array($slug, ['post_pay', 'layout_gap', 'slider', 'slider_media_combo', 'buttons'], true), - 'affix' => $size === 'sidebar' || in_array($slug, ['buttons', 'notice'], true), - 'animation' => $slug !== 'buttons', + 'title' => !in_array($slug, ['post_pay', 'layout_gap', 'slider', 'slider_media_combo', 'buttons', 'posts_toc'], true), + 'affix' => ($size === 'sidebar' && $slug !== 'posts_toc') || in_array($slug, ['buttons', 'notice'], true), + 'animation' => !in_array($slug, ['buttons', 'term_cards', 'term_aggregation', 'media_cards', 'combo_info', 'slider_media_combo'], true), ], 'fields' => WidgetSchemas::for($slug), 'assets' => self::assets($slug), diff --git a/src/WidgetSystem/WidgetDefinition.php b/src/WidgetSystem/WidgetDefinition.php index 54671f3f..64f3eadf 100644 --- a/src/WidgetSystem/WidgetDefinition.php +++ b/src/WidgetSystem/WidgetDefinition.php @@ -80,6 +80,10 @@ final class WidgetDefinition { return $this->size; } + public function common(): array { + return $this->common; + } + public function assets(): array { return $this->assets; } diff --git a/src/WidgetSystem/WidgetSchemas.php b/src/WidgetSystem/WidgetSchemas.php index 2cdecf0c..cd7cccdd 100644 --- a/src/WidgetSystem/WidgetSchemas.php +++ b/src/WidgetSystem/WidgetSchemas.php @@ -38,9 +38,9 @@ final class WidgetSchemas { ]; case 'posts_toc': return [ - ['id' => 'min_headings', 'type' => 'spinner', 'label' => '最少标题数量', 'default' => 3, 'min' => 1, 'max' => 20], - ['id' => 'collapsed', 'type' => 'switcher', 'label' => '默认收起', 'default' => false], - ['id' => 'offset', 'type' => 'spinner', 'label' => '滚动偏移', 'default' => 70, 'min' => 0, 'max' => 300], + ['id' => 'title', 'type' => 'text', 'label' => '标题', 'default' => '文章目录'], + ['id' => 'mini_title', 'type' => 'text', 'label' => '副标题', 'default' => ''], + ['id' => 'in_affix', 'type' => 'switcher', 'label' => '侧栏随动(仅在侧边栏有效)', 'default' => false], ]; case 'quote': return [ @@ -76,10 +76,11 @@ final class WidgetSchemas { ]; case 'recent_comments': return [ - ['id' => 'count', 'type' => 'spinner', 'label' => '显示数量', 'default' => 6, 'min' => 1, 'max' => 50], - ['id' => 'exclude_users', 'type' => 'text', 'label' => '排除用户 ID', 'default' => '', 'sanitize' => 'ids'], - ['id' => 'exclude_posts', 'type' => 'text', 'label' => '排除文章 ID', 'default' => '', 'sanitize' => 'ids'], - ['id' => 'show_source', 'type' => 'switcher', 'label' => '显示评论来源', 'default' => true], + ['id' => 'limit', 'type' => 'spinner', 'label' => '显示数量', 'default' => 8, 'min' => 1, 'max' => 50, 'step' => 1], + ['id' => 'outer', 'type' => 'text', 'label' => '排除用户ID', 'default' => '', 'sanitize' => 'ids', 'description' => '填写需要排除的用户ID,用逗号分割,例如:55,66,100'], + ['id' => 'outpost', 'type' => 'text', 'label' => '排除文章ID', 'default' => '', 'sanitize' => 'ids', 'description' => '填写需要排除的文章ID,用逗号分割,例如:55,66,100'], + ['id' => 'show_source', 'type' => 'switcher', 'label' => '显示来源', 'default' => false], + ['id' => 'style', 'type' => 'radio', 'label' => '显示样式', 'default' => '', 'options' => ['' => '简约', 'style_2' => '风格2']], ]; case 'links': return [ @@ -142,65 +143,137 @@ final class WidgetSchemas { ]; case 'partners': return [ - ['id' => 'rows_pc', 'type' => 'spinner', 'label' => 'PC 行数', 'default' => 2, 'min' => 1, 'max' => 5], - ['id' => 'rows_mobile', 'type' => 'spinner', 'label' => '移动端行数', 'default' => 1, 'min' => 1, 'max' => 4], - ['id' => 'speed', 'type' => 'spinner', 'label' => '滚动速度', 'default' => 30, 'min' => 5, 'max' => 120], - ['id' => 'items', 'type' => 'group', 'label' => '合作伙伴', 'max_items' => 60, 'fields' => self::media_card_fields()], + ['id' => 'rows', 'type' => 'button_set', 'label' => 'PC端显示几行滚动', 'default' => 3, 'options' => [1 => '1行', 2 => '2行', 3 => '3行', 4 => '4行', 5 => '5行', 6 => '6行']], + ['id' => 'm_rows', 'type' => 'button_set', 'label' => '移动端显示几行滚动', 'default' => 3, 'options' => [1 => '1行', 2 => '2行', 3 => '3行', 4 => '4行', 5 => '5行', 6 => '6行']], + ['id' => 'style_type', 'type' => 'radio', 'label' => '选择合作伙伴卡片的呈现方式', 'default' => 'card', 'options' => ['card' => '图文卡片(适合正方形logo)', 'logo' => '纯 Logo', 'chip' => '精简胶囊']], + ['id' => 'card_width', 'type' => 'spinner', 'label' => '单卡片宽度', 'default' => 200, 'min' => 80, 'max' => 500, 'step' => 10, 'unit' => 'px', 'dependency' => ['style_type', '==', 'card'], 'description' => 'PC端每张卡片的宽度(纯 Logo 模式下宽度会自适应图片,此项不生效)'], + ['id' => 'card_height', 'type' => 'spinner', 'label' => '卡片高度', 'default' => 60, 'min' => 50, 'max' => 260, 'step' => 5, 'unit' => 'px', 'dependency' => ['style_type', '!=', 'chip'], 'description' => '每张卡片的高度,纯 Logo 模式建议 70-120'], + ['id' => 'card_bg', 'type' => 'switcher', 'label' => '显示每张合作伙伴卡片的背景', 'default' => true], + ['id' => 'card_radius', 'type' => 'slider', 'label' => '卡片圆角', 'default' => 16, 'min' => 0, 'max' => 60, 'step' => 1, 'unit' => 'px', 'dependency' => ['card_bg', '!=', '']], + ['id' => 'speed', 'type' => 'slider', 'label' => '滚动速度(数越小越快)', 'default' => 3, 'min' => 0.1, 'max' => 10, 'step' => 0.1, 'unit' => 's'], + ['id' => 'direction', 'type' => 'radio', 'label' => '滚动方向', 'default' => 'alternate', 'options' => ['alternate' => '奇数行左,偶数行右', 'left' => '全部向左', 'right' => '全部向右']], + ['id' => 'pause_hover', 'type' => 'switcher', 'label' => '鼠标悬停暂停', 'default' => true], + ['id' => 'show_widget_bg', 'type' => 'switcher', 'label' => '模块背景', 'default' => false, 'description' => '显示整个模块外层背景'], + ['id' => 'go_link', 'type' => 'switcher', 'label' => '外链重定向', 'default' => true, 'description' => '开启后,将链接转为内部go跳转链接'], + ['id' => 'end_fade', 'type' => 'switcher', 'label' => '两端渐隐', 'default' => true, 'description' => '开启后,卡片两端会渐隐,使卡片无缝连接'], + ['id' => 'link_type', 'type' => 'radio', 'label' => '合作伙伴获取方式', 'default' => 'link', 'options' => ['link' => '通过链接获取', 'manual' => '手动添加']], + ['id' => 'link_cats', 'type' => 'terms', 'label' => '选择链接分类', 'taxonomy' => 'link_category', 'default' => [], 'dependency' => ['link_type', '==', 'link'], 'description' => '需显示的链接分类,留空则为全部'], + ['id' => 'link_limit', 'type' => 'spinner', 'label' => '最大获取数量', 'default' => 50, 'min' => 0, 'step' => 5, 'unit' => '个', 'dependency' => ['link_type', '==', 'link']], + ['id' => 'link_orderby', 'type' => 'select', 'label' => '排序方式', 'default' => 'name', 'dependency' => ['link_type', '==', 'link'], 'options' => ['name' => '名称排序', 'updated' => '更新时间', 'rating' => '链接评分', 'rand' => '随机排序']], + ['id' => 'page_links_order', 'type' => 'radio', 'label' => '顺序', 'default' => 'ASC', 'dependency' => ['link_type', '==', 'link'], 'options' => ['ASC' => '升序', 'DESC' => '降序']], + [ + 'id' => 'partners', 'type' => 'group', 'label' => '添加合作伙伴', 'item_label' => '合作伙伴', 'max_items' => 60, 'dependency' => ['link_type', '==', 'manual'], + 'default' => [], + 'fields' => [ + ['id' => 'title', 'type' => 'textarea', 'label' => '名称', 'default' => '', 'attributes' => ['rows' => 1], 'description' => '卡片第一行文案,建议简短'], + ['id' => 'desc', 'type' => 'textarea', 'label' => '简介', 'default' => '', 'allow_html' => true, 'attributes' => ['rows' => 2], 'description' => '卡片第二行文案,支持 HTML(仅在图文卡片模式下显示)'], + ['id' => 'image', 'type' => 'upload', 'label' => 'Logo图片', 'default' => '', 'library' => 'image', 'description' => '如果样式选择为图文卡片,则建议全部为正方形图片'], + ['id' => 'link', 'type' => 'link', 'label' => '链接', 'default' => []], + ], + ], ]; case 'combo_info': - return [['id' => 'items', 'type' => 'group', 'label' => '信息卡片', 'max_items' => 30, 'fields' => array_merge(self::media_card_fields(), [['id' => 'tags', 'type' => 'text', 'label' => '标签', 'default' => '']])]]; + return array_merge(self::obs_animation_fields(), [ + ['id' => 'pc_row', 'type' => 'button_set', 'label' => 'PC端单行排列数量', 'default' => 4, 'options' => [2 => '2个', 3 => '3个', 4 => '4个', 6 => '6个']], + ['id' => 'm_row', 'type' => 'button_set', 'label' => '移动端单行排列数量', 'default' => 1, 'options' => [1 => '1个', 2 => '2个']], + ['id' => 'pc_gap', 'type' => 'button_set', 'label' => 'PC端卡片间距', 'default' => 10, 'options' => [5 => '10px', 10 => '20px']], + ['id' => 'm_gap', 'type' => 'button_set', 'label' => '移动端卡片间距', 'default' => 5, 'options' => [5 => '10px', 10 => '20px']], + ['id' => 'card_radius', 'type' => 'slider', 'label' => '卡片圆角', 'default' => 16, 'min' => 0, 'max' => 30, 'step' => 1, 'unit' => 'px'], + ['id' => 'top_band', 'type' => 'switcher', 'label' => '显示顶部渐变背景', 'default' => true], + ['id' => 'top_band_height', 'type' => 'slider', 'label' => '色带高度', 'default' => 150, 'min' => 100, 'max' => 400, 'step' => 20, 'unit' => 'px', 'dependency' => ['top_band', '==', '1']], + ['id' => 'go_link', 'type' => 'switcher', 'label' => '外链重定向', 'default' => true, 'description' => '开启后,将链接转为内部go跳转链接'], + ['id' => 'cta_text', 'type' => 'text', 'label' => '默认按钮文案', 'default' => '立即体验', 'description' => '每张卡片可单独覆盖'], + ['id' => 'cta_color', 'type' => 'palette', 'label' => '默认按钮颜色', 'default' => '', 'options' => self::cta_palette()], + ['id' => 'cards', 'type' => 'group', 'label' => '添加卡片', 'item_label' => '卡片', 'max_items' => 30, 'default' => [], 'fields' => [ + ['id' => 'title', 'type' => 'textarea', 'label' => '卡片标题', 'default' => '', 'attributes' => ['rows' => 1]], + ['id' => 'icon', 'type' => 'icon', 'label' => '图标', 'default' => ''], + ['id' => 'icon_image', 'type' => 'upload', 'label' => '图片图标', 'default' => '', 'library' => 'image', 'description' => '图标和图片图标二选一,设置图片将覆盖图标'], + [ + 'id' => 'tags', 'type' => 'group', 'label' => '标签', 'item_label' => '标签', 'max_items' => 20, 'default' => [], + 'fields' => [ + ['id' => 'text', 'type' => 'text', 'label' => '标签文字', 'default' => ''], + ['id' => 'color', 'type' => 'palette', 'label' => '标签颜色', 'default' => '', 'options' => self::tag_palette()], + ], + ], + ['id' => 'desc', 'type' => 'textarea', 'label' => '简介描述', 'default' => '', 'allow_html' => true, 'attributes' => ['rows' => 3], 'description' => '卡片描述文案,超出会自动省略(默认3行)'], + ['id' => 'link', 'type' => 'link', 'label' => '链接', 'default' => []], + ['id' => 'cta_text', 'type' => 'text', 'label' => '按钮文案(可选)', 'default' => '', 'description' => '留空则使用默认按钮文案'], + ['id' => 'cta_color', 'type' => 'palette', 'label' => '按钮颜色(可选)', 'default' => '', 'options' => self::cta_palette()], + ['id' => 'band_color', 'type' => 'select', 'label' => '顶部色带配色', 'default' => 'auto', 'options' => ['auto' => '自动(按序循环)', 'blue' => '蓝紫', 'cyan' => '青蓝', 'green' => '青绿', 'yellow' => '橙黄', 'pink' => '粉红', 'purple' => '紫罗兰', 'gray' => '中性灰']], + ]], + ]); + case 'media_cards': + return array_merge(self::obs_animation_fields(), [ + ['id' => 'pc_row', 'type' => 'button_set', 'label' => 'PC端单行排列数量', 'default' => 4, 'options' => self::row_options()], + ['id' => 'm_row', 'type' => 'button_set', 'label' => '移动端单行排列数量', 'default' => 2, 'options' => self::row_options(), 'description' => '请根据此模块放置位置的宽度合理调整单行数量,避免显示不佳'], + ['id' => 'mask_opacity', 'type' => 'slider', 'label' => '遮罩透明度', 'default' => 10, 'min' => 0, 'max' => 90, 'step' => 1, 'unit' => '%', 'description' => '图片上显示的黑色遮罩层的透明度'], + ['id' => 'height_scale', 'type' => 'spinner', 'label' => '封面长宽比例', 'default' => 30, 'min' => 5, 'max' => 300, 'step' => 5, 'unit' => '%'], + ['id' => 'font_size_pc', 'type' => 'spinner', 'label' => 'PC端字体大小', 'default' => 18, 'min' => 10, 'max' => 80, 'step' => 2, 'unit' => 'px'], + ['id' => 'font_size_m', 'type' => 'spinner', 'label' => '移动端字体大小', 'default' => 14, 'min' => 10, 'max' => 80, 'step' => 2, 'unit' => 'px'], + ['id' => 'font_bold', 'type' => 'switcher', 'label' => '粗体显示', 'default' => false], + ['id' => 'font_color', 'type' => 'color', 'label' => '文字颜色', 'default' => ''], + ['id' => 'covers', 'type' => 'group', 'label' => '添加封面', 'item_label' => '内容', 'max_items' => 40, 'default' => [], 'fields' => self::cover_fields(), 'description' => '
由于移动端多数浏览器不支持视频背景功能,所以移动端不会显示视频!
'], + ]); case 'icon_cards': return self::icon_cards_fields(); case 'icon_cover_cards': return self::icon_cover_cards_fields(); - case 'media_cards': default: return [['id' => 'cards', 'type' => 'group', 'label' => '卡片', 'max_items' => 40, 'fields' => self::media_card_fields()]]; } } private static function content(string $slug): array { - if ($slug === 'term_cards' || $slug === 'term_aggregation') { - return [ - ['id' => 'taxonomy', 'type' => 'select', 'label' => '分类法', 'default' => 'category', 'options' => ['category' => '分类', 'post_tag' => '标签']], - ['id' => 'term_ids', 'type' => 'text', 'label' => '分类/标签 ID', 'default' => '', 'sanitize' => 'ids'], - ['id' => 'count', 'type' => 'spinner', 'label' => '数量', 'default' => 8, 'min' => 1, 'max' => 50], - ['id' => 'pc_row', 'type' => 'spinner', 'label' => 'PC 单行数量', 'default' => 4, 'min' => 1, 'max' => 12], - ['id' => 'm_row', 'type' => 'spinner', 'label' => '移动端单行数量', 'default' => 2, 'min' => 1, 'max' => 6], - ]; + switch ($slug) { + case 'posts': + return self::posts_fields(); + case 'tab_posts': + return self::tab_posts_fields(); + case 'oneline_posts': + return self::oneline_posts_fields(); + case 'hot_posts': + return self::hot_posts_fields(); + case 'term_cards': + return self::term_cards_fields(); + case 'term_aggregation': + return self::term_aggregation_fields(); } + return []; + } - $fields = self::post_query_fields(); - if ($slug === 'hot_posts') { - $fields[] = ['id' => 'show_thumbnail', 'type' => 'switcher', 'label' => '显示缩略图', 'default' => true]; - } - if ($slug === 'oneline_posts') { - $fields[] = ['id' => 'autoplay', 'type' => 'switcher', 'label' => '自动滚动', 'default' => true]; - } - if ($slug === 'tab_posts') { - return self::tab_posts_fields(); - } - return $fields; + private static function posts_fields(): array { + return [ + ['id' => 'load_mode', 'type' => 'radio', 'label' => '模块加载方式', 'default' => 'ajax', 'options' => ['detail' => '直接加载', 'ajax' => 'ajax懒加载'], 'description' => 'ajax懒加载:当页面加载完后,根据用户需要在加载当前模块内容,可提高页面渲染效率'], + ['id' => 'cat', 'type' => 'text', 'label' => '分类限制', 'default' => '', 'sanitize' => 'reference_ids', 'description' => '限制请填写对应的ID,多个ID请用英文逗号隔开。如:1,2,3。支持负数进行排除,例如:-1,-2,-3。'], + ['id' => 'zibpay_type', 'type' => 'checkbox', 'label' => '商品类型筛选', 'default' => [], 'options' => self::pay_types(), 'description' => '不做其它筛选'], + ['id' => 'limit_day', 'type' => 'spinner', 'label' => '发布时间限制', 'default' => 0, 'min' => 0, 'step' => 5, 'unit' => '天', 'description' => '仅显示最近多少天发布的文章,为0则不限制'], + ['id' => 'orderby', 'type' => 'select', 'label' => '排序方式', 'default' => '', 'options' => self::posts_orderby()], + ['id' => 'order', 'type' => 'radio', 'label' => '顺序', 'default' => 'desc', 'options' => ['desc' => '降序', 'asc' => '升序']], + ['id' => 'style', 'type' => 'radio', 'label' => '列表样式', 'default' => 'list', 'options' => ['list' => '列表样式', 'card' => '卡片样式', 'mini' => 'mini列表'], 'description' => '
注意:不同样式尺寸不同,请根据放置的位置合理选择。例如:放在宽度较小的侧边栏,则需选择mini样式,否则显示效果不佳
'], + ['id' => 'mini_opt', 'type' => 'checkbox', 'label' => 'mini列表配置', 'default' => [], 'dependency' => ['style', '==', 'mini'], 'options' => self::mini_opt_options(), 'description' => '不做其它筛选'], + ['id' => 'count', 'type' => 'spinner', 'label' => '显示数量', 'default' => 12, 'min' => 4, 'max' => 20, 'step' => 1, 'unit' => '篇'], + ['id' => 'paginate', 'type' => 'radio', 'label' => '翻页按钮', 'default' => '', 'options' => ['' => '不翻页', 'ajax' => 'AJAX追加列表翻页', 'number' => '数字翻页按钮']], + ]; } private static function tab_posts_fields(): array { return [ - ['id' => 'style', 'type' => 'radio', 'label' => '列表样式', 'default' => 'mini', 'options' => ['list' => '列表样式', 'card' => '卡片样式', 'mini' => 'mini列表']], - ['id' => 'mini_opt', 'type' => 'checkbox', 'label' => 'mini列表配置', 'default' => [], 'dependency' => ['style', '==', 'mini'], 'options' => ['show_thumb' => '显示缩略图', 'show_number' => '显示编号(开启翻页后,只在第一页有效)', 'show_meta' => '显示作者、时间、点赞等信息']], - ['id' => 'count', 'type' => 'spinner', 'label' => '显示数量', 'default' => 6, 'min' => 4, 'max' => 20, 'step' => 1], + ['id' => 'style', 'type' => 'radio', 'label' => '列表样式', 'default' => 'mini', 'options' => ['list' => '列表样式', 'card' => '卡片样式', 'mini' => 'mini列表'], 'description' => '
注意:不同样式尺寸不同,请根据放置的位置合理选择。例如:放在宽度较小的侧边栏,则需选择mini样式,否则显示效果不佳
'], + ['id' => 'mini_opt', 'type' => 'checkbox', 'label' => 'mini列表配置', 'default' => [], 'dependency' => ['style', '==', 'mini'], 'options' => self::mini_opt_options(), 'description' => '不做其它筛选'], + ['id' => 'count', 'type' => 'spinner', 'label' => '显示数量', 'default' => 6, 'min' => 4, 'max' => 20, 'step' => 1, 'unit' => '篇'], ['id' => 'paginate', 'type' => 'radio', 'label' => '翻页按钮', 'default' => '', 'options' => ['' => '不翻页', 'ajax' => 'AJAX追加列表翻页', 'number' => '数字翻页按钮']], [ - 'id' => 'tabs', 'type' => 'group', 'label' => '栏目', 'max_items' => 10, + 'id' => 'tabs', 'type' => 'group', 'label' => '栏目', 'max_items' => 10, 'item_label' => '栏目', 'default' => [ ['title' => '热门推荐', 'orderby' => 'views'], ['title' => '最近更新', 'orderby' => 'modified'], ['title' => '猜你喜欢', 'orderby' => 'rand'], ], 'fields' => [ - ['id' => 'title', 'type' => 'textarea', 'label' => '标题(必填)', 'default' => '', 'allow_html' => true, 'attributes' => ['rows' => 1]], + ['id' => 'title', 'type' => 'textarea', 'label' => '标题(必填)', 'default' => '', 'allow_html' => true, 'attributes' => ['rows' => 1], 'description' => '栏目显示的标题,支持HTML代码,注意代码规范'], ['id' => 'cat', 'type' => 'text', 'label' => '分类限制', 'default' => '', 'sanitize' => 'reference_ids'], - ['id' => 'topics', 'type' => 'text', 'label' => '专题限制', 'default' => '', 'sanitize' => 'reference_ids', 'description' => '多个 ID 用英文逗号隔开,支持负数排除。'], - ['id' => 'limit_day', 'type' => 'spinner', 'label' => '发布时间限制', 'default' => 0, 'min' => 0, 'step' => 5, 'max' => 3650], + ['id' => 'zibpay_type', 'type' => 'checkbox', 'label' => '商品类型筛选', 'default' => [], 'options' => self::pay_types(), 'description' => '不做其它筛选'], + ['id' => 'limit_day', 'type' => 'spinner', 'label' => '发布时间限制', 'default' => 0, 'min' => 0, 'step' => 5, 'unit' => '天', 'description' => '仅显示最近多少天发布的文章,为0则不限制'], ['id' => 'orderby', 'type' => 'select', 'label' => '排序方式', 'default' => '', 'options' => self::posts_orderby()], ['id' => 'order', 'type' => 'radio', 'label' => '顺序', 'default' => 'desc', 'options' => ['desc' => '降序', 'asc' => '升序']], ], @@ -208,26 +281,82 @@ final class WidgetSchemas { ]; } - private static function posts_orderby(): array { + private static function oneline_posts_fields(): array { return [ - '' => '默认排序', 'modified' => '更新时间', 'date' => '发布时间', 'views' => '浏览数量', - 'like' => '点赞数量', 'comment_count' => '评论数量', 'favorite' => '收藏数量', - 'rand' => '随机排序', + ['id' => 'cat', 'type' => 'text', 'label' => '分类限制', 'default' => '', 'sanitize' => 'reference_ids', 'description' => '限制请填写对应的ID,多个ID请用英文逗号隔开。如:1,2,3。支持负数进行排除,例如:-1,-2,-3。'], + ['id' => 'zibpay_type', 'type' => 'checkbox', 'label' => '商品类型筛选', 'default' => [], 'options' => self::pay_types(), 'description' => '不做其它筛选'], + ['id' => 'orderby', 'type' => 'select', 'label' => '排序方式', 'default' => '', 'options' => self::posts_orderby()], + ['id' => 'order', 'type' => 'radio', 'label' => '顺序', 'default' => 'desc', 'options' => ['desc' => '降序', 'asc' => '升序']], + ['id' => 'style', 'type' => 'radio', 'label' => '列表样式', 'default' => 'card', 'options' => ['list' => '列表样式', 'card' => '卡片样式', 'mini' => 'mini列表'], 'description' => '
注意:不同样式尺寸不同,请根据放置的位置合理选择。例如:放在宽度较小的侧边栏,则需选择mini样式,否则显示效果不佳
'], + ['id' => 'count', 'type' => 'spinner', 'label' => '显示数量', 'default' => 12, 'min' => 4, 'max' => 20, 'step' => 1, 'unit' => '篇'], ]; } - private static function post_query_fields(): array { + private static function hot_posts_fields(): array { return [ - ['id' => 'category_ids', 'type' => 'text', 'label' => '分类 ID', 'default' => '', 'sanitize' => 'ids'], - ['id' => 'tag_ids', 'type' => 'text', 'label' => '标签 ID', 'default' => '', 'sanitize' => 'ids'], - ['id' => 'pay_type', 'type' => 'select', 'label' => '付费内容类型', 'default' => '', 'options' => self::pay_types()], - ['id' => 'limit_day', 'type' => 'spinner', 'label' => '最近天数', 'default' => 0, 'min' => 0, 'max' => 3650], - ['id' => 'orderby', 'type' => 'select', 'label' => '排序', 'default' => 'date', 'options' => ['date' => '发布时间', 'modified' => '更新时间', 'views' => '浏览量', 'comments' => '评论数', 'likes' => '点赞数', 'favorites' => '收藏数', 'downloads' => '下载量', 'rand' => '随机']], - ['id' => 'order', 'type' => 'radio', 'label' => '顺序', 'default' => 'DESC', 'options' => ['DESC' => '降序', 'ASC' => '升序']], - ['id' => 'count', 'type' => 'spinner', 'label' => '数量', 'default' => 8, 'min' => 1, 'max' => 50], - ['id' => 'style', 'type' => 'button_set', 'label' => '列表样式', 'default' => 'card', 'options' => ['list' => '列表', 'card' => '卡片', 'mini' => 'Mini']], - ['id' => 'paginate', 'type' => 'select', 'label' => '翻页方式', 'default' => 'none', 'options' => ['none' => '不翻页', 'numbered' => '数字分页', 'load_more' => '加载更多', 'infinite' => '无限加载']], - ['id' => 'load_mode', 'type' => 'select', 'label' => '加载方式', 'default' => 'sync', 'options' => ['sync' => '同步', 'ajax' => 'AJAX 延迟加载']], + ['id' => 'orderby', 'type' => 'radio', 'label' => '榜单类型', 'default' => 'views', 'options' => [ + 'views' => '热门榜单(按阅读量排序)', + 'like' => '超赞榜单(按点赞量排序)', + 'comment_count' => '话题榜单(按评论量排序)', + 'favorite' => '收藏榜单(按收藏量排序)', + ]], + ['id' => 'limit_day', 'type' => 'spinner', 'label' => '限制时间(最近X天)', 'default' => 0, 'min' => 0, 'max' => 999999, 'step' => 1, 'unit' => '天', 'description' => '设置多少天内发布的文章有效,为0则不限制时间'], + ['id' => 'count', 'type' => 'spinner', 'label' => '最大显示数量', 'default' => 6, 'min' => 1, 'max' => 20, 'step' => 1, 'unit' => '篇'], + ['id' => 'target_blank', 'type' => 'switcher', 'label' => '新窗口打开', 'default' => false], + ]; + } + + private static function term_cards_fields(): array { + return array_merge(self::obs_animation_fields(), [ + ['id' => 'term_id', 'type' => 'terms', 'label' => '添加分类', 'taxonomy' => 'category', 'default' => [], 'description' => '选择并排序需要的分类,如选择的分类下没有封面则不会显示'], + ['id' => 'type', 'type' => 'radio', 'label' => '卡片样式', 'default' => 'style-1', 'options' => ['style-1' => '简单样式', 'style-2' => '样式二', 'style-3' => '样式三', 'style-4' => '样式四']], + ['id' => 'height_scale', 'type' => 'spinner', 'label' => '卡片长宽比例', 'default' => 60, 'min' => 20, 'max' => 300, 'step' => 5, 'unit' => '%'], + ['id' => 'pc_row', 'type' => 'button_set', 'label' => 'PC端单行排列数量', 'default' => 2, 'options' => self::row_options()], + ['id' => 'm_row', 'type' => 'button_set', 'label' => '移动端单行排列数量', 'default' => 2, 'options' => self::row_options(), 'description' => '请根据此模块放置位置的宽度合理调整单行数量,避免显示不佳'], + ['id' => 'mask_opacity', 'type' => 'slider', 'label' => '遮罩透明度', 'default' => 10, 'min' => 0, 'max' => 90, 'step' => 1, 'unit' => '%', 'description' => '图片上显示的黑色遮罩层的透明度'], + ['id' => 'target_blank', 'type' => 'switcher', 'label' => '新窗口打开', 'default' => false], + ]); + } + + private static function term_aggregation_fields(): array { + return array_merge(self::obs_animation_fields(), [ + ['id' => 'pc_row', 'type' => 'slider', 'label' => '单行布局', 'default' => 2, 'min' => 1, 'max' => 2, 'step' => 1, 'unit' => '个', 'description' => '请根据此模块放置位置的宽度合理调整单行数量'], + ['id' => 'term_id', 'type' => 'terms', 'label' => '添加分类', 'taxonomy' => 'category', 'default' => [], 'description' => '选择并排序需要的分类,如选择的分类下没有文章则不会显示'], + ['id' => 'orderby', 'type' => 'select', 'label' => '排序方式', 'default' => 'modified', 'options' => self::posts_orderby(), 'dependency' => ['term_id', '!=', '']], + ['id' => 'order', 'type' => 'radio', 'label' => '顺序', 'default' => 'desc', 'options' => ['desc' => '降序', 'asc' => '升序'], 'dependency' => ['term_id', '!=', '']], + ['id' => 'count', 'type' => 'spinner', 'label' => '最大文章数量', 'default' => 4, 'min' => 1, 'max' => 20, 'step' => 1, 'unit' => '篇', 'dependency' => ['term_id', '!=', ''], 'description' => '
请确保所选的分类内的文章数量均超过此数量,否则会出现布局错位的问题!
'], + ['id' => 'target_blank', 'type' => 'switcher', 'label' => '新窗口打开', 'default' => false, 'dependency' => ['term_id', '!=', '']], + ]); + } + + private static function obs_animation_fields(): array { + return [ + ['id' => 'obs_animation', 'type' => 'select', 'label' => '入场动画', 'default' => '', 'options' => self::animation_options(), 'description' => '页面滚动到此模块时触发的入场动画[注意:需合理配置适当启用]'], + ['id' => 'animation_repeat', 'type' => 'switcher', 'label' => '重复动画', 'default' => false, 'dependency' => ['obs_animation', '!=', ''], 'description' => '每次滚动到此模块都触发动画[注意:有性能消耗,部分老旧浏览器会有闪烁问题]'], + ]; + } + + private static function row_options(): array { + return [1 => '1个', 2 => '2个', 3 => '3个', 4 => '4个', 6 => '6个', 12 => '12个']; + } + + private static function mini_opt_options(): array { + return [ + 'show_thumb' => '显示缩略图', + 'show_number' => '显示编号(开启翻页后,只在第一页有效)', + 'show_meta' => '显示作者、时间、点赞等信息', + ]; + } + + private static function posts_orderby(): array { + return [ + 'modified' => '更新时间', + 'date' => '发布时间', + 'views' => '浏览数量', + 'like' => '点赞数量', + 'comment_count' => '评论数量', + 'favorite' => '收藏数量', + 'rand' => '随机排序', ]; } @@ -319,25 +448,34 @@ final class WidgetSchemas { ['id' => 'button', 'type' => 'switcher', 'label' => '显示翻页按钮', 'default' => true], ['id' => 'pagination', 'type' => 'switcher', 'label' => '显示指示器', 'default' => true], ['id' => 'effect', 'type' => 'select', 'label' => '切换动画', 'default' => 'slide', 'options' => ['slide' => '滑动', 'fade' => '淡出淡入', 'cube' => '3D方块', 'coverflow' => '3D滑入', 'flip' => '3D翻转']], - ['id' => 'spacebetween', 'type' => 'spinner', 'label' => '幻灯片间距', 'default' => 20, 'min' => 0, 'max' => 500, 'step' => 5], - ['id' => 'speed', 'type' => 'spinner', 'label' => '切换速度', 'default' => 0, 'min' => 0, 'max' => 3000, 'step' => 100], + ['id' => 'spacebetween', 'type' => 'spinner', 'label' => '幻灯片间距', 'default' => 20, 'min' => 0, 'max' => 500, 'step' => 5, 'unit' => 'PX'], + ['id' => 'speed', 'type' => 'spinner', 'label' => '切换速度', 'default' => 0, 'min' => 0, 'max' => 3000, 'step' => 100, 'unit' => '毫秒', 'description' => '设置为“0”,则为自动模式:根据幻灯片大小自动设置最佳速度'], ['id' => 'autoplay', 'type' => 'switcher', 'label' => '自动播放', 'default' => true], - ['id' => 'interval', 'type' => 'spinner', 'label' => '停顿时间', 'default' => 4, 'min' => 0, 'max' => 20, 'dependency' => ['autoplay', '!=', '']], + ['id' => 'interval', 'type' => 'spinner', 'label' => '停顿时间', 'default' => 4, 'min' => 0, 'max' => 20, 'unit' => '秒', 'dependency' => ['autoplay', '!=', '']], ]; $fields = [ - ['id' => 'layout_ratio', 'type' => 'slider', 'label' => '幻灯片宽度占比', 'default' => 60, 'min' => 0, 'max' => 100], - ['id' => 'slide_scale', 'type' => 'spinner', 'label' => '幻灯片长宽比', 'default' => 50, 'min' => 0, 'max' => 500, 'step' => 5], + ['id' => 'obs_animation', 'type' => 'select', 'label' => '入场动画', 'default' => '', 'options' => self::animation_options(), 'description' => '页面滚动到封面区域时触发的入场动画[注意:需合理配置适当启用]'], + ['id' => 'animation_repeat', 'type' => 'switcher', 'label' => '重复动画', 'default' => false, 'dependency' => ['obs_animation', '!=', ''], 'description' => '每次滚动到封面区域都触发动画[注意:有性能消耗,部分老旧浏览器会有闪烁问题]'], + ['type' => 'subheading', 'content' => 'PC端布局配置'], + ['id' => 'layout_ratio', 'type' => 'slider', 'label' => '幻灯片宽度占比', 'default' => 60, 'min' => 0, 'max' => 100, 'step' => 1, 'unit' => '%'], + ['id' => 'slide_scale', 'type' => 'spinner', 'label' => '幻灯片长宽比', 'default' => 50, 'min' => 0, 'max' => 500, 'step' => 5, 'unit' => '%'], ['id' => 'pc_row', 'type' => 'button_set', 'label' => '右侧卡片列数', 'default' => 2, 'options' => [1 => '1列', 2 => '2列', 3 => '3列', 4 => '4列']], ['id' => 'pc_col', 'type' => 'button_set', 'label' => '右侧卡片行数', 'default' => 2, 'options' => [1 => '1行', 2 => '2行', 3 => '3行', 4 => '4行']], - ['id' => 'm_layout_type', 'type' => 'radio', 'label' => '移动端布局方式', 'default' => 'vertical', 'options' => ['default' => '保持PC端布局', 'vertical' => '上下布局', 'hide_slide' => '隐藏幻灯片', 'hide_cover' => '隐藏卡片']], + ['type' => 'subheading', 'content' => '移动端布局方式'], + ['id' => 'm_layout_type', 'type' => 'radio', 'label' => '布局方式', 'default' => 'vertical', 'options' => ['default' => '保持PC端布局', 'vertical' => '上下布局', 'hide_slide' => '隐藏幻灯片', 'hide_cover' => '隐藏卡片']], ['id' => 'm_row', 'type' => 'button_set', 'label' => '移动端卡片单行数量', 'default' => 2, 'dependency' => ['m_layout_type', 'any', 'hide_slide,vertical'], 'options' => [1 => '1个', 2 => '2个', 3 => '3个', 4 => '4个', 6 => '6个']], - ['id' => 'm_cover_ratio', 'type' => 'spinner', 'label' => '移动端卡片长宽比', 'default' => 45, 'min' => 5, 'max' => 300, 'step' => 5, 'dependency' => ['m_layout_type', 'any', 'hide_slide,vertical']], - ['id' => 'slides', 'type' => 'group', 'label' => '幻灯片内容', 'max_items' => 30, 'min_items' => 1, 'default' => $combo_slides, 'fields' => $slide_fields], + ['id' => 'm_cover_ratio', 'type' => 'spinner', 'label' => '移动端卡片长宽比', 'default' => 45, 'min' => 5, 'max' => 300, 'step' => 5, 'unit' => '%', 'dependency' => ['m_layout_type', 'any', 'hide_slide,vertical']], + ['type' => 'subheading', 'content' => '左侧幻灯片'], + ['id' => 'slides', 'type' => 'group', 'label' => '幻灯片内容', 'item_label' => '幻灯片', 'max_items' => 30, 'min_items' => 1, 'default' => $combo_slides, 'fields' => $slide_fields, 'description' => '注意:当添加的幻灯片内容只有一个的时候,则不会以幻灯片的形式展示(不能滑动切换,相当于单个图片或视频)
由于移动端多数浏览器不支持视频背景功能,所以移动端不会显示视频!
'], ['id' => 'option', 'type' => 'accordion', 'label' => '幻灯片设置', 'default' => ['button' => true, 'pagination' => true, 'effect' => 'slide', 'spacebetween' => 20, 'speed' => 0, 'interval' => 4, 'autoplay' => true, 'loop' => true], 'fields' => $combo_option], - ['id' => 'covers', 'type' => 'group', 'label' => '右侧图文视频卡片', 'max_items' => 30, 'default' => array_fill(0, 4, ['image' => $asset_uri . 'user_t.jpg', 'title' => '']), 'fields' => [ - ['id' => 'image', 'type' => 'upload', 'label' => '图片背景', 'default' => '', 'library' => 'image'], ['id' => 'video', 'type' => 'upload', 'label' => '视频背景', 'default' => '', 'library' => 'video'], ['id' => 'hide', 'type' => 'radio', 'label' => '显示规则', 'default' => '', 'options' => ['' => '全部显示', 'pc' => 'PC端不显示', 'm' => '移动端不显示']], ['id' => 'title', 'type' => 'textarea', 'label' => '文字', 'default' => '', 'allow_html' => true, 'attributes' => ['rows' => 1]], ['id' => 'link', 'type' => 'link', 'label' => '跳转链接', 'default' => []], - ]], - ['id' => 'mask_opacity', 'type' => 'slider', 'label' => '遮罩透明度', 'default' => 0, 'min' => 0, 'max' => 90], ['id' => 'font_size_pc', 'type' => 'spinner', 'label' => 'PC端字体大小', 'default' => 16, 'min' => 10, 'max' => 80, 'step' => 2], ['id' => 'font_size_m', 'type' => 'spinner', 'label' => '移动端字体大小', 'default' => 14, 'min' => 10, 'max' => 80, 'step' => 2], ['id' => 'font_bold', 'type' => 'switcher', 'label' => '粗体显示', 'default' => false], ['id' => 'font_color', 'type' => 'color', 'label' => '文字颜色', 'default' => ''], + ['type' => 'subheading', 'content' => '右侧图文视频卡片'], + ['id' => 'covers', 'type' => 'group', 'label' => '右侧图文视频卡片', 'item_label' => '卡片', 'max_items' => 30, 'default' => array_fill(0, 4, ['image' => $asset_uri . 'user_t.jpg', 'title' => '']), 'fields' => self::cover_fields(), 'description' => '
注意:由于移动端多数浏览器不支持视频背景功能,所以移动端不会显示视频!
'], + ['type' => 'subheading', 'content' => '卡片样式配置'], + ['id' => 'mask_opacity', 'type' => 'slider', 'label' => '遮罩透明度', 'default' => 0, 'min' => 0, 'max' => 90, 'step' => 1, 'unit' => '%', 'description' => '图片上显示的黑色遮罩层的透明度'], + ['id' => 'font_size_pc', 'type' => 'spinner', 'label' => 'PC端字体大小', 'default' => 16, 'min' => 10, 'max' => 80, 'step' => 2, 'unit' => 'px'], + ['id' => 'font_size_m', 'type' => 'spinner', 'label' => '移动端字体大小', 'default' => 14, 'min' => 10, 'max' => 80, 'step' => 2, 'unit' => 'px'], + ['id' => 'font_bold', 'type' => 'switcher', 'label' => '粗体显示', 'default' => false], + ['id' => 'font_color', 'type' => 'color', 'label' => '文字颜色', 'default' => ''], ]; } return $fields; @@ -447,6 +585,32 @@ final class WidgetSchemas { ]; } + /** 图文视频封面卡片单元(移动端不显示视频)。 */ + private static function cover_fields(): array { + return [ + ['id' => 'image', 'type' => 'upload', 'label' => '图片背景', 'default' => '', 'library' => 'image'], + ['id' => 'video', 'type' => 'upload', 'label' => '视频背景', 'default' => '', 'library' => 'video', 'description' => '(必填)图片背景、视频背景至少二选一,如果同时设置则PC端视频优先(PC端视频加载失败则显示图片),移动端只显示图片'], + ['id' => 'hide', 'type' => 'radio', 'label' => '显示规则', 'default' => '', 'options' => ['' => '全部显示', 'pc' => 'PC端不显示', 'm' => '移动端不显示']], + ['id' => 'title', 'type' => 'textarea', 'label' => '文字', 'default' => '', 'allow_html' => true, 'attributes' => ['rows' => 1], 'description' => '支持HTML代码'], + ['id' => 'link', 'type' => 'link', 'label' => '跳转链接', 'default' => []], + ]; + } + + private static function cta_palette(): array { + return ['' => '默认'] + self::text_palette(); + } + + private static function tag_palette(): array { + return ['' => '默认'] + self::zib_palette(); + } + + private static function text_palette(): array { + return [ + 'c-red' => '红色', 'c-red-2' => '深红', 'c-yellow' => '黄色', 'c-yellow-2' => '深黄', 'c-cyan' => '青色', + 'c-blue' => '蓝色', 'c-blue-2' => '深蓝', 'c-green' => '绿色', 'c-green-2' => '深绿', 'c-purple' => '紫色', 'c-purple-2' => '深紫', + ]; + } + private static function card_fields(bool $link = false): array { $fields = [['id' => 'title', 'type' => 'text', 'label' => '标题', 'default' => ''], ['id' => 'description', 'type' => 'textarea', 'label' => '说明', 'default' => '']]; if ($link) { @@ -478,13 +642,12 @@ final class WidgetSchemas { private static function notice_post_fields(): array { return [ ['id' => 'cat', 'type' => 'text', 'label' => '分类限制', 'default' => '', 'sanitize' => 'reference_ids'], - ['id' => 'topics', 'type' => 'text', 'label' => '专题限制', 'default' => '', 'sanitize' => 'reference_ids'], ['id' => 'orderby', 'type' => 'select', 'label' => '排序方式', 'default' => '', 'options' => self::posts_orderby()], - ['id' => 'order', 'type' => 'radio', 'label' => '顺序', 'default' => 'desc', 'options' => ['desc' => '升序', 'asc' => '降序']], + ['id' => 'order', 'type' => 'radio', 'label' => '顺序', 'default' => 'desc', 'options' => ['desc' => '降序', 'asc' => '升序']], ]; } private static function pay_types(): array { - return ['' => '全部', 'reading' => '付费阅读', 'download' => '付费下载', 'gallery' => '付费图库', 'video' => '付费视频', 'vip' => 'VIP 专属']; + return ['1' => '付费阅读', '2' => '付费下载', '5' => '付费图片', '6' => '付费视频']; } } diff --git a/tests/widget-system/icon-card-contract.test.cjs b/tests/widget-system/icon-card-contract.test.cjs index 053fb692..b5f79c79 100644 --- a/tests/widget-system/icon-card-contract.test.cjs +++ b/tests/widget-system/icon-card-contract.test.cjs @@ -20,7 +20,7 @@ test('icon card renderers keep vertical and horizontal legacy DOM contracts sepa assert.doesNotMatch(helper, /aksrt-icon-cover-card/); assert.match(widgets, /'customize_icon' => \$card\['customize_icon'\] \?\? ''/); assert.doesNotMatch(widgets, /\$card\['custom_icon'\]/); - assert.doesNotMatch(widgets, /\$card\['icon_image'\]/); + // combo_info legitimately renders icon_image cards; icon cards must not leak media-card fields. assert.doesNotMatch(widgets, /\$card\['description'\]/); assert.match(header, /empty\(\$card\['custom_icon'\]\)/); }); diff --git a/tests/widget-system/pay-card-contract.test.cjs b/tests/widget-system/pay-card-contract.test.cjs index 185c3f7e..a96810f5 100644 --- a/tests/widget-system/pay-card-contract.test.cjs +++ b/tests/widget-system/pay-card-contract.test.cjs @@ -45,7 +45,7 @@ test('paid article and sidebar widget use the Zibll purchase-card DOM', () => { assert.match(pay, /支付金额/); assert.match(pay, /hidden-box show/); assert.match(widget, /'layout' => 'widget'/); - assert.match(shell, /\['post_pay', 'tab_posts'/); + assert.match(shell, /'post_pay', 'tab_posts', 'posts'/); assert.doesNotMatch(single, /aksrt-panel pay-box pay-type-download/); assert.doesNotMatch(single, /get_template_part\('templates\/parts\/resource-download-box'\)/); assert.match(single, /aksrt_get_paid_box_html\(\$post\)/); diff --git a/tests/widget-system/zibll-modern-widgets-contract.test.cjs b/tests/widget-system/zibll-modern-widgets-contract.test.cjs index 24caf2aa..b1d5f8cb 100644 --- a/tests/widget-system/zibll-modern-widgets-contract.test.cjs +++ b/tests/widget-system/zibll-modern-widgets-contract.test.cjs @@ -8,9 +8,10 @@ const read = (file) => fs.readFileSync(path.join(root, file), 'utf8'); test('modern widget schemas expose Zibll field names and defaults', () => { const source = read('src/WidgetSystem/WidgetSchemas.php'); - for (const field of ['mini_opt', 'paginate', 'tabs', "'id' => 'cat'", "'id' => 'topics'", 'layout_ratio', 'slide_scale', 'm_layout_type', 'm_cover_ratio', 'image_layer', 'image_select']) { + for (const field of ['mini_opt', 'paginate', 'tabs', "'id' => 'cat'", "'id' => 'zibpay_type'", 'layout_ratio', 'slide_scale', 'm_layout_type', 'm_cover_ratio', 'image_layer', 'image_select']) { assert.match(source, new RegExp(field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))); } + assert.doesNotMatch(source, /'id' => 'topics'/); assert.match(source, /'热门推荐'.*'orderby' => 'views'/s); assert.match(source, /'最近更新'.*'orderby' => 'modified'/s); assert.match(source, /'猜你喜欢'.*'orderby' => 'rand'/s); @@ -19,13 +20,16 @@ test('modern widget schemas expose Zibll field names and defaults', () => { test('tab and slider renderers retain reference DOM contracts', () => { const content = read('src/WidgetSystem/Renderers/ContentRenderer.php'); const slider = read('src/WidgetSystem/Renderers/SliderRenderer.php'); - for (const token of ['widget-tab-post', 'posts-mini-lists', 'index-tab', 'tab-nav-theme', 'tab-pane fade', 'post_ajax_trigger', 'post_ajax_loader', 'widget-ajaxpager']) assert.match(content, new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))); + for (const token of ['widget-tab-post', 'posts-mini-lists', 'index-tab', 'tab-nav-theme', 'tab-pane fade', 'ias-btn', 'post_ajax_loader', 'widget-ajaxpager']) assert.match(content, new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))); + assert.doesNotMatch(content, /post_ajax_trigger/); for (const token of ['relative zib-slider', 'new-swiper slide-widget', 'swiper-wrapper', 'swiper-slide', 'slide-layer', 'data-swiper-parallax', 'swiper-button-prev', 'swiper-pagination', 'widget-slider-graphic', 'slider-col', 'cover-col', 'widget-graphic-cover']) assert.match(slider, new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))); }); test('reference slider CSS includes responsive height and combo selectors', () => { const css = read('assets/widgets/frontend.css'); - for (const token of ['.new-swiper.slide-widget.scale-height', '--scale-height', '--pc-height', '--m-height', '.widget-slider-graphic', '.m-hide_slide', '.m-hide_cover', '--m-cover-ratio']) assert.match(css, new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))); + const mainCss = read('assets/css/main.css'); + for (const token of ['.new-swiper.slide-widget.scale-height', '--scale-height', '--pc-height', '--m-height', '.widget-slider-graphic', '.m-hide_slide', '.m-hide_cover']) assert.match(css, new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))); + for (const token of ['--m-cover-ratio', '--cover-ratio', '--layout-ratio']) assert.match(mainCss + css, new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))); }); test('icon card widgets expose Zibll vertical and horizontal contracts', () => { @@ -71,9 +75,9 @@ test('vertical notice supports only custom and article sources with Zibll swiper assert.match(schema, new RegExp(field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))); } assert.match(schema, /'options' => \['' => '自定义', 'posts' => '文章'\]/); - assert.doesNotMatch(schema, /forums_opts|notice_forum_fields|zibpay_type|zibpay_price|zibpay_points_price|sales_volume/); + assert.doesNotMatch(schema, /forums_opts|notice_forum_fields|zibpay_price|zibpay_points_price|sales_volume/); assert.doesNotMatch(renderer, /zib_bbs_get_posts_query|forums_opts/); - assert.doesNotMatch(postQuery, /zibpay_type|zibpay_price|zibpay_points_price|sales_volume/); + assert.doesNotMatch(postQuery, /zibpay_price|zibpay_points_price|sales_volume/); assert.doesNotMatch(form + controller + adminJs, /aksrt_widget_remote_options|aksrt-widget-remote-select|aksrt-widget-remote-picker/); for (const token of ['swiper-bulletin mb20 ', 'notice-slide', 'new-swiper', 'data-direction="vertical"', 'data-loop="true"', 'data-autoplay="1"', 'bulletin-icon flex jc shrink0']) { assert.match(renderer, new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));