npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wordrhyme/auto-crud

v1.5.1

Published

Schema-first CRUD components with auto-generated tables and forms

Readme

@wordrhyme/auto-crud

Schema-first CRUD components with auto-generated tables and forms

基于 Zod Schema 自动生成完整 CRUD 界面的低代码 React 组件库。

npm version License: MIT


✨ 特性

  • 🚀 零配置 CRUD: 从 Zod Schema 自动生成表格和表单
  • 🎨 高级数据表格: 基于 TanStack Table,支持排序、过滤、分页
  • 📝 智能表单: 集成 Formily,支持复杂表单联动
  • 🔌 灵活集成: 支持 tRPC、REST API 或内存数据源
  • 🎯 类型安全: 完整的 TypeScript 类型推断
  • 🌐 URL 状态同步: 使用 nuqs 实现 URL 状态管理
  • 🎭 三种过滤模式: Simple / Advanced / Command
  • 🔄 批量操作: 支持批量更新和删除

📦 安装

# pnpm
pnpm add @wordrhyme/auto-crud zod sonner

# npm
npm install @wordrhyme/auto-crud zod sonner

# yarn
yarn add @wordrhyme/auto-crud zod sonner

Peer Dependencies

{
  "react": "^18.0.0 || ^19.0.0",
  "react-dom": "^18.0.0 || ^19.0.0",
  "sonner": "^2.0.0",
  "zod": "^3.0.0 || ^4.0.0"
}

🚀 快速开始

基础示例

import { AutoCrudTable, useAutoCrudResource, createMemoryDataSource } from "@wordrhyme/auto-crud";
import { z } from "zod";

// 1. 定义 Zod Schema
const taskSchema = z.object({
  id: z.string(),
  title: z.string(),
  status: z.enum(["todo", "in-progress", "done"]),
  priority: z.enum(["low", "medium", "high"]),
  createdAt: z.date(),
});

type Task = z.infer<typeof taskSchema>;

// 2. 创建数据源
const dataSource = createMemoryDataSource<Task>({
  data: [
    { id: "1", title: "Task 1", status: "todo", priority: "high", createdAt: new Date() },
    { id: "2", title: "Task 2", status: "done", priority: "low", createdAt: new Date() },
  ],
  onCreate: async (data) => { /* 创建逻辑 */ },
  onUpdate: async (id, data) => { /* 更新逻辑 */ },
  onDelete: async (id) => { /* 删除逻辑 */ },
});

// 3. 使用组件
function TasksPage() {
  const resource = useAutoCrudResource({
    dataSource,
    schema: taskSchema,
  });

  return (
    <AutoCrudTable
      title="任务管理"
      schema={taskSchema}
      resource={resource}
    />
  );
}

与 tRPC 集成

import { AutoCrudTable, useAutoCrudResource, createTRPCDataSource } from "@wordrhyme/auto-crud";
import { trpc } from "@/lib/trpc/client";
import { createSelectSchema } from "drizzle-zod";
import { tasks } from "@/db/schema";

const taskSchema = createSelectSchema(tasks);

function TasksPage() {
  // URL 状态管理
  const [queryParams] = useQueryStates({
    page: parseAsInteger.withDefault(1),
    perPage: parseAsInteger.withDefault(10),
    sort: getSortingStateParser().withDefault([]),
    filters: getFiltersStateParser().withDefault([]),
  });

  // 创建 tRPC 数据源
  const dataSource = createTRPCDataSource({
    router: trpc.tasks,
    queryInput: queryParams,
  });

  const resource = useAutoCrudResource({
    dataSource,
    schema: taskSchema,
  });

  return (
    <AutoCrudTable
      title="任务管理"
      schema={taskSchema}
      resource={resource}
      fields={{
        id: { hidden: true },
        status: { label: "状态" },
        priority: { label: "优先级" },
      }}
      table={{
        filterModes: ["simple", "advanced", "command"],
        batchFields: ["status", "priority"],
      }}
    />
  );
}

📖 核心概念

Schema First 理念

Zod Schema → 自动推断字段类型 → 生成表格列 + 表单字段

优势

  • ✅ 单一数据源(SSOT)
  • ✅ 类型安全
  • ✅ 零手动配置
  • ✅ Schema 变更自动同步

数据源(Data Source)

@wordrhyme/auto-crud 支持三种数据源:

1. 内存数据源(Memory Data Source)

适合:原型开发、演示、测试

import { createMemoryDataSource } from '@wordrhyme/auto-crud';

const dataSource = createMemoryDataSource({
  data: initialData,
  onCreate: async (data) => {
    /* ... */
  },
  onUpdate: async (id, data) => {
    /* ... */
  },
  onDelete: async (id) => {
    /* ... */
  },
});

