| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- import React, { useEffect, useMemo, useState } from 'react';
- import { Layout, Menu } from 'antd';
- import { useLocation, useNavigate, useParams } from 'react-router-dom';
- import { helpMenu, HelpMenuItem } from '../menu';
- import MarkdownViewer, { TocItem } from './MarkdownViewer';
- const { Sider, Content } = Layout;
- // 动态加载 md 文件(Vite 6 写法)
- const files = import.meta.glob('/src/help/docs/**/*.md', { query: '?raw', import: 'default' });
- function findMenuByPath(pathname: string): HelpMenuItem | undefined {
- const flat: HelpMenuItem[] = [];
- const dfs = (arr: HelpMenuItem[]) => arr.forEach((i) => { flat.push(i); i.children && dfs(i.children); });
- dfs(helpMenu);
- return flat.find((i) => i.path.split('#')[0] === pathname);
- }
- const HelpLayout: React.FC = () => {
- const params = useParams();
- const navigate = useNavigate();
- const location = useLocation();
- const pathname = location.pathname;
- const [md, setMd] = useState('');
- const [toc, setToc] = useState<TocItem[]>([]);
- const current = useMemo(() => findMenuByPath(pathname), [pathname]);
- useEffect(() => {
- // 根据菜单项的 doc 加载 md
- const doc = current?.doc;
- if (!doc) { setMd('# 欢迎使用帮助中心'); return; }
- const key = `/src/help/docs/${doc}`;
- const loader = (files as any)[key];
- if (loader) {
- (loader as () => Promise<string> )().then((raw) => setMd(raw));
- } else {
- setMd('> 文档未找到: ' + doc);
- }
- }, [current?.doc]);
- useEffect(() => {
- // 定位到 hash 标题
- if (!location.hash) return;
- const id = decodeURIComponent(location.hash.slice(1));
- const el = document.getElementById(id);
- if (el) {
- el.scrollIntoView({ behavior: 'smooth', block: 'start' });
- }
- }, [md, location.hash]);
- // build antd menu items
- const menuItems = useMemo(() => {
- const toAntd = (items: HelpMenuItem[]): any[] => items.map((it) => ({
- key: it.path,
- label: it.title,
- children: it.children ? toAntd(it.children) : undefined,
- }));
- return toAntd(helpMenu);
- }, []);
- const defaultOpenKeys = useMemo(() => {
- const keys: string[] = [];
- const dfs = (items: HelpMenuItem[]) => {
- items.forEach((it) => {
- if (it.children && it.children.length) {
- keys.push(it.path);
- dfs(it.children);
- }
- });
- };
- dfs(helpMenu);
- return keys;
- }, []);
- return (
- <Layout style={{ height: '100%', background: 'transparent' }}>
- <Sider width={240} style={{ background: '#fff', borderRight: '1px solid #f0f0f0' }}>
- <div style={{ padding: 12, fontWeight: 600 }}>帮助文档</div>
- <Menu
- mode="inline"
- selectedKeys={[pathname + (location.hash || '')]}
- defaultOpenKeys={defaultOpenKeys}
- items={menuItems}
- onClick={(e) => navigate(e.key)}
- />
- </Sider>
- <Content style={{ padding: 16, overflow: 'auto' }}>
- <div style={{ display: 'flex', gap: 24 }}>
- <div style={{ flex: 1, minWidth: 0 }}>
- <MarkdownViewer content={md} onToc={setToc} />
- </div>
- <div style={{ width: 220 }}>
- <div style={{ position: 'sticky', top: 16 }}>
- <div style={{ marginBottom: 8, fontWeight: 600 }}>On this page</div>
- <div>
- {toc.map((t) => (
- <div key={t.id} style={{ marginLeft: (t.level - 1) * 12 }}>
- <a href={`#${t.id}`}>{t.text}</a>
- </div>
- ))}
- </div>
- </div>
- </div>
- </div>
- </Content>
- </Layout>
- );
- };
- export default HelpLayout;
|