96 lines
2.8 KiB
TypeScript
96 lines
2.8 KiB
TypeScript
'use client';
|
|
|
|
import Link from 'next/link';
|
|
import { usePathname } from 'next/navigation';
|
|
import { Folder, FileText } from 'lucide-react';
|
|
import { cn } from '@/lib/utils';
|
|
import type { ManualTreeNode } from '@/lib/types';
|
|
|
|
interface TreeNavProps {
|
|
nodes: ManualTreeNode[];
|
|
}
|
|
|
|
/** 递归渲染节点(含子孙) */
|
|
function TreeItem({ node, depth = 0 }: { node: ManualTreeNode; depth?: number }) {
|
|
const pathname = usePathname();
|
|
const href = `/manual/${node.id}`;
|
|
const active = pathname === href;
|
|
|
|
// ===== 顶层目录 → 分区标题(带分隔线 + 缩进引导线) =====
|
|
if (depth === 0 && node.type === 0) {
|
|
return (
|
|
<div className="border-t border-gray-100 pt-3 first:border-t-0 first:pt-0">
|
|
<div className="flex items-center gap-1.5 px-2 py-1 text-[11px] font-bold uppercase tracking-wider text-slate-400">
|
|
<Folder className="h-3.5 w-3.5 shrink-0 text-amber-500" />
|
|
{node.title}
|
|
</div>
|
|
{node.children.length > 0 && (
|
|
<div className="mt-1 ml-3 space-y-0.5 border-l border-gray-100 pl-2">
|
|
{node.children.map((c) => (
|
|
<TreeItem key={c.id} node={c} depth={depth + 1} />
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// ===== 嵌套目录 → 子标题(带缩进引导线) =====
|
|
if (node.type === 0) {
|
|
return (
|
|
<div className="pt-2">
|
|
<div className="flex items-center gap-1.5 px-2 py-1 text-sm font-semibold text-slate-700">
|
|
<Folder className="h-3.5 w-3.5 shrink-0 text-amber-400" />
|
|
{node.title}
|
|
</div>
|
|
{node.children.length > 0 && (
|
|
<div className="mt-0.5 ml-3 space-y-0.5 border-l border-gray-100 pl-2">
|
|
{node.children.map((c) => (
|
|
<TreeItem key={c.id} node={c} depth={depth + 1} />
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// ===== 文档 → 可点击链接 =====
|
|
return (
|
|
<Link
|
|
href={href}
|
|
className={cn(
|
|
'flex items-center gap-1.5 rounded-md px-2 py-1.5 text-sm transition-colors',
|
|
active
|
|
? 'bg-brand-50 font-medium text-brand-700'
|
|
: 'text-slate-600 hover:bg-slate-100 hover:text-slate-900',
|
|
)}
|
|
>
|
|
<FileText
|
|
className={cn(
|
|
'h-3.5 w-3.5 shrink-0',
|
|
active ? 'text-brand-600' : 'text-slate-400',
|
|
)}
|
|
/>
|
|
<span className="truncate">{node.title}</span>
|
|
</Link>
|
|
);
|
|
}
|
|
|
|
/** 使用手册左侧树形导航(始终展开) */
|
|
export function ManualTreeNav({ nodes }: TreeNavProps) {
|
|
if (nodes.length === 0) {
|
|
return (
|
|
<div className="px-3 py-6 text-center text-xs text-slate-400">
|
|
暂无文档
|
|
</div>
|
|
);
|
|
}
|
|
return (
|
|
<nav className="space-y-1">
|
|
{nodes.map((n) => (
|
|
<TreeItem key={n.id} node={n} />
|
|
))}
|
|
</nav>
|
|
);
|
|
}
|