feat(nav): finish remaining parity gaps (image_select, AJAX search, senior UI)
- Layout image_select previews (zibll PNGs under assets/images/header-layout) - Localize aksrtAdminData.themeUri for admin SPA asset URLs - Layout-2 inline search AJAX suggestions via aksrt_search_suggestions - Appearance→Menus visual senior submenu editor (cards/columns) - Acceptance checklist + automated source contract report
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 18 KiB |
@@ -3,6 +3,14 @@
|
|||||||
* Each key corresponds to a sub-page under the "页面&显示" section.
|
* Each key corresponds to a sub-page under the "页面&显示" section.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/** Theme asset base for layout previews (admin SPA). */
|
||||||
|
function themeAsset(path) {
|
||||||
|
const base = (typeof window !== 'undefined' && window.aksrtAdminData && window.aksrtAdminData.themeUri)
|
||||||
|
? String(window.aksrtAdminData.themeUri).replace(/\/$/, '')
|
||||||
|
: ''
|
||||||
|
return base ? `${base}/${path.replace(/^\//, '')}` : path
|
||||||
|
}
|
||||||
|
|
||||||
/** Homepage sort options — keep in sync with AKSRT_Settings_Home_Cms::homepage_allowed_sorts() */
|
/** Homepage sort options — keep in sync with AKSRT_Settings_Home_Cms::homepage_allowed_sorts() */
|
||||||
const sortOptions = [
|
const sortOptions = [
|
||||||
{ label: '最近更新', value: 'modified' },
|
{ label: '最近更新', value: 'modified' },
|
||||||
@@ -79,23 +87,23 @@ export const pageDisplaySchemas = {
|
|||||||
{
|
{
|
||||||
key: 'header_layout',
|
key: 'header_layout',
|
||||||
label: '电脑端布局',
|
label: '电脑端布局',
|
||||||
type: 'radio',
|
type: 'image_select',
|
||||||
default: '2',
|
default: '1',
|
||||||
options: [
|
options: [
|
||||||
{ label: '布局 1(经典)', value: '1' },
|
{ label: '布局 1(经典)', value: '1', image: themeAsset('assets/images/header-layout/header_layout_1.png') },
|
||||||
{ label: '布局 2(内联搜索)', value: '2' },
|
{ label: '布局 2(内联搜索)', value: '2', image: themeAsset('assets/images/header-layout/header_layout_2.png') },
|
||||||
{ label: '布局 3(紧凑)', value: '3' },
|
{ label: '布局 3(紧凑)', value: '3', image: themeAsset('assets/images/header-layout/header_layout_3.png') },
|
||||||
],
|
],
|
||||||
help: '不同布局切换导航 DOM 结构',
|
help: '不同布局切换导航 DOM 结构(与 zibll image_select 一致)',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'mobile_header_layout',
|
key: 'mobile_header_layout',
|
||||||
label: '移动端布局',
|
label: '移动端布局',
|
||||||
type: 'radio',
|
type: 'image_select',
|
||||||
default: 'center',
|
default: 'center',
|
||||||
options: [
|
options: [
|
||||||
{ label: '居中(含搜索按钮)', value: 'center' },
|
{ label: '居中(含搜索按钮)', value: 'center', image: themeAsset('assets/images/header-layout/mobile_header_layout_center.png') },
|
||||||
{ label: '靠左', value: 'left' },
|
{ label: '靠左', value: 'left', image: themeAsset('assets/images/header-layout/mobile_header_layout_left.png') },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -130,3 +130,35 @@ body.mobile-navbar-show { overflow: hidden; }
|
|||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
z-index: 998;
|
z-index: 998;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navbar-inline-search-panel {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: calc(100% + 8px);
|
||||||
|
min-width: 280px;
|
||||||
|
max-width: 360px;
|
||||||
|
padding: 12px;
|
||||||
|
background: var(--main-bg-color, #fff);
|
||||||
|
border-radius: var(--main-radius, 8px);
|
||||||
|
box-shadow: 0 8px 24px rgba(0,0,0,.12);
|
||||||
|
z-index: 1002;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.navbar-form .form-group:hover .navbar-inline-search-panel,
|
||||||
|
.navbar-form .form-group:focus-within .navbar-inline-search-panel,
|
||||||
|
.navbar-inline-search-panel.is-open {
|
||||||
|
display: block;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
.navbar-inline-search-suggest__item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 6px 4px;
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.navbar-inline-search-suggest__item:hover { color: var(--focus-color); }
|
||||||
|
.navbar-inline-search-hots .but { margin: 0 4px 4px 0; }
|
||||||
|
.aksrt-sme-field { display: block; margin: 4px 0; }
|
||||||
|
.aksrt-sme-field span { display: block; font-size: 12px; color: #646970; }
|
||||||
|
.aksrt-sme-card, .aksrt-sme-col { border: 1px solid #dcdcde; padding: 8px; margin: 8px 0; border-radius: 4px; }
|
||||||
|
|||||||
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,243 @@
|
|||||||
|
/**
|
||||||
|
* Nav menu senior submenu visual editors (graphic cards / multi-column).
|
||||||
|
* Enhances WP Appearance → Menus fields for aksrt_menu_options JSON.
|
||||||
|
*
|
||||||
|
* @package AKSRT
|
||||||
|
*/
|
||||||
|
(function ($) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function parseJSON(raw, fallback) {
|
||||||
|
try {
|
||||||
|
var data = JSON.parse(raw || '');
|
||||||
|
return data && typeof data === 'object' ? data : fallback;
|
||||||
|
} catch (e) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function graphicTemplate() {
|
||||||
|
return {
|
||||||
|
arrangement: '',
|
||||||
|
size: 'md',
|
||||||
|
img_scale: 100,
|
||||||
|
items: [{ img: '', title: '', badge: '', badge_class: 'jb-red', link: { url: '', text: '', target: '' } }],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function multiTemplate() {
|
||||||
|
return {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
icon: '',
|
||||||
|
img_icon: '',
|
||||||
|
link: { url: '', text: '', target: '' },
|
||||||
|
items: [{ title: '', icon: '', img_icon: '', link: { url: '', text: '', target: '' } }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function field(name, label, value, type) {
|
||||||
|
type = type || 'text';
|
||||||
|
return (
|
||||||
|
'<label class="aksrt-sme-field"><span>' +
|
||||||
|
label +
|
||||||
|
'</span><input type="' +
|
||||||
|
type +
|
||||||
|
'" data-name="' +
|
||||||
|
name +
|
||||||
|
'" value="' +
|
||||||
|
String(value || '').replace(/"/g, '"') +
|
||||||
|
'" class="widefat"></label>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderGraphicUI($wrap, data) {
|
||||||
|
var html = '<div class="aksrt-sme-toolbar">';
|
||||||
|
html += '<button type="button" class="button aksrt-sme-add-card">添加卡片</button>';
|
||||||
|
html += '</div><div class="aksrt-sme-cards">';
|
||||||
|
(data.items || []).forEach(function (item, i) {
|
||||||
|
html += '<div class="aksrt-sme-card" data-index="' + i + '">';
|
||||||
|
html += '<div class="aksrt-sme-card-head"><strong>卡片 #' + (i + 1) + '</strong>';
|
||||||
|
html += ' <button type="button" class="button-link-delete aksrt-sme-del-card">删除</button></div>';
|
||||||
|
html += field('img', '图片 URL', item.img || '');
|
||||||
|
html += field('title', '标题', item.title || '');
|
||||||
|
html += field('badge', '徽章', item.badge || '');
|
||||||
|
html += field('badge_class', '徽章 class', item.badge_class || '');
|
||||||
|
html += field('link.url', '链接', (item.link && item.link.url) || '');
|
||||||
|
html += '</div>';
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
html += field('size', '尺寸 sm/md/lg/xs', data.size || 'md');
|
||||||
|
html += field('img_scale', '图片比例%', data.img_scale || 100, 'number');
|
||||||
|
$wrap.html(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMultiUI($wrap, data) {
|
||||||
|
var html = '<div class="aksrt-sme-toolbar">';
|
||||||
|
html += '<button type="button" class="button aksrt-sme-add-col">添加栏目</button>';
|
||||||
|
html += '</div><div class="aksrt-sme-cols">';
|
||||||
|
(data.columns || []).forEach(function (col, i) {
|
||||||
|
html += '<div class="aksrt-sme-col" data-index="' + i + '">';
|
||||||
|
html += '<div class="aksrt-sme-card-head"><strong>栏目 #' + (i + 1) + '</strong>';
|
||||||
|
html += ' <button type="button" class="button-link-delete aksrt-sme-del-col">删除栏目</button></div>';
|
||||||
|
html += field('title', '栏目标题', col.title || '');
|
||||||
|
html += field('icon', '图标 class', col.icon || '');
|
||||||
|
html += field('link.url', '栏目链接', (col.link && col.link.url) || '');
|
||||||
|
html += '<div class="aksrt-sme-items">';
|
||||||
|
(col.items || []).forEach(function (item, j) {
|
||||||
|
html += '<div class="aksrt-sme-item" data-j="' + j + '">';
|
||||||
|
html += field('items.' + j + '.title', '链接标题', item.title || '');
|
||||||
|
html += field('items.' + j + '.link.url', '链接 URL', (item.link && item.link.url) || '');
|
||||||
|
html += field('items.' + j + '.icon', '图标', item.icon || '');
|
||||||
|
html += '</div>';
|
||||||
|
});
|
||||||
|
html += '<button type="button" class="button aksrt-sme-add-item">添加链接</button>';
|
||||||
|
html += '</div></div>';
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
$wrap.html(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readGraphic($wrap) {
|
||||||
|
var data = graphicTemplate();
|
||||||
|
data.size = $wrap.find('[data-name="size"]').val() || 'md';
|
||||||
|
data.img_scale = parseInt($wrap.find('[data-name="img_scale"]').val(), 10) || 100;
|
||||||
|
data.items = [];
|
||||||
|
$wrap.find('.aksrt-sme-card').each(function () {
|
||||||
|
var $c = $(this);
|
||||||
|
data.items.push({
|
||||||
|
img: $c.find('[data-name="img"]').val() || '',
|
||||||
|
title: $c.find('[data-name="title"]').val() || '',
|
||||||
|
badge: $c.find('[data-name="badge"]').val() || '',
|
||||||
|
badge_class: $c.find('[data-name="badge_class"]').val() || '',
|
||||||
|
link: { url: $c.find('[data-name="link.url"]').val() || '', text: '', target: '' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readMulti($wrap) {
|
||||||
|
var data = { columns: [] };
|
||||||
|
$wrap.find('.aksrt-sme-col').each(function () {
|
||||||
|
var $col = $(this);
|
||||||
|
var col = {
|
||||||
|
title: $col.find('[data-name="title"]').first().val() || '',
|
||||||
|
icon: $col.find('[data-name="icon"]').first().val() || '',
|
||||||
|
img_icon: '',
|
||||||
|
link: { url: $col.find('[data-name="link.url"]').first().val() || '', text: '', target: '' },
|
||||||
|
items: [],
|
||||||
|
};
|
||||||
|
$col.find('.aksrt-sme-item').each(function () {
|
||||||
|
var $it = $(this);
|
||||||
|
col.items.push({
|
||||||
|
title: $it.find('input[data-name$=".title"]').val() || '',
|
||||||
|
icon: $it.find('input[data-name$=".icon"]').val() || '',
|
||||||
|
img_icon: '',
|
||||||
|
link: { url: $it.find('input[data-name$=".link.url"]').val() || '', text: '', target: '' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
data.columns.push(col);
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function enhanceItem($item) {
|
||||||
|
if ($item.data('aksrtSme')) return;
|
||||||
|
$item.data('aksrtSme', 1);
|
||||||
|
|
||||||
|
var $graphicTa = $item.find('textarea[name*="[graphic_card_opts_json]"]');
|
||||||
|
var $multiTa = $item.find('textarea[name*="[multi_column_links_opts_json]"]');
|
||||||
|
var $type = $item.find('select[name*="[submenu_type]"]');
|
||||||
|
if (!$graphicTa.length && !$multiTa.length) return;
|
||||||
|
|
||||||
|
var $ui = $('<div class="aksrt-sme-ui" style="margin:8px 0;padding:10px;border:1px solid #dcdcde;background:#fff;border-radius:4px;"></div>');
|
||||||
|
$graphicTa.closest('p').before($ui);
|
||||||
|
|
||||||
|
function sync() {
|
||||||
|
var type = $type.val();
|
||||||
|
if (type === 'graphic_card') {
|
||||||
|
$graphicTa.closest('p').hide();
|
||||||
|
$multiTa.closest('p').hide();
|
||||||
|
var g = parseJSON($graphicTa.val(), graphicTemplate());
|
||||||
|
renderGraphicUI($ui, g);
|
||||||
|
$ui.show();
|
||||||
|
} else if (type === 'multi_column_links') {
|
||||||
|
$graphicTa.closest('p').hide();
|
||||||
|
$multiTa.closest('p').hide();
|
||||||
|
var m = parseJSON($multiTa.val(), multiTemplate());
|
||||||
|
renderMultiUI($ui, m);
|
||||||
|
$ui.show();
|
||||||
|
} else {
|
||||||
|
$ui.hide().empty();
|
||||||
|
$graphicTa.closest('p').show();
|
||||||
|
$multiTa.closest('p').show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$type.on('change', sync);
|
||||||
|
$ui.on('click', '.aksrt-sme-add-card', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var g = readGraphic($ui);
|
||||||
|
g.items.push({ img: '', title: '', badge: '', badge_class: 'jb-red', link: { url: '', text: '', target: '' } });
|
||||||
|
renderGraphicUI($ui, g);
|
||||||
|
});
|
||||||
|
$ui.on('click', '.aksrt-sme-del-card', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$(this).closest('.aksrt-sme-card').remove();
|
||||||
|
});
|
||||||
|
$ui.on('click', '.aksrt-sme-add-col', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var m = readMulti($ui);
|
||||||
|
m.columns.push({
|
||||||
|
title: '',
|
||||||
|
icon: '',
|
||||||
|
img_icon: '',
|
||||||
|
link: { url: '', text: '', target: '' },
|
||||||
|
items: [{ title: '', icon: '', img_icon: '', link: { url: '', text: '', target: '' } }],
|
||||||
|
});
|
||||||
|
renderMultiUI($ui, m);
|
||||||
|
});
|
||||||
|
$ui.on('click', '.aksrt-sme-del-col', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$(this).closest('.aksrt-sme-col').remove();
|
||||||
|
});
|
||||||
|
$ui.on('click', '.aksrt-sme-add-item', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var $col = $(this).closest('.aksrt-sme-col');
|
||||||
|
var m = readMulti($ui);
|
||||||
|
var idx = $col.index();
|
||||||
|
if (m.columns[idx]) {
|
||||||
|
m.columns[idx].items.push({ title: '', icon: '', img_icon: '', link: { url: '', text: '', target: '' } });
|
||||||
|
}
|
||||||
|
renderMultiUI($ui, m);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Before save, write UI → textarea
|
||||||
|
$('#update-nav-menu').on('submit.aksrtSme', function () {
|
||||||
|
var type = $type.val();
|
||||||
|
if (type === 'graphic_card') {
|
||||||
|
$graphicTa.val(JSON.stringify(readGraphic($ui), null, 2));
|
||||||
|
} else if (type === 'multi_column_links') {
|
||||||
|
$multiTa.val(JSON.stringify(readMulti($ui), null, 2));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hide raw JSON by default; toggle available via double-click title
|
||||||
|
$graphicTa.closest('p').find('label').attr('title', '双击显示原始 JSON');
|
||||||
|
sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
function boot() {
|
||||||
|
$('#menu-to-edit .menu-item').each(function () {
|
||||||
|
enhanceItem($(this));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(boot);
|
||||||
|
$(document).on('menu-item-added', function () {
|
||||||
|
setTimeout(boot, 50);
|
||||||
|
});
|
||||||
|
})(jQuery);
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
/**
|
||||||
|
* Enhance layout-2 inline search with AJAX suggestions (aksrt_search_suggestions).
|
||||||
|
* Extends existing panel search without dual systems.
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function initInlineSearchSuggest() {
|
||||||
|
var form = document.querySelector('form[data-aksrt-search-form="inline"]');
|
||||||
|
if (!form || !window.aksrtData || !aksrtData.ajaxUrl) return;
|
||||||
|
|
||||||
|
var input = form.querySelector(".search-input");
|
||||||
|
var group = form.querySelector(".form-group.relative, .form-group");
|
||||||
|
if (!input || !group) return;
|
||||||
|
|
||||||
|
var panel = group.querySelector(".navbar-inline-search-panel");
|
||||||
|
if (!panel) {
|
||||||
|
panel = document.createElement("div");
|
||||||
|
panel.className = "dropdown-menu hover-show-con navbar-inline-search-panel";
|
||||||
|
group.appendChild(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
var timer = null;
|
||||||
|
var searchConfig = (window.aksrtData && aksrtData.search) || {};
|
||||||
|
var suggestionsEnabled = searchConfig.suggestionsEnabled !== false;
|
||||||
|
|
||||||
|
function typeValue() {
|
||||||
|
var t = form.querySelector('input[name="aksrt_search_type"]');
|
||||||
|
return (t && t.value) || searchConfig.defaultType || "post";
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderItems(items) {
|
||||||
|
var html = "";
|
||||||
|
if (items && items.length) {
|
||||||
|
html += '<div class="muted-2-color em09 mb6">' + (searchConfig.suggestTitle || "搜索建议") + "</div>";
|
||||||
|
html += '<div class="navbar-inline-search-suggest">';
|
||||||
|
items.forEach(function (it) {
|
||||||
|
html +=
|
||||||
|
'<a class="navbar-inline-search-suggest__item" href="' +
|
||||||
|
(it.url || "#") +
|
||||||
|
'"><span class="title">' +
|
||||||
|
(it.title || "") +
|
||||||
|
'</span>' +
|
||||||
|
(it.meta ? '<span class="meta muted-2-color em09">' + it.meta + "</span>" : "") +
|
||||||
|
"</a>";
|
||||||
|
});
|
||||||
|
html += "</div>";
|
||||||
|
}
|
||||||
|
// Keep hot keywords block if present as static sibling content was replaced —
|
||||||
|
// re-append hots from data attribute if we stored them.
|
||||||
|
var hots = form.getAttribute("data-hots");
|
||||||
|
if (hots) {
|
||||||
|
try {
|
||||||
|
var arr = JSON.parse(hots);
|
||||||
|
if (arr && arr.length) {
|
||||||
|
html += '<div class="muted-2-color em09 mb6 mt10">热门搜索</div><div class="navbar-inline-search-hots">';
|
||||||
|
arr.forEach(function (kw) {
|
||||||
|
html +=
|
||||||
|
'<a class="but jb-sm" href="' +
|
||||||
|
(window.location.origin || "") +
|
||||||
|
"/?s=" +
|
||||||
|
encodeURIComponent(kw) +
|
||||||
|
'">' +
|
||||||
|
kw +
|
||||||
|
"</a> ";
|
||||||
|
});
|
||||||
|
html += "</div>";
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
panel.innerHTML = html || panel.innerHTML;
|
||||||
|
panel.classList.add("is-open");
|
||||||
|
panel.style.display = "block";
|
||||||
|
panel.style.visibility = "visible";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Persist hot keywords for re-render after suggestions
|
||||||
|
var hotLinks = panel.querySelectorAll(".navbar-inline-search-hots a");
|
||||||
|
if (hotLinks.length) {
|
||||||
|
var kws = [];
|
||||||
|
hotLinks.forEach(function (a) {
|
||||||
|
kws.push((a.textContent || "").trim());
|
||||||
|
});
|
||||||
|
form.setAttribute("data-hots", JSON.stringify(kws));
|
||||||
|
}
|
||||||
|
|
||||||
|
function request(q) {
|
||||||
|
if (!suggestionsEnabled || q.length < 2) {
|
||||||
|
// restore hots-only
|
||||||
|
if (form.getAttribute("data-hots")) {
|
||||||
|
renderItems([]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("POST", aksrtData.ajaxUrl, true);
|
||||||
|
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
|
||||||
|
xhr.onload = function () {
|
||||||
|
if (xhr.status < 200 || xhr.status >= 300) return;
|
||||||
|
try {
|
||||||
|
var json = JSON.parse(xhr.responseText);
|
||||||
|
if (json.success && json.data && json.data.items) {
|
||||||
|
renderItems(json.data.items);
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
};
|
||||||
|
xhr.send(
|
||||||
|
"action=aksrt_search_suggestions&nonce=" +
|
||||||
|
encodeURIComponent(aksrtData.nonce) +
|
||||||
|
"&q=" +
|
||||||
|
encodeURIComponent(q) +
|
||||||
|
"&type=" +
|
||||||
|
encodeURIComponent(typeValue())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
input.addEventListener("input", function () {
|
||||||
|
var q = (input.value || "").trim();
|
||||||
|
if (timer) clearTimeout(timer);
|
||||||
|
timer = setTimeout(function () {
|
||||||
|
request(q);
|
||||||
|
}, 180);
|
||||||
|
});
|
||||||
|
|
||||||
|
input.addEventListener("focus", function () {
|
||||||
|
panel.style.display = "block";
|
||||||
|
panel.style.visibility = "visible";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === "function") {
|
||||||
|
window.AksrtFrontend.register("aksrt-inline-search-suggest", function () {
|
||||||
|
initInlineSearchSuggest();
|
||||||
|
});
|
||||||
|
} else if (document.readyState === "loading") {
|
||||||
|
document.addEventListener("DOMContentLoaded", initInlineSearchSuggest);
|
||||||
|
} else {
|
||||||
|
initInlineSearchSuggest();
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -132,6 +132,7 @@ class Admin {
|
|||||||
'siteUrl' => home_url('/'),
|
'siteUrl' => home_url('/'),
|
||||||
'adminUrl' => admin_url('/'),
|
'adminUrl' => admin_url('/'),
|
||||||
'themeVersion' => wp_get_theme()->get('Version') ?: '3.4.0-dev',
|
'themeVersion' => wp_get_theme()->get('Version') ?: '3.4.0-dev',
|
||||||
|
'themeUri' => defined('AKSRT_URI') ? AKSRT_URI : get_template_directory_uri(),
|
||||||
'buildOk' => $missing_chunks === [],
|
'buildOk' => $missing_chunks === [],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,24 @@ class NavMenuOptions {
|
|||||||
public static function init(): void {
|
public static function init(): void {
|
||||||
add_action('wp_nav_menu_item_custom_fields', [self::class, 'render_fields'], 10, 2);
|
add_action('wp_nav_menu_item_custom_fields', [self::class, 'render_fields'], 10, 2);
|
||||||
add_action('wp_update_nav_menu_item', [self::class, 'save'], 10, 2);
|
add_action('wp_update_nav_menu_item', [self::class, 'save'], 10, 2);
|
||||||
|
add_action('admin_enqueue_scripts', [self::class, 'enqueue_admin']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function enqueue_admin(string $hook): void {
|
||||||
|
if ($hook !== 'nav-menus.php') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$path = get_template_directory() . '/assets/js/admin-nav-menu-senior.js';
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wp_enqueue_script(
|
||||||
|
'aksrt-admin-nav-menu-senior',
|
||||||
|
get_template_directory_uri() . '/assets/js/admin-nav-menu-senior.js',
|
||||||
|
['jquery'],
|
||||||
|
(string) filemtime($path),
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -31,15 +31,8 @@ class SchemaPageDisplay {
|
|||||||
['key' => 'header_scroll_down_hide', 'label' => '下滑隐藏导航栏', 'type' => 'switch', 'default' => true],
|
['key' => 'header_scroll_down_hide', 'label' => '下滑隐藏导航栏', 'type' => 'switch', 'default' => true],
|
||||||
]],
|
]],
|
||||||
['key' => 'layout-type', 'title' => '布局类型', 'fields' => [
|
['key' => 'layout-type', 'title' => '布局类型', 'fields' => [
|
||||||
['key' => 'header_layout', 'label' => '桌面端布局', 'type' => 'radio', 'default' => '1', 'options' => [
|
['key' => 'header_layout', 'label' => '桌面端布局', 'type' => 'image_select', 'default' => '1', 'options' => self::header_layout_options()],
|
||||||
['label' => '布局 1(经典)', 'value' => '1'],
|
['key' => 'mobile_header_layout', 'label' => '移动端布局', 'type' => 'image_select', 'default' => 'center', 'options' => self::mobile_header_layout_options()],
|
||||||
['label' => '布局 2(内联搜索)', 'value' => '2'],
|
|
||||||
['label' => '布局 3(紧凑)', 'value' => '3'],
|
|
||||||
]],
|
|
||||||
['key' => 'mobile_header_layout', 'label' => '移动端布局', 'type' => 'radio', 'default' => 'center', 'options' => [
|
|
||||||
['label' => '居中(含搜索按钮)', 'value' => 'center'],
|
|
||||||
['label' => '靠左', 'value' => 'left'],
|
|
||||||
]],
|
|
||||||
['key' => 'mobile_navbar_align', 'label' => '移动端菜单方向', 'type' => 'radio', 'default' => 'left', 'options' => [
|
['key' => 'mobile_navbar_align', 'label' => '移动端菜单方向', 'type' => 'radio', 'default' => 'left', 'options' => [
|
||||||
['label' => '顶部(关闭手势)', 'value' => 'top'],
|
['label' => '顶部(关闭手势)', 'value' => 'top'],
|
||||||
['label' => '左侧滑出', 'value' => 'left'],
|
['label' => '左侧滑出', 'value' => 'left'],
|
||||||
@@ -532,4 +525,24 @@ class SchemaPageDisplay {
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return list<array{label:string,value:string,image:string}> */
|
||||||
|
private static function header_layout_options(): array {
|
||||||
|
$base = defined('AKSRT_URI') ? trailingslashit(AKSRT_URI) . 'assets/images/header-layout/' : '';
|
||||||
|
return [
|
||||||
|
['label' => '布局 1(经典)', 'value' => '1', 'image' => $base . 'header_layout_1.png'],
|
||||||
|
['label' => '布局 2(内联搜索)', 'value' => '2', 'image' => $base . 'header_layout_2.png'],
|
||||||
|
['label' => '布局 3(紧凑)', 'value' => '3', 'image' => $base . 'header_layout_3.png'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return list<array{label:string,value:string,image:string}> */
|
||||||
|
private static function mobile_header_layout_options(): array {
|
||||||
|
$base = defined('AKSRT_URI') ? trailingslashit(AKSRT_URI) . 'assets/images/header-layout/' : '';
|
||||||
|
return [
|
||||||
|
['label' => '居中(含搜索按钮)', 'value' => 'center', 'image' => $base . 'mobile_header_layout_center.png'],
|
||||||
|
['label' => '靠左', 'value' => 'left', 'image' => $base . 'mobile_header_layout_left.png'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -161,7 +161,19 @@ trait ScriptsPart1b {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$hmb_js = get_template_directory() . '/assets/js/header-newadd.js';
|
|
||||||
|
$inline_search_js = get_template_directory() . '/assets/js/header-inline-search.js';
|
||||||
|
if (file_exists($inline_search_js)) {
|
||||||
|
wp_enqueue_script(
|
||||||
|
'aksrt-header-inline-search',
|
||||||
|
$base_uri . '/assets/js/header-inline-search.js',
|
||||||
|
[],
|
||||||
|
(string) filemtime($inline_search_js),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$hmb_js = get_template_directory() . '/assets/js/header-newadd.js';
|
||||||
if (file_exists($hmb_js)) {
|
if (file_exists($hmb_js)) {
|
||||||
wp_enqueue_script(
|
wp_enqueue_script(
|
||||||
'aksrt-header-newadd',
|
'aksrt-header-newadd',
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# Top nav zibll 1:1 acceptance checklist
|
||||||
|
|
||||||
|
Branch: `worktree-nav-zibll-1to1`
|
||||||
|
|
||||||
|
## Desktop
|
||||||
|
- [ ] Layout 1: logo | menu | publish/VIP | theme(+lang) | user | search icon
|
||||||
|
- [ ] Layout 2: logo | menu | inline search(+hot/suggest) | CTAs | user (no search icon)
|
||||||
|
- [ ] Layout 3: logo | CTAs/user | menu right cluster
|
||||||
|
- [ ] Fixed `body.nav-fixed` 88px reserve; hero show-slide pull-up
|
||||||
|
- [ ] Scroll-down hide / scroll-up show
|
||||||
|
- [ ] Submenu hover + chevron; overflow folding "更多"
|
||||||
|
- [ ] Publish single vs multi; VIP hover cards gated by switches
|
||||||
|
- [ ] User box density + user-card publish type URL
|
||||||
|
- [ ] Header theme colors when custom enabled
|
||||||
|
- [ ] Language dropdown when i18n enabled + position header/both
|
||||||
|
|
||||||
|
## Mobile
|
||||||
|
- [ ] center/left header; drawer left/right/top
|
||||||
|
- [ ] `body.mobile-navbar-show` open/close + fixed-body + swipe
|
||||||
|
- [ ] mobile menus depth 2 + user box + widgets
|
||||||
|
- [ ] posts-nav-box TOC on single posts
|
||||||
|
|
||||||
|
## Admin
|
||||||
|
- [ ] SPA image_select previews for layouts
|
||||||
|
- [ ] newposts types multi + VIP switches persist
|
||||||
|
- [ ] Appearance → Menus: icon/badge/senior visual editor saves meta
|
||||||
|
- [ ] `cd assets/admin && npm run build` after schema changes
|
||||||
|
|
||||||
|
## Page/section navs
|
||||||
|
- [ ] section-navs builds `.posts-nav-lists` on `[data-nav]` pages
|
||||||
|
- [ ] page-navs enqueued on posts-navs template
|
||||||
|
|
||||||
|
## Deploy notes
|
||||||
|
- Admin `build/` is gitignored — run build on deploy host.
|
||||||
|
- PR: open from branch `worktree-nav-zibll-1to1` against main on Gitea.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
PASS all nav zibll source contracts
|
||||||