yggjs_rlayout使用教程 v0.1.0

创建项目

首先,确保您的系统已安装Node.js。然后使用npm全局安装pnpm和yggjs_react:

bash 复制代码
npm install -g pnpm
npm install -g yggjs_react

创建项目:

bash 复制代码
ggr create hello

启动服务:

bash 复制代码
cd hello
pnpm install --registry=https://registry.npmmirror.com
pnpm dev

整合rlayout

安装依赖:

bash 复制代码
pnpm add react-router-dom
pnpm add yggjs_rlayout

效果预览

首页

文档页面

关于页面

完整代码

package.json

json 复制代码
{
  "name": "hello",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "packageManager": "pnpm@8.15.0",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-router-dom": "^6.30.1",
    "yggjs_rlayout": "^0.1.1",
    "zustand": "^5.0.1"
  },
  "devDependencies": {
    "@types/react": "^18.3.23",
    "@types/react-dom": "^18.3.7",
    "@typescript-eslint/eslint-plugin": "^6.21.0",
    "@typescript-eslint/parser": "^6.21.0",
    "@vitejs/plugin-react": "^4.3.4",
    "eslint": "^8.57.1",
    "eslint-plugin-react-hooks": "^4.6.2",
    "eslint-plugin-react-refresh": "^0.4.14",
    "typescript": "^5.9.2",
    "vite": "^4.5.14"
  },
  "pnpm": {
    "registry": "https://registry.npmmirror.com"
  }
}

tsconfig.json

json 复制代码
{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": [
      "ES2020",
      "DOM",
      "DOM.Iterable"
    ],
    "module": "ESNext",
    "skipLibCheck": true,
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": [
    "src"
  ],
  "references": [
    {
      "path": "./tsconfig.node.json"
    }
  ]
}

tsconfig.node.json

json 复制代码
{
  "compilerOptions": {
    "composite": true,
    "skipLibCheck": true,
    "module": "ESNext",
    "moduleResolution": "bundler",
    "allowSyntheticDefaultImports": true
  },
  "include": [
    "vite.config.ts"
  ]
}

vite.config.ts

ts 复制代码
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
})

index.html

html 复制代码
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React + TS</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

.npmrc

bash 复制代码
# 使用国内镜像源加速包下载
registry=https://registry.npmmirror.com

# React 相关包的镜像源
@types:registry=https://registry.npmmirror.com
@typescript-eslint:registry=https://registry.npmmirror.com
@vitejs:registry=https://registry.npmmirror.com

# 禁用包锁定文件的自动更新
package-lock=false

# 设置缓存目录
cache-dir=.pnpm-cache

# 启用严格的 peer dependencies 检查
strict-peer-dependencies=false

# 设置网络超时时间
network-timeout=60000

# 启用进度条
progress=true

# 禁用工作区模式,强制在当前目录安装依赖
ignore-workspace=true

src/main.tsx

tsx 复制代码
import React from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Layout from "./pages/Layout";
import Dashboard from "./pages/Dashboard";
import Docs from "./pages/Docs";
import About from "./pages/About";
import "./styles.css";

const root = createRoot(document.getElementById("root")!);
root.render(
  <React.StrictMode>
    <BrowserRouter
      future={{ v7_startTransition: true, v7_relativeSplatPath: true }}
    >
      <Routes>
        <Route path="/" element={<Layout />}>
          <Route index element={<Dashboard />} />
          <Route path="docs" element={<Docs />} />
          <Route path="docs/api" element={<Docs />} />
          <Route path="about" element={<About />} />
        </Route>
      </Routes>
    </BrowserRouter>
  </React.StrictMode>
);

src/styles.css

css 复制代码
:root {
  --bg: #0b1020;
  --panel: #121a36;
  --card: #111a2a;
  --text: #cfe4ff;
  --muted: #7aa2ff;
  --primary: #2fd7ff;
  --accent: #7c4dff;
  --grid: #0e1630;
  --box: #1d284b;
}
* {
  box-sizing: border-box;
}
html,
body,
#root {
  height: 100%;
}
body {
  margin: 0;
  font-family: Inter, system-ui, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
  background: radial-gradient(70% 70% at 50% 0%, #0e1938 0%, #070b1a 100%);
  color: var(--text);
}

.app {
  padding: 24px;
}
.hero {
  padding: 32px;
  border: 1px solid #223066;
  background: linear-gradient(
    135deg,
    rgba(47, 215, 255, 0.06),
    rgba(124, 77, 255, 0.06)
  );
  border-radius: 16px;
  backdrop-filter: blur(6px);
}
.hero h1 {
  margin: 0 0 8px;
  color: var(--primary);
}
.hero p {
  margin: 0;
  color: var(--muted);
}

.nav-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  margin: 24px 0;
}
.card {
  display: block;
  padding: 14px 16px;
  border-radius: 12px;
  background: var(--card);
  color: var(--text);
  text-decoration: none;
  border: 1px solid #1b2550;
  transition: all 0.2s;
}
.card:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.25);
  border-color: #2b3c7a;
}

