Skip to content

Next.js 版本

HaloLight Next.js 版本基于 Next.js 14 App Router 构建,采用 React 18 + TypeScript。

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

GitHubhttps://github.com/halolight/halolight

特性

  • 🏗️ Next.js 14 App Router - 服务端组件与流式渲染
  • Zustand 状态管理 - 轻量级状态管理方案
  • 🎨 主题系统 - 11 种皮肤,明暗模式,View Transitions
  • 🔐 认证系统 - 完整登录/注册/找回密码流程
  • 📊 仪表盘 - 数据可视化与业务管理
  • 🛡️ 权限控制 - RBAC 细粒度权限管理
  • 📑 多标签页 - 标签栏管理
  • 命令面板 - 快捷键导航

技术栈

技术版本说明
Next.js14.xReact 全栈框架 (App Router)
React18.xUI 库
TypeScript5.x类型安全
Tailwind CSS4.x原子化 CSS
shadcn/uilatestUI 组件库 (28 组件)
Zustand5.x状态管理 (6 Store)
TanStack Query5.x服务端状态
React Hook Form7.x表单处理
Zod4.x数据验证
react-grid-layout1.x拖拽布局
Recharts3.x图表可视化
Framer Motion12.x动画效果
Mock.js1.x数据模拟
next-pwa5.xPWA 支持

核心特性

  • 可配置仪表盘 - 9 种小部件,拖拽布局,响应式适配
  • 多标签导航 - 浏览器式标签,右键菜单,状态缓存
  • 权限系统 - RBAC 权限控制,路由守卫,权限组件
  • 主题系统 - 11 种皮肤,明暗模式,View Transitions
  • 多账户切换 - 快速切换账户,记住登录状态
  • 命令面板 - 键盘快捷键 (⌘K),全局搜索
  • 实时通知 - WebSocket 推送,通知中心

目录结构

