191 lines
4.7 KiB
React
191 lines
4.7 KiB
React
|
|
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
|