.routes ul {
  padding-left: 18px;
}
.routes a {
  color: var(--primary);
}

.page {
  padding: 24px;
}
.panel {
  margin: 16px 0;
  padding: 16px;
  border-radius: 12px;
  background: var(--panel);
  border: 1px solid #1b2550;
}

.box {
  padding: 12px 14px;
  border-radius: 10px;
  background: var(--box);
  border: 1px solid #1b2550;
}

.chip {
  padding: 6px 10px;
  border-radius: 999px;
  border: 1px solid #2a3a7a;
  background: #0e1630;
  color: var(--text);
  cursor: pointer;
}
.chip.active {
  background: linear-gradient(
    135deg,
    rgba(47, 215, 255, 0.18),
    rgba(124, 77, 255, 0.18)
  );
  border-color: #3a54b6;
}

src/pages/Layout.tsx

tsx 复制代码
import React from "react";
import { useLocation, Link, Outlet } from "react-router-dom";
import {
  TechLayout,
  TechButton,
  TechUserCenter,
  createBreadcrumb,
  type TechMenuItem,
  type TechUserCenterItem,
} from "yggjs_rlayout/tech";

// 创建 Link 适配器组件,匹配 LinkLikeComponent 接口
const LinkAdapter: React.FC<{
  to: string;
  className?: string;
  children?: React.ReactNode;
}> = ({ to, className, children }) => {
  return (
    <Link to={to} className={className}>
      {children}
    </Link>
  );
};

