XinTable 表格组件

XinTable 是一款开箱即用的 CRUD 表格组件,通过 JSON 配置即可快速生成带搜索、新增、编辑、删除、分页、排序、筛选等完整功能的表格页面。

核心能力

  • 自动 CRUD — 只需配置 apiaccessName,自动对接后端增删改查接口
  • JSON 驱动列配置 — 列定义同时控制表格展示、搜索表单、编辑表单
  • 搜索表单 — 基于列配置自动生成,支持展开/折叠
  • 关键字搜索 — 内置快速搜索框,支持远程模糊搜索
  • 操作栏 — 内置新增/编辑/删除按钮,带权限控制
  • 表格工具 — 刷新、密度切换、边框切换、列显示设置
  • 表单复用 — 新增/编辑共用同一个弹窗表单,通过 FormMode 区分
  • 完全可定制 — 所有内置行为均可通过回调替换

基础用法

import XinTable from '@/components/XinTable';
import type { XinTableColumn } from '@/components/XinTable/typings';
import type { XinTableInstance } from '@/components/XinTable/typings';

const columns: XinTableColumn<User>[] = [
  { dataIndex: 'id', title: 'ID', valueType: 'digit', hideInForm: true },
  { dataIndex: 'username', title: '用户名', valueType: 'text' },
  { dataIndex: 'email', title: '邮箱', valueType: 'text' },
  { dataIndex: 'status', title: '状态', valueType: 'select', fieldProps: {
    options: [{ label: '启用', value: 1 }, { label: '禁用', value: 0 }]
  }},
  { dataIndex: 'created_at', title: '创建时间', valueType: 'date', hideInForm: true },
];

<XinTable<User>
  api="/admin/user"
  accessName="user"
  rowKey="id"
  columns={columns}
/>

仅此配置即可生成完整的用户管理页面。

列配置(XinTableColumn)

XinTableColumn 同时继承了 FormColumnTableColumnType,一份配置同时控制表格列搜索表单编辑表单的展示。

扩展属性

属性类型默认值说明
dataIndexstring-字段名
titlestring-列标题 / 字段标签
valueTypeFieldValue-字段类型,决定渲染组件(同 XinForm)
fieldPropsobject-传入字段组件的属性
hideInSearchbooleanfalse在搜索表单中隐藏
hideInFormbooleanfalse在新增/编辑表单中隐藏
hideInTablebooleanfalse在表格中隐藏
hideInCreatebooleanfalse仅在新增表单中隐藏
hideInUpdatebooleanfalse仅在编辑表单中隐藏
searchFormColumn-在搜索表单中的独立配置(覆盖默认)

列控制示例

const columns: XinTableColumn[] = [
  {
    dataIndex: 'id',
    title: 'ID',
    valueType: 'digit',
    hideInForm: true,    // 新增/编辑均隐藏
    hideInSearch: true,  // 搜索表单中隐藏
  },
  {
    dataIndex: 'password',
    title: '密码',
    valueType: 'password',
    hideInTable: true,   // 表格中隐藏
    hideInSearch: true,
    hideInUpdate: true,  // 仅编辑时隐藏(新增时显示)
  },
];

显示控制

开关属性

属性类型默认值说明
addShowbooleantrue是否显示新增按钮
editShowboolean | ((record: T) => boolean)true是否显示编辑按钮,可传入函数按行判断
deleteShowboolean | ((record: T) => boolean)true是否显示删除按钮,可传入函数按行判断
searchShowbooleantrue是否显示高级搜索栏
operateShowbooleantrue是否显示操作列
paginationShowbooleantrue是否显示分页
keywordSearchShowbooleantrue是否显示快速搜索框

按行控制编辑/删除

<XinTable
  columns={columns}
  editShow={(record) => record.status === 'active'}   // 仅启用状态可编辑
  deleteShow={(record) => record.role !== 'admin'}    // 管理员不可删除
/>

搜索功能

高级搜索(SearchForm)

点击操作栏的"搜索"按钮可展开/折叠高级搜索表单。搜索列由 columnshideInSearch !== true 的字段自动生成。

<XinTable
  columns={columns}
  searchProps={{
    grid: true,
    rowProps: { gutter: [16, 16] },
    submitter: {
      submitText: '查询',
      resetText: '清空',
    },
  }}
/>

关键字搜索

内置快速搜索输入框,输入后回车或点击搜索图标触发远程搜索。请求参数中会携带 keywordSearch 字段。

自定义搜索表单中的字段

当需要搜索表单中字段使用不同于表格/表单的配置时,可通过 search 属性覆盖:

