This commit is contained in:
xunbu
2026-01-07 00:12:13 +08:00
parent 5a98578596
commit cd500c50c5

View File

@@ -19,8 +19,8 @@
} }
@media print { @media print {
#toc-btn, #toc-sidebar { display: none !important; }
html, main { padding: 0 !important; } html, main { padding: 0 !important; }
#toc-panel, #toc-btn { display: none !important; }
} }
html { font-size: 15px; } html { font-size: 15px; }
@@ -28,126 +28,154 @@
/* 目录按钮 */ /* 目录按钮 */
#toc-btn { #toc-btn {
position: fixed; left: 10px; top: 50%; transform: translateY(-50%); position: fixed;
width: 30px; height: 50px; left: 10px;
background: #f8f8f8; border: 1px solid #ddd; border-radius: 0 4px 4px 0; top: 10px;
cursor: pointer; z-index: 1000; width: 32px;
display: flex; align-items: center; justify-content: center; height: 32px;
font-size: 14px; color: #666; background: #fff;
opacity: 0; transition: left 0.3s ease, opacity 0.2s; border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: #666;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
} }
#toc-btn:hover { background: #eee; } #toc-btn:hover { background: #f5f5f5; }
#toc-btn.visible { opacity: 1; }
#toc-btn.open { left: 270px; }
/* 侧边栏 */ /* 目录面板 */
#toc-sidebar { #toc-panel {
position: fixed; left: -260px; top: 0; width: 260px; height: 100vh; position: fixed;
background: #fafafa; border-right: 1px solid #ddd; left: 0;
z-index: 999; transition: left 0.3s ease; top: 0;
padding: 15px; box-sizing: border-box; width: 240px;
height: 100vh;
background: #fff;
border-right: 1px solid #ddd;
z-index: 9999;
padding: 50px 15px 20px;
box-sizing: border-box;
overflow-y: auto;
transform: translateX(-100%);
transition: transform 0.2s ease;
} }
#toc-sidebar.open { left: 0; } #toc-panel.show { transform: translateX(0); }
#toc-sidebar h3 { .toc-item { margin: 2px 0; }
margin: 0 0 15px; .toc-link {
font-size: 16px; display: flex;
color: #333; align-items: center;
padding-bottom: 10px; justify-content: space-between;
border-bottom: 1px solid #e0e0e0; padding: 6px 10px;
}
#toc-list { list-style: none; padding: 0; margin: 0; }
#toc-list li { margin: 2px 0; }
#toc-list li a {
color: #555; color: #555;
text-decoration: none; text-decoration: none;
display: block; font-size: 13px;
padding: 6px 10px; border-radius: 3px;
border-radius: 4px; cursor: pointer;
font-size: 14px;
transition: background 0.15s;
} }
#toc-list li a:hover { .toc-link:hover { background: #f5f5f5; }
background: #e8e8e8; .toc-link.active { background: #007bff; color: #fff; }
color: #0066cc;
.toc-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.toc-expander {
width: 18px;
height: 18px;
line-height: 16px;
text-align: center;
font-size: 12px;
color: #999;
cursor: pointer;
flex-shrink: 0;
} }
#toc-list .toc-h1 { .toc-expander:hover { color: #333; }
font-weight: 600;
font-size: 15px; .toc-children { display: none; padding-left: 12px; }
margin-top: 8px; .toc-children.expanded { display: block; }
}
#toc-list .toc-h1 > a { color: #333; } .toc-item[data-level="1"] > .toc-link { font-weight: 500; }
#toc-list .toc-h2 { margin-left: 0; } .toc-item[data-level="2"] > .toc-link { padding-left: 20px; }
#toc-list .toc-h2 > a { padding-left: 24px; font-size: 14px; } .toc-item[data-level="3"] > .toc-link { padding-left: 30px; }
#toc-list .toc-h3 { margin-left: 0; } .toc-item[data-level="4"] > .toc-link { padding-left: 40px; }
#toc-list .toc-h3 > a { padding-left: 36px; font-size: 13px; color: #666; } .toc-item[data-level="5"] > .toc-link { padding-left: 50px; }
#toc-list .toc-h4 { margin-left: 0; }
#toc-list .toc-h4 > a { padding-left: 48px; font-size: 12px; color: #888; }
#toc-list .toc-h5 > a { padding-left: 60px; font-size: 12px; color: #888; }
#toc-list .toc-h6 > a { padding-left: 72px; font-size: 12px; color: #888; }
</style> </style>
</head> </head>
<body> <body>
<div id="toc-panel"><ul class="toc-list" id="toc-list"></ul></div>
<!-- 目录按钮 --> <button id="toc-btn" onclick="toggleToc()"></button>
<div id="toc-btn" title="目录"></div>
<!-- 侧边栏 -->
<div id="toc-sidebar">
<h3>目录</h3>
<ul id="toc-list"></ul>
</div>
<main>{{markdown}}</main> <main>{{markdown}}</main>
</body> </body>
{{renderMathInElement}} {{renderMathInElement}}
{{mermaid}} {{mermaid}}
<script> <script>
(function(){ (function() {
const btn = document.getElementById('toc-btn'); const panel = document.getElementById('toc-panel');
const sidebar = document.getElementById('toc-sidebar');
const list = document.getElementById('toc-list'); const list = document.getElementById('toc-list');
const main = document.querySelector('main'); let headings = [];
// 生成目录 function init() {
const headings = main.querySelectorAll('h1, h2, h3, h4'); headings = Array.from(document.querySelectorAll('main h1, main h2, main h3, main h4, main h5, main h6'));
if (headings.length === 0) { btn.style.display = 'none'; return; } if (headings.length === 0) return;
headings.forEach((h, i) => { headings.forEach((h, i) => { if (!h.id) h.id = 'h-' + i; });
if (!h.id) h.id = 'h-' + i;
const li = document.createElement('li');
li.className = 'toc-' + h.tagName.toLowerCase();
li.innerHTML = '<a href="#' + h.id + '">' + h.textContent + '</a>';
list.appendChild(li);
});
// 切换目录 const root = { level: 0, children: [] };
function toggleTOC() { const stack = [root];
const open = sidebar.classList.toggle('open'); headings.forEach(h => {
btn.classList.toggle('open', open); const level = parseInt(h.tagName[1]);
btn.textContent = open ? '✕' : '☰'; const item = { id: h.id, text: h.textContent.trim(), level, children: [] };
while (stack.length > 1 && stack[stack.length - 1].level >= level) stack.pop();
stack[stack.length - 1].children.push(item);
stack.push(item);
});
render(root.children, list);
} }
btn.onclick = toggleTOC;
// 左侧滑动/悬停检测 function render(items, parent) {
let touchStartX = 0; items.forEach(item => {
const li = document.createElement('li');
li.className = 'toc-item';
li.dataset.level = item.level;
document.addEventListener('touchstart', e => { touchStartX = e.touches[0].clientX; }); let html = `<a class="toc-link" href="#${item.id}">
document.addEventListener('touchmove', e => { <span class="toc-text">${item.text}</span>`;
const x = e.touches[0].clientX; if (item.children.length) {
if (touchStartX > 30 && x < 50 && !sidebar.classList.contains('open')) { html += `<span class="toc-expander" onclick="event.stopPropagation(); toggle(this)">+</span>`;
btn.classList.add('visible'); }
} html += `</a>`;
li.innerHTML = html;
if (item.children.length) {
const ul = document.createElement('ul');
ul.className = 'toc-children';
render(item.children, ul);
li.appendChild(ul);
if (item.level === 1) { ul.classList.add('expanded'); li.querySelector('.toc-expander').textContent = '-'; }
}
parent.appendChild(li);
});
}
function toggle(btn) {
const ul = btn.closest('li').querySelector('.toc-children');
ul.classList.toggle('expanded');
btn.textContent = ul.classList.contains('expanded') ? '-' : '+';
}
window.toggleToc = function() { panel.classList.toggle('show'); };
document.querySelectorAll('.toc-link').forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const id = this.getAttribute('href').slice(1);
document.getElementById(id).scrollIntoView({ behavior: 'smooth' });
});
}); });
document.addEventListener('mousemove', e => { init();
if (e.clientX < 50 && !sidebar.classList.contains('open')) {
btn.classList.add('visible');
} else {
btn.classList.remove('visible');
}
});
})(); })();
setTimeout(() => { setTimeout(() => {