// 布局组件
export default function Layout() {
  const location = useLocation();

  // 头部菜单项 - 使用 to 属性进行 SPA 导航
  const headerMenuItems: TechMenuItem[] = [
    { key: "dash", label: "面板", icon: "dashboard", to: "/" },
    { key: "docs", label: "文档", icon: "book", to: "/docs" },
    { key: "about", label: "关于", icon: "info", to: "/about" },
  ];

  // 用户中心菜单项
  const userCenterItems: TechUserCenterItem[] = [
    {
      key: "profile",
      label: "个人资料",
      icon: "profile",
      onClick: () => alert("跳转到个人资料页面"),
    },
    {
      key: "settings",
      label: "账户设置",
      icon: "settings",
      onClick: () => alert("跳转到账户设置页面"),
    },
    {
      key: "help",
      label: "帮助中心",
      icon: "help",
      onClick: () => alert("跳转到帮助中心"),
    },
    {
      key: "logout",
      label: "退出登录",
      icon: "logout",
      danger: true,
      onClick: () => {
        if (confirm("确定要退出登录吗?")) {
          alert("已退出登录");
        }
      },
    },
  ];

  // 侧边栏菜单项 - 使用 to 属性进行 SPA 导航
  const sidebarItems: TechMenuItem[] = [
    { key: "home", label: "首页", icon: "home", to: "/" },
    { key: "guide", label: "概览", icon: "guide", to: "/docs" },
    { key: "api", label: "接口文档", icon: "api", to: "/docs/api" },
    { key: "about", label: "关于", icon: "info", to: "/about" },
  ];

  // 根据当前路径确定选中的菜单项
  const selectedHeaderKey = location.pathname.startsWith("/docs")
    ? "docs"
    : location.pathname.startsWith("/about")
    ? "about"
    : "dash";

  const selectedSidebarKey = location.pathname.startsWith("/docs/api")
    ? "api"
    : location.pathname.startsWith("/docs")
    ? "guide"
    : location.pathname.startsWith("/about")
    ? "about"
    : "home";

  // 处理搜索
  const handleSearch = (value: string) => {
    console.log("Search:", value);
    if (value.trim()) {
      alert(`正在搜索: "${value}"`);
    }
  };

  // 处理菜单选中事件
  const handleMenuSelect = (key: string) => {
    console.log("Header menu selected:", key);
  };

  // 处理侧边栏选中事件
  const handleSidebarSelect = (key: string) => {
    console.log("Sidebar menu selected:", key);
  };

  // Footer配置
  const footerSections = [
    {
      title: "产品",
      links: [
        { label: "功能", href: "#features" },
        { label: "价格", href: "#pricing" },
        { label: "使用教程", href: "#docs", icon: "book" as const },
        { label: "接口文档", href: "#api", icon: "api" as const },
      ],
    },
    {
      title: "企业",
      links: [
        { label: "关于我们", href: "#about" },
        { label: "联系我们", href: "#contact" },
        { label: "赞助", href: "#careers" },
        { label: "博客", href: "#blog" },
      ],
    },
    {
      title: "支持",
      links: [
        { label: "用户中心", href: "#help" },
        { label: "社区", href: "#community" },
        { label: "状态", href: "#status" },
        { label: "反馈", href: "#feedback" },
      ],
    },
  ];

  // 社交连接
  const socialLinks = [
    { label: "GitHub", href: "#github", icon: "api" as const },
    { label: "Twitter", href: "#twitter", icon: "info" as const },
    { label: "Discord", href: "#discord", icon: "guide" as const },
  ];

  // 创建面包屑导航(简约版不需要图标)
  const breadcrumbItems = createBreadcrumb()
    .add("Dashboard", "/")
    .add("SPA 导航演示")
    .build();

  return (
    <TechLayout
      // Header配置
      brand="YGG Admin"
      headerMenuItems={headerMenuItems}
      selectedHeaderKey={selectedHeaderKey}
      onHeaderMenuSelect={handleMenuSelect}
      onSearch={handleSearch}
      headerExtra={
        <TechUserCenter
          username="张三"
          userInfo="zhangsan@example.com"
          items={userCenterItems}
          showUsername={false}
          onAvatarClick={() => console.log("Avatar clicked")}
        />
      }
      version="v0.1.0"
      // Sidebar配置
      sidebarItems={sidebarItems}
      selectedSidebarKey={selectedSidebarKey}
      onSidebarSelect={handleSidebarSelect}
      // SPA 导航配置 - 关键配置
      headerMenuLinkComponent={LinkAdapter}
      sidebarLinkComponent={LinkAdapter}
      // Footer配置
      footerProps={{
        description:
          "YGG Admin 是一个现代化的科技风管理后台框架,提供完整的布局解决方案和组件库。",
        sections: footerSections,
        socialLinks: socialLinks,
        copyright: "© 2024 YGG Admin. All rights reserved.",
      }}
      // 页面头部
      breadcrumb={breadcrumbItems}
      title="YGG RLayout - 快速构建科技风后台管理系统模板"
      pageActions={
        <>
          <TechButton variant="secondary">新建</TechButton>
          <TechButton variant="primary" icon="deploy">
            部署
          </TechButton>
        </>
      }
    >
      {/* 渲染子路由内容 */}
      <Outlet />
    </TechLayout>
  );
}

src/pages/Dashboard.tsx

tsx 复制代码
import { TechCard, TechButton } from "yggjs_rlayout/tech";