halolight/
├── src/
│   ├── app/                      # App Router 页面
│   │   ├── (auth)/              # 认证路由组
│   │   │   ├── login/           # 登录
│   │   │   ├── register/        # 注册
│   │   │   ├── forgot-password/ # 忘记密码
│   │   │   ├── reset-password/  # 重置密码
│   │   │   └── layout.tsx       # 认证布局
│   │   ├── (dashboard)/         # 仪表盘路由组
│   │   │   ├── page.tsx         # 仪表盘首页(可配置)
│   │   │   ├── accounts/        # 账户与权限
│   │   │   ├── analytics/       # 数据分析
│   │   │   ├── calendar/        # 日程管理
│   │   │   ├── docs/            # 帮助文档
│   │   │   ├── documents/       # 文档管理
│   │   │   ├── files/           # 文件存储
│   │   │   ├── messages/        # 消息中心
│   │   │   ├── notifications/   # 通知中心
│   │   │   ├── profile/         # 个人资料
│   │   │   ├── users/           # 用户管理
│   │   │   ├── settings/        # 系统设置
│   │   │   │   └── teams/       # 团队管理
│   │   │   │       └── roles/   # 角色管理
│   │   │   └── layout.tsx       # 仪表盘布局
│   │   ├── (legal)/             # 法律条款路由组
│   │   │   ├── privacy/         # 隐私政策
│   │   │   ├── terms/           # 服务条款
│   │   │   └── layout.tsx
│   │   ├── layout.tsx           # 根布局
│   │   ├── error.tsx            # 错误页面
│   │   └── not-found.tsx        # 404 页面
│   ├── components/
│   │   ├── ui/                  # shadcn/ui 组件 (28 个)
│   │   ├── layout/              # 布局组件 (11 个)
│   │   │   ├── admin-layout.tsx # 管理后台布局
│   │   │   ├── sidebar.tsx      # 可折叠侧边栏
│   │   │   ├── header.tsx       # 页头(通知/错误/用户菜单)
│   │   │   ├── footer.tsx       # 页脚
│   │   │   ├── tab-bar.tsx      # 多标签导航
│   │   │   ├── command-menu.tsx # 命令面板 (⌘K)
│   │   │   ├── quick-settings.tsx # 界面设置面板
│   │   │   ├── theme-toggle.tsx # 主题切换
│   │   │   └── pending-overlay.tsx # 加载遮罩
│   │   ├── dashboard/           # 仪表盘组件
│   │   │   ├── configurable-dashboard.tsx # 可配置仪表盘
│   │   │   ├── charts.tsx       # 图表组件
│   │   │   ├── stats-card.tsx   # 统计卡片
│   │   │   └── recent-activity.tsx # 最近活动
│   │   └── shared/              # 共享组件
│   ├── hooks/                   # React Hooks (15 个)
│   │   ├── use-users.ts         # 用户 CRUD
│   │   ├── use-teams.ts         # 团队管理
│   │   ├── use-messages.ts      # 消息管理
│   │   ├── use-notifications.ts # 通知管理
│   │   ├── use-calendar.ts      # 日历数据
│   │   ├── use-documents.ts     # 文档管理
│   │   ├── use-files.ts         # 文件管理
│   │   ├── use-dashboard.ts     # 仪表盘状态
│   │   ├── use-dashboard-data.ts # 仪表盘数据 Hook 集合
│   │   ├── use-chart-palette.ts # 图表配色(主题感知)
│   │   ├── use-action-mutation.ts # Server Action 封装
│   │   ├── use-keep-alive.tsx   # 页面状态缓存
│   │   ├── use-tdk.ts           # TDK 管理
│   │   └── use-title.ts         # 页面标题
│   ├── stores/                  # Zustand Stores (6 个)
│   │   ├── auth-store.ts        # 认证状态(含多账户)
│   │   ├── ui-settings-store.ts # UI 设置
│   │   ├── dashboard-store.ts   # 仪表盘状态
│   │   ├── navigation-store.ts  # 导航状态
│   │   ├── tabs-store.ts        # 标签页状态
│   │   └── error-store.ts       # 错误收集
│   ├── providers/               # React Providers (8 个)
│   │   ├── app-providers.tsx    # Provider 聚合
│   │   ├── auth-provider.tsx    # 认证 Provider
│   │   ├── theme-provider.tsx   # 主题 Provider
│   │   ├── query-provider.tsx   # TanStack Query
│   │   ├── error-provider.tsx   # 错误处理
│   │   ├── permission-provider.tsx # 权限检查
│   │   ├── websocket-provider.tsx # WebSocket 实时通知
│   │   └── keep-alive-provider.tsx # 页面保活
│   ├── actions/                 # Server Actions
│   ├── config/                  # 配置文件
│   │   ├── routes.ts            # 路由与权限配置
│   │   └── tdk.ts               # TDK 配置
│   ├── lib/                     # 工具库
│   │   └── api/                 # API 客户端
│   ├── mock/                    # Mock 数据 (9 模块)
│   └── middleware.ts            # 中间件(认证+安全头)
├── public/
│   ├── manifest.json            # PWA 清单
│   ├── sw.js                    # Service Worker
│   ├── icons/                   # PWA 图标 (8 尺寸)
│   ├── screenshots/             # PWA 截图
│   └── fonts/                   # 自托管字体
├── next.config.mjs              # Next.js + PWA 配置
├── tailwind.config.js
├── tsconfig.json
└── package.json

快速开始

环境要求

  • Node.js >= 18.0.0
  • pnpm >= 9.x

安装

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

环境变量

bash
cp .env.example .env.local
env
# .env.local 示例
NEXT_PUBLIC_API_URL=/api
NEXT_PUBLIC_MOCK=true              # 开启 Mock 数据
NEXT_PUBLIC_DEMO_EMAIL=admin@halolight.h7ml.cn
NEXT_PUBLIC_DEMO_PASSWORD=123456
NEXT_PUBLIC_SHOW_DEMO_HINT=false
NEXT_PUBLIC_WS_URL=                # WebSocket 地址
NEXT_PUBLIC_APP_TITLE=Admin Pro
NEXT_PUBLIC_BRAND_NAME=Halolight

启动开发

bash
pnpm dev

访问 http://localhost:3000

构建生产

bash
pnpm build
pnpm start

演示账号

角色邮箱密码
管理员admin@halolight.h7ml.cn123456
普通用户user@halolight.h7ml.cn123456

核心功能

1。状态管理 (Zustand)

tsx
// stores/auth-store.ts
interface AuthState {
  user: AccountWithToken | null
  accounts: AccountWithToken[]        // 多账户列表
  activeAccountId: string | null      // 当前账户

  login: (data: LoginRequest) => Promise<void>
  register: (data: RegisterRequest) => Promise<void>
  logout: () => Promise<void>
  switchAccount: (accountId: string) => Promise<void>  // 快速切换账户
  forgotPassword: (email: string) => Promise<void>
  resetPassword: (token: string, password: string) => Promise<void>
  checkAuth: () => Promise<void>
}

