Command Palette

Search for a command to run...

Docs
快速开始

快速开始

5分钟内开始使用 Pivot UI 组件库构建您的第一个 API 文档页面。

本指南将帮助您在 5 分钟内开始使用 Pivot UI 组件库构建您的第一个 API 文档页面。

第一步:创建项目

使用 Next.js 创建一个新项目:

第二步:安装依赖

安装 Pivot UI 所需的核心依赖:

第三步:配置 utils

创建 lib/utils.ts 文件:

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

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):

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>
  );
}

第六步:添加更多组件

复制按钮

参数展示组件

高级示例页面

创建一个更复杂的示例:

app/advanced-api/page.tsx
"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>
  );
}

第七步:国际化支持

如果需要多语言支持:

然后在应用中使用:

app/layout.tsx
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-displayexample-displayresponse-item
  • 交互功能try-it-out-paneltheme-togglelanguage-switcher
  • 代码生成curl-generatorpython-generatorphp-generator
  • 布局容器navigation-sidebaroperation-boxsection-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. 自定义主题

根据您的品牌调整颜色和样式:

styles/globals.css
: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";

需要帮助?查看我们的 安装指南 或浏览 在线查看器 查看更多示例。