feat(pages): 1:1 文章导航 posts-navs (zibll postsnavs)
Per-category full post lists, affix TOC shell, admin drag-sort settings modal + AJAX save (navs_show_cat_id / name / desc).
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
/**
|
||||
* AKSRT 文章导航页 — zibll postsnavs shell + admin settings.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
/* header card */
|
||||
.page-template-posts-navs .content-wrap,
|
||||
main.page-template-posts-navs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
main.page-template-posts-navs .content-wrap {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
main.page-template-posts-navs .sidebar {
|
||||
flex: 0 0 260px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.posts-navs .title-theme h2 {
|
||||
margin: 0;
|
||||
font-size: 1.15em;
|
||||
}
|
||||
|
||||
.posts-navs__list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.posts-navs__item {
|
||||
margin: 0 0 10px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.posts-navs__item .text-ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.posts-navs__item .icon-circle {
|
||||
display: inline-block;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
border-radius: 50%;
|
||||
background: var(--muted-3-color, #ccc);
|
||||
margin: 0 0.55em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.posts-navs__item a {
|
||||
color: var(--color-ink, inherit);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.posts-navs__item a:hover {
|
||||
color: var(--focus-color, var(--color-primary));
|
||||
}
|
||||
|
||||
.posts-navs .theme-box {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.posts-navs .box-body.notop {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
/* TOC sidebar filled by section-navs.js */
|
||||
.posts-nav-box .posts-nav-lists,
|
||||
.posts-nav-box .aksrt-section-nav {
|
||||
max-height: 400px;
|
||||
padding-left: 10px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.posts-nav-box .zib-widget {
|
||||
background: var(--main-bg-color, var(--surface-1, #fff));
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 4px var(--main-shadow, rgba(0, 0, 0, 0.06));
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.posts-nav-lists .bl {
|
||||
border-left: 1px solid var(--main-border-color, #eee);
|
||||
padding: 12px 10px 12px 0;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.posts-nav-lists li {
|
||||
position: relative;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.posts-nav-lists li a {
|
||||
margin: 0 10px;
|
||||
display: block;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
margin-right: 20px;
|
||||
color: var(--muted-2-color, #666);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.posts-nav-lists li a:hover,
|
||||
.posts-nav-lists li.active a {
|
||||
background: var(--main-shadow, rgba(0, 0, 0, 0.04));
|
||||
color: var(--focus-color, var(--color-primary));
|
||||
}
|
||||
|
||||
.posts-nav-lists li.n-H1 a,
|
||||
.posts-nav-lists li.n-H2 a {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* settings modal */
|
||||
#post-navs-sz.modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1050;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
#post-navs-sz.modal.is-open,
|
||||
#post-navs-sz.modal.show {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#post-navs-sz .modal-dialog {
|
||||
width: min(640px, 100%);
|
||||
max-height: 90vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#post-navs-sz .modal-content {
|
||||
background: var(--main-bg-color, #fff);
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
#post-navs-sz .modal-header,
|
||||
#post-navs-sz .modal-body,
|
||||
#post-navs-sz .modal-footer {
|
||||
padding: 16px 20px;
|
||||
}
|
||||
|
||||
#post-navs-sz .modal-header {
|
||||
border-bottom: 1px solid var(--main-border-color, #eee);
|
||||
}
|
||||
|
||||
#post-navs-sz .modal-footer {
|
||||
border-top: 1px solid var(--main-border-color, #eee);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#post-navs-sz .close {
|
||||
float: right;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#post-navs-sz .form-control {
|
||||
width: 100%;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid var(--main-border-color, #ddd);
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#post-navs-sz .form-group {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
#post-navs-sz .row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -8px;
|
||||
}
|
||||
|
||||
#post-navs-sz .col-xs-6,
|
||||
#post-navs-sz .col-sm-6 {
|
||||
width: 50%;
|
||||
padding: 0 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.cat-group {
|
||||
height: 200px;
|
||||
background: var(--body-bg-color, var(--surface-2, #f6f7f9));
|
||||
border: 1px solid var(--main-border-color, #ddd);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
padding: 5px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.sortable-cat-item {
|
||||
background: var(--main-border-color, #e8e8e8);
|
||||
padding: 4px 8px;
|
||||
transition: 0.2s;
|
||||
border-radius: 4px;
|
||||
margin: 2px;
|
||||
display: inline-block;
|
||||
cursor: grab;
|
||||
width: calc(100% - 15px);
|
||||
min-width: 100px;
|
||||
box-sizing: border-box;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.sortable-cat-item .badge {
|
||||
margin-left: 4px;
|
||||
background: rgba(0, 0, 0, 0.08);
|
||||
color: inherit;
|
||||
border-radius: 10px;
|
||||
padding: 0 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#cat-sortable-ok .sortable-cat-item {
|
||||
background: rgba(73, 214, 253, 0.12);
|
||||
color: #069cf3;
|
||||
}
|
||||
|
||||
.sortable-cat-item.is-dragging {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.sortable-cat-item.sortable-selected {
|
||||
background: rgba(251, 82, 82, 0.1) !important;
|
||||
color: #fb5454 !important;
|
||||
}
|
||||
|
||||
.cat-group.is-drop-target {
|
||||
outline: 2px dashed var(--focus-color, #f04494);
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
main.page-template-posts-navs .sidebar {
|
||||
flex: 1 1 100%;
|
||||
order: -1;
|
||||
}
|
||||
|
||||
#post-navs-sz .col-xs-6,
|
||||
#post-navs-sz .col-sm-6 {
|
||||
width: 100%;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
/**
|
||||
* AKSRT 文章导航页 — admin settings modal + HTML5 drag-sort (no SortableJS dependency).
|
||||
* TOC generation is handled by section-navs.js via [data-nav] + .posts-nav-box.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function qs(sel, root) {
|
||||
return (root || document).querySelector(sel);
|
||||
}
|
||||
|
||||
function qsa(sel, root) {
|
||||
return Array.prototype.slice.call((root || document).querySelectorAll(sel));
|
||||
}
|
||||
|
||||
function ajaxUrl() {
|
||||
if (window.aksrtData && window.aksrtData.ajaxUrl) return window.aksrtData.ajaxUrl;
|
||||
if (window.ajaxurl) return window.ajaxurl;
|
||||
return '/wp-admin/admin-ajax.php';
|
||||
}
|
||||
|
||||
function toast(msg, type) {
|
||||
if (window.aksrt && typeof window.aksrt.toast === 'function') {
|
||||
window.aksrt.toast(msg, type || 'info');
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line no-alert
|
||||
window.alert(msg);
|
||||
}
|
||||
|
||||
function initModal() {
|
||||
var modal = qs('#post-navs-sz');
|
||||
if (!modal) return;
|
||||
|
||||
function openModal(e) {
|
||||
if (e) e.preventDefault();
|
||||
modal.classList.add('is-open', 'show');
|
||||
modal.setAttribute('aria-hidden', 'false');
|
||||
}
|
||||
|
||||
function closeModal(e) {
|
||||
if (e) e.preventDefault();
|
||||
modal.classList.remove('is-open', 'show');
|
||||
modal.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
|
||||
qsa('[data-toggle="modal"][data-target="#post-navs-sz"], a[href="#post-navs-sz"]').forEach(function (el) {
|
||||
el.addEventListener('click', openModal);
|
||||
});
|
||||
// Title [设置] uses data-target.
|
||||
qsa('[data-target="#post-navs-sz"]').forEach(function (el) {
|
||||
el.addEventListener('click', openModal);
|
||||
});
|
||||
qsa('[data-dismiss="modal"]', modal).forEach(function (btn) {
|
||||
btn.addEventListener('click', closeModal);
|
||||
});
|
||||
modal.addEventListener('click', function (e) {
|
||||
if (e.target === modal) closeModal(e);
|
||||
});
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Escape' && modal.classList.contains('is-open')) closeModal(e);
|
||||
});
|
||||
}
|
||||
|
||||
function initDragSort() {
|
||||
var left = qs('#cat-sortable');
|
||||
var right = qs('#cat-sortable-ok');
|
||||
if (!left || !right) return;
|
||||
|
||||
var dragEl = null;
|
||||
|
||||
function onDragStart(e) {
|
||||
dragEl = e.currentTarget;
|
||||
dragEl.classList.add('is-dragging');
|
||||
try {
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.dataTransfer.setData('text/plain', dragEl.getAttribute('data-id') || '');
|
||||
} catch (err) { /* IE */ }
|
||||
}
|
||||
|
||||
function onDragEnd() {
|
||||
if (dragEl) dragEl.classList.remove('is-dragging');
|
||||
dragEl = null;
|
||||
qsa('.cat-group').forEach(function (g) {
|
||||
g.classList.remove('is-drop-target');
|
||||
});
|
||||
}
|
||||
|
||||
function bindItem(el) {
|
||||
el.setAttribute('draggable', 'true');
|
||||
el.addEventListener('dragstart', onDragStart);
|
||||
el.addEventListener('dragend', onDragEnd);
|
||||
}
|
||||
|
||||
qsa('.sortable-cat-item', left).forEach(bindItem);
|
||||
qsa('.sortable-cat-item', right).forEach(bindItem);
|
||||
|
||||
[left, right].forEach(function (group) {
|
||||
group.addEventListener('dragover', function (e) {
|
||||
e.preventDefault();
|
||||
group.classList.add('is-drop-target');
|
||||
if (!dragEl) return;
|
||||
var after = getDragAfterElement(group, e.clientY);
|
||||
if (after == null) {
|
||||
group.appendChild(dragEl);
|
||||
} else {
|
||||
group.insertBefore(dragEl, after);
|
||||
}
|
||||
});
|
||||
group.addEventListener('dragleave', function () {
|
||||
group.classList.remove('is-drop-target');
|
||||
});
|
||||
group.addEventListener('drop', function (e) {
|
||||
e.preventDefault();
|
||||
group.classList.remove('is-drop-target');
|
||||
});
|
||||
});
|
||||
|
||||
function getDragAfterElement(container, y) {
|
||||
var els = qsa('.sortable-cat-item:not(.is-dragging)', container);
|
||||
var closest = { offset: Number.NEGATIVE_INFINITY, element: null };
|
||||
els.forEach(function (child) {
|
||||
var box = child.getBoundingClientRect();
|
||||
var offset = y - box.top - box.height / 2;
|
||||
if (offset < 0 && offset > closest.offset) {
|
||||
closest = { offset: offset, element: child };
|
||||
}
|
||||
});
|
||||
return closest.element;
|
||||
}
|
||||
}
|
||||
|
||||
function initSave() {
|
||||
var form = qs('#aksrt-posts-navs-settings');
|
||||
if (!form) return;
|
||||
|
||||
form.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
var ids = [];
|
||||
qsa('#cat-sortable-ok .sortable-cat-item').forEach(function (item) {
|
||||
var id = item.getAttribute('data-id');
|
||||
if (id) ids.push(id);
|
||||
});
|
||||
if (!ids.length) {
|
||||
toast('请选择需要显示的分类', 'danger');
|
||||
return;
|
||||
}
|
||||
|
||||
var fd = new FormData(form);
|
||||
fd.set('action', 'aksrt_post_navs_settings');
|
||||
// PHP accepts array; append each.
|
||||
fd.delete('navs_show_cat_id');
|
||||
ids.forEach(function (id) {
|
||||
fd.append('navs_show_cat_id[]', id);
|
||||
});
|
||||
|
||||
var btn = qs('button[type="submit"]', form);
|
||||
if (btn) btn.disabled = true;
|
||||
|
||||
fetch(ajaxUrl(), {
|
||||
method: 'POST',
|
||||
body: fd,
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (data) {
|
||||
if (!data || data.error) {
|
||||
toast((data && data.msg) || '保存失败', 'danger');
|
||||
return;
|
||||
}
|
||||
toast(data.msg || '保存成功', 'success');
|
||||
if (data.reload) {
|
||||
window.setTimeout(function () { window.location.reload(); }, 400);
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
toast('网络错误', 'danger');
|
||||
})
|
||||
.finally(function () {
|
||||
if (btn) btn.disabled = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build simple TOC into .posts-nav-box if section-navs hasn't already.
|
||||
* Uses category h2 headings inside .posts-navs.
|
||||
*/
|
||||
function initFallbackToc() {
|
||||
var box = qs('.posts-nav-box');
|
||||
var root = qs('.posts-navs[data-nav]');
|
||||
if (!box || !root) return;
|
||||
if (box.querySelector('.posts-nav-lists, .aksrt-section-nav, .toc-list')) return;
|
||||
|
||||
var headings = qsa('.title-theme h2, h2', root);
|
||||
if (headings.length < 1) return;
|
||||
|
||||
var title = box.getAttribute('data-title') || '导航目录';
|
||||
var list = '';
|
||||
headings.forEach(function (h, i) {
|
||||
if (!h.id) h.id = 'navs-sec-' + i;
|
||||
list += '<li class="n-H2"><a class="text-ellipsis" href="#' + h.id + '">' + (h.textContent || '').trim() + '</a></li>';
|
||||
});
|
||||
|
||||
box.innerHTML =
|
||||
'<div class="box-body notop"><div class="title-theme">' + title + '</div></div>' +
|
||||
'<div class="zib-widget"><div class="posts-nav-lists scroll-y mini-scrollbar list-unstyled">' +
|
||||
'<ul class="bl relative nav">' + list + '</ul></div></div>';
|
||||
|
||||
qsa('a[href^="#"]', box).forEach(function (a) {
|
||||
a.addEventListener('click', function (e) {
|
||||
var id = a.getAttribute('href');
|
||||
var el = id ? document.getElementById(id.replace(/^#/, '')) : null;
|
||||
if (!el) return;
|
||||
e.preventDefault();
|
||||
var top = el.getBoundingClientRect().top + window.pageYOffset - 90;
|
||||
window.scrollTo({ top: top, behavior: 'smooth' });
|
||||
});
|
||||
});
|
||||
|
||||
function highlight() {
|
||||
var y = window.pageYOffset + 120;
|
||||
var current = headings[0];
|
||||
headings.forEach(function (h) {
|
||||
if (h.offsetTop <= y) current = h;
|
||||
});
|
||||
qsa('li', box).forEach(function (li) {
|
||||
var a = li.querySelector('a');
|
||||
if (a && current && a.getAttribute('href') === '#' + current.id) {
|
||||
li.classList.add('active');
|
||||
} else {
|
||||
li.classList.remove('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
window.addEventListener('scroll', highlight, { passive: true });
|
||||
highlight();
|
||||
}
|
||||
|
||||
function init() {
|
||||
initModal();
|
||||
initDragSort();
|
||||
initSave();
|
||||
// Defer so section-navs.js can own TOC first.
|
||||
window.setTimeout(initFallbackToc, 50);
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
})();
|
||||
@@ -255,6 +255,9 @@ if (class_exists('\AKSRT\Core\LinksSubmit')) {
|
||||
if (class_exists('\AKSRT\Core\LinksPageMeta')) {
|
||||
\AKSRT\Core\LinksPageMeta::get_instance()->init();
|
||||
}
|
||||
if (class_exists('\AKSRT\Core\PostsNavs')) {
|
||||
\AKSRT\Core\PostsNavs::get_instance()->init();
|
||||
}
|
||||
if (class_exists('\AKSRT\Messages\SiteNotice')) {
|
||||
\AKSRT\Messages\SiteNotice::get_instance()->init();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* 文章导航页设置 AJAX(zibll action/post_navs.php parity).
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Core;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class PostsNavs {
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
public static function get_instance(): self {
|
||||
if (null === self::$instance) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function init(): void {
|
||||
add_action('wp_ajax_aksrt_post_navs_settings', [$this, 'ajax_save_settings']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Super-admin only: save navs_page_name / desc / ordered cat IDs.
|
||||
*/
|
||||
public function ajax_save_settings(): void {
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('权限不足', 'aksrt')]);
|
||||
}
|
||||
if (!check_ajax_referer('aksrt_post_navs_settings', '_wpnonce', false)) {
|
||||
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('安全校验失败', 'aksrt')]);
|
||||
}
|
||||
|
||||
$page_id = isset($_POST['page_id']) ? absint($_POST['page_id']) : 0;
|
||||
if ($page_id <= 0 || get_post_type($page_id) !== 'page') {
|
||||
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('无效页面', 'aksrt')]);
|
||||
}
|
||||
|
||||
$cat_ids = [];
|
||||
if (isset($_POST['navs_show_cat_id'])) {
|
||||
$raw = wp_unslash($_POST['navs_show_cat_id']);
|
||||
if (is_array($raw)) {
|
||||
$cat_ids = array_values(array_filter(array_map('intval', $raw)));
|
||||
} elseif (is_string($raw) && $raw !== '') {
|
||||
$decoded = json_decode($raw, true);
|
||||
if (is_array($decoded)) {
|
||||
$cat_ids = array_values(array_filter(array_map('intval', $decoded)));
|
||||
} else {
|
||||
$cat_ids = array_values(array_filter(array_map('intval', explode(',', $raw))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($cat_ids)) {
|
||||
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('请选择需要显示的分类', 'aksrt')]);
|
||||
}
|
||||
|
||||
if (isset($_POST['navs_page_name'])) {
|
||||
update_post_meta($page_id, 'navs_page_name', sanitize_text_field(wp_unslash((string) $_POST['navs_page_name'])));
|
||||
}
|
||||
if (isset($_POST['navs_page_desc'])) {
|
||||
update_post_meta($page_id, 'navs_page_desc', sanitize_text_field(wp_unslash((string) $_POST['navs_page_desc'])));
|
||||
}
|
||||
update_post_meta($page_id, 'navs_show_cat_id', $cat_ids);
|
||||
|
||||
wp_send_json([
|
||||
'error' => 0,
|
||||
'msg' => __('保存成功', 'aksrt'),
|
||||
'reload'=> true,
|
||||
'ids' => $cat_ids,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -404,6 +404,39 @@ trait ScriptsPart1b {
|
||||
}
|
||||
}
|
||||
|
||||
// 文章导航页(zibll postsnavs.php)
|
||||
if (is_page_template('templates/pages/page-posts-navs.php')) {
|
||||
$pn_css = get_template_directory() . '/assets/css/routes/posts-navs.css';
|
||||
if (file_exists($pn_css)) {
|
||||
wp_enqueue_style(
|
||||
'aksrt-route-posts-navs',
|
||||
$base_uri . '/assets/css/routes/posts-navs.css',
|
||||
['aksrt-tokens'],
|
||||
(string) filemtime($pn_css)
|
||||
);
|
||||
}
|
||||
$section_navs = get_template_directory() . '/assets/js/section-navs.js';
|
||||
if (file_exists($section_navs)) {
|
||||
wp_enqueue_script(
|
||||
'aksrt-section-navs',
|
||||
$base_uri . '/assets/js/section-navs.js',
|
||||
['jquery'],
|
||||
(string) filemtime($section_navs),
|
||||
true
|
||||
);
|
||||
}
|
||||
$pn_js = get_template_directory() . '/assets/js/posts-navs-page.js';
|
||||
if (file_exists($pn_js)) {
|
||||
wp_enqueue_script(
|
||||
'aksrt-posts-navs-page',
|
||||
$base_uri . '/assets/js/posts-navs-page.js',
|
||||
['jquery'],
|
||||
(string) filemtime($pn_js),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 交易中心共享壳层(购物车 / 订单 / 收益)
|
||||
$is_commerce_center = is_page_template('templates/pages/page-orders.php')
|
||||
|| is_page_template('templates/pages/page-cart.php')
|
||||
|
||||
@@ -1,65 +1,228 @@
|
||||
<?php
|
||||
/**
|
||||
* Template Name: AKSRT - Posts Navigation
|
||||
* Template Name: AKSRT - 文章导航
|
||||
* Template Post Type: page
|
||||
*
|
||||
* 文章导航页:分类+标签的导航聚合页。
|
||||
* Zibll pages/postsnavs.php 1:1 — selected categories with full post lists,
|
||||
* affix TOC sidebar, admin drag-sort settings modal.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
get_header();
|
||||
$post = get_queried_object();
|
||||
$post_id = $post instanceof WP_Post ? (int) $post->ID : (int) get_queried_object_id();
|
||||
|
||||
$categories = get_categories(['hide_empty' => true, 'orderby' => 'count', 'order' => 'DESC', 'number' => 20]);
|
||||
$tags = get_tags(['hide_empty' => true, 'orderby' => 'count', 'order' => 'DESC', 'number' => 40]);
|
||||
|
||||
if (function_exists('aksrt_archive_open_shell')) {
|
||||
aksrt_archive_open_shell([
|
||||
'main_class' => 'site-main container content-shell content-shell--page page-content-default',
|
||||
'has_sidebar' => false,
|
||||
'page_kind' => 'posts-navs',
|
||||
]);
|
||||
$show_cat_id = get_post_meta($post_id, 'navs_show_cat_id', true);
|
||||
if (!is_array($show_cat_id)) {
|
||||
$show_cat_id = $show_cat_id !== '' && $show_cat_id !== null
|
||||
? array_values(array_filter(array_map('intval', (array) $show_cat_id)))
|
||||
: [];
|
||||
} else {
|
||||
$show_cat_id = array_values(array_filter(array_map('intval', $show_cat_id)));
|
||||
}
|
||||
|
||||
$page_desc = (string) get_post_meta($post_id, 'navs_page_desc', true);
|
||||
$page_name = (string) get_post_meta($post_id, 'navs_page_name', true);
|
||||
$title = get_the_title($post_id);
|
||||
$page_name = $page_name !== '' ? $page_name : $title;
|
||||
|
||||
$all_cats = get_categories([
|
||||
'orderby' => 'term_group',
|
||||
'order' => 'ASC',
|
||||
'hide_empty' => false,
|
||||
]);
|
||||
|
||||
$can_admin = is_user_logged_in() && current_user_can('manage_options');
|
||||
|
||||
get_header();
|
||||
?>
|
||||
<div class="content-main content-main--page content-main--posts-navs">
|
||||
<?php
|
||||
get_template_part('templates/parts/page-header', null, [
|
||||
'title' => esc_html(get_the_title() ?: __('文章导航', 'aksrt')),
|
||||
<style>
|
||||
.page-share a { margin-right: 10px; }
|
||||
.page-share a > text { display: none; }
|
||||
</style>
|
||||
|
||||
<div class="container">
|
||||
<div class="main-bg theme-box radius8 main-shadow box-body">
|
||||
<div class="box-body">
|
||||
<h2 class="theme-box">
|
||||
<?php echo esc_html($page_name); ?>
|
||||
<?php if ($can_admin) : ?>
|
||||
<small>
|
||||
<a style="cursor:pointer" href="javascript:;" data-toggle="modal" data-target="#post-navs-sz">
|
||||
[<?php esc_html_e('设置', 'aksrt'); ?>]
|
||||
</a>
|
||||
</small>
|
||||
<?php endif; ?>
|
||||
</h2>
|
||||
<div class="flex ab jsb hh">
|
||||
<div class="muted-color mr20">
|
||||
<?php echo esc_html($page_desc !== '' ? $page_desc : __('暂无简介内容,请设置', 'aksrt')); ?>
|
||||
</div>
|
||||
<div class="pull-right page-share mt20">
|
||||
<?php
|
||||
if (function_exists('aksrt_render_share_buttons')) {
|
||||
aksrt_render_share_buttons(['compact' => true]);
|
||||
} elseif (function_exists('aksrt_get_share_html')) {
|
||||
echo aksrt_get_share_html(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main class="container page-template-posts-navs">
|
||||
<div class="content-wrap">
|
||||
<div class="content-layout">
|
||||
<div class="posts-navs" data-nav="posts">
|
||||
<?php
|
||||
if (!empty($show_cat_id)) {
|
||||
foreach ($show_cat_id as $c_id) {
|
||||
$the_cat = get_category((int) $c_id);
|
||||
if (!$the_cat || is_wp_error($the_cat)) {
|
||||
continue;
|
||||
}
|
||||
$posts = get_posts([
|
||||
'category' => (int) $c_id,
|
||||
'numberposts' => -1,
|
||||
'post_status' => 'publish',
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
'suppress_filters' => false,
|
||||
]);
|
||||
?>
|
||||
if (empty($posts)) {
|
||||
continue;
|
||||
}
|
||||
$section_id = 'navs-cat-' . (int) $c_id;
|
||||
echo '<div class="theme-box">';
|
||||
echo '<div class="box-body notop"><div class="title-theme"><h2 id="' . esc_attr($section_id) . '">' . esc_html($the_cat->name) . '</h2></div></div>';
|
||||
echo '<div class="main-bg radius8 main-shadow box-body"><ul class="posts-navs__list">';
|
||||
foreach ($posts as $p) {
|
||||
$time_tip = get_the_time('Y年m月d日 H:i', $p);
|
||||
$time_ago = human_time_diff(get_the_time('U', $p), current_time('timestamp')) . __('前', 'aksrt');
|
||||
$views = function_exists('aksrt_get_post_views') ? (int) aksrt_get_post_views($p->ID) : 0;
|
||||
$likes = (int) get_post_meta($p->ID, 'like', true);
|
||||
if ($likes <= 0) {
|
||||
$likes = (int) get_post_meta($p->ID, 'aksrt_like', true);
|
||||
}
|
||||
$post_tip = sprintf(
|
||||
/* translators: 1: views 2: likes */
|
||||
__('阅读(%1$s) 点赞(%2$s)', 'aksrt'),
|
||||
(string) $views,
|
||||
(string) $likes
|
||||
);
|
||||
$subtitle = (string) get_post_meta($p->ID, 'subtitle', true);
|
||||
if ($subtitle === '') {
|
||||
$subtitle = (string) get_post_meta($p->ID, 'aksrt_subtitle', true);
|
||||
}
|
||||
echo '<li class="posts-navs__item">';
|
||||
echo '<div class="text-ellipsis">';
|
||||
echo '<span title="' . esc_attr($time_tip) . '" class="muted-2-color">' . esc_html($time_ago) . '</span>';
|
||||
echo '<span class="icon-circle"></span>';
|
||||
echo '<a title="' . esc_attr($post_tip) . '" href="' . esc_url(get_permalink($p->ID)) . '">';
|
||||
echo esc_html(get_the_title($p->ID));
|
||||
if ($subtitle !== '') {
|
||||
echo ' <span class="muted-2-color">' . esc_html($subtitle) . '</span>';
|
||||
}
|
||||
echo '</a></div></li>';
|
||||
}
|
||||
echo '</ul></div></div>';
|
||||
}
|
||||
} elseif ($can_admin) {
|
||||
echo '<div class="theme-box main-bg radius8 main-shadow box-body">';
|
||||
echo '<p class="muted-2-color">' . esc_html__('尚未选择分类,请点击标题旁「设置」配置要展示的分类。', 'aksrt') . '</p>';
|
||||
echo '</div>';
|
||||
} else {
|
||||
echo '<div class="theme-box main-bg radius8 main-shadow box-body">';
|
||||
echo '<p class="muted-2-color">' . esc_html__('暂无导航内容', 'aksrt') . '</p>';
|
||||
echo '</div>';
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar">
|
||||
<div class="posts-nav-box" data-affix="true" data-title="<?php esc_attr_e('导航目录', 'aksrt'); ?>"></div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div class="aksrt-posts-navs">
|
||||
<?php if (!empty($categories)) : ?>
|
||||
<section class="aksrt-posts-navs__section">
|
||||
<h2 class="aksrt-posts-navs__heading"><?php esc_html_e('热门分类', 'aksrt'); ?></h2>
|
||||
<div class="aksrt-posts-navs__cats">
|
||||
<?php foreach ($categories as $cat) : ?>
|
||||
<a href="<?php echo esc_url(get_category_link($cat->term_id)); ?>" class="aksrt-posts-navs__cat-chip">
|
||||
<span class="aksrt-posts-navs__cat-name"><?php echo esc_html($cat->name); ?></span>
|
||||
<span class="aksrt-posts-navs__cat-count"><?php echo esc_html((string) $cat->count); ?></span>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
<?php if ($can_admin) : ?>
|
||||
<div class="modal fade" id="post-navs-sz" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<form id="aksrt-posts-navs-settings" class="aksrt-posts-navs-settings">
|
||||
<div class="modal-header">
|
||||
<p class="modal-title">
|
||||
<?php esc_html_e('当前页面设置', 'aksrt'); ?>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="<?php esc_attr_e('关闭', 'aksrt'); ?>">
|
||||
<i class="fa fa-times" aria-hidden="true"></i>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="navs_page_name"><?php esc_html_e('标题', 'aksrt'); ?></label>
|
||||
<input type="text" class="form-control" id="navs_page_name" name="navs_page_name" placeholder="<?php esc_attr_e('自定义标题', 'aksrt'); ?>" value="<?php echo esc_attr($page_name); ?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="navs_page_desc"><?php esc_html_e('简介', 'aksrt'); ?></label>
|
||||
<input type="text" class="form-control" id="navs_page_desc" name="navs_page_desc" placeholder="<?php esc_attr_e('自定义简介', 'aksrt'); ?>" value="<?php echo esc_attr($page_desc); ?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><?php esc_html_e('请将需要显示的分类拖动到选区并排序', 'aksrt'); ?></label>
|
||||
<div class="row text-center">
|
||||
<div class="col-xs-6 col-sm-6">
|
||||
<p class="text-muted"><?php esc_html_e('未选择的分类', 'aksrt'); ?></p>
|
||||
<div class="cat-group mini-scrollbar" id="cat-sortable">
|
||||
<?php
|
||||
foreach ($all_cats as $the_cat) {
|
||||
$c_id = (int) $the_cat->term_id;
|
||||
if (in_array($c_id, $show_cat_id, true)) {
|
||||
continue;
|
||||
}
|
||||
$c_name = rtrim((string) get_category_parents($c_id, false, '>'), '>');
|
||||
echo '<span class="sortable-cat-item" draggable="true" data-id="' . esc_attr((string) $c_id) . '">'
|
||||
. esc_html($c_name)
|
||||
. '<span class="badge">' . (int) $the_cat->count . '</span></span>';
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($tags)) : ?>
|
||||
<section class="aksrt-posts-navs__section">
|
||||
<h2 class="aksrt-posts-navs__heading"><?php esc_html_e('热门标签', 'aksrt'); ?></h2>
|
||||
<div class="aksrt-posts-navs__tags">
|
||||
<?php foreach ($tags as $tag) : ?>
|
||||
<a href="<?php echo esc_url(get_tag_link($tag->term_id)); ?>" class="aksrt-posts-navs__tag"><?php echo esc_html($tag->name); ?></a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-6">
|
||||
<p class="text-muted"><?php esc_html_e('已选择的分类', 'aksrt'); ?></p>
|
||||
<div class="cat-group mini-scrollbar" id="cat-sortable-ok">
|
||||
<?php
|
||||
foreach ($show_cat_id as $c_id) {
|
||||
$the_cat = get_category((int) $c_id);
|
||||
if (!$the_cat || is_wp_error($the_cat)) {
|
||||
continue;
|
||||
}
|
||||
$c_name = rtrim((string) get_category_parents((int) $c_id, false, '>'), '>');
|
||||
echo '<span class="sortable-cat-item" draggable="true" data-id="' . esc_attr((string) $c_id) . '">'
|
||||
. esc_html($c_name)
|
||||
. '<span class="badge">' . (int) $the_cat->count . '</span></span>';
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<input type="hidden" name="action" value="aksrt_post_navs_settings">
|
||||
<input type="hidden" name="page_id" value="<?php echo (int) $post_id; ?>">
|
||||
<?php wp_nonce_field('aksrt_post_navs_settings', '_wpnonce', false); ?>
|
||||
<button type="submit" class="btn btn-primary" evt="action"><?php esc_html_e('确认修改', 'aksrt'); ?></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navs-bj"></div>
|
||||
<?php
|
||||
if (function_exists('aksrt_archive_close_shell')) {
|
||||
aksrt_archive_close_shell(['has_sidebar' => false]);
|
||||
}
|
||||
endif;
|
||||
|
||||
get_footer();
|
||||
|
||||
Reference in New Issue
Block a user