本指南将帮助您在 5 分钟内开始使用 Pivot UI 组件库构建您的第一个 API 文档页面。
第一步:创建项目
使用 Next.js 创建一个新项目:
第二步:安装依赖
安装 Pivot UI 所需的核心依赖:
第三步:配置 utils
创建 lib/utils.ts
文件:
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
第四步:添加核心组件
安装状态码组件
或者手动创建 components/pivot/status-code.tsx
:
"use client";
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const statusCodeVariants = cva(
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors",
{
variants: {
variant: {
default: "border-transparent text-primary-foreground",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
},
);
function getStatusCodeColor(code: number) {
if (code >= 100 && code < 200) return "bg-blue-500";
if (code >= 200 && code < 300) return "bg-green-500";
if (code >= 300 && code < 400) return "bg-yellow-500";
if (code >= 400 && code < 500) return "bg-orange-500";
if (code >= 500) return "bg-red-500";
return "bg-gray-500";
}
export interface StatusCodeProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof statusCodeVariants> {
code: number;
}
const StatusCode = React.forwardRef<HTMLDivElement, StatusCodeProps>(
({ className, variant, code, ...props }, ref) => {
const colorClass = getStatusCodeColor(code);
return (
<div
className={cn(statusCodeVariants({ variant }), colorClass, className)}
ref={ref}
{...props}
>
{code}
</div>
);
},
);
StatusCode.displayName = "StatusCode";
export { StatusCode, statusCodeVariants };
安装方法标签组件
安装 OpenAPI 处理 Hook
第五步:创建您的第一个 API 文档页面
创建 pages/api-docs.tsx
(或在 App Router 中创建 app/api-docs/page.tsx
):
"use client";
import { StatusCode } from "@/components/pivot/status-code";
import { MethodLabel } from "@/components/pivot/method-label";
import { useOpenApi } from "@/lib/hooks/use-openapi";
import type { OpenAPIV3 } from "openapi-types";
// 示例 OpenAPI 规范(简化版)
const sampleSpec: OpenAPIV3.Document = {
openapi: "3.0.0",
info: {
title: "用户管理 API",
version: "1.0.0",
description: "一个简单的用户管理 API 示例",
},
paths: {
"/api/users": {
get: {
summary: "获取用户列表",
description: "获取系统中所有用户的分页列表",
tags: ["用户管理"],
parameters: [
{
name: "page",
in: "query",
schema: { type: "integer", default: 1 },
description: "页码",
},
{
name: "limit",
in: "query",
schema: { type: "integer", default: 20 },
description: "每页数量",
},
],
responses: {
"200": {
description: "成功返回用户列表",
content: {
"application/json": {
schema: {
type: "object",
properties: {
users: {
type: "array",
items: { $ref: "#/components/schemas/User" },
},
total: { type: "integer" },
},
},
},
},
},
"400": { description: "请求参数错误" },
"500": { description: "服务器内部错误" },
},
},
post: {
summary: "创建新用户",
description: "在系统中创建一个新的用户账户",
tags: ["用户管理"],
requestBody: {
required: true,
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CreateUser" },
},
},
},
responses: {
"201": {
description: "用户创建成功",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/User" },
},
},
},
"400": { description: "请求数据无效" },
"409": { description: "用户已存在" },
},
},
},
},
components: {
schemas: {
User: {
type: "object",
required: ["id", "email", "name"],
properties: {
id: { type: "string", description: "用户唯一标识" },
email: { type: "string", format: "email", description: "用户邮箱" },
name: { type: "string", description: "用户姓名" },
createdAt: {
type: "string",
format: "date-time",
description: "创建时间",
},
},
},
CreateUser: {
type: "object",
required: ["email", "name"],
properties: {
email: { type: "string", format: "email", description: "用户邮箱" },
name: { type: "string", description: "用户姓名" },
password: { type: "string", minLength: 8, description: "用户密码" },
},
},
},
},
};
export default function ApiDocsPage() {
const openApi = useOpenApi(sampleSpec);
const operationsByTag = openApi.getOperationsByTag();
return (
<main className="container mx-auto px-4 py-8">
<div className="max-w-4xl mx-auto space-y-8">
{/* 页面标题 */}
<div className="text-center space-y-4">
<h1 className="text-4xl font-bold">API 文档</h1>
<p className="text-xl text-muted-foreground">
使用 Pivot UI 构建的现代化 API 文档
</p>
<div className="text-sm text-muted-foreground">
版本: {openApi.getInfo()?.version} | 标题:{" "}
{openApi.getInfo()?.title}
</div>
</div>
{/* API 端点列表 */}
<div className="space-y-8">
{Object.entries(operationsByTag).map(([tag, operations]) => (
<div key={tag} className="space-y-4">
<h2 className="text-2xl font-semibold border-b pb-2">{tag}</h2>
{operations.map((op, index) => (
<div key={index} className="border rounded-lg p-6 space-y-4">
{/* 端点标题 */}
<div className="flex items-center gap-3">
<MethodLabel method={op.method as any} />
<code className="text-lg font-mono text-muted-foreground">
{op.path}
</code>
</div>
{/* 操作信息 */}
<div>
<h3 className="text-lg font-semibold">
{op.operation.summary}
</h3>
{op.operation.description && (
<p className="text-muted-foreground mt-1">
{op.operation.description}
</p>
)}
</div>
{/* 参数信息 */}
{op.operation.parameters &&
op.operation.parameters.length > 0 && (
<div>
<h4 className="font-semibold mb-2">查询参数</h4>
<div className="space-y-2">
{op.operation.parameters.map(
(param: any, pIndex: number) => (
<div
key={pIndex}
className="flex items-center gap-3 p-3 bg-muted/50 rounded"
>
<code className="font-mono text-sm">
{param.name}
</code>
<span className="text-xs px-2 py-1 bg-blue-100 text-blue-800 rounded">
{param.schema?.type || "string"}
</span>
<span className="text-sm text-muted-foreground flex-1">
{param.description}
</span>
{param.required && (
<span className="text-xs px-2 py-1 bg-red-100 text-red-800 rounded">
必填
</span>
)}
</div>
),
)}
</div>
</div>
)}
{/* 响应状态码 */}
<div>
<h4 className="font-semibold mb-3">响应状态码</h4>
<div className="space-y-2">
{Object.entries(op.operation.responses || {}).map(
([status, response]: [string, any]) => (
<div key={status} className="flex items-center gap-3">
<StatusCode code={parseInt(status)} />
<span className="text-sm">
{response.description || "无描述"}
</span>
</div>
),
)}
</div>
</div>
</div>
))}
</div>
))}
</div>
</div>
</main>
);
}
第六步:添加更多组件
复制按钮
参数展示组件
高级示例页面
创建一个更复杂的示例:
"use client";
import { useOpenApi } from "@/lib/hooks/use-openapi";
import { StatusCode } from "@/components/pivot/status-code";
import { MethodLabel } from "@/components/pivot/method-label";
import { CopyButton } from "@/components/pivot/copy-button";
import { ParameterItem } from "@/components/pivot/parameter-item";
export default function AdvancedApiPage() {
const openApi = useOpenApi(sampleSpec); // 使用上面定义的 spec
return (
<main className="container mx-auto px-4 py-8">
<div className="max-w-4xl mx-auto space-y-8">
<h1 className="text-4xl font-bold text-center">高级 API 文档示例</h1>
<div className="border rounded-lg p-6 space-y-6">
{/* 端点标题 */}
<div className="flex items-center gap-3">
<MethodLabel method="GET" />
<code className="text-lg font-mono">/api/users/{"{id}"}</code>
<CopyButton text="/api/users/{id}" size="sm" />
</div>
{/* 描述 */}
<div>
<h2 className="text-xl font-semibold">获取用户详情</h2>
<p className="text-muted-foreground">
根据用户 ID 获取特定用户的详细信息
</p>
</div>
{/* 路径参数 */}
<div>
<h3 className="font-semibold mb-3">路径参数</h3>
<ParameterItem
parameter={{
name: "id",
in: "path",
required: true,
schema: { type: "string" },
description: "用户的唯一标识符",
}}
components={openApi.getComponents()}
/>
</div>
{/* 响应示例 */}
<div>
<h3 className="font-semibold mb-3">响应状态码</h3>
<div className="space-y-2">
<div className="flex items-center gap-3">
<StatusCode code={200} />
<span className="text-sm">成功返回用户信息</span>
</div>
<div className="flex items-center gap-3">
<StatusCode code={404} />
<span className="text-sm">用户不存在</span>
</div>
<div className="flex items-center gap-3">
<StatusCode code={500} />
<span className="text-sm">服务器内部错误</span>
</div>
</div>
</div>
{/* 示例响应数据 */}
<div>
<h3 className="font-semibold mb-3">示例响应</h3>
<div className="bg-slate-950 text-slate-50 p-4 rounded-lg overflow-x-auto">
<pre className="text-sm">
{`{
"id": "user_123",
"email": "user@example.com",
"name": "张三",
"createdAt": "2024-01-15T10:30:00Z"
}`}
</pre>
<div className="mt-2">
<CopyButton
text='{"id":"user_123","email":"user@example.com","name":"张三","createdAt":"2024-01-15T10:30:00Z"}'
variant="ghost"
size="sm"
/>
</div>
</div>
</div>
</div>
</div>
</main>
);
}
第七步:国际化支持
如果需要多语言支持:
然后在应用中使用:
import { I18nProvider } from "@/registry/lib/i18n/";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<I18nProvider>{children}</I18nProvider>
</body>
</html>
);
}
下一步
恭喜!您已经成功创建了第一个使用 Pivot UI 的 API 文档页面。接下来您可以:
1. 探索更多组件
查看 组件文档 了解所有可用的 89+ 组件:
- 数据展示:
schema-display
、example-display
、response-item
- 交互功能:
try-it-out-panel
、theme-toggle
、language-switcher
- 代码生成:
curl-generator
、python-generator
、php-generator
- 布局容器:
navigation-sidebar
、operation-box
、section-title
2. 学习工具函数
了解 Hooks 和工具函数 来处理复杂的 OpenAPI 数据:
# 安装示例生成工具
npx shadcn@latest add https://pivotkit.vercel.app/r/generate-example.json
# 使用示例
import { generateExample } from "@/lib/utils/generate-example";
const example = generateExample(schema, components);
3. 构建完整应用
参考 API 文档模板 了解如何构建完整的文档应用。
4. 自定义主题
根据您的品牌调整颜色和样式:
:root {
--status-success: #10b981;
--status-error: #ef4444;
--status-warning: #f59e0b;
}
5. 集成真实 API
将示例数据替换为您的真实 OpenAPI 规范:
// 从 URL 加载
const spec = await fetch("/api/openapi.json").then((r) => r.json());
// 或从文件导入
import spec from "./openapi.json";