2. tRPC 数据源(tRPC Data Source)

适合:全栈 TypeScript 项目

import { createTRPCDataSource } from '@wordrhyme/auto-crud';

const dataSource = createTRPCDataSource({
  router: trpc.tasks,
  queryInput: { page: 1, perPage: 10 },
});

要求:后端需要使用 @wordrhyme/auto-crud-server 创建 CRUD 路由。

3. 自定义数据源(Custom Data Source)

适合:REST API、GraphQL 等

const dataSource: DataSource<Task> = {
  list: async (params) => {
    const response = await fetch(`/api/tasks?${new URLSearchParams(params)}`);
    return response.json();
  },
  get: async (id) => {
    const response = await fetch(`/api/tasks/${id}`);
    return response.json();
  },
  create: async (data) => {
    const response = await fetch('/api/tasks', {
      method: 'POST',
      body: JSON.stringify(data),
    });
    return response.json();
  },
  update: async (id, data) => {
    const response = await fetch(`/api/tasks/${id}`, {
      method: 'PATCH',
      body: JSON.stringify(data),
    });
    return response.json();
  },
  delete: async (id) => {
    await fetch(`/api/tasks/${id}`, { method: 'DELETE' });
  },
  deleteMany: async (ids) => {
    await fetch('/api/tasks/batch', {
      method: 'DELETE',
      body: JSON.stringify({ ids }),
    });
  },
};

🎨 组件 API

<AutoCrudTable />

完整的 CRUD 表格组件。

Props

interface AutoCrudTableProps<TSchema> {
  // 必需
  schema: TSchema; // Zod Schema
  resource: UseAutoCrudResourceReturn<TSchema>; // useAutoCrudResource 返回值

  // 可选
  title?: string;
  description?: string;
  fields?: Fields; // 统一字段配置
  table?: {
    hidden?: string[]; // 隐藏的列
    overrides?: Record<string, any>; // 列覆盖配置
    filterModes?: FilterMode | FilterMode[]; // 过滤模式
    batchFields?: (string | BatchUpdateField)[]; // 批量更新字段
    /** @deprecated 新代码请使用 actions.batch */
    batchActions?: BatchActionConfig<z.output<TSchema>>;
    defaultSort?: any[]; // 手动 resource 的 UI 默认排序;useAutoCrudResource 请配置 options.defaultSort
  };
  form?: {
    overrides?: Record<string, any>; // 表单覆盖配置
    columns?: number; // 表单列数
  };
  /**
   * 行操作配置,或统一配置 toolbar/row/batch。
   *
   * 旧写法 actions={[...]} 仍表示行操作;
   * 新写法 actions={{ toolbar, row, batch }} 可集中配置三类操作。
   */
  actions?:
    | RowActionConfig<z.output<TSchema>>
    | {
        toolbar?: ToolbarActionConfig;
        row?: RowActionConfig<z.output<TSchema>>;
        batch?: BatchActionConfig<z.output<TSchema>>;
      };
  /** @deprecated 新代码请使用 actions.toolbar */
  toolbar?: ToolbarActionConfig;
  /** @deprecated 新代码请使用 actions.toolbar */
  toolbarActions?: ToolbarActionConfig;
}

useAutoCrudResource()

连接数据源到组件的 Hook。

参数

interface UseAutoCrudResourceConfig<TData> {
  dataSource: DataSource<TData>; // 数据源
  schema: z.ZodType<TData>; // Zod Schema
  options?: {
    defaultSort?: Array<{ id: string; desc: boolean }> | false;
    // undefined: schema 有 createdAt 时默认 createdAt desc
    // false: 不应用默认排序
  };
}

返回值

interface UseAutoCrudResourceReturn<TSchema, TData> {
  // 主键字段名;未提供时 AutoCrudTable 默认按 "id" 读取
  idKey?: keyof TData & string;

  // 表格数据
  tableData: {
    data: TData[];
    pageCount: number;
  };

  // 模态框状态
  modal: {
    variant: 'dialog' | 'sheet';
    createOpen: boolean;
    editOpen: boolean;
    viewOpen: boolean;
    deleteOpen: boolean;
    selected: TData | null;
    copySource: TData | null;
  };

  // 操作方法
  handlers: {
    openCreate: () => void;
    openEdit: (item: TData) => void;
    openView: (item: TData) => void;
    openDelete: (item: TData) => void;
    copyRow: (item: TData) => void;
    closeModals: () => void;
    submitCreate: (data: TData) => Promise<void>;
    submitUpdate: (data: TData) => Promise<void>;
    confirmDelete: () => Promise<void>;
    deleteMany: (rows: TData[]) => Promise<void>;
    updateMany: (rows: TData[], data: Record<string, unknown>) => Promise<void>;
  };

