🔔欢迎

HaloLight 多框架管理后台文档已上线!

支持 12+ 框架版本,欢迎体验。

Skip to content

React 版本

HaloLight React 版本基于 React 19 + Vite 6 构建,是一个纯客户端渲染 (CSR) 的单页应用 (SPA)。

在线预览https://halolight-react.h7ml.cn/

GitHubhttps://github.com/halolight/halolight-react

技术栈

技术版本说明
React19.xUI 框架
Vite6.x构建工具
TypeScript5.x类型安全
React Router6.x客户端路由
Zustand5.x状态管理
TanStack Query5.x服务端状态
React Hook Form7.x表单处理
Zod4.x数据验证
Tailwind CSS4.x原子化 CSS
shadcn/uilatestUI 组件库
react-grid-layout1.5.x拖拽布局
Recharts3.x图表可视化
Framer Motion12.x动画效果
Mock.js1.x数据模拟

目录结构

halolight-react/
├── src/
│   ├── pages/                     # 页面组件
│   │   ├── auth/                  # 认证页面
│   │   │   ├── login/
│   │   │   ├── register/
│   │   │   ├── forgot-password/
│   │   │   └── reset-password/
│   │   ├── dashboard/             # 仪表盘
│   │   └── legal/                 # 法律条款
│   ├── components/
│   │   ├── ui/                    # shadcn/ui 组件 (20+)
│   │   ├── layout/                # 布局组件
│   │   │   ├── admin-layout.tsx
│   │   │   ├── auth-layout.tsx
│   │   │   ├── sidebar.tsx
│   │   │   ├── header.tsx
│   │   │   └── footer.tsx
│   │   ├── dashboard/             # 仪表盘组件
│   │   │   ├── configurable-dashboard.tsx
│   │   │   ├── widget-wrapper.tsx
│   │   │   ├── stats-widget.tsx
│   │   │   ├── chart-widget.tsx
│   │   │   └── ...
│   │   └── shared/                # 共享组件
│   ├── hooks/                     # 自定义 Hooks
│   │   ├── use-users.ts
│   │   ├── use-auth.ts
│   │   ├── use-theme.ts
│   │   └── ...
│   ├── stores/                    # Zustand Stores
│   │   ├── auth.ts
│   │   ├── ui-settings.ts
│   │   ├── dashboard-layout.ts
│   │   └── tabs.ts
│   ├── lib/
│   │   ├── api/                   # API 服务
│   │   ├── auth/                  # 认证逻辑
│   │   ├── validations/           # Zod schemas
│   │   └── utils.ts               # 工具函数
│   ├── routes/                    # 路由配置
│   │   └── index.tsx
│   ├── config/                    # 配置文件
│   │   ├── routes.ts
│   │   └── tdk.ts
│   ├── types/                     # 类型定义
│   ├── mock/                      # Mock 数据
│   ├── providers/                 # Context Providers
│   ├── App.tsx
│   └── main.tsx
├── public/                        # 静态资源
├── vite.config.ts
├── tsconfig.json
└── package.json

快速开始

安装

bash
git clone https://github.com/halolight/halolight-react.git
cd halolight-react
pnpm install

环境变量

bash
cp .env.example .env.development
env
# .env.development 示例
VITE_API_URL=/api
VITE_MOCK=true
VITE_APP_TITLE=Admin Pro
VITE_BRAND_NAME=Halolight
VITE_DEMO_EMAIL=admin@halolight.h7ml.cn
VITE_DEMO_PASSWORD=123456
VITE_SHOW_DEMO_HINT=true

启动开发

bash
pnpm dev

访问 http://localhost:5173

构建生产

bash
pnpm build
pnpm preview

核心功能

状态管理 (Zustand)

tsx
// stores/auth.ts
import { create } from 'zustand'
import { persist } from 'zustand/middleware'

interface AuthState {
  user: User | null
  token: string | null
  isAuthenticated: boolean
  login: (credentials: LoginCredentials) => Promise<void>
  logout: () => void
  hasPermission: (permission: string) => boolean
}

