npm package: @flow97/react-toolkit
Description: 一套面向企业级应用的前端工具集，基于 React、Ant Design 与 TanStack Query。开箱即用的应用壳、权限/菜单、通用列表（分页/无限滚动）、抽屉/弹框表单与国际化支持，助你快速搭建一致、可维护的中后台应用。
Author: chaos1ee
Latest version: 0.1.13

# @flow97/react-toolkit

一套面向企业级应用的前端工具集，基于 React、Ant Design 与 TanStack Query。开箱即用的应用壳、权限/菜单、通用列表（分页/无限滚动）、抽屉/弹框表单与国际化支持，助你快速搭建一致、可维护的中后台应用。

## 亮点特性

- 应用壳与导航：`Layout`、侧边菜单、面包屑、登录页、404
- 列表范式：`QueryList`（分页）、`InfiniteList`（无限滚动）
- 表单弹层：`useFormModal`、`useFormDrawer`
- 权限体系：`RequireAuth`、`AuthButton`，以及权限/菜单路由片段
- 数据获取：服务层 hooks（如 `useAuth`、`useMenuList`、`useGames`）
- 子路径导出按域组织，Tree Shaking 友好

## 安装与要求

```bash
pnpm add @flow97/react-toolkit
```

对等依赖（需由你的应用提供）：

- react、react-dom：^19
- antd：^6
- react-router：^7
- @tanstack/react-query：^5

样式：

```ts
import '@flow97/react-toolkit/style.css'
```

## 快速上手

```tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import 'react-toolkits/style.css'
import { createRoot } from 'react-dom/client'
import { RouterProvider } from 'react-router'
import { ToolkitProvider } from 'react-toolkits/components'
import { AuthMode } from 'react-toolkits/constants'

import router from './router'

// 创建 QueryClient 实例
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnWindowFocus: false,
      retry: false,
      staleTime: 5 * 60 * 1000,
      gcTime: 10 * 60 * 1000,
    },
    mutations: {
      retry: false,
    },
  },
})

const root = createRoot(document.getElementById('root') as HTMLElement)

root.render(
  <QueryClientProvider client={queryClient}>
    <ToolkitProvider collapsible gameApiV2 auth={{ mode: AuthMode.GROUP_BASED }} loginPath="/sign_in" mainPagePath="/">
      <RouterProvider router={router} />
    </ToolkitProvider>
  </QueryClientProvider>,
)
```

**重要提示**：`QueryClientProvider` 必须包裹 `ToolkitProvider`，因为 `react-toolkits` 内部使用了 `@tanstack/react-query` 的 hooks（如 `useQueryClient`、`useQuery`、`useMutation`）。

### 配置项（`ToolkitProvider`）

- **loginPath**: 登录页路径（未鉴权时会跳转）
- **mainPagePath**: 登录后主页路径（鉴权通过时进入）
- **auth.mode**: 认证模式，建议使用 `AuthMode.GROUP_BASED`
- **collapsible**: 侧边栏是否可折叠
- **gameApiV2**: 是否启用游戏相关 V2 接口适配（如不涉及可忽略）
- 其余高级配置见类型定义 `react-toolkits/components` 内导出的 Provider Props

## 子路径导出（推荐）

- `react-toolkits/components`：组件与 Hooks（如 `Layout`、`ToolkitProvider`、`InfiniteList`）
- `react-toolkits/hooks`：表单弹层 Hooks（如 `useFormDrawer`、`useFormModal`）
- `react-toolkits/pages`：内置页面与路由片段（如 `permissionRoutes`、`menuRoutes`、`SignIn`）
- `react-toolkits/services`：请求相关 Hooks（如 `useAuth`、`useMenuList`、`useGames`）
- `react-toolkits/constants`：常量与枚举（如 `APP_ID_HEADER`、`AuthMode`）
- `react-toolkits/types`：公共类型
- `react-toolkits/utils`：工具函数
- `react-toolkits/locale` 与 `react-toolkits/locale/*`：国际化资源与工具

示例：

```ts
import { Layout } from 'react-toolkits/components'
import { useFormDrawer } from 'react-toolkits/hooks'
import { permissionRoutes } from 'react-toolkits/pages'
import { useAuth } from 'react-toolkits/services'
import { APP_ID_HEADER } from 'react-toolkits/constants'
import type { NavMenuItem } from 'react-toolkits/types'
```

