429 lines
13 KiB
PHP
429 lines
13 KiB
PHP
|
|
<?php
|
|||
|
|
/**
|
|||
|
|
* 面包屑导航(没有适配版)
|
|||
|
|
*
|
|||
|
|
* @package SimpleBreadcrumb
|
|||
|
|
* @author 石头厝
|
|||
|
|
* @version 3.0.0
|
|||
|
|
* @link https://www.shitoucuo.com/
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
class SimpleBreadcrumb_Plugin implements Typecho_Plugin_Interface
|
|||
|
|
{
|
|||
|
|
/**
|
|||
|
|
* 激活插件
|
|||
|
|
*/
|
|||
|
|
public static function activate()
|
|||
|
|
{
|
|||
|
|
Typecho_Plugin::factory('Widget_Archive')->footer = array('SimpleBreadcrumb_Plugin', 'footer');
|
|||
|
|
return _t('面包屑导航插件已激活,请在文章页面使用 SimpleBreadcrumb_Plugin::show() 调用');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 禁用插件
|
|||
|
|
*/
|
|||
|
|
public static function deactivate()
|
|||
|
|
{
|
|||
|
|
return _t('面包屑导航插件已禁用');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 插件配置面板(简化版)
|
|||
|
|
*/
|
|||
|
|
public static function config(Typecho_Widget_Helper_Form $form)
|
|||
|
|
{
|
|||
|
|
// 是否显示面包屑
|
|||
|
|
$showBreadcrumb = new Typecho_Widget_Helper_Form_Element_Radio(
|
|||
|
|
'showBreadcrumb',
|
|||
|
|
array(
|
|||
|
|
'1' => _t('显示'),
|
|||
|
|
'0' => _t('不显示')
|
|||
|
|
),
|
|||
|
|
'1',
|
|||
|
|
_t('是否显示面包屑导航')
|
|||
|
|
);
|
|||
|
|
$form->addInput($showBreadcrumb);
|
|||
|
|
|
|||
|
|
// 首页文字
|
|||
|
|
$homeText = new Typecho_Widget_Helper_Form_Element_Text(
|
|||
|
|
'homeText',
|
|||
|
|
NULL,
|
|||
|
|
'首页',
|
|||
|
|
_t('首页显示文字')
|
|||
|
|
);
|
|||
|
|
$form->addInput($homeText);
|
|||
|
|
|
|||
|
|
// 分隔符
|
|||
|
|
$separator = new Typecho_Widget_Helper_Form_Element_Select(
|
|||
|
|
'separator',
|
|||
|
|
array(
|
|||
|
|
'»' => '»',
|
|||
|
|
'>' => '>',
|
|||
|
|
'/' => '/',
|
|||
|
|
'→' => '→'
|
|||
|
|
),
|
|||
|
|
'»',
|
|||
|
|
_t('分隔符样式')
|
|||
|
|
);
|
|||
|
|
$form->addInput($separator);
|
|||
|
|
|
|||
|
|
// 是否显示当前页面
|
|||
|
|
$showCurrent = new Typecho_Widget_Helper_Form_Element_Radio(
|
|||
|
|
'showCurrent',
|
|||
|
|
array(
|
|||
|
|
'1' => _t('显示'),
|
|||
|
|
'0' => _t('不显示')
|
|||
|
|
),
|
|||
|
|
'1',
|
|||
|
|
_t('是否显示当前页面')
|
|||
|
|
);
|
|||
|
|
$form->addInput($showCurrent);
|
|||
|
|
|
|||
|
|
// 是否显示分类
|
|||
|
|
$showCategory = new Typecho_Widget_Helper_Form_Element_Radio(
|
|||
|
|
'showCategory',
|
|||
|
|
array(
|
|||
|
|
'1' => _t('显示'),
|
|||
|
|
'0' => _t('不显示')
|
|||
|
|
),
|
|||
|
|
'1',
|
|||
|
|
_t('文章页面是否显示分类')
|
|||
|
|
);
|
|||
|
|
$form->addInput($showCategory);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 个人配置面板
|
|||
|
|
*/
|
|||
|
|
public static function personalConfig(Typecho_Widget_Helper_Form $form) {}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 输出面包屑导航(前端调用方法)
|
|||
|
|
*/
|
|||
|
|
public static function show($widget = null)
|
|||
|
|
{
|
|||
|
|
$options = Helper::options();
|
|||
|
|
$config = $options->plugin('SimpleBreadcrumb');
|
|||
|
|
|
|||
|
|
// 检查是否显示
|
|||
|
|
if (!$config->showBreadcrumb) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果没有传递widget,使用全局
|
|||
|
|
if ($widget === null) {
|
|||
|
|
$widget = Typecho_Widget::widget('Widget_Archive');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否在首页且需要显示
|
|||
|
|
if ($widget->is('index')) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取面包屑HTML
|
|||
|
|
$breadcrumb = self::generateBreadcrumb($widget, $config);
|
|||
|
|
|
|||
|
|
if (!empty($breadcrumb)) {
|
|||
|
|
// 输出CSS和面包屑HTML
|
|||
|
|
$html = '<style>' . self::getStyles() . '</style>';
|
|||
|
|
$html .= '<div class="simple-breadcrumb">' . $breadcrumb . '</div>';
|
|||
|
|
|
|||
|
|
echo $html;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 生成面包屑HTML
|
|||
|
|
*/
|
|||
|
|
private static function generateBreadcrumb($widget, $config)
|
|||
|
|
{
|
|||
|
|
$breadcrumbs = array();
|
|||
|
|
$options = Helper::options();
|
|||
|
|
|
|||
|
|
// 添加首页
|
|||
|
|
$homeText = isset($config->homeText) ? $config->homeText : '首页';
|
|||
|
|
$breadcrumbs[] = '<a href="' . $options->siteUrl . '" class="breadcrumb-home">' .
|
|||
|
|
htmlspecialchars($homeText) . '</a>';
|
|||
|
|
|
|||
|
|
$separator = isset($config->separator) ? $config->separator : '»';
|
|||
|
|
$showCurrent = isset($config->showCurrent) ? $config->showCurrent : '1';
|
|||
|
|
$showCategory = isset($config->showCategory) ? $config->showCategory : '1';
|
|||
|
|
|
|||
|
|
// 分类页面
|
|||
|
|
if ($widget->is('category')) {
|
|||
|
|
$category = $widget->getArchiveTitle();
|
|||
|
|
if ($showCurrent) {
|
|||
|
|
$breadcrumbs[] = '<span class="breadcrumb-current">' . htmlspecialchars($category) . '</span>';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 文章页面
|
|||
|
|
elseif ($widget->is('post')) {
|
|||
|
|
// 添加分类
|
|||
|
|
if ($showCategory) {
|
|||
|
|
$categories = $widget->categories;
|
|||
|
|
if ($categories && count($categories) > 0) {
|
|||
|
|
$category = current($categories);
|
|||
|
|
$breadcrumbs[] = '<a href="' . $category['permalink'] . '" class="breadcrumb-category">' .
|
|||
|
|
htmlspecialchars($category['name']) . '</a>';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加当前文章
|
|||
|
|
if ($showCurrent) {
|
|||
|
|
$breadcrumbs[] = '<span class="breadcrumb-current">' . htmlspecialchars($widget->title) . '</span>';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 独立页面
|
|||
|
|
elseif ($widget->is('page')) {
|
|||
|
|
// 处理父页面(如果有)
|
|||
|
|
if ($widget->parent > 0) {
|
|||
|
|
$parent = $widget->widget('Widget_Archive', "type=page&pageId={$widget->parent}");
|
|||
|
|
if ($parent->have()) {
|
|||
|
|
$breadcrumbs[] = '<a href="' . $parent->permalink . '" class="breadcrumb-parent">' .
|
|||
|
|
htmlspecialchars($parent->title) . '</a>';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($showCurrent) {
|
|||
|
|
$breadcrumbs[] = '<span class="breadcrumb-current">' . htmlspecialchars($widget->title) . '</span>';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 标签页面
|
|||
|
|
elseif ($widget->is('tag')) {
|
|||
|
|
if ($showCurrent) {
|
|||
|
|
$tag = $widget->getArchiveTitle();
|
|||
|
|
$breadcrumbs[] = '<span class="breadcrumb-current">标签: ' . htmlspecialchars($tag) . '</span>';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 作者页面
|
|||
|
|
elseif ($widget->is('author')) {
|
|||
|
|
if ($showCurrent) {
|
|||
|
|
$author = $widget->getArchiveTitle();
|
|||
|
|
$breadcrumbs[] = '<span class="breadcrumb-current">作者: ' . htmlspecialchars($author) . '</span>';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 日期归档
|
|||
|
|
elseif ($widget->is('archive') && !$widget->is('category') && !$widget->is('tag') && !$widget->is('author')) {
|
|||
|
|
if ($showCurrent) {
|
|||
|
|
$archive = $widget->getArchiveTitle();
|
|||
|
|
$breadcrumbs[] = '<span class="breadcrumb-current">' . htmlspecialchars($archive) . '</span>';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 搜索页面
|
|||
|
|
elseif ($widget->is('search')) {
|
|||
|
|
if ($showCurrent) {
|
|||
|
|
$keywords = $widget->keywords;
|
|||
|
|
$breadcrumbs[] = '<span class="breadcrumb-current">搜索: ' . htmlspecialchars($keywords) . '</span>';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果没有面包屑内容(只有首页),根据设置决定是否显示
|
|||
|
|
if (count($breadcrumbs) <= 1) {
|
|||
|
|
return ''; // 只显示首页时不输出
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 组合HTML
|
|||
|
|
$html = '<nav class="breadcrumb-nav" aria-label="面包屑导航">';
|
|||
|
|
$html .= '<ol class="breadcrumb-list">';
|
|||
|
|
|
|||
|
|
$count = count($breadcrumbs);
|
|||
|
|
foreach ($breadcrumbs as $index => $crumb) {
|
|||
|
|
$html .= '<li class="breadcrumb-item">';
|
|||
|
|
$html .= $crumb;
|
|||
|
|
if ($index < $count - 1) {
|
|||
|
|
$html .= '<span class="breadcrumb-separator">' . htmlspecialchars($separator) . '</span>';
|
|||
|
|
}
|
|||
|
|
$html .= '</li>';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$html .= '</ol>';
|
|||
|
|
$html .= '</nav>';
|
|||
|
|
|
|||
|
|
return $html;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 输出CSS样式到footer(保持空,避免重复加载)
|
|||
|
|
*/
|
|||
|
|
public static function footer()
|
|||
|
|
{
|
|||
|
|
// CSS已经在show()方法中内联输出
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取CSS样式 - 简化版,专门适配.dark夜间模式
|
|||
|
|
*/
|
|||
|
|
private static function getStyles()
|
|||
|
|
{
|
|||
|
|
return '
|
|||
|
|
/* Simple Breadcrumb Navigation CSS - v3.0.0 */
|
|||
|
|
|
|||
|
|
/* 基础样式 */
|
|||
|
|
.simple-breadcrumb {
|
|||
|
|
margin: 20px 0;
|
|||
|
|
padding: 15px 0;
|
|||
|
|
font-size: 14px;
|
|||
|
|
line-height: 1.5;
|
|||
|
|
clear: both;
|
|||
|
|
text-align: center;
|
|||
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 导航结构 */
|
|||
|
|
.breadcrumb-nav {
|
|||
|
|
display: inline-block;
|
|||
|
|
max-width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.breadcrumb-list {
|
|||
|
|
list-style: none;
|
|||
|
|
padding: 0;
|
|||
|
|
margin: 0;
|
|||
|
|
display: inline-flex;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.breadcrumb-item {
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
margin: 5px 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.breadcrumb-item a {
|
|||
|
|
text-decoration: none;
|
|||
|
|
padding: 4px 8px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
transition: all 0.2s ease;
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: #1e87f0;
|
|||
|
|
background-color: rgba(30, 135, 240, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.breadcrumb-item a:hover {
|
|||
|
|
color: #0d6efd;
|
|||
|
|
background-color: rgba(30, 135, 240, 0.2);
|
|||
|
|
transform: translateY(-1px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.breadcrumb-item span.breadcrumb-current {
|
|||
|
|
font-weight: 600;
|
|||
|
|
padding: 4px 8px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
color: #333;
|
|||
|
|
background-color: rgba(0, 0, 0, 0.05);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.breadcrumb-separator {
|
|||
|
|
margin: 0 10px;
|
|||
|
|
opacity: 0.6;
|
|||
|
|
font-weight: 300;
|
|||
|
|
color: #666;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ========== .dark 夜间模式适配 ========== */
|
|||
|
|
.dark .simple-breadcrumb {
|
|||
|
|
background-color: #1a1d21;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 15px 20px;
|
|||
|
|
border: 1px solid #343a40;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dark .breadcrumb-item a {
|
|||
|
|
color: #4dabf7;
|
|||
|
|
background-color: rgba(77, 171, 247, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dark .breadcrumb-item a:hover {
|
|||
|
|
color: #339af0;
|
|||
|
|
background-color: rgba(77, 171, 247, 0.2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dark .breadcrumb-item span.breadcrumb-current {
|
|||
|
|
color: #e9ecef;
|
|||
|
|
background-color: rgba(255, 255, 255, 0.05);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dark .breadcrumb-separator {
|
|||
|
|
color: #adb5bd;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ========== 响应式设计 ========== */
|
|||
|
|
@media (max-width: 768px) {
|
|||
|
|
.simple-breadcrumb {
|
|||
|
|
margin: 15px 0;
|
|||
|
|
padding: 12px 15px;
|
|||
|
|
font-size: 13px;
|
|||
|
|
text-align: left;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.breadcrumb-list {
|
|||
|
|
justify-content: flex-start;
|
|||
|
|
overflow-x: auto;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
-webkit-overflow-scrolling: touch;
|
|||
|
|
padding-bottom: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.breadcrumb-item {
|
|||
|
|
flex-shrink: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.breadcrumb-separator {
|
|||
|
|
margin: 0 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dark .simple-breadcrumb {
|
|||
|
|
padding: 12px 15px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@media (max-width: 480px) {
|
|||
|
|
.simple-breadcrumb {
|
|||
|
|
margin: 10px 0;
|
|||
|
|
padding: 10px 12px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.breadcrumb-item a,
|
|||
|
|
.breadcrumb-item span.breadcrumb-current {
|
|||
|
|
padding: 3px 6px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.breadcrumb-separator {
|
|||
|
|
margin: 0 6px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dark .simple-breadcrumb {
|
|||
|
|
padding: 10px 12px;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ========== 打印样式 ========== */
|
|||
|
|
@media print {
|
|||
|
|
.simple-breadcrumb {
|
|||
|
|
display: none;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ========== 无障碍支持 ========== */
|
|||
|
|
.breadcrumb-item a:focus {
|
|||
|
|
outline: 2px solid #1e87f0;
|
|||
|
|
outline-offset: 2px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dark .breadcrumb-item a:focus {
|
|||
|
|
outline-color: #4dabf7;
|
|||
|
|
}
|
|||
|
|
';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
?>
|