nex_design/src/pages/DocsPage.jsx

191 lines
4.7 KiB
React
Raw Normal View History

2025-11-05 06:17:47 +00:00
import { useState, useEffect } from 'react'
import { Layout, Menu, Spin } from 'antd'
import { FileTextOutlined } from '@ant-design/icons'
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import rehypeRaw from 'rehype-raw'
import rehypeHighlight from 'rehype-highlight'
import 'highlight.js/styles/github.css'
import './DocsPage.css'
const { Sider, Content } = Layout
// 文档目录数据
const docsMenuData = [
{
key: 'design',
label: '设计规范',
children: [
{
key: 'design-cookbook',
label: '设计手册',
path: '/docs/DESIGN_COOKBOOK.md',
},
],
},
{
key: 'components',
label: '组件文档',
children: [
{
key: 'components-overview',
label: '组件概览',
path: '/docs/components/README.md',
},
{
key: 'page-title-bar',
label: 'PageTitleBar',
path: '/docs/components/PageTitleBar.md',
},
{
key: 'list-action-bar',
label: 'ListActionBar',
path: '/docs/components/ListActionBar.md',
},
{
key: 'tree-filter-panel',
label: 'TreeFilterPanel',
path: '/docs/components/TreeFilterPanel.md',
},
{
key: 'list-table',
label: 'ListTable',
path: '/docs/components/ListTable.md',
},
{
key: 'detail-drawer',
label: 'DetailDrawer',
path: '/docs/components/DetailDrawer.md',
},
{
key: 'info-panel',
label: 'InfoPanel',
path: '/docs/components/InfoPanel.md',
},
{
key: 'confirm-dialog',
label: 'ConfirmDialog',
path: '/docs/components/ConfirmDialog.md',
},
{
key: 'toast',
label: 'Toast',
path: '/docs/components/Toast.md',
},
],
},
{
key: 'pages',
label: '页面文档',
children: [
{
key: 'main-layout',
label: '主布局',
path: '/docs/pages/main-layout.md',
},
],
},
]
function DocsPage() {
const [selectedKey, setSelectedKey] = useState('design-cookbook')
const [markdownContent, setMarkdownContent] = useState('')
const [loading, setLoading] = useState(false)
// 构建菜单项
const menuItems = docsMenuData.map((group) => ({
key: group.key,
label: group.label,
icon: <FileTextOutlined />,
children: group.children.map((item) => ({
key: item.key,
label: item.label,
})),
}))
// 根据 key 查找文档路径
const findDocPath = (key) => {
for (const group of docsMenuData) {
const found = group.children.find((item) => item.key === key)
if (found) return found.path
}
return null
}
// 加载 markdown 文件
const loadMarkdown = async (key) => {
const path = findDocPath(key)
if (!path) return
setLoading(true)
try {
const response = await fetch(path)
if (response.ok) {
const text = await response.text()
setMarkdownContent(text)
} else {
setMarkdownContent('# 文档加载失败\n\n无法加载该文档请稍后重试。')
}
} catch (error) {
console.error('Error loading markdown:', error)
setMarkdownContent('# 文档加载失败\n\n' + error.message)
} finally {
setLoading(false)
}
}
// 处理菜单点击
const handleMenuClick = ({ key }) => {
setSelectedKey(key)
loadMarkdown(key)
}
// 初始加载默认文档
useEffect(() => {
loadMarkdown(selectedKey)
}, [])
return (
<div className="docs-page">
<Layout className="docs-layout">
{/* 左侧目录 */}
<Sider width={280} className="docs-sider" theme="light">
<div className="docs-sider-header">
<h2>文档中心</h2>
</div>
<Menu
mode="inline"
selectedKeys={[selectedKey]}
defaultOpenKeys={['design', 'components', 'pages']}
items={menuItems}
onClick={handleMenuClick}
className="docs-menu"
/>
</Sider>
{/* 右侧内容 */}
<Content className="docs-content">
<div className="docs-content-wrapper">
{loading ? (
<div className="docs-loading">
<Spin size="large" tip="加载中..." />
</div>
) : (
<div className="markdown-body">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw, rehypeHighlight]}
>
{markdownContent}
</ReactMarkdown>
</div>
)}
</div>
</Content>
</Layout>
</div>
)
}
export default DocsPage