export const useAuthStore = create<AuthState>()(
  persist(
    (set, get) => ({
      user: null,
      token: null,
      isAuthenticated: false,

      login: async (credentials) => {
        const response = await authApi.login(credentials)
        set({
          user: response.user,
          token: response.token,
          isAuthenticated: true,
        })
      },

      logout: () => {
        set({ user: null, token: null, isAuthenticated: false })
      },

      hasPermission: (permission) => {
        const { user } = get()
        if (!user) return false
        return user.permissions.some(p =>
          p === '*' || p === permission ||
          (p.endsWith(':*') && permission.startsWith(p.slice(0, -1)))
        )
      },
    }),
    {
      name: 'auth-storage',
      partialize: (state) => ({ token: state.token, user: state.user }),
    }
  )
)

数据获取 (TanStack Query)

tsx
// hooks/use-users.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { usersApi } from '@/lib/api'

export function useUsers(params?: UserQueryParams) {
  return useQuery({
    queryKey: ['users', params],
    queryFn: () => usersApi.getList(params),
  })
}

export function useCreateUser() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: usersApi.create,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] })
    },
  })
}

// 在组件中使用
function UsersPage() {
  const { data: users, isLoading, error } = useUsers()

  if (isLoading) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>

  return <div>{/* 渲染用户列表 */}</div>
}

路由配置 (React Router)

tsx
// routes/index.tsx
import { createBrowserRouter, Navigate } from 'react-router-dom'
import { DashboardLayout } from '@/layouts/dashboard-layout'
import { AuthLayout } from '@/layouts/auth-layout'

export const router = createBrowserRouter([
  {
    path: '/',
    element: <Navigate to="/dashboard" replace />,
  },
  {
    path: '/login',
    element: <AuthLayout />,
    children: [
      { index: true, element: <LoginPage /> },
    ],
  },
  {
    path: '/',
    element: <DashboardLayout />,
    children: [
      { path: 'dashboard', element: <HomePage /> },
      { path: 'users', element: <UsersPage /> },
      { path: 'settings', element: <SettingsPage /> },
      // 更多路由...
    ],
  },
])

权限 Hook

tsx
// hooks/use-permission.ts
import { useAuthStore } from '@/stores/auth'

export function usePermission(permission: string): boolean {
  const hasPermission = useAuthStore((state) => state.hasPermission)
  return hasPermission(permission)
}

export function usePermissions(permissions: string[]): boolean {
  const hasPermission = useAuthStore((state) => state.hasPermission)
  return permissions.every(p => hasPermission(p))
}
tsx
// 使用
function DeleteButton() {
  const canDelete = usePermission('users:delete')

  if (!canDelete) return null

  return <Button variant="destructive">删除</Button>
}

权限组件

tsx
// components/permission-guard.tsx
import { usePermission } from '@/hooks/use-permission'

interface PermissionGuardProps {
  permission: string
  children: React.ReactNode
  fallback?: React.ReactNode
}

export function PermissionGuard({
  permission,
  children,
  fallback = null,
}: PermissionGuardProps) {
  const hasPermission = usePermission(permission)

  if (!hasPermission) return fallback

  return <>{children}</>
}
tsx
<!-- 使用 -->
<PermissionGuard permission="users:delete" fallback={<span>无权限</span>}>
  <DeleteButton />
</PermissionGuard>

可拖拽仪表盘

tsx
// components/dashboard/configurable-dashboard.tsx
import GridLayout from 'react-grid-layout'
import { useDashboardStore } from '@/stores/dashboard-layout'

export function ConfigurableDashboard() {
  const { layout, setLayout, isEditing } = useDashboardStore()

  return (
    <GridLayout
      layout={layout}
      onLayoutChange={setLayout}
      cols={12}
      rowHeight={80}
      isDraggable={isEditing}
      isResizable={isEditing}
      margin={[16, 16]}
    >
      {layout.map((item) => (
        <div key={item.i}>
          <WidgetWrapper widget={getWidget(item.i)} />
        </div>
      ))}
    </GridLayout>
  )
}

主题切换

tsx
// hooks/use-theme.ts
import { useEffect, useState } from 'react'

type Theme = 'light' | 'dark' | 'system'

export function useTheme() {
  const [theme, setTheme] = useState<Theme>('system')

  const actualTheme = theme === 'system'
    ? window.matchMedia('(prefers-color-scheme: dark)').matches
      ? 'dark'
      : 'light'
    : theme

  useEffect(() => {
    document.documentElement.classList.remove('light', 'dark')
    document.documentElement.classList.add(actualTheme)
  }, [actualTheme])

  const toggleTheme = () => {
    setTheme(actualTheme === 'dark' ? 'light' : 'dark')
  }

  return { theme, actualTheme, setTheme, toggleTheme }
}