// 使用 Cookie 存储 Token,支持"记住我"(7天/1天)
Cookies.set("token", response.token, {
  expires: data.remember ? 7 : 1,
  secure: process.env.NODE_ENV === "production",
  sameSite: "strict",
})

2。数据获取 (TanStack Query)

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

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

  // 查询用户列表
  const { data, isLoading } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
  })

  // 创建用户
  const createUser = useMutation({
    mutationFn: createUserApi,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] })
    },
  })

  return { data, isLoading, createUser }
}

3。权限控制

tsx
// 路由权限配置
export const ROUTE_PERMISSIONS: Record<string, Permission> = {
  "/": "dashboard:view",
  "/users": "users:view",
  "/analytics": "analytics:view",
  // ...
}

// 权限检查
const { hasPermission } = usePermission()
if (hasPermission("users:delete")) {
  // 显示删除按钮
}
tsx
// 权限守卫组件
<PermissionGuard permission="users:delete" fallback={<Disabled />}>
  <DeleteButton />
</PermissionGuard>

4。可拖拽仪表盘

tsx
// 仪表盘编辑模式
const { isEditing, setIsEditing, addWidget, removeWidget, resetToDefault } = useDashboardStore()

// 响应式布局 (列数自动适配)
// lg: 12列, md: 8列, sm: 4列, xs: 2列, mobile: 1列

支持 9 种小部件类型:

小部件类型说明数据来源
stats数据统计卡片(4 指标)useDashboardStats
chart-line折线图(访问趋势)useDashboardVisits
chart-bar柱状图(销售统计)useDashboardSales
chart-pie饼图(流量占比)useDashboardPie
recent-users最近用户列表useDashboardUsers
notifications通知列表useDashboardNotifications
tasks待办任务useDashboardTasks
calendar今日日程useDashboardCalendar
quick-actions快捷操作入口静态配置

主题系统

皮肤预设

支持 11 种皮肤预设,带实时预览和平滑过渡动画:

预设名称说明
defaultShadcn · Neutral官方默认中性色
blueShadcn · Blue蓝色主色 + Charts 冷色调
emeraldShadcn · Emerald清新绿色,适合数据展示
amberShadcn · Amber琥珀/橙色,温暖明快
violetShadcn · Violet紫色高饱和,科技感
roseShadcn · Rose玫红主色,图表撞色
tealShadcn · Teal青色主色,现代感
slateShadcn · Slate低饱和灰蓝,工具感
ocean旧 · 深海蓝蓝绿渐变
sunset旧 · 暮光橙橙粉撞色
aurora旧 · 极光绿青绿 + 紫色

CSS 变量 (OKLch)

css
/* 示例变量定义 */
:root {
  --background: 100% 0 0;
  --foreground: 14.9% 0.017 285.75;
  --primary: 51.1% 0.262 276.97;
  --primary-foreground: 100% 0 0;
  --muted: 96.4% 0.004 285.75;
  --accent: 96.4% 0.004 285.75;
  /* ... */
}

页面路由

路径页面权限
/重定向到仪表盘-
/login登录公开
/register注册公开
/forgot-password忘记密码公开
/reset-password重置密码公开
/dashboard可配置仪表盘dashboard:view
/accounts账号与权限settings:view
/analytics数据分析analytics:view
/calendar日程管理calendar:view
/documents文档管理documents:view
/files文件存储files:view
/messages消息中心messages:view
/notifications通知中心notifications:view
/users用户管理users:view
/settings系统设置settings:view
/settings/teams团队设置settings:view
/settings/teams/roles角色管理settings:view
/profile个人资料settings:view
/docs帮助文档documents:view
/privacy隐私政策公开
/terms服务条款公开

环境变量

配置示例

bash
cp .env.example .env.local
env
# .env.local
NEXT_PUBLIC_API_URL=/api
NEXT_PUBLIC_MOCK=true
NEXT_PUBLIC_DEMO_EMAIL=admin@halolight.h7ml.cn
NEXT_PUBLIC_DEMO_PASSWORD=123456
NEXT_PUBLIC_SHOW_DEMO_HINT=false
NEXT_PUBLIC_WS_URL=
NEXT_PUBLIC_APP_TITLE=Admin Pro
NEXT_PUBLIC_BRAND_NAME=Halolight

变量说明