### 权限与菜单集成示例

```tsx
import { createBrowserRouter } from 'react-router'
import { Layout } from 'react-toolkits/components'
import { permissionRoutes, menuRoutes, SignIn } from 'react-toolkits/pages'

const router = createBrowserRouter([
  {
    path: '/',
    element: <Layout />, // 自动接入面包屑、侧边栏与鉴权
    children: [
      // 权限/菜单内置片段（可按需选择）
      ...permissionRoutes,
      ...menuRoutes,
    ],
  },
  { path: '/sign_in', element: <SignIn /> },
])

export default router
```

## 国际化（Locale）

内置基础语言包与上下文工具，也可与第三方 i18n 方案并存：

```ts
import { useTranslation } from 'react-toolkits/locale'
import zhCN from 'react-toolkits/locale/zh_CN'
import enGB from 'react-toolkits/locale/en_GB'

const { t } = useTranslation()
// t('FilterFormWrapper.confirmText') → "查询"
```

如果你已使用 `react-i18next` 等，可以仅复用本包的页面/菜单能力。

### 自定义菜单文案

- 使用 `react-toolkits/locale` 的 `useTranslation` 配合内置 key，或在你的 i18n 方案中映射同名 key
- 可按需引入 `react-toolkits/locale/zh_CN`、`en_GB` 等作为基础词条

## 目录结构（概览）

```
src/
  components/        // 组件与 Hooks（以 react-toolkits/components 导出）
  hooks/             // 表单弹层 Hooks（以 react-toolkits/hooks 导出）
  features/          // 特性模块（权限/菜单等：hooks/组件/服务聚合）
  pages/             // 内置页面与路由片段
  services/          // 数据服务 hooks（依赖 libs/ky 与全局上下文）
  constants/         // 常量与枚举
  utils/             // 工具函数
  locale/            // 国际化上下文与语言包
```

## 常用用法示例

### 列表（分页 `QueryList`）

```tsx
import { QueryList } from 'react-toolkits/components'

export default function UserTable() {
  return (
    <QueryList
      queryKey={['users']}
      queryFn={async ({ page, pageSize }) => {
        // 返回 { list: T[]; total: number }
        const res = await fetch(`/api/users?page=${page}&pageSize=${pageSize}`)
        return res.json()
      }}
      columns={[
        { title: 'ID', dataIndex: 'id' },
        { title: 'Name', dataIndex: 'name' },
      ]}
    />
  )
}
```

### 列表（无限滚动 `InfiniteList`）

```tsx
import { InfiniteList } from 'react-toolkits/components'

export default function LogList() {
  return (
    <InfiniteList
      queryKey={['logs']}
      queryFn={async ({ pageParam = 1 }) => {
        // 返回 { list: T[]; nextPage?: number }
        const res = await fetch(`/api/logs?page=${pageParam}`)
        return res.json()
      }}
      itemRender={item => <div>{item.message}</div>}
    />
  )
}
```

### 表单弹层（`useFormDrawer`）

```tsx
import { Button } from 'antd'
import { useFormDrawer } from 'react-toolkits/hooks'

export default function CreateUser() {
  const { show, drawer } = useFormDrawer({
    title: '新建用户',
    onConfirm: async values => {
      await fetch('/api/users', { method: 'POST', body: JSON.stringify(values) })
    },
    content: (
      <>
        <Form.Item name="name" label="姓名" rules={[{ required: true }]}>
          <Input />
        </Form.Item>
        <Form.Item name="role" label="角色" rules={[{ required: true }]}>
          <Select
            options={[
              { value: 'admin', label: '管理员' },
              { value: 'user', label: '用户' },
            ]}
          />
        </Form.Item>
      </>
    ),
  })

  return (
    <>
      <Button onClick={show}>新建</Button>
      {drawer}
    </>
  )
}
```

### 表单弹层（`useFormModal`）

