feat: 文档Tab重构为左右布局——左侧栏目录树+右侧内容区(GitBook式),点击目录右侧显示,默认打开版本变更
This commit is contained in:
+63
-49
@@ -1932,35 +1932,40 @@ function inMd(t) {
|
||||
// ── 📚 文档 Tab(2026-08-11 新增,开源项目式文档体系)──
|
||||
let _docsState = { loaded: false, currentPath: null, view: 'tree' }; // tree | versions | read
|
||||
|
||||
// ── 📚 文档 Tab(2026-08-11 重构为左右布局:左侧栏目录 + 右侧内容)──
|
||||
let _docsState = { currentPath: null, tree: null };
|
||||
|
||||
async function renderDocs() {
|
||||
const el = document.getElementById('tab-docs');
|
||||
if (!el) return;
|
||||
el.innerHTML =
|
||||
'<div class="flex gap-2 items-center mb-3 border-b border-slate-800 pb-2">' +
|
||||
'<button class="doc-nav-btn px-3 py-1.5 text-sm rounded-lg border border-slate-700 ' + (_docsState.view === 'tree' ? 'bg-slate-800 text-blue-400' : 'text-slate-400') + '" onclick="_docsView(\'tree\')">📖 目录</button>' +
|
||||
'<button class="doc-nav-btn px-3 py-1.5 text-sm rounded-lg border border-slate-700 ' + (_docsState.view === 'versions' ? 'bg-slate-800 text-blue-400' : 'text-slate-400') + '" onclick="_docsView(\'versions\')">📜 版本变更</button>' +
|
||||
'<span class="text-xs text-slate-500 ml-2">文档中心:帮助文档 + 版本变更说明(面向运营者)</span>' +
|
||||
'</div>' +
|
||||
'<div id="docs-body" class="text-sm"></div>';
|
||||
if (_docsState.view === 'tree') {
|
||||
await _renderDocsTree();
|
||||
} else if (_docsState.view === 'versions') {
|
||||
await _renderDocsVersions();
|
||||
'<div class="flex gap-4" style="min-height:600px">' +
|
||||
// 左侧栏:目录树
|
||||
'<div class="w-64 flex-shrink-0 border-r border-slate-800 pr-3 overflow-y-auto" style="max-height:75vh">' +
|
||||
'<div class="text-xs font-semibold text-slate-400 mb-2 uppercase tracking-wide sticky top-0 bg-slate-950 py-1">📚 文档目录</div>' +
|
||||
'<div id="docs-tree" class="space-y-1 text-sm"></div>' +
|
||||
'</div>' +
|
||||
// 右侧:内容区
|
||||
'<div class="flex-1 min-w-0 overflow-y-auto" style="max-height:75vh">' +
|
||||
'<div id="docs-content" class="text-sm"></div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
// 首次加载:渲染目录树
|
||||
await _renderDocsTree();
|
||||
// 默认显示版本变更(运营者最关心的)
|
||||
if (!_docsState.currentPath) {
|
||||
await _docsRead('VERSIONS.md');
|
||||
}
|
||||
}
|
||||
|
||||
async function _docsView(view) {
|
||||
_docsState.view = view;
|
||||
await renderDocs();
|
||||
}
|
||||
|
||||
async function _renderDocsTree() {
|
||||
const body = document.getElementById('docs-body');
|
||||
const tree = document.getElementById('docs-tree');
|
||||
const data = await fetchJSON('/api/docs/index');
|
||||
if (!data.ok) {
|
||||
body.innerHTML = '<div class="text-red-400">' + (data.error || '加载失败') + '</div>';
|
||||
tree.innerHTML = '<div class="text-red-400 text-xs">' + (data.error || '加载失败') + '</div>';
|
||||
return;
|
||||
}
|
||||
_docsState.tree = data.index;
|
||||
const byCat = {};
|
||||
data.index.forEach(d => {
|
||||
byCat[d.category] = byCat[d.category] || [];
|
||||
@@ -1968,54 +1973,63 @@ async function _renderDocsTree() {
|
||||
});
|
||||
let html = '';
|
||||
for (const [cat, docs] of Object.entries(byCat)) {
|
||||
html += '<div class="mb-4">' +
|
||||
'<div class="text-xs font-semibold text-slate-400 mb-1 uppercase tracking-wide">' + cat + '</div>' +
|
||||
html += '<div class="mb-3">' +
|
||||
'<div class="text-[11px] font-semibold text-slate-500 mb-1 uppercase tracking-wide">' + cat + '</div>' +
|
||||
'<div class="space-y-0.5">' +
|
||||
docs.map(d =>
|
||||
'<div class="cursor-pointer hover:bg-slate-800/60 px-2 py-1 rounded text-slate-300" onclick="_docsRead(\'' + d.path + '\')">' +
|
||||
(d.core ? '⭐ ' : '📄 ') + d.title + '</div>'
|
||||
'<div class="cursor-pointer hover:bg-slate-800/60 px-2 py-1 rounded text-slate-300 truncate" title="' + d.path + '" onclick="_docsRead(\'' + d.path + '\')">' +
|
||||
(d.core ? '⭐ ' : '') + d.title + '</div>'
|
||||
).join('') +
|
||||
'</div></div>';
|
||||
}
|
||||
body.innerHTML = html;
|
||||
tree.innerHTML = html;
|
||||
// 高亮当前选中项
|
||||
_docsHighlight();
|
||||
}
|
||||
|
||||
async function _renderDocsVersions() {
|
||||
const body = document.getElementById('docs-body');
|
||||
const data = await fetchJSON('/api/docs/versions');
|
||||
if (!data.ok) {
|
||||
body.innerHTML = '<div class="text-red-400">' + (data.error || '加载失败') + '</div>';
|
||||
return;
|
||||
function _docsHighlight() {
|
||||
document.querySelectorAll('#docs-tree [title]').forEach(el => el.classList.remove('bg-slate-800', 'text-blue-400'));
|
||||
if (_docsState.currentPath) {
|
||||
document.querySelectorAll('#docs-tree [title="' + _docsState.currentPath + '"]').forEach(el => {
|
||||
el.classList.add('bg-slate-800', 'text-blue-400');
|
||||
});
|
||||
}
|
||||
body.innerHTML = data.versions.map(v => {
|
||||
const parts = v.title.split('—');
|
||||
const date = parts[0].trim();
|
||||
const title = parts.slice(1).join('—').trim() || v.title;
|
||||
return '<div class="mb-4 border-l-2 border-blue-700 pl-3">' +
|
||||
'<div class="text-xs text-slate-500">' + date + '</div>' +
|
||||
'<div class="font-semibold text-slate-200 mb-1">' + title + '</div>' +
|
||||
'<div class="text-slate-400 whitespace-pre-wrap text-xs leading-relaxed">' + v.body + '</div>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
async function _docsRead(path) {
|
||||
_docsState.view = 'read';
|
||||
_docsState.currentPath = path;
|
||||
const body = document.getElementById('docs-body');
|
||||
const navBar = body.closest('#tab-docs').querySelector('.flex.gap-2');
|
||||
// 更新导航显示当前路径
|
||||
_docsHighlight();
|
||||
const content = document.getElementById('docs-content');
|
||||
if (!content) return;
|
||||
content.innerHTML = '<div class="text-slate-500 text-xs">加载中...</div>';
|
||||
const data = await fetchJSON('/api/docs/read?path=' + encodeURIComponent(path));
|
||||
if (!data.ok) {
|
||||
body.innerHTML = '<div class="text-red-400">' + (data.error || '加载失败') + '</div>';
|
||||
content.innerHTML = '<div class="text-red-400">' + (data.error || '加载失败') + '</div>';
|
||||
return;
|
||||
}
|
||||
body.innerHTML =
|
||||
'<div class="mb-2">' +
|
||||
'<button class="text-xs text-blue-400 hover:underline" onclick="_docsView(\'tree\')">← 返回目录</button>' +
|
||||
'<span class="text-xs text-slate-500 ml-2">' + path + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="prose-invert bg-slate-900/50 border border-slate-800 rounded-xl p-4 whitespace-pre-wrap text-sm leading-relaxed text-slate-300 font-mono">' + inMd(data.content) + '</div>';
|
||||
// 版本变更用特殊排版(日期+标题+正文分段)
|
||||
if (path === 'VERSIONS.md') {
|
||||
content.innerHTML = _renderVersionsHTML(data.content);
|
||||
} else {
|
||||
content.innerHTML =
|
||||
'<div class="mb-2 text-xs text-slate-500 border-b border-slate-800 pb-2">📄 ' + path + '</div>' +
|
||||
'<div class="prose-invert bg-slate-900/50 border border-slate-800 rounded-xl p-5 whitespace-pre-wrap leading-relaxed text-slate-300">' + inMd(data.content) + '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
function _renderVersionsHTML(md) {
|
||||
// 解析 VERSIONS.md:按 ## 分段,每段一个版本卡片
|
||||
const parts = md.split(/^##\s+/m).filter(p => p.trim() && !p.startsWith('阅读指南') && !p.startsWith('维护说明'));
|
||||
const cards = parts.map(p => {
|
||||
const lines = p.split('\n');
|
||||
const title = lines[0].trim();
|
||||
const body = lines.slice(1).join('\n').trim();
|
||||
return '<div class="mb-4 border border-slate-800 rounded-xl p-4 bg-slate-900/50">' +
|
||||
'<div class="font-semibold text-slate-100 mb-2 text-base">' + title + '</div>' +
|
||||
'<div class="text-slate-300 whitespace-pre-wrap leading-relaxed text-[13px]">' + inMd(body) + '</div>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
return '<div class="mb-2 text-xs text-slate-500 border-b border-slate-800 pb-2">📜 版本变更记录(给运营者的详细说明)</div>' + cards;
|
||||
}
|
||||
|
||||
function renderResearch() {
|
||||
|
||||
Reference in New Issue
Block a user