变量名说明默认值
NEXT_PUBLIC_API_URLAPI 基础路径/api
NEXT_PUBLIC_MOCK是否启用 Mock 数据true
NEXT_PUBLIC_DEMO_EMAIL演示账号邮箱admin@halolight.h7ml.cn
NEXT_PUBLIC_DEMO_PASSWORD演示账号密码123456
NEXT_PUBLIC_SHOW_DEMO_HINT是否显示演示提示false
NEXT_PUBLIC_WS_URLWebSocket 地址-
NEXT_PUBLIC_APP_TITLE应用标题Admin Pro
NEXT_PUBLIC_BRAND_NAME品牌名称Halolight

使用方式

tsx
// 在客户端组件中使用
const apiUrl = process.env.NEXT_PUBLIC_API_URL
const isMock = process.env.NEXT_PUBLIC_MOCK === 'true'

常用命令

bash
pnpm dev            # 启动开发服务器
pnpm build          # 生产构建
pnpm start          # 预览生产构建
pnpm lint           # 代码检查
pnpm lint:fix       # 自动修复
pnpm type-check     # 类型检查
pnpm test           # 运行测试
pnpm test:coverage  # 测试覆盖率

测试

bash
pnpm test           # 运行测试(watch 模式)
pnpm test:run       # 单次运行
pnpm test:coverage  # 覆盖率报告
pnpm test:ui        # Vitest UI 界面

测试示例

tsx
// __tests__/components/button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from '@/components/ui/button'

describe('Button Component', () => {
  it('renders correctly', () => {
    render(<Button>Click me</Button>)
    expect(screen.getByText('Click me')).toBeInTheDocument()
  })

  it('handles click events', () => {
    const handleClick = vi.fn()
    render(<Button onClick={handleClick}>Click me</Button>)
    fireEvent.click(screen.getByText('Click me'))
    expect(handleClick).toHaveBeenCalledTimes(1)
  })
})

配置

Next.js 配置

js
// next.config.mjs
import withPWA from "next-pwa"

const nextConfig = {
  // 包导入优化 - 减少打包体积
  experimental: {
    optimizePackageImports: [
      "@radix-ui/react-*",
      "lucide-react",
      "framer-motion",
      "@tanstack/react-query",
      "recharts",
      "zustand",
    ],
  },

  // 生产环境移除 console
  compiler: {
    removeConsole: { exclude: ["error", "warn"] },
  },

  // 关闭 source map
  productionBrowserSourceMaps: false,

  // 图片优化
  images: {
    formats: ["image/avif", "image/webp"],
  },
}

const pwaConfig = withPWA({
  dest: "public",
  register: true,
  skipWaiting: true,
  disable: process.env.NODE_ENV === "development",
})

export default pwaConfig(nextConfig)

部署

Vercel (推荐)

Deploy with Vercel

bash
vercel

Docker

dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
EXPOSE 3000
CMD ["pnpm", "start"]
bash
docker build -t halolight-nextjs .
docker run -p 3000:3000 halolight-nextjs

其他平台

CI/CD

项目配置了完整的 GitHub Actions CI 工作流:

yaml
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm lint
      - run: pnpm type-check

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm test:coverage
      - uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}

  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm build

  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm audit --audit-level=high

高级功能

多标签导航

tsx
// stores/tabs-store.ts
interface Tab {
  id: string
  title: string
  path: string
  icon?: string
  closable?: boolean  // 首页不可关闭
}

// 右键菜单功能
- 刷新页面
- 关闭标签
- 关闭其他
- 关闭右侧
- 关闭所有

页面状态缓存 (Keep-Alive)

tsx
// hooks/use-keep-alive.tsx

// 自动保存/恢复滚动位置
useScrollRestore()

// 保存表单状态
const [values, saveValues, clearCache] = useFormCache('filter-form', initialValues)

// 保存自定义状态
const [state, setState] = useStateCache('my-key', initialValue)

命令面板 (⌘K)

tsx
// components/layout/command-menu.tsx
// 支持键盘快速导航、主题切换、账户切换、退出登录等操作

快捷键:
-K / Ctrl+K - 打开命令面板
- 搜索页面 - 快速导航到任意页面
- 切换主题 - 明暗模式切换
- 切换账户 - 多账户快速切换

实时通知 (WebSocket)

tsx
// providers/websocket-provider.tsx
const { status, lastMessage, sendMessage, reconnect } = useWebSocket()

