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