export default function Dashboard() {
  return (
    <div>
      <h1 style={{ color: "var(--tech-text)", marginBottom: "24px" }}>
        面板
      </h1>

      <div className="tech-cards">
        <TechCard
          title="系统概览"
          subtitle="当前系统运行状态"
          icon="dashboard"
          variant="default"
          hoverable
        >
          <div style={{ padding: "16px 0" }}>
            <div
              style={{
                display: "grid",
                gridTemplateColumns: "repeat(auto-fit, minmax(120px, 1fr))",
                gap: "16px",
              }}
            >
              <div style={{ textAlign: "center" }}>
                <div
                  style={{
                    fontSize: "24px",
                    fontWeight: "bold",
                    color: "var(--tech-accent)",
                  }}
                >
                  1,234
                </div>
                <div
                  style={{ fontSize: "12px", color: "var(--tech-text-muted)" }}
                >
                  总用户数
                </div>
              </div>
              <div style={{ textAlign: "center" }}>
                <div
                  style={{
                    fontSize: "24px",
                    fontWeight: "bold",
                    color: "var(--tech-accent)",
                  }}
                >
                  567
                </div>
                <div
                  style={{ fontSize: "12px", color: "var(--tech-text-muted)" }}
                >
                  活跃用户
                </div>
              </div>
              <div style={{ textAlign: "center" }}>
                <div
                  style={{
                    fontSize: "24px",
                    fontWeight: "bold",
                    color: "var(--tech-accent)",
                  }}
                >
                  89%
                </div>
                <div
                  style={{ fontSize: "12px", color: "var(--tech-text-muted)" }}
                >
                  系统负载
                </div>
              </div>
            </div>
          </div>
        </TechCard>

        <TechCard
          title="快速操作"
          subtitle="常用功能快捷入口"
          icon="deploy"
          variant="glass"
          hoverable
          actions={
            <>
              <TechButton variant="ghost" size="small">
                查看更多
              </TechButton>
              <TechButton variant="primary" size="small">
                立即操作
              </TechButton>
            </>
          }
        >
          <div
            style={{
              display: "flex",
              gap: "12px",
              flexWrap: "wrap",
              padding: "16px 0",
            }}
          >
            <TechButton variant="secondary" size="small" icon="user">
              用户管理
            </TechButton>
            <TechButton variant="secondary" size="small" icon="settings">
              系统设置
            </TechButton>
            <TechButton variant="secondary" size="small" icon="api">
              API 配置
            </TechButton>
          </div>
        </TechCard>

        <TechCard
          title="最近活动"
          subtitle="系统最新动态"
          icon="guide"
          variant="outlined"
          hoverable
        >
          <div style={{ padding: "16px 0" }}>
            <div
              style={{
                fontSize: "14px",
                color: "var(--tech-text-muted)",
                lineHeight: 1.6,
              }}
            >
              <div style={{ marginBottom: "8px" }}>
                • 用户 张三 登录系统 (2分钟前)
              </div>
              <div style={{ marginBottom: "8px" }}>
                • 系统配置已更新 (15分钟前)
              </div>
              <div style={{ marginBottom: "8px" }}>
                • 新增 3 个用户 (1小时前)
              </div>
              <div>• 数据备份完成 (2小时前)</div>
            </div>
          </div>
        </TechCard>
      </div>

      <div style={{ marginTop: "32px" }}>
        <h2 style={{ color: "var(--tech-text)", marginBottom: "16px" }}>
          SPA 导航演示说明
        </h2>
        <TechCard
          title="Link/to 导航功能"
          subtitle="基于 react-router-dom 的单页应用导航"
          icon="guide"
          variant="filled"
          hoverable
        >
          <div style={{ padding: "16px 0" }}>
            <p
              style={{
                color: "var(--tech-text-muted)",
                margin: "0 0 16px 0",
                fontSize: "14px",
                lineHeight: 1.5,
              }}
            >
              这个演示展示了如何在 YGG Admin 中使用 Link/to 进行 SPA 导航:
            </p>
            <ul
              style={{
                color: "var(--tech-text-muted)",
                fontSize: "14px",
                margin: 0,
                paddingLeft: "20px",
                lineHeight: 1.6,
              }}
            >
              <li>
                头部菜单和侧边栏菜单都使用 <code>to</code> 属性而不是{" "}
                <code>href</code>
              </li>
              <li>
                通过 <code>linkComponent</code> 属性传入 react-router-dom 的
                Link 组件
              </li>
              <li>页面切换无需刷新,保持 SPA 体验</li>
              <li>URL 会正确更新,支持浏览器前进后退</li>
              <li>菜单项会根据当前路由自动高亮显示</li>
            </ul>
          </div>
        </TechCard>
        <br />
      </div>
    </div>
  );
}

src/pages/About.tsx

tsx 复制代码
import { TechCard, TechButton } from 'yggjs_rlayout/tech';