  // 加载状态
  mutations: {
    isCreating: boolean;
    isUpdating: boolean;
    isDeleting: boolean;
  };
}

🔧 字段配置

Field 类型

interface Field {
  /** 字段标签(表格和表单共用) */
  label?: string;
  /** 是否隐藏(表格和表单都隐藏) */
  hidden?: boolean;
  /** 表格特定配置 */
  table?: {
    hidden?: boolean; // 仅表格隐藏
    meta?: Record<string, unknown>; // 筛选器配置
    [key: string]: unknown;
  };
  /** 表单特定配置 */
  form?: {
    'x-hidden'?: boolean; // 仅表单隐藏
    'x-component'?: string; // 组件类型
    'x-component-props'?: Record<string, unknown>; // 组件属性
    'x-reactions'?: object; // 字段联动
    [key: string]: unknown;
  };
}

type Fields = Record<string, Field>;

基础配置

<AutoCrudTable
  schema={taskSchema}
  resource={resource}
  fields={{
    id: { hidden: true },           // 表格和表单都隐藏
    title: { label: "标题" },        // 自定义标签
    createdAt: {
      label: "创建时间",
      form: { "x-hidden": true },    // 仅表单隐藏
    },
  }}
/>

表格筛选器配置

<AutoCrudTable
  schema={taskSchema}
  resource={resource}
  fields={{
    status: {
      label: "状态",
      table: {
        meta: {
          variant: "select",  // "text" | "select" | "multiSelect" | "date" | "dateRange" | "range"
          options: [
            { label: "待处理", value: "pending" },
            { label: "已完成", value: "completed" },
          ],
        },
      },
    },
    amount: {
      label: "金额",
      table: {
        meta: {
          variant: "range",
          range: [0, 10000],
          unit: "¥",
        },
      },
    },
  }}
/>

表单组件配置

<AutoCrudTable
  schema={taskSchema}
  resource={resource}
  fields={{
    description: {
      label: "描述",
      form: {
        "x-component": "Textarea",
        "x-component-props": { rows: 5 },
      },
    },
    assignee: {
      label: "负责人",
      form: {
        "x-component": "Combobox",
        "x-component-props": { placeholder: "选择负责人" },
      },
    },
  }}
/>

字段联动

<AutoCrudTable
  schema={taskSchema}
  resource={resource}
  fields={{
    endDate: {
      label: "结束日期",
      form: {
        "x-reactions": {
          dependencies: ["startDate"],
          when: "{{$deps[0] !== undefined}}",
          fulfill: {
            state: {
              visible: true,
              disabled: false,
            },
          },
        },
      },
    },
  }}
/>

🎯 过滤模式

Simple 模式(默认)

  • 适合:快速筛选常用字段
  • 特点:下拉选择框,一键清除
  • 示例:状态筛选、优先级筛选
table={{
  filterModes: "simple",
}}

Advanced 模式(高级筛选)

  • 适合:复杂多条件查询
  • 特点:Notion 风格,支持 AND/OR 逻辑
  • 示例:status = "done" AND priority = "high"
table={{
  filterModes: "advanced",
}}

Command 模式(命令面板)

  • 适合:键盘流用户
  • 特点:Linear 风格,快捷键 Cmd+K
  • 示例:快速搜索并添加筛选条件
table={{
  filterModes: "command",
}}

多模式切换

table={{
  filterModes: ["simple", "advanced", "command"],  // 第一个为默认模式
}}

🔧 工具栏操作配置

toolbar 数组控制页面顶部右侧工具栏的按钮,设计思路完全对齐行操作 actions。旧 prop toolbarActions 仍兼容读取,但新代码请使用 toolbar

内置操作类型:refresh(刷新)、create(新建)、import(导入)、export(导出)。

只添加自定义按钮(内置保持默认)

只传 type: "custom" 项时,所有内置按钮(刷新、导入、导出、新建)保持原样,custom 项按 position 插入首部或尾部。

<AutoCrudTable
  toolbar={[
    {
      type: 'custom',
      component: <Button variant="outline">分类管理</Button>,
      position: 'start',
    },
    { type: 'custom', component: <Button variant="outline">批量标签</Button> }, // 默认 position: "end"
  ]}
/>
// 渲染顺序: 分类管理 · [刷新] · [导入] · [导出] · [新建] · 批量标签

调整内置按钮顺序

数组中只要包含任意内置 type,就会完全接管工具栏 —— 未列出的内置按钮直接隐藏,渲染顺序严格按数组

<AutoCrudTable
  toolbar={[
    { type: 'create' }, // 新建移到最前
    { type: 'export', label: '导出 CSV' }, // 覆盖文案
    // import 未列出 → 隐藏
  ]}