{
  dataIndex: 'status',
  title: '状态',
  valueType: 'select',
  fieldProps: { options: [{ label: '启用', value: 1 }, { label: '禁用', value: 0 }] },
  // 搜索表单中允许多选和清空
  search: {
    valueType: 'select',
    fieldProps: {
      mode: 'multiple',
      allowClear: true,
      options: [{ label: '启用', value: 1 }, { label: '禁用', value: 0 }],
    },
  },
}

操作栏自定义

顶部操作栏(actionBarRender)

可自定义新增按钮、搜索按钮、关键字搜索框的渲染:

<XinTable
  columns={columns}
  actionBarRender={(buttons) => [
    buttons.add,
    buttons.keywordSearch,  // 仅显示新增和搜索
  ]}
/>

工具栏(toolBarRender)

可自定义刷新、密度、边框、列设置等工具按钮:

<XinTable
  columns={columns}
  toolBarRender={(buttons) => [
    buttons.reload,
    buttons.columnSetting,   // 仅显示刷新和列设置
  ]}
/>

操作列(operateRender)

可自定义表格每一行的操作按钮:

<XinTable
  columns={columns}
  operateRender={(record, defaultButtons) => [
    defaultButtons.edit,
    <Button key="view" onClick={() => viewDetail(record)}>查看</Button>,
    defaultButtons.del,
  ]}
/>

操作列可通过 operateProps 设置列属性:

<XinTable
  columns={columns}
  operateProps={{
    width: 200,
    fixed: 'right',
  }}
/>

自定义请求

自定义数据请求

<XinTable
  columns={columns}
  handleRequest={async (params) => {
    const res = await customApi.getList(params);
    return { data: res.list, total: res.total };
  }}
/>

请求参数预处理

<XinTable
  columns={columns}
  requestParams={(params) => ({
    ...params,
    includeDeleted: false,
    extraFilter: { org_id: currentOrg.id },
  })}
/>

自定义表单提交

<XinTable
  columns={columns}
  handleFinish={async (values, mode, formRef, defaultValue) => {
    if (mode === 'create') {
      await customCreate(values);
    } else {
      await customUpdate(defaultValue?.id, values);
    }
    return true; // 返回 true 关闭弹窗并刷新表格
  }}
/>

XinTableInstance 实例方法

通过 tableRef 获取表格实例:

import { useRef } from 'react';
import type { XinTableInstance } from '@/components/XinTable/typings';

const tableRef = useRef<XinTableInstance>(null);

// 外部刷新
<Button onClick={() => tableRef.current?.reload()}>刷新</Button>
<Button onClick={() => tableRef.current?.reset()}>重置</Button>

<XinTable tableRef={tableRef} ... />

实例方法列表

方法类型说明
reload() => Promise<void>刷新表格(保持当前分页和搜索条件)
reset() => Promise<void>重置搜索条件并回到第一页
getDataSource() => T[]获取当前表格数据
setDataSourceDispatch<SetStateAction<T[]>>设置表格数据
getTotal() => number获取数据总数
getLoading() => boolean获取加载状态
setLoadingDispatch<SetStateAction<boolean>>设置加载状态
setPageInfo(page?: number, pageSize?: number) => void设置分页参数
getForm() => XinFormRef<T> | null获取新增/编辑表单实例
getSearchForm() => FormInstance<T>获取搜索表单实例

表格工具

内置了刷新、密度切换、边框切换、列显示设置四个工具按钮。

密度切换

模式说明
large默认大小
middle中等密度
small紧凑模式

列显示设置

通过勾选 Tree 控件控制哪些列在表格中显示,设置实时生效。

权限控制

accessName 不仅用于接口路径的拼接,还用于按钮权限控制:

  • {accessName}.create — 新增按钮
  • {accessName}.update — 编辑按钮
  • {accessName}.delete — 删除按钮

使用 AuthButton 组件包裹,无权限时按钮不会渲染。

XinTableProps 完整属性