// 监听新通知
useRealtimeNotifications((notification) => {
  console.log('新通知:', notification)
})

// 连接状态
status === 'Open' // 已连接
status === 'Connecting' // 连接中
status === 'Closed' // 已断开

PWA 支持

js
// next.config.mjs
const pwaConfig = withPWA({
  dest: "public",
  register: true,
  skipWaiting: true,
  disable: process.env.NODE_ENV === "development",
  runtimeCaching: [
    // 字体缓存 (1年)
    { urlPattern: /\.(?:woff|woff2|ttf)$/i, handler: "CacheFirst" },
    // 图片缓存 (24小时)
    { urlPattern: /\.(?:jpg|png|svg|webp)$/i, handler: "StaleWhileRevalidate" },
    // Next.js 静态资源 (1年)
    { urlPattern: /\/_next\/static\/.+\.(js|css)$/i, handler: "CacheFirst" },
    // 页面数据 (1小时)
    { urlPattern: /\/_next\/data\/.+\.json$/i, handler: "NetworkFirst" },
  ],
})

功能特性:

  • 离线访问 - Service Worker 缓存静态资源
  • 安装到桌面 - 支持 Add to Home Screen
  • 自托管字体 - Inter + JetBrains Mono
  • 图标支持 - 8 种尺寸 (72x72 ~ 512x512)

性能优化

图片优化

tsx
// 使用 Next.js Image 组件
import Image from 'next/image'

<Image
  src="/images/hero.png"
  alt="Hero"
  width={800}
  height={600}
  priority // 优先加载
  placeholder="blur" // 模糊占位符
/>

// next.config.mjs
images: {
  formats: ["image/avif", "image/webp"],
}

懒加载组件

tsx
// 动态导入组件
import dynamic from 'next/dynamic'

const DashboardChart = dynamic(
  () => import('@/components/dashboard/chart'),
  {
    loading: () => <Skeleton />,
    ssr: false // 禁用 SSR
  }
)

预加载

tsx
// 路由预加载
import Link from 'next/link'

<Link href="/dashboard" prefetch>
  Dashboard
</Link>

// 数据预加载
queryClient.prefetchQuery({
  queryKey: ['users'],
  queryFn: fetchUsers,
})

包导入优化

js
// next.config.mjs
experimental: {
  optimizePackageImports: [
    "@radix-ui/react-*",
    "lucide-react",
    "framer-motion",
    "@tanstack/react-query",
    "recharts",
    "zustand",
  ],
}

常见问题

Q:如何关闭 Mock 数据?

A:在 .env.local 中设置 NEXT_PUBLIC_MOCK=false,并配置真实的 API 地址。

env
NEXT_PUBLIC_MOCK=false
NEXT_PUBLIC_API_URL=https://api.example.com

Q:如何添加新页面?

A:在 src/app/(dashboard) 下创建新目录和 page.tsx 文件。

tsx
// src/app/(dashboard)/my-page/page.tsx
export default function MyPage() {
  return <div>My Page</div>
}

// 添加路由权限
// src/config/routes.ts
export const ROUTE_PERMISSIONS = {
  // ...
  "/my-page": "my-page:view",
}

Q:如何自定义主题颜色?

A:修改 tailwind.config.js 中的 CSS 变量。

js
// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        primary: {
          DEFAULT: 'oklch(var(--primary))',
          foreground: 'oklch(var(--primary-foreground))',
        },
      },
    },
  },
}
css
/* app/globals.css */
:root {
  --primary: 51.1% 0.262 276.97; /* 修改为你的颜色 */
}

Q:如何禁用 PWA?

A:在 next.config.mjs 中设置 disable: true

js
const pwaConfig = withPWA({
  dest: "public",
  disable: true, // 禁用 PWA
})

Q:如何部署到静态托管平台?

A:配置静态导出模式。

js
// next.config.mjs
export default {
  output: 'export',
  images: {
    unoptimized: true, // 静态导出需要禁用图片优化
  },
}
bash
pnpm build
# 输出到 out/ 目录

与其他版本对比

特性Next.jsVueAngular
SSR/SSG✅ (Nuxt)✅ (Universal)
状态管理ZustandPiniaServices + RxJS
路由App RouterVue RouterAngular Router
构建工具Next.jsViteesbuild + Vite
组件库shadcn/uishadcn-vueAngular Material
学习曲线中等较低较高
性能优秀优秀优秀

相关链接