/>
// 渲染顺序: 新建 · 导出 CSV(导入按钮消失)

覆盖内置按钮行为

<AutoCrudTable
  toolbar={[
    { type: 'import' },
    { type: 'export' },
    {
      type: 'create',
      label: '发布商品',
      onClick: () => router.push('/products/new'), // 跳转详情页而非弹窗
    },
  ]}
/>

完全替换内置按钮组件

<AutoCrudTable
  toolbar={[
    { type: 'export' },
    {
      type: 'create',
      // 整个按钮替换
      component: (
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <Button>
              <Plus className="mr-2 h-4 w-4" />
              新建
            </Button>
          </DropdownMenuTrigger>
          <DropdownMenuContent>
            <DropdownMenuItem onClick={() => create('simple')}>简易商品</DropdownMenuItem>
            <DropdownMenuItem onClick={() => create('sku')}>多规格商品</DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      ),
    },
  ]}
/>

混合 custom 和内置

<AutoCrudTable
  toolbar={[
    { type: 'custom', component: <CategoryFilter onChange={setCategory} /> },
    { type: 'export' },
    { type: 'create', label: '发布', onClick: () => router.push('/products/new') },
  ]}
/>
// 渲染顺序: 分类筛选 · 导出 · 发布(导入隐藏)

permissions 的交互

toolbarpermissions 的交互规则如下:

  • permissions.can.create = false → 即使配置了 { type: "create" },新建按钮仍不渲染
  • permissions.can.export = false → 即使配置了 { type: "export" },导出按钮仍不渲染
  • 内置项即使使用 component 完全替换渲染,也仍然受对应权限控制
  • type: "custom" 不受 permissions 影响,由业务方自行控制
<AutoCrudTable
  permissions={{
    can: { create: isAdmin, export: true, delete: isAdmin },
  }}
  toolbar={[
    { type: 'export' }, // ✅ 始终渲染
    { type: 'create', label: '发布商品' }, // 🔒 仅 isAdmin 时渲染
    {
      type: 'custom', // ✅ 不受 permissions 影响
      component: isEditor ? <Button>审核</Button> : null, // 业务方自行守卫
    },
  ]}
/>

toolbar 传参高级语法(函数式)

如果你的目标是拦截并修改基于原顺序的所有内容,你还可以传递一个函数:

<AutoCrudTable
  // defaults 即内置按钮的信息,在此数组里你可以随意 map、过滤或插值
  toolbar={(defaults) =>
    defaults.map((btn) =>
      btn.type === 'create'
        ? { ...btn, label: '发布商品', onClick: () => router.push('/products/new') }
        : btn,
    )
  }
/>
// 非常适合“只盖头不换面”的场景,不会导致内置按钮丢失或顺序打乱

ToolbarActionItem 类型

type ActionMeta = {
  id?: string; // 插件注册时用于区分同 type 的 action
  order?: number; // 插件注册排序,默认 100
  hidden?: boolean; // 显式隐藏该 action
};

type ToolbarActionItem = ToolbarBuiltinActionItem | ToolbarCustomActionItem;

type ToolbarActionConfig =
  | ToolbarActionItem[]
  | ((defaults: ToolbarBuiltinActionItem[]) => ToolbarActionItem[]);

type ToolbarBuiltinActionItem = ActionMeta & {
  type: 'refresh' | 'create' | 'import' | 'export';
  onClick?: () => void; // 覆盖默认行为
  label?: string; // 覆盖默认文案
  component?: React.ReactNode | ((context: AutoCrudToolbarContext) => React.ReactNode);
};

type ToolbarCustomActionItem = ActionMeta & {
  type: 'custom';
  component: React.ReactNode | ((context: AutoCrudToolbarContext) => React.ReactNode);
  position?: 'start' | 'end'; // 仅在无内置项时生效,默认 "end"
};

interface AutoCrudToolbarContext {
  crudId: string;
  idKey: string;
  rowIds: string[];
  selectedRowIds: string[];
  selectedCount: number;
  refresh?: () => Promise<unknown>;
  openImport?: () => void;
  exportData?: () => Promise<void>;
  openCreate?: () => void;
  isRefreshing: boolean;
  isExporting: boolean;
}

AutoCrudToolbarContext 是工具栏自定义组件的正式 command contract。自定义按钮需要复用 AutoCrud 内置能力时,直接调用 context 中的命令,不要绕过内部状态重新实现。

例如业务侧希望把默认“新建”按钮替换成“手动创建”,可以替换 type: "create" 的组件并调用 openCreate

<AutoCrudTable
  toolbar={[
    {
      type: 'create',
      component: ({ openCreate }) => (
        <Button onClick={() => openCreate?.()}>手动创建</Button>
      ),
    },
  ]}