```tsx
import { Button, Form, Input, Select } from 'antd'
import { useFormModal } from 'react-toolkits/hooks'

export default function EditUser() {
  const { show, modal } = useFormModal({
    title: '编辑用户',
    onConfirm: async values => {
      await fetch('/api/users', { method: 'PUT', body: JSON.stringify(values) })
    },
    content: (
      <>
        <Form.Item name="name" label="姓名" rules={[{ required: true }]}>
          <Input />
        </Form.Item>
        <Form.Item name="role" label="角色" rules={[{ required: true }]}>
          <Select
            options={[
              { value: 'admin', label: '管理员' },
              { value: 'user', label: '用户' },
            ]}
          />
        </Form.Item>
      </>
    ),
  })

  return (
    <>
      <Button onClick={() => show({ initialValues: { name: 'John', role: 'user' } })}>编辑</Button>
      {modal}
    </>
  )
}
```

### 服务层 Hooks（`react-toolkits/services`）

```tsx
import { useAuth, useMenuList } from 'react-toolkits/services'

export function UseDataExample() {
  const { data: permissions } = useAuth()
  const { data: menus } = useMenuList()
  // 根据你的业务渲染
  return null
}
```

### Ky 与请求拦截（`useKy`）

- 内置 `useKy` 封装了鉴权与 `APP_ID_HEADER` 注入逻辑，结合 Provider 的上下文使用
- 你也可以直接使用 `fetch`，但推荐统一通过 `services` 或 `useKy`

```tsx
import { useEffect } from 'react'
import { useKy } from 'react-toolkits/components'
import { APP_ID_HEADER } from 'react-toolkits/constants'

export default function FetchWithKy() {
  const ky = useKy({
    headers: {
      [APP_ID_HEADER]: 'my-app-id',
    },
  })

  useEffect(() => {
    ky.get('/api/ping').json()
  }, [ky])

  return null
}
```

提示：若你的应用已有全局请求层，可仅复用本包页面/组件，将头与鉴权逻辑放到你的层中保持一致性。

## 迁移指南（从根导入 → 子路径导出）

过去：

```ts
import { ToolkitProvider, Layout, useFormDrawer } from '@flow97/react-toolkit'
```

现在（推荐）：

```ts
import { ToolkitProvider, Layout } from 'react-toolkits/components'
import { useFormDrawer, useFormModal } from 'react-toolkits/hooks'
import { permissionRoutes } from 'react-toolkits/pages'
import { useAuth } from 'react-toolkits/services'
```

收益：边界清晰、按需打包更友好（Tree Shaking）。

### 权限版本兼容

- 历史上存在不同权限数据结构，`AuthMode` 用于适配后端变体
- 新项目建议使用 `V3`；旧项目如为 `V2`，只需在 `ToolkitProvider` 中切换版本

## 常见问题（FAQ）

- 如何引入样式？
  - 在应用入口一次性引入：`import 'react-toolkits/style.css'`
- Provider 放哪？
  - 在应用根部使用 `ToolkitProvider`，传入登录路径、主页路径、权限版本等配置。
- 如何自定义菜单？
  - 通过 `Layout` 的 `items` 或参考示例应用的 `menu-items.tsx`。
- 如何统一请求与拦截？
  - 使用 `useKy` 或 `services` 中的 hooks（自动注入鉴权与 App-ID 头）。
  - 若需要自定义拦截，传入 `useKy` 的配置或在应用层包裹一层。
- 列表的数据结构需要什么格式？
- 分页：`{ list: T[]; total: number }`；无限滚动：`{ list: T[]; nextPage?: number }`
- 可以只用页面和路由，不用 Provider 吗？
- 不建议。多数功能依赖上下文（鉴权、国际化、请求）。若必须，请自行在应用层提供等价上下文。

## 本地开发与构建

```bash
# 构建
pnpm -C packages/react-toolkits build

# 开发调试（监听）
pnpm -C packages/react-toolkits dev
```

构建将产出多入口：`lib/index.js`、`lib/components.js`、`lib/hooks.js`、`lib/pages.js`、`lib/services.js`、`lib/types.js`、`lib/utils.js`，以及 `locale/*`。

提示：发布前请确保示例应用验证通过，并更新 `CHANGELOG.md` 描述变更与迁移说明。

## 变更与发布

- 更新日志见 `CHANGELOG.md`
- 遵循 semver；涉及子路径导出/目录变更会在次版本提供迁移说明

## 许可证

MIT