属性类型说明
apistring必填,CRUD 接口地址
accessNamestring必填,权限标识前缀
rowKeystring必填,主键字段名
columnsXinTableColumn<T>[]必填,列配置
tableRefRefObject<XinTableInstance<T>>表格实例引用
addShowboolean显示新增按钮(默认 true
editShowboolean | ((record: T) => boolean)显示编辑按钮(默认 true
deleteShowboolean | ((record: T) => boolean)显示删除按钮(默认 true
searchShowboolean显示高级搜索栏(默认 true
operateShowboolean显示操作列(默认 true
paginationShowboolean显示分页(默认 true
keywordSearchShowboolean显示快速搜索框(默认 true
formPropsXinFormProps | false新增/编辑表单属性,传 false 禁用
modalPropsXinFormProps['modalProps']弹窗属性
searchPropsSearchFormProps | false搜索栏属性,传 false 禁用
operatePropsTableColumnType<T>操作列属性
cardPropsCardProps外层卡片属性
paginationPaginationProps分页配置(当前页、总数、onChange 除外)
actionBarRender(dom: ActionNode) => ReactNode[]自定义顶部操作栏
toolBarRender(dom: ToolBarNode) => ReactNode[]自定义工具栏
operateRender(record: T, dom: OperateNode) => ReactNode[]自定义操作列
handleRequest(params: RequestParams) => Promise<{ data: T[]; total: number }>自定义数据请求
requestParams(params: RequestParams) => RequestParams请求参数预处理
handleFinish(values, mode, formRef, defaultValue?) => Promise<boolean>自定义表单提交

同时继承 TableProps<T>columnsrowKeyonChangepagination 外的全部属性。

请求参数说明

interface RequestParams {
  page?: number;          // 当前页,默认 1
  pageSize?: number;      // 每页条数,默认 10
  keywordSearch?: string; // 关键字搜索值
  filterValues?: Record<string, any>;  // 列筛选值
  sorterValue?: { field: string; order: 'asc' | 'desc' };  // 排序参数
}

API 接口约定

组件内置的增删改查方法遵循以下接口约定:

操作方法路径请求体
列表GET{api}Query 参数
新增POST{api}Body JSON
编辑PUT{api}/{id}Body JSON
删除DELETE{api}/{id}-

如需使用其他接口格式,可通过 handleRequesthandleFinish 自定义。

完整示例

import { useRef } from 'react';
import XinTable from '@/components/XinTable';
import type { XinTableColumn, XinTableInstance } from '@/components/XinTable/typings';
import { Button, message } from 'antd';

interface User {
  id: number;
  username: string;
  email: string;
  role: string;
  status: number;
  created_at: string;
}

const userColumns: XinTableColumn<User>[] = [
  {
    dataIndex: 'id',
    title: 'ID',
    valueType: 'digit',
    hideInForm: true,
    hideInSearch: true,
    width: 80,
  },
  {
    dataIndex: 'username',
    title: '用户名',
    valueType: 'text',
    colProps: { span: 12 },
    fieldProps: { maxLength: 50 },
    rules: [{ required: true, message: '请输入用户名' }],
  },
  {
    dataIndex: 'email',
    title: '邮箱',
    valueType: 'text',
    colProps: { span: 12 },
    rules: [
      { required: true, message: '请输入邮箱' },
      { type: 'email', message: '邮箱格式不正确' },
    ],
  },
  {
    dataIndex: 'role',
    title: '角色',
    valueType: 'select',
    colProps: { span: 12 },
    fieldProps: {
      options: [
        { label: '管理员', value: 'admin' },
        { label: '普通用户', value: 'user' },
      ],
    },
    // 搜索时允许多选
    search: {
      valueType: 'select',
      fieldProps: {
        mode: 'multiple',
        allowClear: true,
        options: [
          { label: '管理员', value: 'admin' },
          { label: '普通用户', value: 'user' },
        ],
      },
    },
  },
  {
    dataIndex: 'status',
    title: '状态',
    valueType: 'radioButton',
    colProps: { span: 12 },
    fieldProps: {
      options: [
        { label: '启用', value: 1 },
        { label: '禁用', value: 0 },
      ],
    },
  },
  {
    dataIndex: 'created_at',
    title: '创建时间',
    valueType: 'date',
    hideInForm: true,
  },
];

export default function UserTable() {
  const tableRef = useRef<XinTableInstance<User>>(null);

  return (
    <XinTable<User>
      tableRef={tableRef}
      api="/admin/user"
      accessName="user"
      rowKey="id"
      columns={userColumns}
      // 搜索配置
      searchProps={{
        submitter: {
          submitText: '查询',
          resetText: '清空',
        },
      }}
      // 表单配置
      modalProps={{ width: 600 }}
      // 分页配置
      pagination={{
        pageSize: 15,
        pageSizeOptions: ['10', '15', '20', '50'],
      }}
      // 卡片配置
      cardProps={{ variant: 'borderless' }}
      // 按行控制显示
      editShow={(record) => record.status === 1}
      // 自定义操作列
      operateRender={(record, buttons) => [
        <Button
          key="resetPwd"
          size="small"
          onClick={() => message.info(`重置密码: ${record.username}`)}
        >
          重置密码
        </Button>,
        buttons.edit,
        buttons.del,
      ]}
    />
  );
}