/>

这里“手动创建”只是业务按钮文案,不是新的 AutoCrud 流程;它仍然打开 AutoCrud 原生的新建弹窗。openCreateopenImportexportData 会按对应权限和能力可选暴露,扩展方应使用 ?.() 或自行控制 disabled 状态。

工具栏扩展 Resolver

宿主应用可以通过 setToolbarResolver 为指定 CRUD 注入额外工具栏动作。resolver 是普通纯函数,不是 React Hook;不要在 resolver 内调用 useRouteruseMemo 等 React Hooks。

import { AutoCrudTable, setToolbarResolver } from '@wordrhyme/auto-crud';
import type { AutoCrudToolbarResolver } from '@wordrhyme/auto-crud';

const toolbarResolver: AutoCrudToolbarResolver = (targetId, ownerActions, context) => {
  if (targetId !== 'com.wordrhyme.shop.products') return [...ownerActions];

  return [
    ...ownerActions,
    {
      type: 'custom',
      component: <Button disabled={context.selectedCount === 0}>批量发布</Button>,
    },
  ];
};

setToolbarResolver(toolbarResolver);

插件式操作注册

跨模块扩展推荐使用 crudActions.register。它按 targetId 和区域注入操作,不需要业务页把所有插件动作手动拼到 props 中。

import { crudActions } from '@wordrhyme/auto-crud';

crudActions.register({
  targetId: 'com.wordrhyme.shop.products',
  zone: 'toolbar', // 'toolbar' | 'row' | 'batch'
  ownerId: 'com.wordrhyme.product-lab',
  actions: [
    { type: 'export', hidden: true },
    { type: 'create', label: '发布商品', order: 80 },
    {
      type: 'custom',
      id: 'bulk-publish',
      position: 'end',
      component: ({ selectedCount }) => (
        <Button disabled={selectedCount === 0}>批量发布</Button>
      ),
    },
  ],
});

同一个 ownerId + targetId + zone 再次注册会替换该 owner 的旧动作;卸载插件或页面时可调用 crudActions.unregister(ownerId)


🎬 行操作配置

actions 旧数组写法控制每行下拉菜单的操作项;统一写法可使用 actions.row。两种写法都支持覆盖内置操作、添加自定义操作、完全自定义顺序。

只添加自定义项(内置不变)

<AutoCrudTable
  actions={{
    row: [
      { type: 'custom', label: '分配', onClick: (row) => assign(row.id) },
      {
        type: 'custom',
        label: '预览',
        onClick: (row) => preview(row),
        position: 'start',
      },
    ],
  }}
/>
// 渲染顺序: 预览 · 查看 · 编辑 · 复制 · 删除 · 分配

覆盖内置行为或隐藏某项

数组中只要包含任意内置 type,数组就会完全接管 —— 未列出的内置项自动隐藏。

<AutoCrudTable
  actions={{
    row: [
      { type: 'view', onClick: (row) => router.push(`/tasks/${row.id}`) }, // 覆盖跳转
      { type: 'custom', label: '分配', onClick: (row) => assign(row.id) },
      { type: 'edit' }, // 默认行为
      // copy / delete 未列出 → 隐藏
    ],
  }}
/>

完全自定义顺序

<AutoCrudTable
  actions={[
    { type: 'edit' },
    { type: 'copy' },
    { type: 'custom', label: '归档', onClick: archive, separator: true },
    { type: 'delete', separator: true },
  ]}
/>

支持函数配置模式

如果只需要在原来的基础上做细微拦截处理,传入一个函数更省事:

<AutoCrudTable
  actions={(defaults) =>
    defaults.map((action) =>
      action.type === 'delete'
        ? { ...action, label: '下架', variant: 'default' }
        : action,
    )
  }
/>

RowActionItem 类型

type RowActionItem<T> = RowBuiltinActionItem<T> | RowCustomActionItem<T>;

type RowActionConfig<T> =
  | RowActionItem<T>[]
  | ((defaults: RowBuiltinActionItem<T>[]) => RowActionItem<T>[]);

type RowBuiltinActionItem<T> = ActionMeta & {
  type: 'view' | 'edit' | 'copy' | 'delete';
  onClick?: (row: T) => void; // 不传则使用默认行为
  label?: string; // 不传则使用默认文案
  separator?: boolean; // 此项前加分隔线
};

type RowCustomActionItem<T> = ActionMeta & {
  type: 'custom';
  label?: string;
  onClick?: (row: T) => void;
  component?:
    | React.ReactNode
    | ((context: AutoCrudRowActionContext<T>) => React.ReactNode);
  position?: 'start' | 'end'; // 仅无内置项时生效,默认 end
  separator?: boolean;
  variant?: 'default' | 'destructive';
};

