npm.io
1.4.3 • Published 2h ago

@wordrhyme/auto-crud

Licence
MIT
Version
1.4.3
Deps
31
Size
762 kB
Vulns
0
Weekly
0
Stars
2

@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} />;
}

工具函数

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 } from './lib/format';
export { humanize } from './lib/humanize';

贡献

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


许可证

MIT wordrhyme


相关链接


灵感来源