export default function About() {
  return (
    <div>
      <h1 style={{ color: 'var(--tech-text)', marginBottom: '24px' }}>
        关于 YGG Admin
      </h1>
      
      <div className="tech-cards">
        <TechCard
          title="项目介绍"
          subtitle="现代化的科技风管理后台框架"
          icon="info"
          variant="default"
          hoverable
        >
          <div style={{ padding: '16px 0' }}>
            <p style={{ color: 'var(--tech-text-muted)', margin: '0 0 16px 0', fontSize: '14px', lineHeight: 1.5 }}>
              YGG Admin 是一个基于 React 的现代化管理后台组件库,
              专注于提供科技感十足的用户界面和完整的布局解决方案。
            </p>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '16px', marginTop: '20px' }}>
              <div style={{ textAlign: 'center', padding: '16px', background: 'rgba(90, 162, 255, 0.05)', borderRadius: '8px' }}>
                <div style={{ fontSize: '20px', fontWeight: 'bold', color: 'var(--tech-accent)', marginBottom: '8px' }}>🚀</div>
                <div style={{ fontSize: '14px', color: 'var(--tech-text)' }}>现代化设计</div>
              </div>
              <div style={{ textAlign: 'center', padding: '16px', background: 'rgba(90, 162, 255, 0.05)', borderRadius: '8px' }}>
                <div style={{ fontSize: '20px', fontWeight: 'bold', color: 'var(--tech-accent)', marginBottom: '8px' }}>⚡</div>
                <div style={{ fontSize: '14px', color: 'var(--tech-text)' }}>高性能</div>
              </div>
              <div style={{ textAlign: 'center', padding: '16px', background: 'rgba(90, 162, 255, 0.05)', borderRadius: '8px' }}>
                <div style={{ fontSize: '20px', fontWeight: 'bold', color: 'var(--tech-accent)', marginBottom: '8px' }}>🎨</div>
                <div style={{ fontSize: '14px', color: 'var(--tech-text)' }}>科技风格</div>
              </div>
            </div>
          </div>
        </TechCard>

        <TechCard
          title="核心特性"
          subtitle="为现代 Web 应用而生"
          icon="deploy"
          variant="glass"
          hoverable
        >
          <div style={{ padding: '16px 0' }}>
            <ul style={{ color: 'var(--tech-text-muted)', fontSize: '14px', margin: 0, paddingLeft: '20px', lineHeight: 1.8 }}>
              <li><strong>完整布局方案</strong> - TechLayout 提供头部、侧边栏、面包屑等完整功能</li>
              <li><strong>SPA 路由支持</strong> - 原生支持 react-router-dom 的 Link 组件</li>
              <li><strong>科技风设计</strong> - 渐变背景、发光效果、毛玻璃质感</li>
              <li><strong>响应式布局</strong> - 适配桌面端和移动端</li>
              <li><strong>主题定制</strong> - 通过 CSS 变量轻松定制主题</li>
              <li><strong>TypeScript</strong> - 完整的类型定义支持</li>
            </ul>
          </div>
        </TechCard>

        <TechCard
          title="技术栈"
          subtitle="基于现代前端技术构建"
          icon="api"
          variant="outlined"
          hoverable
        >
          <div style={{ padding: '16px 0' }}>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(120px, 1fr))', gap: '12px' }}>
              {[
                { name: 'React 18', color: '#61DAFB' },
                { name: 'TypeScript', color: '#3178C6' },
                { name: 'Vite', color: '#646CFF' },
                { name: 'CSS Variables', color: '#1572B6' },
                { name: 'React Router', color: '#CA4245' },
                { name: 'Vitest', color: '#6E9F18' }
              ].map(tech => (
                <div key={tech.name} style={{
                  padding: '12px 8px',
                  background: `${tech.color}15`,
                  border: `1px solid ${tech.color}30`,
                  borderRadius: '6px',
                  textAlign: 'center'
                }}>
                  <div style={{ fontSize: '12px', color: tech.color, fontWeight: 'bold' }}>
                    {tech.name}
                  </div>
                </div>
              ))}
            </div>
          </div>
        </TechCard>
      </div>

      <div style={{ marginTop: '32px' }}>
        <h2 style={{ color: 'var(--tech-text)', marginBottom: '16px' }}>
          开始使用
        </h2>
        <TechCard
          title="安装和使用"
          subtitle="快速集成到你的项目中"
          icon="guide"
          variant="filled"
          hoverable
          actions={
            <>
              <TechButton variant="ghost" size="small">查看文档</TechButton>
              <TechButton variant="primary" size="small">立即开始</TechButton>
            </>
          }
        >
          <div style={{ padding: '16px 0' }}>
            <pre style={{
              background: 'rgba(0,0,0,0.3)',
              padding: '16px',
              borderRadius: '8px',
              color: 'var(--tech-text)',
              fontSize: '13px',
              lineHeight: 1.5,
              overflow: 'auto',
              margin: '0 0 16px 0'
            }}>
{`# 安装
npm install yggjs_rlayout

# 使用
import { TechLayout } from 'yggjs_rlayout/tech';

function App() {
  return (
    <TechLayout brand="Your App">
      {/* 你的内容 */}
    </TechLayout>
  );
}`}
            </pre>
            <p style={{ color: 'var(--tech-text-muted)', margin: 0, fontSize: '14px', lineHeight: 1.5 }}>
              这个演示页面展示了 SPA 导航的完整实现,
              点击头部和侧边栏的菜单项体验无刷新页面切换。
            </p>
          </div>
        </TechCard>

        <br />
      </div>
    </div>
  );
}