diff --git a/assets/css/frontend/widget-batch3.css b/assets/css/frontend/widget-batch3.css index 6853d0f9..5479da84 100644 --- a/assets/css/frontend/widget-batch3.css +++ b/assets/css/frontend/widget-batch3.css @@ -530,3 +530,210 @@ color: var(--color-primary, #3b82f6); flex-shrink: 0; } + + +/* ---- Pricing plans ---- */ +.aksrt-pricing { margin-bottom: 16px; } +.aksrt-pricing__cards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 16px; +} +.aksrt-pricing__card { + position: relative; + border: 1px solid var(--color-line, #e5e7eb); + border-radius: 14px; + padding: 22px 18px 18px; + background: var(--surface-1, #fff); + display: flex; + flex-direction: column; + min-height: 100%; +} +.aksrt-pricing__card.is-featured { + box-shadow: 0 10px 28px rgba(0,0,0,.08); + border-color: transparent; + border-top: 3px solid var(--plan-accent, #3b82f6); +} +.aksrt-pricing__card.accent-pink { --plan-accent: #ec4899; } +.aksrt-pricing__card.accent-purple { --plan-accent: #a855f7; } +.aksrt-pricing__card.accent-blue { --plan-accent: #3b82f6; } +.aksrt-pricing__card.accent-green { --plan-accent: #22c55e; } +.aksrt-pricing__card.accent-yellow { --plan-accent: #eab308; } +.aksrt-pricing__card.accent-cyan { --plan-accent: #06b6d4; } +.aksrt-pricing__badge { + position: absolute; + top: 12px; + right: 12px; + font-size: 11px; + padding: 2px 8px; + border-radius: 999px; + background: var(--plan-accent, #3b82f6); + color: #fff; +} +.aksrt-pricing__name { font-size: 18px; font-weight: 700; margin: 0 0 6px; } +.aksrt-pricing__desc { font-size: 13px; color: var(--muted-2-color, #888); margin: 0 0 12px; } +.aksrt-pricing__price { display: flex; align-items: baseline; gap: 10px; margin-bottom: 12px; } +.aksrt-pricing__main { font-size: 32px; font-weight: 800; color: var(--plan-accent, #3b82f6); line-height: 1; } +.aksrt-pricing__mark { font-size: .55em; margin-right: 2px; } +.aksrt-pricing__original { font-size: 13px; color: #999; text-decoration: line-through; } +.aksrt-pricing__features { list-style: none; margin: 0 0 16px; padding: 0; flex: 1; } +.aksrt-pricing__features li { display: flex; gap: 8px; font-size: 13px; padding: 5px 0; } +.aksrt-pricing__features i { color: var(--plan-accent, #3b82f6); margin-top: 2px; } +.aksrt-pricing__btn { + display: block; + text-align: center; + padding: 10px 14px; + border-radius: 999px; + background: var(--plan-accent, #3b82f6); + color: #fff !important; + text-decoration: none; + font-weight: 600; +} +.aksrt-pricing__trust { + display: flex; + flex-wrap: wrap; + gap: 14px 22px; + margin-top: 18px; + justify-content: center; +} +.aksrt-pricing__trust-item { display: flex; gap: 8px; align-items: flex-start; font-size: 13px; } +.aksrt-pricing__trust-item i { color: var(--color-primary, #3b82f6); margin-top: 2px; } +.aksrt-pricing__trust-item strong { display: block; } +.aksrt-pricing__trust-item span { color: var(--muted-2-color, #888); font-size: 12px; } + +/* ---- FAQ ---- */ +.aksrt-faq { margin-bottom: 16px; } +.aksrt-faq__search { display: flex; gap: 8px; margin-bottom: 14px; } +.aksrt-faq__search-input { + flex: 1; + border: 1px solid var(--color-line, #e5e7eb); + border-radius: 8px; + padding: 8px 12px; +} +.aksrt-faq__search-btn { + border: 0; + border-radius: 8px; + padding: 8px 14px; + background: var(--color-primary, #3b82f6); + color: #fff; + cursor: pointer; +} +.aksrt-faq__item { + border: 1px solid var(--color-line, #e5e7eb); + border-radius: 10px; + padding: 0; + margin-bottom: 10px; + background: var(--surface-1, #fff); +} +.aksrt-faq__q { + list-style: none; + display: flex; + align-items: center; + gap: 10px; + padding: 12px 14px; + cursor: pointer; + font-weight: 600; +} +.aksrt-faq__q::-webkit-details-marker { display: none; } +.aksrt-faq__num { + width: 28px; + height: 28px; + border-radius: 8px; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 12px; + background: var(--surface-2, #f3f4f6); + color: var(--color-primary, #3b82f6); + flex-shrink: 0; +} +.aksrt-faq__a { + padding: 0 14px 14px 52px; + font-size: 13px; + color: var(--muted-2-color, #666); + line-height: 1.6; +} +.aksrt-faq__aside { display: grid; gap: 12px; margin-top: 14px; } +.aksrt-faq__doc, .aksrt-faq__help { + border-radius: 10px; + border: 1px solid var(--color-line, #e5e7eb); + padding: 14px; + background: var(--surface-2, #f8fafc); +} +.aksrt-faq__help-btn { + display: inline-block; + margin-top: 8px; + padding: 6px 12px; + border-radius: 999px; + background: var(--color-primary, #3b82f6); + color: #fff !important; + text-decoration: none; + font-size: 13px; +} + +/* ---- Stats ---- */ +.aksrt-stats { margin-bottom: 16px; } +.aksrt-stats__intro { margin-bottom: 14px; } +.aksrt-stats__intro-title { font-size: 22px; margin: 0 0 6px; } +.aksrt-stats__intro-sub { color: var(--muted-2-color, #888); margin: 0; font-size: 13px; } +.aksrt-stats__grid { + display: grid; + grid-template-columns: repeat(var(--aksrt-stats-pc, 4), minmax(0, 1fr)); + gap: 12px; +} +@media (max-width: 767.98px) { + .aksrt-stats__grid { + grid-template-columns: repeat(var(--aksrt-stats-m, 2), minmax(0, 1fr)); + } +} +.aksrt-stats__item { + border-radius: 12px; + padding: 16px 14px; + background: var(--surface-1, #fff); + border: 1px solid var(--color-line, #e5e7eb); + text-align: center; +} +.aksrt-stats__item.color-pink { --stat-c: #ec4899; } +.aksrt-stats__item.color-purple { --stat-c: #a855f7; } +.aksrt-stats__item.color-orange { --stat-c: #f97316; } +.aksrt-stats__item.color-blue { --stat-c: #3b82f6; } +.aksrt-stats__item.color-green { --stat-c: #22c55e; } +.aksrt-stats__icon { font-size: 20px; color: var(--stat-c, #3b82f6); margin-bottom: 8px; } +.aksrt-stats__value { font-size: 28px; font-weight: 800; color: var(--stat-c, #3b82f6); line-height: 1.1; } +.aksrt-stats__prefix, .aksrt-stats__unit { font-size: .55em; font-weight: 600; } +.aksrt-stats__label { margin-top: 6px; font-size: 12px; color: var(--muted-2-color, #888); } + +/* ---- Forum topic lists ---- */ +.aksrt-ftopic-list { list-style: none; margin: 0; padding: 0; } +.aksrt-ftopic-list__item { + display: flex; + align-items: flex-start; + gap: 8px; + padding: 8px 0; + border-bottom: 1px dashed var(--color-line, #e5e7eb); +} +.aksrt-ftopic-list__item:last-child { border-bottom: 0; } +.aksrt-ftopic-list__link { + flex: 1; + min-width: 0; + color: inherit; + text-decoration: none; + font-size: 13px; + line-height: 1.45; +} +.aksrt-ftopic-list__views { + flex-shrink: 0; + font-size: 11px; + color: var(--muted-2-color, #999); +} +.aksrt-ftopic-list.is-detail .aksrt-ftopic-list__item { flex-direction: column; gap: 4px; } +.aksrt-ftopic-list__meta { + display: flex; + gap: 10px; + font-size: 11px; + color: var(--muted-2-color, #999); +} + +/* Forum widget sidebar shell */ +.aksrt-forum-widget-zone { margin-top: 12px; } +.aksrt-forum-widget-zone .zib-widget { margin-bottom: 14px; } diff --git a/inc/modules/widgets-register.php b/inc/modules/widgets-register.php index 124e3d15..885cdc44 100644 --- a/inc/modules/widgets-register.php +++ b/inc/modules/widgets-register.php @@ -63,6 +63,11 @@ function aksrt_register_widgets() { register_widget('\AKSRT\Widgets\WidgetTextTitle'); register_widget('\AKSRT\Widgets\WidgetUserLists'); + register_widget('\AKSRT\Widgets\WidgetPricingPlans'); + register_widget('\AKSRT\Widgets\WidgetFaq'); + register_widget('\AKSRT\Widgets\WidgetStats'); + + // Forum widgets if (class_exists('\AKSRT\Widgets\WidgetForumBoardList')) { register_widget('\AKSRT\Widgets\WidgetForumBoardList'); @@ -82,6 +87,9 @@ function aksrt_register_widgets() { if (class_exists('\AKSRT\Widgets\WidgetForumPlateModerator')) { register_widget('\AKSRT\Widgets\WidgetForumPlateModerator'); } + if (class_exists('\AKSRT\Widgets\WidgetForumTopicLists')) { + register_widget('\AKSRT\Widgets\WidgetForumTopicLists'); + } // Shop product widgets (§6.9) register_widget('\AKSRT\Widgets\WidgetProductList'); diff --git a/src/AKSRT/Core/SetupPart1.php b/src/AKSRT/Core/SetupPart1.php index 61345257..26634b99 100644 --- a/src/AKSRT/Core/SetupPart1.php +++ b/src/AKSRT/Core/SetupPart1.php @@ -382,6 +382,29 @@ trait SetupPart1 { 'after_title' => $title_after, ]); + // ── Forum / BBS widget areas (zibll bbs_* parity lite) ── + $forum_widgets = [ + 'bbs_home_sidebar' => [__('论坛首页 · 侧栏', 'aksrt'), __('论坛首页侧栏区域。', 'aksrt')], + 'bbs_home_top_fluid' => [__('论坛首页 · 顶部全宽', 'aksrt'), __('论坛首页顶部全宽。', 'aksrt')], + 'bbs_plate_sidebar' => [__('论坛版块 · 侧栏', 'aksrt'), __('版块页侧栏。', 'aksrt')], + 'bbs_plate_top_fluid' => [__('论坛版块 · 顶部全宽', 'aksrt'), __('版块页顶部全宽。', 'aksrt')], + 'bbs_posts_sidebar' => [__('论坛帖子 · 侧栏', 'aksrt'), __('帖子页侧栏挂件区。', 'aksrt')], + 'bbs_posts_top_fluid' => [__('论坛帖子 · 顶部全宽', 'aksrt'), __('帖子页顶部全宽。', 'aksrt')], + 'bbs_new_posts_sidebar_top' => [__('论坛发帖 · 侧栏顶部', 'aksrt'), __('发帖页侧栏顶部。', 'aksrt')], + 'bbs_new_posts_sidebar_bottom' => [__('论坛发帖 · 侧栏底部', 'aksrt'), __('发帖页侧栏底部。', 'aksrt')], + ]; + foreach ($forum_widgets as $sid => $meta) { + register_sidebar([ + 'id' => $sid, + 'name' => $meta[0], + 'description' => $meta[1], + 'before_widget' => $card_wrapper_before, + 'after_widget' => '', + 'before_title' => $title_before, + 'after_title' => $title_after, + ]); + } + // ── Shop widget areas (§6.9) ── $shop_widgets = [ 'shop_product_top_fluid' => ['商品详情页 · 顶部通栏', '商品详情页顶部全宽区域。'], diff --git a/src/AKSRT/Widgets/WidgetFaq.php b/src/AKSRT/Widgets/WidgetFaq.php new file mode 100644 index 00000000..138a4f8b --- /dev/null +++ b/src/AKSRT/Widgets/WidgetFaq.php @@ -0,0 +1,189 @@ + esc_html__('可折叠 FAQ 列表,支持搜索框与侧栏文档卡。', 'aksrt'), + 'classname' => 'widget_aksrt_faq', + ] + ); + } + + public function widget($args, $instance): void { + $faqs = $this->normalize_faqs($instance['faqs'] ?? []); + if (!$faqs) { + return; + } + + 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 + } + + $uid = 'aksrt-faq-' . esc_attr($this->id); + echo '
' . esc_html($doc_desc) . '
'; + } + echo '' . esc_html($sub) . '
'; + } + if ($help_btn !== '' && $help_url !== '') { + echo '' . esc_html($help_btn) . ''; + } + echo '+ +
++ +
++ +
++ +
++ +
++ +
++ +
++ +
+ sanitize_text_field((string) ($new_instance['title'] ?? '')), + 'faqs' => $this->normalize_faqs($new_instance['faqs'] ?? []), + 'search_s' => !empty($new_instance['search_s']) ? 1 : 0, + 'search_placeholder' => sanitize_text_field((string) ($new_instance['search_placeholder'] ?? '')), + 'search_btn_text' => sanitize_text_field((string) ($new_instance['search_btn_text'] ?? '')), + 'doc_title' => sanitize_text_field((string) ($new_instance['doc_title'] ?? '')), + 'doc_desc' => sanitize_text_field((string) ($new_instance['doc_desc'] ?? '')), + 'help_title' => sanitize_text_field((string) ($new_instance['help_title'] ?? '')), + 'help_subtitle' => sanitize_text_field((string) ($new_instance['help_subtitle'] ?? '')), + 'help_btn_text' => sanitize_text_field((string) ($new_instance['help_btn_text'] ?? '')), + 'help_btn_url' => esc_url_raw(trim((string) ($new_instance['help_btn_url'] ?? ''))), + ]; + } +} diff --git a/src/AKSRT/Widgets/WidgetForumTopicLists.php b/src/AKSRT/Widgets/WidgetForumTopicLists.php new file mode 100644 index 00000000..72d559e1 --- /dev/null +++ b/src/AKSRT/Widgets/WidgetForumTopicLists.php @@ -0,0 +1,176 @@ + esc_html__('按排序/版块筛选展示论坛帖子。', 'aksrt'), + 'classname' => 'widget_aksrt_forum_topic_lists', + ] + ); + } + + public function widget($args, $instance): void { + if (!post_type_exists('aksrt_forum_topic')) { + return; + } + + $count = max(1, min(30, (int) ($instance['count'] ?? 8))); + $orderby = sanitize_key((string) ($instance['orderby'] ?? 'date')); + $style = sanitize_key((string) ($instance['style'] ?? 'mini')); + $plate_id = (int) ($instance['plate_id'] ?? 0); + $current_plate = !empty($instance['current_plate']); + + if ($current_plate) { + $resolved = $this->resolve_current_plate_id(); + if ($resolved <= 0) { + return; + } + $plate_id = $resolved; + } + + $query = [ + 'post_type' => 'aksrt_forum_topic', + 'post_status' => 'publish', + 'posts_per_page' => $count, + 'order' => 'DESC', + ]; + + switch ($orderby) { + case 'views': + $query['meta_key'] = '_aksrt_views'; + $query['orderby'] = 'meta_value_num'; + break; + case 'comments': + $query['orderby'] = 'comment_count'; + break; + case 'modified': + $query['orderby'] = 'modified'; + break; + default: + $query['orderby'] = 'date'; + } + + if ($plate_id > 0) { + $query['meta_query'] = [[ + 'key' => '_aksrt_plate_id', + 'value' => $plate_id, + ]]; + } + + $q = new \WP_Query($query); + if (!$q->have_posts()) { + wp_reset_postdata(); + return; + } + + 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 + } + + $list_class = $style === 'detail' ? 'aksrt-ftopic-list is-detail' : 'aksrt-ftopic-list is-mini'; + echo '+ +
++ +
++ +
++ +
++ +
++ +
+ sanitize_text_field((string) ($new_instance['title'] ?? '')), + 'count' => max(1, min(30, (int) ($new_instance['count'] ?? 8))), + 'orderby' => $orderby, + 'style' => ($new_instance['style'] ?? '') === 'detail' ? 'detail' : 'mini', + 'plate_id' => max(0, (int) ($new_instance['plate_id'] ?? 0)), + 'current_plate' => !empty($new_instance['current_plate']) ? 1 : 0, + ]; + } +} diff --git a/src/AKSRT/Widgets/WidgetPricingPlans.php b/src/AKSRT/Widgets/WidgetPricingPlans.php new file mode 100644 index 00000000..a6d11bdb --- /dev/null +++ b/src/AKSRT/Widgets/WidgetPricingPlans.php @@ -0,0 +1,236 @@ + esc_html__('多列定价卡片 + 信任条(落地页/VIP 介绍)。', 'aksrt'), + 'classname' => 'widget_aksrt_pricing_plans', + ] + ); + } + + public function widget($args, $instance): void { + $plans = $this->normalize_plans($instance['lists'] ?? $instance['plans'] ?? []); + if (!$plans) { + return; + } + $trust = $this->normalize_trust($instance['trust_items'] ?? []); + $currency = sanitize_text_field((string) ($instance['currency'] ?? '¥')); + if ($currency === '') { + $currency = '¥'; + } + + echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + $title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base); + if ($title !== '') { + echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + echo '' . esc_html($plan['desc']) . '
'; + } + $price = (float) $plan['price']; + $show = (float) $plan['show_price']; + echo '+ +
++ +
++ +
++ +
++ +
+ sanitize_text_field((string) ($new_instance['title'] ?? '')), + 'currency' => sanitize_text_field((string) ($new_instance['currency'] ?? '¥')), + 'lists' => $this->normalize_plans($new_instance['lists'] ?? []), + 'trust_items' => $this->normalize_trust($new_instance['trust_items'] ?? []), + 'go_link' => !empty($new_instance['go_link']) ? 1 : 0, + ]; + } +} diff --git a/src/AKSRT/Widgets/WidgetStats.php b/src/AKSRT/Widgets/WidgetStats.php new file mode 100644 index 00000000..5614ee31 --- /dev/null +++ b/src/AKSRT/Widgets/WidgetStats.php @@ -0,0 +1,167 @@ + esc_html__('数字统计卡片网格(站点数据展示)。', 'aksrt'), + 'classname' => 'widget_aksrt_stats', + ] + ); + } + + public function widget($args, $instance): void { + $stats = $this->normalize_stats($instance['stats'] ?? []); + if (!$stats) { + return; + } + $cols = max(1, min(4, (int) ($instance['columns'] ?? 4))); + $m_cols = max(1, min(3, (int) ($instance['mobile_columns'] ?? 2))); + $intro_title = trim((string) ($instance['intro_title'] ?? '')); + $intro_sub = trim((string) ($instance['intro_subtitle'] ?? '')); + + 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-stats-pc:%d;--aksrt-stats-m:%d;', $cols, $m_cols); + echo '' . esc_html($intro_sub) . '
'; + } + echo '+ +
++ +
++ + +
++ +
++ +
+ sanitize_text_field((string) ($new_instance['title'] ?? '')), + 'stats' => $this->normalize_stats($new_instance['stats'] ?? []), + 'columns' => max(1, min(4, (int) ($new_instance['columns'] ?? 4))), + 'mobile_columns' => max(1, min(3, (int) ($new_instance['mobile_columns'] ?? 2))), + 'intro_title' => sanitize_text_field((string) ($new_instance['intro_title'] ?? '')), + 'intro_subtitle' => sanitize_text_field((string) ($new_instance['intro_subtitle'] ?? '')), + ]; + } +} diff --git a/templates/forum/forum-compose.php b/templates/forum/forum-compose.php index 35ba22af..7a09320b 100644 --- a/templates/forum/forum-compose.php +++ b/templates/forum/forum-compose.php @@ -436,6 +436,19 @@ $initial_content = $draft_data ? $draft_data['content'] : ''; + + + + diff --git a/templates/forum/forum-plate.php b/templates/forum/forum-plate.php index 2fcebeed..e6feb75e 100644 --- a/templates/forum/forum-plate.php +++ b/templates/forum/forum-plate.php @@ -56,8 +56,9 @@ $max_pages = (int) ($view['pagination']['max_pages'] ?? 1); ?>