interface AutoCrudRowActionContext<T> {
  crudId: string;
  idKey: string;
  row: T;
  rowId?: string;
  openView: (row: T) => void;
  openEdit?: (row: T) => void;
  copyRow?: (row: T) => void;
  openDelete?: (row: T) => void;
}

🔄 批量操作

批量更新

<AutoCrudTable
  schema={taskSchema}
  resource={resource}
  table={{
    batchFields: ["status", "priority"],
  }}
/>

功能

  • 选择多行
  • 点击批量更新按钮
  • 选择字段和新值
  • 一键更新所有选中行

批量悬浮栏操作顺序

actions.batch 控制多选后悬浮操作栏,设计思路对齐 actions.rowactions.toolbar。旧的 table.batchActions 仍兼容,但新代码建议使用 actions.batch

内置操作类型:batchUpdate(批量更新字段下拉)、export(导出选中行)、delete(删除选中行)。

只传 type: "custom" 项时,默认批量操作保持原样;包含任意内置 type 时,将完全接管顺序,未列出的内置操作不渲染。

AutoCrudTable 默认不在悬浮栏重复显示导出按钮;需要悬浮栏导出时,在 actions.batch 中显式列出 { type: 'export' }

<AutoCrudTable
  schema={taskSchema}
  resource={resource}
  table={{
    batchFields: ['status', 'priority'],
  }}
  actions={{
    batch: [
      { type: 'batchUpdate' },
      {
        type: 'custom',
        label: '同步选中项',
        onClick: (rows) => syncSelected(rows),
      },
      { type: 'export' },
      { type: 'delete' },
    ],
  }}
/>

也可以用函数式写法基于默认顺序插入业务操作:

<AutoCrudTable
  actions={{
    batch: (defaults) => [
      defaults[0], // batchUpdate
      {
        type: 'custom',
        component: ({ rows }) => <Button size="sm">同步 {rows.length} 项</Button>,
      },
      defaults[2], // delete
    ],
  }}
/>

批量删除

// 自动启用,无需配置
<AutoCrudTable schema={taskSchema} resource={resource} />

功能

  • 选择多行
  • 点击批量删除按钮
  • 确认删除
  • 一次性删除所有选中行

🔌 与其他库集成

与 Drizzle ORM 集成

import { createSelectSchema } from "drizzle-zod";
import { tasks } from "@/db/schema";

const taskSchema = createSelectSchema(tasks);

<AutoCrudTable schema={taskSchema} resource={resource} />

与 Next.js 集成

// app/tasks/page.tsx
"use client";

import { AutoCrudTable } from "@wordrhyme/auto-crud";

export default function TasksPage() {
  return <AutoCrudTable schema={taskSchema} resource={resource} />;
}

🧰 工具函数

配置日期展示策略

通过 setDateFormatter 可以让 AutoCrud 内部日期跟随宿主的语言和时区策略:

import { setDateFormatter } from '@wordrhyme/auto-crud';

const disposeDateFormatter = setDateFormatter((date, options) =>
  new Intl.DateTimeFormat(getCurrentLocale(), {
    ...options,
    timeZone: getCurrentTimeZone(),
  }).format(date),
);

// 应用卸载时移除当前注册
disposeDateFormatter();

// 传入 undefined 可清空全部注册
setDateFormatter(undefined);

setDateFormatter 是进程/运行时级配置,建议在应用启动时注册一个稳定的 formatter。多个注册可以重叠,清理函数只移除对应注册,允许乱序清理。SSR 场景不要在每个请求中重复调用 setter;如需按请求选择语言或时区,应由稳定 formatter 通过宿主提供的并发安全上下文读取当前请求策略。

Schema Bridge - 核心转换函数

import {
  parseZodField, // 解析 Zod 字段类型
  createTableSchema, // Zod Schema → TanStack Table 列定义
  createSelectColumn, // 创建选择列
  createActionsColumn, // 创建操作列
  createFormSchema, // Zod Schema → Formily Schema
  createEditFormSchema, // 创建编辑表单 Schema(排除平台托管字段)
} from '@wordrhyme/auto-crud';

自定义列渲染

使用 createTableSchemaoverrides 参数自定义列渲染:

import { createTableSchema } from "@wordrhyme/auto-crud";
import { Badge } from "@shadcn/ui";

const columns = createTableSchema(taskSchema, {
  overrides: {
    status: {
      label: "状态",
      // 自定义 cell 渲染
      cell: ({ row }) => {
        const status = row.getValue("status");
        return (
          <Badge variant={status === "done" ? "success" : "secondary"}>
            {status}
          </Badge>
        );
      },
    },
    priority: {
      label: "优先级",
      // 自定义列配置
      size: 100,
      enableSorting: false,
    },
  },
  exclude: ["id", "createdAt"],  // 排除字段
});