页面路由

路径页面权限
/重定向到 /dashboard-
/login登录公开
/register注册公开
/forgot-password忘记密码公开
/reset-password重置密码公开
/dashboard仪表盘dashboard:view
/users用户列表users:list
/users/create创建用户users:create
/users/:id用户详情users:view
/users/:id/edit编辑用户users:update
/roles角色管理roles:list
/permissions权限管理permissions:list
/settings系统设置settings:view
/profile个人中心登录即可

路由守卫

tsx
// components/auth-guard.tsx
import { Navigate, useLocation } from 'react-router-dom'
import { useAuthStore } from '@/stores/auth'

interface AuthGuardProps {
  children: React.ReactNode
  permission?: string
}

export function AuthGuard({ children, permission }: AuthGuardProps) {
  const location = useLocation()
  const { isAuthenticated, hasPermission } = useAuthStore()

  if (!isAuthenticated) {
    return <Navigate to="/login" state={{ from: location }} replace />
  }

  if (permission && !hasPermission(permission)) {
    return <Navigate to="/403" replace />
  }

  return <>{children}</>
}

UI 组件

基于 shadcn/ui,已集成 20+ 组件:

  • 表单:Button、Input、Textarea、Select、Checkbox、RadioGroup、Switch、Slider、DatePicker
  • 数据展示:Table、Card、Badge、Avatar、Progress、Skeleton
  • 反馈:Dialog、Sheet、AlertDialog、Toast、Tooltip、Popover
  • 导航:Tabs、Breadcrumb、Pagination、DropdownMenu、Command
  • 布局:Accordion、Collapsible、ScrollArea、Separator

Recharts 图表集成

tsx
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'

const data = [
  { name: 'Jan', value: 400 },
  { name: 'Feb', value: 300 },
  { name: 'Mar', value: 600 },
  // ...
]

function Chart() {
  return (
    <ResponsiveContainer width="100%" height={300}>
      <LineChart data={data}>
        <CartesianGrid strokeDasharray="3 3" />
        <XAxis dataKey="name" />
        <YAxis />
        <Tooltip />
        <Line type="monotone" dataKey="value" stroke="#8884d8" />
      </LineChart>
    </ResponsiveContainer>
  )
}

PWA 支持

项目内置 PWA 支持,包括:

  • Service Worker 注册
  • 离线缓存
  • 应用清单 (manifest.json)
  • 多尺寸图标
json
// public/manifest.json
{
  "name": "Admin Pro",
  "short_name": "Admin",
  "start_url": "/",
  "display": "standalone",
  "theme_color": "#ffffff",
  "background_color": "#ffffff",
  "icons": [
    {
      "src": "/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    }
  ]
}

部署

Vercel

bash
vercel

Netlify

bash
netlify deploy --prod

Nginx

nginx
server {
    listen 80;
    server_name example.com;
    root /var/www/halolight-react/dist;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /api {
        proxy_pass http://backend:3000;
    }
}

Docker

dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

与 Next.js 版本对比

功能React 版本Next.js 版本
渲染模式CSR (客户端渲染)SSR/SSG/ISR
状态管理ZustandZustand
数据获取TanStack QueryTanStack Query
表单验证React Hook Form + ZodReact Hook Form + Zod
拖拽布局react-grid-layoutreact-grid-layout
组件库shadcn/uishadcn/ui
路由React RouterNext.js App Router
图表RechartsRecharts
SSR内置支持
SEOreact-helmet-async内置支持
部署静态托管 / CDNVercel / 任意平台

何时选择 React 版本?

  • 纯前端应用,后端 API 独立部署
  • 不需要 SSR/SEO 优化
  • 已有后端服务,只需要前端管理界面
  • 希望使用更轻量的技术栈
  • 部署到纯静态托管服务

何时选择 Next.js 版本?

  • 需要 SEO 优化
  • 需要 SSR/SSG 提升首屏性能
  • 希望前后端一体化
  • 需要 API Routes / Server Actions
  • 部署到 Vercel 等全栈平台