From 1c8d56aea079196ed48f406a635a6f1bbf29f23f Mon Sep 17 00:00:00 2001
From: Tevin <tingquanren@163.com>
Date: Thu, 09 Apr 2026 16:57:24 +0800
Subject: [PATCH] feat(example): 添加侧边菜单示例页面
---
example/App.tsx | 78 ++++++++++++++++++
example/main.tsx | 22 +++++
example/pages/side-menu/SideMenuPage.tsx | 113 ++++++++++++++++++++++++++++
3 files changed, 209 insertions(+), 4 deletions(-)
diff --git a/example/App.tsx b/example/App.tsx
index 6b7b9cb..28ba3e3 100644
--- a/example/App.tsx
+++ b/example/App.tsx
@@ -1,8 +1,80 @@
+import { useState } from 'react';
+import type { ReactNode } from 'react';
+import { SideMenuPage } from './pages/side-menu/SideMenuPage';
+
+/** 组件配置 */
+const components: Array<{ name: string; path: string; component: ReactNode }> = [
+ {
+ name: 'CSideMenu',
+ path: '/pages/side-menu/SideMenuPage.tsx',
+ component: <SideMenuPage />,
+ },
+];
+
+/**
+ * 组件展示主页面
+ * 左侧:组件列表
+ * 右侧:iframe 展示组件示例
+ */
function App() {
+ const [activeIndex, setActiveIndex] = useState(0);
+
return (
- <div style={{ padding: '20px' }}>
- <h1>admin2-components</h1>
- <p>组件展示示例(待开发)</p>
+ <div style={{ display: 'table', width: '100%', height: '100vh' }}>
+ {/* 左侧组件列表 */}
+ <div
+ style={{
+ display: 'table-cell',
+ width: '200px',
+ borderRight: '1px solid #eee',
+ padding: '16px',
+ background: '#fafafa',
+ verticalAlign: 'top',
+ }}
+ >
+ <h3 style={{ marginTop: 0 }}>组件列表</h3>
+ <ul style={{ listStyle: 'none', padding: 0, margin: 0 }}>
+ {components.map((comp, index) => (
+ <li key={comp.name} style={{ marginBottom: '8px' }}>
+ <button
+ onClick={() => setActiveIndex(index)}
+ style={{
+ width: '100%',
+ padding: '8px 12px',
+ textAlign: 'left',
+ background: activeIndex === index ? '#1890ff' : '#fff',
+ color: activeIndex === index ? '#fff' : '#333',
+ border: '1px solid #d9d9d9',
+ borderRadius: '4px',
+ cursor: 'pointer',
+ fontSize: '14px',
+ }}
+ >
+ {comp.name}
+ </button>
+ </li>
+ ))}
+ </ul>
+ </div>
+
+ {/* 右侧 iframe 容器 */}
+ <div
+ style={{
+ display: 'table-cell',
+ background: '#fff',
+ verticalAlign: 'top',
+ }}
+ >
+ <iframe
+ src={`http://localhost:5173/#/preview/${components[activeIndex].path}`}
+ style={{
+ width: '100%',
+ height: '100%',
+ border: 'none',
+ }}
+ title={components[activeIndex].name}
+ />
+ </div>
</div>
);
}
diff --git a/example/main.tsx b/example/main.tsx
index 361e7cf..67bd719 100644
--- a/example/main.tsx
+++ b/example/main.tsx
@@ -1,5 +1,25 @@
import { createRoot } from 'react-dom/client';
import './index.css';
import App from './App';
+import { SideMenuPage } from './pages/side-menu/SideMenuPage';
-createRoot(document.getElementById('root')!).render(<App />);
+/** 预览页面 - 无 shell,直接渲染组件 */
+function PreviewPage() {
+ const hash = window.location.hash;
+ const path = hash.replace('#/preview/', '');
+
+ if (path.includes('side-menu')) {
+ return <SideMenuPage />;
+ }
+
+ return <div>Unknown component</div>;
+}
+
+const root = createRoot(document.getElementById('root')!);
+
+// 检查是否是预览模式
+if (window.location.hash.includes('/preview/')) {
+ root.render(<PreviewPage />);
+} else {
+ root.render(<App />);
+}
diff --git a/example/pages/side-menu/SideMenuPage.tsx b/example/pages/side-menu/SideMenuPage.tsx
new file mode 100644
index 0000000..0da4975
--- /dev/null
+++ b/example/pages/side-menu/SideMenuPage.tsx
@@ -0,0 +1,113 @@
+import React, { useState, useEffect } from 'react';
+import { CSideMenu } from '../../../src';
+import type { MenuItem } from '../../../src/framework/sideMenu/types';
+
+/** 模拟菜单数据 */
+const mockTree: MenuItem = {
+ key: '1',
+ label: '导航1',
+ type: 'folder',
+ children: [
+ {
+ key: '1-1',
+ label: '子菜单1-1',
+ type: 'folder',
+ children: [
+ { key: '1-1-1', label: '页面1-1-1', path: '/page1-1-1', pageName: 'Page111', type: 'file' },
+ { key: '1-1-2', label: '页面1-1-2', path: '/page1-1-2', pageName: 'Page112', type: 'file' },
+ ],
+ },
+ {
+ key: '1-2',
+ label: '页面1-2',
+ path: '/page1-2',
+ pageName: 'Page12',
+ type: 'file',
+ },
+ ],
+};
+
+/** 已打开的页面列表 */
+const mockPanesOnShelf = [
+ { key: '1-1-1' },
+ { key: '1-2' },
+];
+
+/**
+ * CSideMenu 组件示例页
+ */
+export function SideMenuPage() {
+ const [collapsed, setCollapsed] = useState(false);
+ const [curActivePaneKey, setCurActivePaneKey] = useState<string | number>('1-1-1');
+ const [isMobile, setIsMobile] = useState(false);
+
+ // 检测移动端 - 与 CSideMenu 组件的判断逻辑一致
+ useEffect(() => {
+ const checkMobile = () => {
+ const width = window.innerWidth;
+ const screenWidth = window.screen.width;
+ setIsMobile(width <= 992 && screenWidth <= 1024);
+ };
+ checkMobile();
+ window.addEventListener('resize', checkMobile);
+ return () => window.removeEventListener('resize', checkMobile);
+ }, []);
+
+ const handleClickMenuItem = (item: MenuItem) => {
+ console.log('点击菜单项:', item);
+ setCurActivePaneKey(item.key);
+ };
+
+ const handleSetMenuCollapse = (value: boolean | void) => {
+ if (typeof value === 'boolean') {
+ setCollapsed(value);
+ } else {
+ setCollapsed((prev) => !prev);
+ }
+ };
+
+ return (
+ <div style={{ display: 'flex', height: '100vh' }}>
+ <div style={{ flexShrink: 0 }}>
+ <CSideMenu
+ title="管理后台"
+ tree={mockTree}
+ collapsed={collapsed}
+ curActivePaneKey={curActivePaneKey}
+ panesOnShelf={mockPanesOnShelf}
+ onClickMenuItem={handleClickMenuItem}
+ onSetMenuCollapse={handleSetMenuCollapse}
+ />
+ </div>
+
+ <div style={{ flex: 1, padding: '20px', position: 'relative' }}>
+ {/* 移动端折叠/展开触发器 */}
+ {isMobile && (
+ <button
+ onClick={() => handleSetMenuCollapse()}
+ style={{
+ position: 'absolute',
+ top: '10px',
+ left: '10px',
+ zIndex: 100,
+ padding: '8px 12px',
+ background: '#1890ff',
+ color: '#fff',
+ border: 'none',
+ borderRadius: '4px',
+ cursor: 'pointer',
+ }}
+ >
+ {collapsed ? '展开菜单' : '收起菜单'}
+ </button>
+ )}
+
+ <h2>CSideMenu 组件示例</h2>
+ <p>当前选中: {curActivePaneKey}</p>
+ <p>折叠状态: {collapsed ? '收起' : '展开'}</p>
+ </div>
+ </div>
+ );
+}
+
+export default SideMenuPage;
--
Gitblit v1.9.1