自定义表单渲染

使用 createFormSchemaoverrides 参数自定义表单组件:

import { createFormSchema } from '@wordrhyme/auto-crud';

const formSchema = createFormSchema(taskSchema, {
  overrides: {
    description: {
      'x-component': 'Textarea',
      'x-component-props': { rows: 5 },
    },
    status: {
      'x-component': 'Select',
      'x-component-props': {
        options: [
          { label: '待办', value: 'todo' },
          { label: '完成', value: 'done' },
        ],
      },
    },
    dueDate: {
      'x-component': 'DatePicker',
      'x-component-props': { format: 'YYYY-MM-DD' },
    },
  },
  layout: 'grid',
  gridColumns: 2,
  exclude: ['id', 'createdAt'],
});

🔀 三种 Schema 类型

@wordrhyme/auto-crud 支持三种 Schema 输入格式,通过 SchemaAdapter 统一处理:

1. Zod Schema(推荐)

import { z } from 'zod';

const taskSchema = z.object({
  id: z.string(),
  title: z.string(),
  status: z.enum(['todo', 'in-progress', 'done']),
  priority: z.enum(['low', 'medium', 'high']),
  createdAt: z.date(),
});

2. JSON Schema

import type { JSONSchema } from '@wordrhyme/auto-crud';

const taskSchema: JSONSchema = {
  type: 'object',
  properties: {
    id: { type: 'string' },
    title: { type: 'string', title: '标题' },
    status: {
      type: 'string',
      enum: ['todo', 'in-progress', 'done'],
      title: '状态',
    },
    createdAt: { type: 'string', format: 'date-time' },
  },
  required: ['id', 'title'],
};

3. 简化配置(Simple Config)

import type { SimpleFieldsConfig } from '@wordrhyme/auto-crud';

const taskSchema: SimpleFieldsConfig = {
  id: { type: 'string', required: true },
  title: { type: 'string', label: '标题', required: true },
  status: {
    type: 'select',
    label: '状态',
    options: ['todo', 'in-progress', 'done'],
  },
  description: { type: 'textarea', label: '描述' },
  createdAt: { type: 'datetime' },
};

SchemaAdapter 使用

import { SchemaAdapter } from '@wordrhyme/auto-crud';

// 自动检测 Schema 类型
const type = SchemaAdapter.detectType(schema); // "zod" | "json" | "simple"

// 转换为统一的字段定义
const fields = SchemaAdapter.toUnified(schema);

// 转换为 Formily Schema
const formilySchema = SchemaAdapter.toFormily(fields);

🧩 基础组件(自定义组合)

如果 AutoCrudTable 不能满足需求,可以使用基础组件自行组合:

组件层级

AutoCrudTable         ← 高级封装,一站式 CRUD
  ├── AutoTable       ← 表格 + 工具栏 + 筛选器
  │   ├── DataTable   ← 纯表格组件(TanStack Table)
  │   ├── AutoTableActionBar    ← 批量操作栏
  │   └── AutoTableSimpleFilters ← 简单筛选器
  ├── AutoForm        ← 表单组件(Formily)
  └── CrudFormModal   ← 表单弹窗(创建/编辑/查看)

使用 DataTable 组件

import { DataTable, createTableSchema, createSelectColumn, createActionsColumn } from "@wordrhyme/auto-crud";

function CustomTable({ data }) {
  const columns = [
    createSelectColumn(),
    ...createTableSchema(taskSchema, { exclude: ["id"] }),
    createActionsColumn([
      { label: "查看", onClick: (row) => console.log("View", row) },
      { label: "编辑", onClick: (row) => console.log("Edit", row) },
      { label: "复制", onClick: (row) => console.log("Copy", row) },
      { label: "删除", onClick: (row) => console.log("Delete", row), separator: true, variant: "destructive" },
    ]),
  ];

  return (
    <DataTable
      columns={columns}
      data={data}
      pageCount={10}
    />
  );
}

使用 AutoForm 组件

import { AutoForm, createFormSchema } from "@wordrhyme/auto-crud";
import { createForm } from "@formily/core";

function CustomForm() {
  const form = createForm();
  const formSchema = createFormSchema(taskSchema, {
    exclude: ["id", "createdAt"],
    layout: "grid",
    gridColumns: 2,
  });

  return (
    <AutoForm
      form={form}
      schema={formSchema}
      onSubmit={(values) => console.log(values)}
    />
  );
}

使用 useDataTable Hook

