diff --git a/assets/admin/src/constants/sections/page-display.js b/assets/admin/src/constants/sections/page-display.js index 7bb773ce..9430faf1 100644 --- a/assets/admin/src/constants/sections/page-display.js +++ b/assets/admin/src/constants/sections/page-display.js @@ -86,16 +86,6 @@ export const pageDisplaySection = { desc: '首页内容模型、主列表、分类 Tab 与首页交互配置。', component: 'HomepageSettings', }), - schemaPage({ - key: 'page-display-float-btn', - name: 'PageDisplayFloatBtn', - label: '悬浮按钮', - route: '/page-display/float-btn', - title: '悬浮按钮', - desc: '悬浮按钮的显示规则、样式与滚动隐藏设置。', - canonicalDomain: 'page_display', - section: 'float-btn', - }), schemaPage({ key: 'page-display-category', name: 'PageDisplayCategory', diff --git a/assets/admin/src/pages/content/HeroManager.vue b/assets/admin/src/pages/content/HeroManager.vue index 4963f9fb..23e1779b 100644 --- a/assets/admin/src/pages/content/HeroManager.vue +++ b/assets/admin/src/pages/content/HeroManager.vue @@ -5,21 +5,13 @@ - Hero 显示开关 - 顶部多功能组件(搜索/卡片/轮播参数) + 显示范围 + 顶部多功能组件(页面类型/搜索/卡片) - - - - - 关闭后,首页 Hero 区块将不再渲染。 - - - - - 保存 Hero 开关 - + + 幻灯片内容在此维护。页面类型启用、设备规则、搜索覆盖层与图标卡片请到「顶部多功能组件」配置;全不选页面类型即为关闭。 + @@ -216,10 +208,7 @@ function goHeaderComponents() { } const loading = ref(true) const saving = ref(false) -const settingsLoading = ref(false) -const settingsSaving = ref(false) const slides = ref([]) -const heroEnabled = ref(true) const modalShow = ref(false) const editing = ref(emptyForm()) @@ -348,36 +337,7 @@ async function loadSlides() { } } -async function loadHeroSettings() { - settingsLoading.value = true - try { - const res = await api.getHomepageSettings() - const data = res?.data || {} - heroEnabled.value = data.homepage_module_hero_enabled !== false - } catch (e) { - message.error(e.message || '加载 Hero 开关失败') - } finally { - settingsLoading.value = false - } -} -async function saveHeroSettings() { - settingsSaving.value = true - try { - const res = await api.saveHomepageSettings({ - homepage_module_hero_enabled: heroEnabled.value, - }) - if (res?.code === 'success') { - message.success('Hero 开关已保存') - return - } - throw new Error(res?.message || '保存 Hero 开关失败') - } catch (e) { - message.error(e.message || '保存 Hero 开关失败') - } finally { - settingsSaving.value = false - } -} function openCreate() { editing.value = emptyForm() @@ -495,7 +455,6 @@ function handleDelete(row) { onMounted(() => { loadSlides() - loadHeroSettings() }) diff --git a/assets/admin/src/schema/sections/page-display/index.js b/assets/admin/src/schema/sections/page-display/index.js index 20b2e71c..28db59bf 100644 --- a/assets/admin/src/schema/sections/page-display/index.js +++ b/assets/admin/src/schema/sections/page-display/index.js @@ -626,14 +626,17 @@ export const pageDisplaySchemas = { { key: 'header_components_cards_enabled', label: '启用图标卡片', type: 'switch', default: false, help: '在顶部组件区域显示图标卡片入口' }, { key: 'header_components_show_on', label: '按页面类型启用', type: 'checkbox', default: ['home'], options: [ { label: '首页', value: 'home' }, - { label: '文章', value: 'single' }, - { label: '页面', value: 'page' }, - { label: '分类', value: 'category' }, - { label: '标签', value: 'tag' }, - { label: '搜索', value: 'search' }, - { label: '归档', value: 'archive' }, - { label: '专题', value: 'special' }, - ], help: '选择哪些页面类型显示顶部组件,不选则关闭' }, + { label: '专题页', value: 'topics' }, + { label: '分类页', value: 'category' }, + { label: '标签页', value: 'tag' }, + { label: '用户页', value: 'author' }, + { label: '文章页', value: 'single' }, + { label: '搜索页', value: 'search' }, + { label: '[论坛]首页', value: 'forum_home' }, + { label: '[论坛]板块页面', value: 'forum_plate' }, + { label: '[论坛]帖子页面', value: 'forum_post' }, + { label: '其它页面', value: 'page' }, + ], help: '选择启用顶部多功能组件的页面类型,一个都不选则为关闭此功能' }, { key: 'header_components_device_visibility', label: '设备显示规则', type: 'radio', default: '', options: [ { label: '全部', value: '' }, { label: '仅电脑', value: 'only_pc' }, @@ -742,74 +745,6 @@ export const pageDisplaySchemas = { ], }, - /** - * Floating button settings - */ - 'float-btn': { - key: 'page_display.float-btn', - title: '悬浮按钮', - icon: 'fas fa-circle-chevron-up', - description: '配置页面悬浮按钮的显示规则、样式和滚动隐藏行为。', - defaults: { - float_btn_style: 'round', - float_btn_position: 'bottom', - float_btn_scroll_hide: false, - float_btn_scroll_down_hide: true, - float_btn_max: 5, - float_btn_size: 44, - float_btn_spacing: 10, - float_btn_shadow: true, - float_btn_animation: true, - float_btn_opacity: 100, - float_btn_active_color: '', - float_btn_bg_color: '', - float_btn_pc_s: true, - float_btn_m_s: true, - float_btn_filter_css: '', - }, - groups: [ - { - key: 'basic', - title: '悬浮按钮设置', - icon: 'fas fa-circle-notch', - fields: [ - { - key: 'float_btn_style', - label: '按钮风格', - type: 'radio', - default: 'round', - options: [ - { label: '圆形', value: 'round' }, - { label: '方形', value: 'square' }, - ], - }, - { - key: 'float_btn_position', - label: '按钮位置', - type: 'radio', - default: 'bottom', - options: [ - { label: '底部', value: 'bottom' }, - { label: '居中', value: 'center' }, - ], - }, - { key: 'float_btn_scroll_hide', label: '滚动时隐藏', type: 'switch', default: false }, - { key: 'float_btn_scroll_down_hide', label: '下滑时隐藏', type: 'switch', default: true }, - { key: 'float_btn_max', label: '最大显示数', type: 'number', default: 5, min: 1, max: 10 }, - { key: 'float_btn_size', label: '按钮尺寸', type: 'number', default: 44, min: 32, max: 64 }, - { key: 'float_btn_spacing', label: '按钮间距', type: 'number', default: 10, min: 4, max: 24 }, - { key: 'float_btn_shadow', label: '显示阴影', type: 'switch', default: true }, - { key: 'float_btn_animation', label: '悬停动画', type: 'switch', default: true }, - { key: 'float_btn_opacity', label: '按钮透明度', type: 'number', default: 100, min: 30, max: 100 }, - { key: 'float_btn_active_color', label: '激活颜色', type: 'color', default: '' }, - { key: 'float_btn_bg_color', label: '背景颜色', type: 'color', default: '' }, - { key: 'float_btn_pc_s', label: 'PC端显示', type: 'switch', default: true }, - { key: 'float_btn_m_s', label: '移动端显示', type: 'switch', default: true }, - { key: 'float_btn_filter_css', label: '过滤 CSS', type: 'textarea', default: '' }, - ], - }, - ], - }, footer: { key: 'page_display.footer', title: '底部页脚', diff --git a/assets/js/core/article.js b/assets/js/core/article.js index f4e02461..b8dd91c7 100644 --- a/assets/js/core/article.js +++ b/assets/js/core/article.js @@ -157,85 +157,116 @@ return null; } + function collectActionButtons(scope, selector) { + scope = scope || document; + var list = []; + if (!scope) return list; + if (scope.nodeType === 1 && scope.matches && scope.matches(selector)) { + list.push(scope); + } + if (typeof scope.querySelectorAll === "function") { + var found = scope.querySelectorAll(selector); + for (var i = 0; i < found.length; i++) list.push(found[i]); + } else if (scope.nodeType === 9) { + var all = document.querySelectorAll(selector); + for (var j = 0; j < all.length; j++) list.push(all[j]); + } + // also keep legacy id hooks + var legacyId = selector.indexOf("action-like") >= 0 ? "action-like" : (selector.indexOf("action-favorite") >= 0 ? "action-favorite" : ""); + if (legacyId) { + var legacy = findInScope(scope, legacyId); + if (legacy && list.indexOf(legacy) === -1) list.push(legacy); + } + return list; + } + function initInteractionBar(root) { var scope = root || document; - // Element roots (AJAX rehydrate) do not implement getElementById - var likeBtn = findInScope(scope, "action-like"); - var favBtn = findInScope(scope, "action-favorite"); + // Bind all like/favorite controls (in-content + footer tabbar extend). + var likeBtns = collectActionButtons(scope, ".action-like[data-post-id]"); + var favBtns = collectActionButtons(scope, ".action-favorite[data-post-id]"); - if (likeBtn && likeBtn.dataset.aksrtInteractionBound) return; - if (favBtn && favBtn.dataset.aksrtInteractionBound) return; - - if (likeBtn) { - var likePostId = likeBtn.dataset.postId ? String(likeBtn.dataset.postId) : ""; - - function applyLikeState(isActive, count) { - likeBtn.dataset.initialActive = isActive ? "1" : "0"; - likeBtn.classList.toggle("is-active", isActive); - var likeIcon = likeBtn.querySelector("i"); - if (likeIcon) { - likeIcon.className = isActive ? "fas fa-heart" : "far fa-heart"; - } - if ("number" === typeof count && !isNaN(count)) { - setBaseStatCount("likes", count); - } else { - updateStatCounts("likes", getBaseStatCount("likes")); - } - } - - applyLikeState(likeBtn.dataset.initialActive === "1", getBaseStatCount("likes")); - - likeBtn.addEventListener("click", function () { - if (!likePostId || likeBtn.disabled) return; - var nextState = likeBtn.dataset.initialActive !== "1"; - setButtonBusy(likeBtn, true); - requestInteraction("aksrt_toggle_like", likePostId, nextState).then(function (data) { - applyLikeState(!!data.active, parseInt(data.count, 10) || 0); - showToast(data.active ? "感谢你的点赞" : "已取消点赞", "success"); - }).catch(function (error) { - showToast(error && error.message ? error.message : "点赞失败", "error"); - }).finally(function () { - setButtonBusy(likeBtn, false); - }); - }); + function paintLike(btn, isActive) { + btn.dataset.initialActive = isActive ? "1" : "0"; + btn.classList.toggle("is-active", isActive); + var icon = btn.querySelector("i"); + if (icon) icon.className = isActive ? "fas fa-heart" : "far fa-heart"; } - if (favBtn) { - var favoritePostId = favBtn.dataset.postId ? String(favBtn.dataset.postId) : ""; - - function applyFavoriteState(isActive, count) { - favBtn.dataset.initialActive = isActive ? "1" : "0"; - favBtn.classList.toggle("is-active", isActive); - var favoriteIcon = favBtn.querySelector("i"); - if (favoriteIcon) { - favoriteIcon.className = isActive ? "fas fa-star" : "far fa-star"; - } - if ("number" === typeof count && !isNaN(count)) { - setBaseStatCount("favorites", count); - } else { - updateStatCounts("favorites", getBaseStatCount("favorites")); - } - } - - applyFavoriteState(favBtn.dataset.initialActive === "1", getBaseStatCount("favorites")); - - favBtn.addEventListener("click", function () { - if (!favoritePostId || favBtn.disabled) return; - var nextState = favBtn.dataset.initialActive !== "1"; - setButtonBusy(favBtn, true); - requestInteraction("aksrt_toggle_favorite", favoritePostId, nextState).then(function (data) { - applyFavoriteState(!!data.active, parseInt(data.count, 10) || 0); - showToast(data.active ? "收藏成功" : "已取消收藏", "success"); - }).catch(function (error) { - showToast(error && error.message ? error.message : "收藏失败", "error"); - }).finally(function () { - setButtonBusy(favBtn, false); - }); - }); + function paintFavorite(btn, isActive) { + btn.dataset.initialActive = isActive ? "1" : "0"; + btn.classList.toggle("is-active", isActive); + var icon = btn.querySelector("i"); + if (icon) icon.className = isActive ? "fas fa-star" : "far fa-star"; } - if (likeBtn) likeBtn.dataset.aksrtInteractionBound = "1"; - if (favBtn) favBtn.dataset.aksrtInteractionBound = "1"; + function applyLikeState(isActive, count) { + for (var i = 0; i < likeBtns.length; i++) paintLike(likeBtns[i], isActive); + if ("number" === typeof count && !isNaN(count)) { + setBaseStatCount("likes", count); + } else { + updateStatCounts("likes", getBaseStatCount("likes")); + } + } + + function applyFavoriteState(isActive, count) { + for (var i = 0; i < favBtns.length; i++) paintFavorite(favBtns[i], isActive); + if ("number" === typeof count && !isNaN(count)) { + setBaseStatCount("favorites", count); + } else { + updateStatCounts("favorites", getBaseStatCount("favorites")); + } + } + + if (likeBtns.length) { + var seedLike = likeBtns[0]; + if (!seedLike.dataset.aksrtInteractionBound) { + applyLikeState(seedLike.dataset.initialActive === "1", getBaseStatCount("likes")); + likeBtns.forEach(function (likeBtn) { + if (likeBtn.dataset.aksrtInteractionBound) return; + likeBtn.dataset.aksrtInteractionBound = "1"; + likeBtn.addEventListener("click", function () { + var postId = likeBtn.dataset.postId ? String(likeBtn.dataset.postId) : ""; + if (!postId || likeBtn.disabled) return; + var nextState = likeBtn.dataset.initialActive !== "1"; + setButtonBusy(likeBtn, true); + requestInteraction("aksrt_toggle_like", postId, nextState).then(function (data) { + applyLikeState(!!data.active, parseInt(data.count, 10) || 0); + showToast(data.active ? "感谢你的点赞" : "已取消点赞", "success"); + }).catch(function (error) { + showToast(error && error.message ? error.message : "点赞失败", "error"); + }).finally(function () { + setButtonBusy(likeBtn, false); + }); + }); + }); + } + } + + if (favBtns.length) { + var seedFav = favBtns[0]; + if (!seedFav.dataset.aksrtInteractionBound) { + applyFavoriteState(seedFav.dataset.initialActive === "1", getBaseStatCount("favorites")); + favBtns.forEach(function (favBtn) { + if (favBtn.dataset.aksrtInteractionBound) return; + favBtn.dataset.aksrtInteractionBound = "1"; + favBtn.addEventListener("click", function () { + var postId = favBtn.dataset.postId ? String(favBtn.dataset.postId) : ""; + if (!postId || favBtn.disabled) return; + var nextState = favBtn.dataset.initialActive !== "1"; + setButtonBusy(favBtn, true); + requestInteraction("aksrt_toggle_favorite", postId, nextState).then(function (data) { + applyFavoriteState(!!data.active, parseInt(data.count, 10) || 0); + showToast(data.active ? "收藏成功" : "已取消收藏", "success"); + }).catch(function (error) { + showToast(error && error.message ? error.message : "收藏失败", "error"); + }).finally(function () { + setButtonBusy(favBtn, false); + }); + }); + }); + } + } } window.AksrtFrontend.register("aksrt-search-clear", function () { initSearchClear(); }); diff --git a/assets/js/post-share.js b/assets/js/post-share.js index 7765328c..dd0234b3 100644 --- a/assets/js/post-share.js +++ b/assets/js/post-share.js @@ -123,8 +123,9 @@ } function initShareDropdown() { - var trigger = document.getElementById('action-share-trigger'); var dropdown = document.getElementById('share-dropdown'); + var triggers = Array.prototype.slice.call(document.querySelectorAll('#action-share-trigger, .action-share[data-type="share"], .footer-tabbar .action-share')).filter(function (el, idx, arr) { return arr.indexOf(el) === idx; }); + var trigger = triggers[0] || null; if (!trigger || !dropdown || dropdown.dataset.aksrtShareBound === '1') return; dropdown.dataset.aksrtShareBound = '1'; @@ -160,6 +161,16 @@ toggleDropdown(); }); + triggers.slice(1).forEach(function (extraTrigger) { + extraTrigger.addEventListener('click', function (event) { + event.preventDefault(); + event.stopPropagation(); + // Reuse primary trigger click semantics by synthetic toggle + trigger.click(); + }); + }); + + document.addEventListener('click', function (event) { if (!trigger.contains(event.target) && !dropdown.contains(event.target)) { closeDropdown(); diff --git a/footer.php b/footer.php index 36b787d0..0a37041a 100644 --- a/footer.php +++ b/footer.php @@ -471,27 +471,38 @@ if ($floating_enabled) : - + - - + + - + diff --git a/inc/classes/admin/schema/class-aksrt-admin-schema-page-display.php b/inc/classes/admin/schema/class-aksrt-admin-schema-page-display.php index bf7c8a04..023879a7 100644 --- a/inc/classes/admin/schema/class-aksrt-admin-schema-page-display.php +++ b/inc/classes/admin/schema/class-aksrt-admin-schema-page-display.php @@ -78,13 +78,16 @@ class AKSRT_Admin_Schema_Page_Display { ['key' => 'header_components_cards_enabled', 'label' => '启用图标卡片', 'type' => 'switch', 'default' => false], ['key' => 'header_components_show_on', 'label' => '按页面类型启用', 'type' => 'checkbox', 'default' => ['home'], 'options' => [ ['label' => '首页', 'value' => 'home'], - ['label' => '文章', 'value' => 'single'], - ['label' => '页面', 'value' => 'page'], - ['label' => '分类', 'value' => 'category'], - ['label' => '标签', 'value' => 'tag'], - ['label' => '搜索', 'value' => 'search'], - ['label' => '归档', 'value' => 'archive'], - ['label' => '专题', 'value' => 'special'], + ['label' => '专题页', 'value' => 'topics'], + ['label' => '分类页', 'value' => 'category'], + ['label' => '标签页', 'value' => 'tag'], + ['label' => '用户页', 'value' => 'author'], + ['label' => '文章页', 'value' => 'single'], + ['label' => '搜索页', 'value' => 'search'], + ['label' => '[论坛]首页', 'value' => 'forum_home'], + ['label' => '[论坛]板块页面', 'value' => 'forum_plate'], + ['label' => '[论坛]帖子页面', 'value' => 'forum_post'], + ['label' => '其它页面', 'value' => 'page'], ]], ['key' => 'header_components_device_visibility', 'label' => '设备显示规则', 'type' => 'radio', 'default' => '', 'options' => [ ['label' => '全部', 'value' => ''], @@ -494,37 +497,6 @@ class AKSRT_Admin_Schema_Page_Display { ]], ], ], - 'float-btn' => [ - 'key' => 'float-btn', - 'title' => '悬浮按钮', - 'version' => '1.1.0', - 'option_key' => 'aksrt_settings', - 'groups' => [ - ['key' => 'basic', 'title' => '悬浮按钮设置', 'fields' => [ - ['key' => 'float_btn_style', 'label' => '按钮风格', 'type' => 'radio', 'default' => 'round', 'options' => [ - ['label' => '圆形', 'value' => 'round'], - ['label' => '方形', 'value' => 'square'], - ]], - ['key' => 'float_btn_position', 'label' => '按钮位置', 'type' => 'radio', 'default' => 'bottom', 'options' => [ - ['label' => '底部', 'value' => 'bottom'], - ['label' => '居中', 'value' => 'center'], - ]], - ['key' => 'float_btn_scroll_hide', 'label' => '滚动时隐藏', 'type' => 'switch', 'default' => false], - ['key' => 'float_btn_scroll_down_hide', 'label' => '下滑时隐藏', 'type' => 'switch', 'default' => true], - ['key' => 'float_btn_max', 'label' => '最大显示数', 'type' => 'number', 'default' => 5, 'min' => 1, 'max' => 10], - ['key' => 'float_btn_size', 'label' => '按钮尺寸', 'type' => 'number', 'default' => 44, 'min' => 32, 'max' => 64], - ['key' => 'float_btn_spacing', 'label' => '按钮间距', 'type' => 'number', 'default' => 10, 'min' => 4, 'max' => 24], - ['key' => 'float_btn_shadow', 'label' => '显示阴影', 'type' => 'switch', 'default' => true], - ['key' => 'float_btn_animation', 'label' => '悬停动画', 'type' => 'switch', 'default' => true], - ['key' => 'float_btn_opacity', 'label' => '按钮透明度', 'type' => 'number', 'default' => 100, 'min' => 30, 'max' => 100], - ['key' => 'float_btn_active_color', 'label' => '激活颜色', 'type' => 'color', 'default' => ''], - ['key' => 'float_btn_bg_color', 'label' => '背景颜色', 'type' => 'color', 'default' => ''], - ['key' => 'float_btn_pc_s', 'label' => 'PC端显示', 'type' => 'switch', 'default' => true], - ['key' => 'float_btn_m_s', 'label' => '移动端显示', 'type' => 'switch', 'default' => true], - ['key' => 'float_btn_filter_css', 'label' => '过滤CSS', 'type' => 'textarea', 'default' => ''], - ]], - ], - ], ], ]; } diff --git a/inc/classes/scripts-traits/trait-aksrt-scripts-part-1b.php b/inc/classes/scripts-traits/trait-aksrt-scripts-part-1b.php index 49bafcca..9cfe7fc9 100644 --- a/inc/classes/scripts-traits/trait-aksrt-scripts-part-1b.php +++ b/inc/classes/scripts-traits/trait-aksrt-scripts-part-1b.php @@ -177,15 +177,19 @@ trait AKSRT_Scripts_Part_1b { // CMS 形态判定(可能在博客流页面也需要 grid-cms 卡片样式) $is_cms_mode = function_exists('aksrt_is_cms_mode') ? aksrt_is_cms_mode() : false; - // 首页/归档/搜索:卡片 + 顶部多功能组件 + 侧栏 + // 首页/归档/搜索:卡片 + 侧栏 if ($is_home_or_archive) { - $enqueue_module('header-components'); $enqueue_module('post-card'); $enqueue_module('widgets'); $enqueue_module('sidebar'); $enqueue_module('breadcrumb'); } + // 顶部多功能组件:按 page-type 启用结果入队(对齐 zibll,不绑死首页) + if (function_exists('aksrt_header_slide_is_show') && aksrt_header_slide_is_show()) { + $enqueue_module('header-components'); + } + // 单篇文章:所有 single 相关样式 if ($is_single_post) { $enqueue_module('single-post'); diff --git a/inc/classes/settings/class-aksrt-settings-display-page-groups.php b/inc/classes/settings/class-aksrt-settings-display-page-groups.php index ea16285a..6d71ed09 100644 --- a/inc/classes/settings/class-aksrt-settings-display-page-groups.php +++ b/inc/classes/settings/class-aksrt-settings-display-page-groups.php @@ -51,7 +51,24 @@ trait AKSRT_Settings_Display_Page_Groups { */ public static function get_header_components_settings(): array { $s = self::get_all(); + $allowed_page_types = [ + 'home', + 'topics', + 'category', + 'tag', + 'author', + 'single', + 'search', + 'forum_home', + 'forum_plate', + 'forum_post', + 'page', + ]; $show_on = isset($s['header_components_show_on']) ? (array) $s['header_components_show_on'] : ['home']; + $show_on = array_values(array_intersect( + array_map('sanitize_key', $show_on), + $allowed_page_types + )); $show_type = $s['header_components_device_visibility'] ?? ''; if (!in_array($show_type, ['', 'only_pc', 'only_sm'], true)) { $show_type = ''; @@ -60,7 +77,7 @@ trait AKSRT_Settings_Display_Page_Groups { 'search_enabled' => self::header_components_search_enabled(), 'search_config' => self::get_header_components_search_config(), 'cards_enabled' => self::header_components_cards_enabled(), - 'show_on_pages' => array_values(array_filter(array_map('sanitize_key', $show_on))), + 'show_on_pages' => $show_on, 'show_type' => $show_type, 'filter_blur' => isset($s['header_components_filter_blur_devices']) ? array_values(array_intersect(array_map('sanitize_key', (array) $s['header_components_filter_blur_devices']), ['pc', 'mobile'])) : [], 'card_show' => isset($s['header_components_card_show_devices']) ? array_values(array_intersect(array_map('sanitize_key', (array) $s['header_components_card_show_devices']), ['pc', 'mobile'])) : ['pc', 'mobile'], diff --git a/inc/helpers/aksrt-homepage.php b/inc/helpers/aksrt-homepage.php index 49a2d99c..cc5aced7 100644 --- a/inc/helpers/aksrt-homepage.php +++ b/inc/helpers/aksrt-homepage.php @@ -822,27 +822,165 @@ if (!function_exists('aksrt_homepage_render_section')) { } } +if (!function_exists('aksrt_header_components_page_type_keys')) { + /** + * Allowed page-type keys for header multi-function (zibll CFS_Module::page_type). + * + * @return list + */ + function aksrt_header_components_page_type_keys(): array { + return [ + 'home', + 'topics', + 'category', + 'tag', + 'author', + 'single', + 'search', + 'forum_home', + 'forum_plate', + 'forum_post', + 'page', + ]; + } +} + +if (!function_exists('aksrt_header_slide_excluded_page_templates')) { + /** + * Page templates excluded from the generic "page" type (zibll parity). + * + * @return list + */ + function aksrt_header_slide_excluded_page_templates(): array { + return [ + 'templates/pages/page-sign.php', + 'templates/pages/page-login.php', + 'templates/pages/page-register.php', + 'templates/pages/page-reset-password.php', + 'templates/pages/page-bind-account.php', + 'templates/pages/page-verify-email.php', + 'templates/pages/page-download.php', + 'templates/pages/page-download-file.php', + 'templates/pages/page-forums.php', + ]; + } +} + +if (!function_exists('aksrt_header_slide_match_page_type')) { + /** + * Whether the current main query matches a header-slider page-type key. + * + * Mirrors zibll zib_header_slide_is_show() matching rules. + */ + function aksrt_header_slide_match_page_type(string $type): bool { + global $wp_query; + + $type = sanitize_key($type); + if ($type === '' || !($wp_query instanceof WP_Query)) { + return false; + } + + $raw_post_type = $wp_query->get('post_type'); + if (is_array($raw_post_type)) { + $post_type = (string) reset($raw_post_type); + } else { + $post_type = (string) $raw_post_type; + } + + // Special keys that are not plain is_{$type} flags. + if ($type === 'topics') { + return is_tax('topics'); + } + + if ($type === 'forum_home') { + return is_page_template('templates/pages/page-forums.php'); + } + + if ($type === 'forum_plate') { + return is_singular('aksrt_forum_plate') || $post_type === 'aksrt_forum_plate'; + } + + if ($type === 'forum_post') { + return is_singular('aksrt_forum_topic') || $post_type === 'aksrt_forum_topic'; + } + + // Generic WP_Query is_* flags (home/category/tag/author/single/search/page). + $query_flag = 'is_' . $type; + if (empty($wp_query->{$query_flag})) { + return false; + } + + if ($type === 'single') { + // Only ordinary posts; CPTs use dedicated keys. + return $post_type === '' || $post_type === 'post'; + } + + if ($type === 'page') { + foreach (aksrt_header_slide_excluded_page_templates() as $template) { + if (is_page_template($template)) { + return false; + } + } + return true; + } + + return true; + } +} + if (!function_exists('aksrt_header_slide_is_show')) { /** * Whether the header multi-function slider should render (zibll header-slider contract). + * + * Master switch: non-empty header_components_show_on + current page-type match. + * Device rule: header_components_device_visibility (only_pc / only_sm). */ function aksrt_header_slide_is_show(): bool { - if (is_admin()) { + global $wp_query; + + if (is_admin() || !($wp_query instanceof WP_Query)) { return false; } - // Match zibll: only first page of the posts index / front page. - if (!(is_front_page() || is_home())) { + // Not on paginated views (zibll: query_vars['paged'] non-empty). + $paged = (int) $wp_query->get('paged'); + if ($paged > 1) { return false; } - if (function_exists('aksrt_homepage_get_current_page') && aksrt_homepage_get_current_page() > 1) { return false; } - return function_exists('aksrt_homepage_module_hero_enabled') - ? aksrt_homepage_module_hero_enabled() - : true; + $settings = class_exists('AKSRT_Settings') + ? AKSRT_Settings::get_header_components_settings() + : []; + $show_on = isset($settings['show_on_pages']) ? (array) $settings['show_on_pages'] : []; + $allowed = array_values(array_intersect( + array_map('sanitize_key', $show_on), + aksrt_header_components_page_type_keys() + )); + + // Empty selection = feature off (zibll header_slider_show). + if ($allowed === []) { + return false; + } + + // Device visibility (zibll header_slider_show_type). + $show_type = (string) ($settings['show_type'] ?? ''); + if ($show_type === 'only_pc' && wp_is_mobile()) { + return false; + } + if ($show_type === 'only_sm' && !wp_is_mobile()) { + return false; + } + + foreach ($allowed as $type) { + if (aksrt_header_slide_match_page_type($type)) { + return true; + } + } + + return false; } } diff --git a/inc/helpers/aksrt-template-tags.php b/inc/helpers/aksrt-template-tags.php index 0b26dba7..17ee5b9a 100644 --- a/inc/helpers/aksrt-template-tags.php +++ b/inc/helpers/aksrt-template-tags.php @@ -1283,3 +1283,104 @@ add_action('save_post_aksrt_banner', 'aksrt_flush_banner_cache'); add_action('deleted_post', 'aksrt_flush_banner_cache'); add_action('trashed_post', 'aksrt_flush_banner_cache'); endif; + + +if (!function_exists('aksrt_should_use_footer_tabbar_single_extend')) : + /** + * Whether single-post extended footer tabbar should replace the default items. + * Zibll: footer_tabbar_single.s === extend && is_single(post). + */ + function aksrt_should_use_footer_tabbar_single_extend(): bool { + if (!is_singular('post')) { + return false; + } + if (!class_exists('AKSRT_Settings') || !method_exists('AKSRT_Settings', 'get_footer_tabbar_single')) { + return false; + } + return AKSRT_Settings::get_footer_tabbar_single() === 'extend'; + } +endif; + +if (!function_exists('aksrt_render_footer_tabbar_single_extend')) : + /** + * Render single-post extended footer tabbar actions (like / favorite / share / comment). + * + * @param bool $show_text Whether to show text labels under icons. + */ + function aksrt_render_footer_tabbar_single_extend(bool $show_text = true): void { + $post_id = (int) get_the_ID(); + if ($post_id <= 0) { + return; + } + + $settings = function_exists('aksrt_resolve_single_post_display_settings') + ? aksrt_resolve_single_post_display_settings($post_id) + : []; + $like_on = !isset($settings['like_enabled']) || !empty($settings['like_enabled']); + $favorite_on = !isset($settings['favorite_enabled']) || !empty($settings['favorite_enabled']); + $share_on = !isset($settings['share_enabled']) || !empty($settings['share_enabled']); + $comments_open = comments_open($post_id) + && !(function_exists('aksrt_are_comments_closed') && aksrt_are_comments_closed()); + + $like_count = function_exists('aksrt_get_post_likes') ? (int) aksrt_get_post_likes($post_id) : 0; + $favorite_count = function_exists('aksrt_get_post_favorites') ? (int) aksrt_get_post_favorites($post_id) : 0; + $is_liked = function_exists('aksrt_is_post_liked') ? aksrt_is_post_liked($post_id) : false; + $is_favorited = function_exists('aksrt_is_post_favorited') ? aksrt_is_post_favorited($post_id) : false; + $comment_count = (int) get_comments_number($post_id); + + if ($like_on) : + ?> + + + + + + + + + + + + + + + + + + + + .header.show-slide{--header-color:' . esc_attr($_nav_color_for_header) . ';}'; + } + $swiper_config = [ 'effect' => $slider_cfg['effect'] ?? 'fade', 'loop' => $slide_count > 1 && ($slider_cfg['loop'] ?? true),
+ 幻灯片内容在此维护。页面类型启用、设备规则、搜索覆盖层与图标卡片请到「顶部多功能组件」配置;全不选页面类型即为关闭。 +