import { useDataTable, DataTable, DataTablePagination } from "@wordrhyme/auto-crud";

function CustomDataTable({ data, pageCount }) {
  const { table } = useDataTable({
    data,
    columns,
    pageCount,
    initialState: {
      pagination: { pageIndex: 0, pageSize: 10 },
      sorting: [{ id: "createdAt", desc: true }],
    },
  });

  return (
    <div>
      <DataTable table={table} />
      <DataTablePagination table={table} />
    </div>
  );
}

组件组合示例

import {
  AutoTable,
  AutoForm,
  CrudFormModal,
  useAutoCrudResource,
  createTableSchema,
  createFormSchema,
} from "@wordrhyme/auto-crud";

function CustomCrudPage() {
  const resource = useAutoCrudResource({ dataSource, schema: taskSchema });

  const columns = createTableSchema(taskSchema, {
    overrides: {
      status: {
        cell: ({ row }) => <StatusBadge status={row.getValue("status")} />,
      },
    },
  });

  const formSchema = createFormSchema(taskSchema, {
    exclude: [
      "id",
      "createdAt",
      "updatedAt",
      "createdBy",
      "createdByType",
      "updatedBy",
      "updatedByType",
    ],
  });

  return (
    <div>
      {/* 自定义表格 */}
      <AutoTable
        columns={columns}
        data={resource.tableData.data}
        pageCount={resource.tableData.pageCount}
        filterMode="simple"
      />

      {/* 自定义表单弹窗 */}
      <CrudFormModal
        mode={resource.modal.editOpen ? "edit" : "create"}
        open={resource.modal.createOpen || resource.modal.editOpen}
        onClose={resource.handlers.closeModals}
        schema={formSchema}
        initialValues={resource.modal.selected}
        onSubmit={resource.modal.editOpen
          ? resource.handlers.submitUpdate
          : resource.handlers.submitCreate}
      />
    </div>
  );
}

📦 导出的类型

// Auto-CRUD 组件
export { AutoCrudTable } from './components/auto-crud/auto-crud-table';
export type {
  Field,
  Fields,
  AutoCrudTableProps,
} from './components/auto-crud/auto-crud-table';
export { AutoForm } from './components/auto-crud/auto-form';
export { AutoTable } from './components/auto-crud/auto-table';
export { AutoTableActionBar } from './components/auto-crud/auto-table-action-bar';
export { AutoTableSimpleFilters } from './components/auto-crud/auto-table-simple-filters';
export { CrudFormModal } from './components/auto-crud/crud-form-modal';

// 数据表格组件
export { DataTable } from './components/data-table/data-table';
export { DataTableAdvancedToolbar } from './components/data-table/data-table-advanced-toolbar';
export { DataTableColumnHeader } from './components/data-table/data-table-column-header';
export { DataTableFacetedFilter } from './components/data-table/data-table-faceted-filter';
export { DataTablePagination } from './components/data-table/data-table-pagination';
export { DataTableToolbar } from './components/data-table/data-table-toolbar';
export { DataTableViewOptions } from './components/data-table/data-table-view-options';

// Hooks
export { useAutoCrudResource, noopToastAdapter } from './hooks/use-auto-crud-resource';
export type {
  ToastAdapter,
  CrudHooks,
  UseAutoCrudResourceOptions,
} from './hooks/use-auto-crud-resource';
export { useDataTable } from './hooks/use-data-table';
export { useReadableFilters } from './hooks/use-readable-filters';
export { useUrlState, useQueryState, useQueryStates } from './hooks/use-url-state';

// Schema Bridge - 核心工具
export {
  parseZodField,
  createTableSchema,
  createSelectColumn,
  createActionsColumn,
} from './lib/schema-bridge/zod-to-columns';
export {
  createFormSchema,
  createEditFormSchema,
} from './lib/schema-bridge/zod-to-formily';
export { SchemaAdapter } from './lib/schema-bridge/schema-adapter';
export type {
  ColumnOverrides,
  FormSchemaOverrides,
  CreateTableSchemaOptions,
  CreateFormSchemaOptions,
  UnifiedSchema,
  JSONSchema,
  SimpleFieldsConfig,
  UnifiedField,
} from './lib/schema-bridge';

// 数据源
export { createTRPCDataSource, createMemoryDataSource } from './lib/data-source';
export type { DataSource, ListParams, ListResult } from './lib/data-source';

// 工具函数
export { cn } from './lib/utils';
export { formatDate, setDateFormatter } from './lib/format';
export type { DateFormatter } from './lib/format';
export { humanize } from './lib/humanize';

🤝 贡献

欢迎贡献代码!请查看 CONTRIBUTING.md


📄 许可证

MIT © wordrhyme


🔗 相关链接


💡 灵感来源