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

@chengkejie/form-page

v1.2.0

Published

A configurable Form + Table + Pagination component for React with Fusion Design

Readme

@chengkejie/form-page

可配置的 React 搜索表单 + 表格 + 分页 一体化组件,基于 Fusion Design (@alifd/next) 和 ahooks。

本文档为完整 API 参考,供开发者和 AI 编码助手在调用本组件时查阅。所有 prop 名、类型、默认值均与源码 src/types.ts / src/FormPage.tsx 一致。


目录


安装

npm i @chengkejie/form-page

并在入口处引入样式:

import '@chengkejie/form-page/dist/esm/styles/index.module.scss';

快速开始

import { FormPage, FormPageProvider, FormPageRef } from '@chengkejie/form-page';
import '@chengkejie/form-page/dist/esm/styles/index.module.scss';

// 1) 查询接口:接收 (表单数据, 分页参数, 是否按钮触发),返回 { data, total }
async function fetchList(data: any, params: any) {
  const res = await request('/api/list', { data: { ...data, ...params } });
  return { data: res.list, total: res.total };
}

function App() {
  const ref = useRef<FormPageRef>(null);

  return (
    // 2) 用 Provider 注入列设置持久化 / 公司选择等外部服务(可选)
    <FormPageProvider
      config={{
        fetchTableKeys: async (tableId) => [],
        onSaveTableKeys: async (params) => {},
      }}
    >
      {/* 3) 配置条件、列、按钮 */}
      <FormPage
        ref={ref}
        searchFn={fetchList}
        conditionProps={[
          { type: 'input', name: 'keyword', label: '关键词', col: 6 },
          { type: 'select', name: 'status', label: '状态', col: 6, options: [{ label: '启用', value: 1 }] },
        ]}
        tableProps={{
          tableId: 'demo-table',
          primaryKey: 'id',
          columns: [
            { title: '名称', dataIndex: 'name', width: 200, ellipsis: true },
            { title: '状态', dataIndex: 'status', width: 120, ellipsis: true, lock: 'left' },
          ],
        }}
        buttonProps={{ buttons: ['submit', 'reset', 'export', 'setting'] }}
      />
    </FormPageProvider>
  );
}

FormPage Props

| Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | searchFn | (data, params, fromButton) => Promise<{ data: any[]; total: number }> | () => {} | 查询接口。data=表单字段值,params=分页参数,fromButton=是否由按钮触发(区分搜索/重置与翻页静默刷新) | | exportFn | (...args) => any | () => {} | 导出接口,复用同一表单字段,点击「导出」按钮时触发 | | rowSelectionFn | ((keys, records) => void) \| null | null | 行选择回调;传 null 不启用行选择 | | tableProps | TableConfigProps | {} | 表格配置(含 tableId / columns / primaryKey 等) | | conditionProps | ConditionItem[] | [] | 搜索条件配置数组,每项的 type 决定渲染哪种控件 | | buttonProps | ButtonConfigProps | {} | 操作按钮区配置 | | extra | ReactNode | — | 渲染在搜索条件下方、表格上方的额外自定义区域 | | showPagination | boolean | true | 是否显示分页器 | | autoSearch | boolean | true | 挂载后是否自动发起首次查询 | | onReset | () => void | () => {} | 重置按钮触发的回调(在内部清空逻辑之外额外执行) | | onFieldChange | (name: string, value: any) => void | () => {} | 任意表单字段值变化回调 | | pageSizeList | number[] | [10, 20, 50, 100] | 分页条数可选项 | | alignLabelWidth | boolean | true | 标签宽度自动对齐:同一竖列(24 栅格列起点相同)的标签统一取组内最长字数宽度并右对齐。详见 标签宽度对齐。传 false 退回默认 75px 最小宽度 |

searchFn 返回值需符合 ahooks useFusionTable 约定:{ data: 表格数据数组, total: 总条数 }。


tableProps 表格配置

tableProps 除了 columns 外,其余字段会透传给 Fusion 的 Table.StickyLock。

| 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | tableId | string | '' | 表格唯一标识,用作列设置(显示/隐藏、列宽)的持久化 key | | columns | any[] | — | 列配置,见 columns 列配置 | | primaryKey | string | 'key' | 行数据的唯一字段名(行选择 / key 复用) | | isTree | boolean | false | 是否树形表格 | | isZebra | boolean | false | 是否斑马纹 | | hasBorder | boolean | true | 是否显示边框 | | defaultPageSize | number | 10 | 默认每页条数 | | [key: string] | any | — | 其它 Fusion Table props 透传(如 sort、fixedHeader 等) |


columns 列配置

每列 = Fusion Table.Column 的全部 props + 库扩展属性。库会自动给每列加上 asyncResizable: true(异步可拖拽调宽),无需手动设置。

Fusion 常用 props

| 字段 | 类型 | 说明 | |------|------|------| | dataIndex | string | 对应的数据字段名(支持 a.b 取值) | | title | ReactNode | 表头文本 | | width | number \| string | 列宽。锁列(lock)时必须为数值 | | lock | 'left' \| 'right' \| true | 锁列 | | cell | ReactNode \| ((value, index, record) => ReactNode) | 自定义单元格渲染 | | sortable | boolean | 是否可排序 | | align | 'left' \| 'center' \| 'right' | 单元格对齐 |

库扩展属性 ⭐

| 字段 | 类型 | 说明 | |------|------|------| | ellipsis | true | 单元格省略号 + hover 气泡。库自动把单元格内容包进 Balloon,触发器宽度取自本列 width(width - 32,32 为左右内边距)。锁列也不会断层。若同时配置了 cell,则包裹 cell 的输出。 | | balloon | ReactNode | 表头标题旁追加的提示节点(如一个带说明的问号气泡) | | children | column[] | 多级表头(渲染为 Table.ColumnGroup),递归支持 |

列配置示例

columns={[
  // 1) 基础列 + 省略号气泡(自动用 width=200)
  { title: '名称', dataIndex: 'name', width: 200, ellipsis: true },

  // 2) 锁列 + 省略号气泡(width 必填,库内部已用 width 处理,不会断层)
  { title: '状态', dataIndex: 'status', width: 120, ellipsis: true, lock: 'left' },

  // 3) 省略号气泡 + 自定义 cell:气泡内展示 cell 的返回值
  {
    title: '金额', dataIndex: 'amount', width: 140, ellipsis: true,
    cell: (v) => `¥${v?.toLocaleString()}`,
  },

  // 4) 自定义 cell(不使用省略号气泡)
  { title: '操作', dataIndex: 'op', cell: (v, i, record) => <a onClick={() => edit(record)}>编辑</a> },

  // 5) 表头带提示气泡
  { title: '转化率', dataIndex: 'rate', balloon: <Balloon trigger={<QuestionMark />}>转化率 = 成功数 / 总数</Balloon> },

  // 6) 多级表头
  {
    title: '地址', children: [
      { title: '省', dataIndex: 'province', width: 100 },
      { title: '市', dataIndex: 'city', width: 100 },
    ],
  },
]}

注:ellipsis 旧用法 cell: renderText(width) 已由库内置;现在只需声明 ellipsis: true。


conditionProps 搜索条件配置

每个条件项由公共布局属性 + 各类型特有字段组成。条件项按 24 栅格在一行内自动换行。

公共属性(BaseConditionItem)

| 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | col | number | 是 | 占据的栅格列数(24 栅格制,如 6、8、12) | | label | string | 否 | 表单项标签文本 | | labelWidth | number | 否 | 标签宽度,按「字数」计:传几渲染成几 em(1em ≈ 1 个全角字宽)。仅 label 为字符串时生效,可突破默认 75px 最小宽度;配置后该项不参与自动对齐分组 | | textAlign | 'left' \| 'center' \| 'right' | 否 | 对齐方式,不传则按行内位置自动计算(行首 left / 中间 center / 行尾 right) | | wrapperCol | object | 否 | Form.Item 的 wrapperCol | | labelCol | object | 否 | Form.Item 的 labelCol |

标签宽度对齐(alignLabelWidth)

<FormPage alignLabelWidth>(默认 true)开启自动对齐。规则:

  1. 组件模拟 24 栅格逐项摆放(放不下整项换行),得到每个条件项的列起点偏移(0/6/12/18…);
  2. 列起点相同的项归为同一竖列(注意不是按 col 值分组——col: 12 的项从行首开始,起点为 0,与 col: 6 的行首项同组);
  3. 组内取「未单独配 labelWidth 的字符串标签」的最长字数(stringWidth:全角=1、半角两字=1,+0.5 缓冲,向上取整)作为该竖列统一宽度;
  4. 标签在统一宽度内右对齐——短标签左侧留白、右缘与组内最长标签对齐,使同一竖列的输入框起点连成一条竖线。
// col 6 ×4 + col 12 + col 6 + col 12 + col 6 的竖列分组:
// 起点 0  → 名称、日期、创建时间(col12)、增强日期(col12)   同组右缘对齐
// 起点 6  → 状态、月份
// 起点 12 → 类型(树)、公司、金额区间、自定义
// 起点 18 → 地区(级联)、远程搜索
<FormPage alignLabelWidth {...} />   // 传 false 退回默认 75px 最小宽度

单独配置 labelWidth 的项优先用自己的宽度,且不计入组内最长值统计。 字数估算函数 stringWidth(s) 已从包内导出,可用于业务侧自行推导展示。

各类型(type)

| type | 渲染控件 | 特有字段 | customProps 类型 | |--------|----------|----------|---------------------| | input | Input | name, options? | InputProps | | select | Select | name, options? | SelectProps | | treeSelect | TreeSelect | name, options? | TreeSelectProps | | cascaderSelect | CascaderSelect | name, options? | CascaderSelectProps | | datePicker | DatePicker | name, options? | DatePickerProps | | monthPicker | DatePicker.MonthPicker | name, options? | MonthPickerProps | | rangePicker | 两个 DatePicker | name: [start, end], options: [.,.], customProps?: [.,.] | DatePickerProps[] | | section | 两个 Input(区间) | name: [a, b], options: [.,.], unit?: string, customProps?: [.,.] | InputProps[] | | companySelect | Select(公司选择) | name, options?, hasDefault? | SelectProps | | extraDatePicker | DatePicker/MonthPicker/TimePicker + 快捷按钮 | name: string[], options: [], extra: {...}, customProps?: [] | DatePickerProps[] | | selectSearch | Select(远程搜索) | name, options?, request: (keyword) => Promise<any[]> | SelectProps | | customNode | 完全自定义 | node: (field) => ReactNode | — | | custom(兜底) | 任意 | type: string, custom: ReactNode | — |

name / options / customProps 通过 field.init(name, options, customProps) 绑定到 Fusion Field,options 即 Field 的初始化选项(如 { rules, initValue }),customProps 直接透传给底层控件。

类型常量

可用 FormPageData.Type 引用类型字符串常量(注意 cascaderSelect 为小写):

FormPageData.Type.Select         // 'select'
FormPageData.Type.Input          // 'input'
FormPageData.Type.TreeSelect     // 'treeSelect'
FormPageData.Type.cascaderSelect // 'cascaderSelect'
FormPageData.Type.DatePicker     // 'datePicker'
FormPageData.Type.MonthPicker    // 'monthPicker'
FormPageData.Type.RangePicker    // 'rangePicker'
FormPageData.Type.Section        // 'section'
FormPageData.Type.CompanySelect  // 'companySelect'
FormPageData.Type.ExtraDatePicker// 'extraDatePicker'
FormPageData.Type.SelectSearch   // 'selectSearch'
FormPageData.Type.CustomNode     // 'customNode'

extraDatePicker 的 extra 结构

extra: {
  name: string;            // 存储当前选中按钮索引的字段名
  defaultBtn: number;      // 默认选中的按钮索引
  buttons: Array<{
    name: string;          // 按钮显示文本
    date: any[];           // 该按钮对应的日期值,与外层 name 一一对应
    datePickerType?: 'datePicker' | 'monthPicker' | 'timePicker';
    props?: DatePickerProps | MonthPickerProps | TimePickerProps;
  }>;
}

companySelect / selectSearch 依赖说明

  • companySelect 依赖 FormPageProvider 注入的 onSearchCompany / getCompanyInfo;hasDefault: true 且当前用户非管理员时锁定为当前公司。
  • selectSearch 通过自身的 request(keyword) 获取选项,自带防抖,无需 Provider。

条件配置示例

conditionProps={[
  { type: 'input',  name: 'keyword', label: '关键词', col: 6 },
  { type: 'select', name: 'status',  label: '状态', col: 6,
    options: { dataSource: [{ label: '启用', value: 1 }, { label: '停用', value: 0 }] } },

  // 区间:两个 Input,字段名 minAge / maxAge
  { type: 'section', name: ['minAge', 'maxAge'], label: '年龄', col: 8, unit: '岁',
    options: [{}, {}] },

  // 远程搜索
  { type: 'selectSearch', name: 'userId', label: '用户', col: 6,
    request: async (kw) => {
      const list = await searchUser(kw);
      return list.map(u => ({ label: u.name, value: u.id }));
    } },

  // 完全自定义
  { type: 'customNode', label: '高级', col: 12,
    node: (field) => <MyComplexControl value={field.getValue('x')} onChange={(v) => field.setValue('x', v)} /> },
]}

buttonProps 按钮配置

interface ButtonConfigProps {
  /** @deprecated 不再生效;按钮列自动占据本行剩余宽度以紧贴最右侧 */
  col?: number;
  buttons: Array<string | ReactNode | { name: string; style?: 'primary' | 'ghost' }>;
  /** 按钮整体风格,默认 'primary';可被单按钮配置覆盖 */
  buttonStyle?: 'primary' | 'ghost';
  /** 按钮级覆盖:key 为内置按钮名,优先级最高 */
  buttonStyles?: Partial<Record<'submit' | 'reset' | 'export' | 'setting', { style?: 'primary' | 'ghost' }>>;
}

buttons 中的字符串映射到内置按钮,未匹配的项直接作为 ReactNode 渲染(可放自定义 JSX):

| 字符串 | 按钮 | 行为 | |--------|------|------| | 'submit' | 搜索 | 标记为按钮触发并提交查询 | | 'reset' | 重置 | 清空表单 / 排序 / 选中行,触发 onReset | | 'export' | 导出 | 调用 exportFn | | 'setting' | 设置 | 打开列显示/隐藏设置弹窗(依赖 tableId) |

按钮风格(三级粒度)

风格优先级:buttonStyles[name] > buttons 内联对象 > buttonStyle > 默认 primary

| 风格 | 视觉 | 说明 | |------|------|------| | 'primary'(默认) | 搜索/导出 = 主题色实心主按钮;重置 = 白底主题橙描边幽灵按钮 | 两个主操作实心 + 重置描边 | | 'ghost' | 全部 = 白底 + 主题橙(#ff7e07)描边幽灵按钮 | 权重一致,见下图风格 |

默认 'primary' 下,「重置」按钮即白底主题橙描边样式(同 ghost 的 ghostBtn 视觉),因此示例 ② 的 { name: 'reset', style: 'ghost' } 在默认风格下为冗余写法,主要用于「整体 ghost」时强调仍按需覆盖。

// ① 整体风格:全部幽灵描边
buttonProps={{ buttons: ['submit', 'reset', 'export'], buttonStyle: 'ghost' }}

// ② 内联覆盖:整体默认,仅重置单独指定为幽灵
buttonProps={{ buttons: ['submit', { name: 'reset', style: 'ghost' }, 'export'] }}

// ③ 按钮级覆盖:整体幽灵,但搜索保持实心主按钮
buttonProps={{
  buttons: ['submit', 'reset', 'export'],
  buttonStyle: 'ghost',
  buttonStyles: { submit: { style: 'primary' } },
}}

// ④ 混排自定义 JSX(自定义按钮不受风格控制)
buttonProps={{
  buttons: ['submit', 'reset', <Button key="custom" onClick={...}>自定义</Button>, 'setting'],
}}

内联对象 { name, style } 的 name 必须是内置按钮名('submit' | 'reset' | 'export' | 'setting'),否则按未知按钮渲染兜底。


Ref 方法(FormPageRef)

通过 ref 获取命令式句柄:

const ref = useRef<FormPageRef>(null);
<FormPage ref={ref} ... />

| 方法 | 签名 | 说明 | |------|------|------| | submit | () => void | 手动提交查询(不标记为按钮触发) | | reset | () => void | 重置表单、排序、选中行 | | refresh | () => void | 静默刷新当前页(如外部数据变更后调用) | | resetWidth | () => void | 重置所有列宽为初始值 | | selectedRowKeysChange | (keys: any[], records?: any[]) => void | 程序化修改选中行(需开启行选择) | | field | Fusion Field 实例 | 直接读写表单值:field.getValue(name) / field.setValue(name, v) / field.validate(...) 等 |

ref.current?.submit();
ref.current?.reset();
ref.current?.refresh();
ref.current?.resetWidth();
ref.current?.field.getValue('keyword');
ref.current?.selectedRowKeysChange([1, 2]);

FormPageProvider 外部服务注入

可选:用 <FormPageProvider config={...}> 包裹,注入列设置持久化与公司选择等外部服务。

import { FormPageProvider, FormPageConfig } from '@chengkejie/form-page';

const config: FormPageConfig = {
  // 列设置(显示/隐藏)持久化
  fetchTableKeys:  async (tableId) => [...],                       // 读取已隐藏的列
  onSaveTableKeys: async ({ tableId, value, name, tableName }) => {...}, // 保存隐藏列
  getTableName:    () => '当前表格名',                              // 设置弹窗用

  // 公司选择
  onSearchCompany: async (keyword) => [{ name, id }],             // 按关键字搜公司
  getCompanyInfo:  () => ({ id, name, isManage }),                // 当前公司/是否管理员
};

| 字段 | 签名 | 用于 | |------|------|------| | fetchTableKeys | (tableId) => Promise<Array<{ tableId, value }>> | 列显示/隐藏持久化读取 | | onSaveTableKeys | (params: { tableId, value, name, tableName? }) => Promise<any> | 列显示/隐藏持久化保存 | | getTableName | () => string | SettingDialog 显示表格名 | | onSearchCompany | (keyword) => Promise<Array<{ name, id }>> | companySelect 远程搜索 | | getCompanyInfo | () => { id, name, isManage } | companySelect 默认公司 / 锁定判断 |

不使用「设置」弹窗或「公司选择」时,可省略对应字段,甚至完全不用 Provider。


子组件(独立导出)

以下组件均从包入口导出(见 src/index.tsx),可在 condition 体系之外单独使用。

SettingDialog — 列定制弹窗

interface SettingDialogProps {
  onChange: (id: string) => void;   // 保存成功后回调,入参为 tableId
  id: string;                       // 表格 ID(tableId)
  dataSource: any[];                // 列配置数组;弹窗用 title→label、dataIndex→value 生成勾选项
  container?: HTMLElement | null;   // 弹窗挂载容器
  hiddenKeys: string[];             // 当前已隐藏列的 dataIndex 列表
  onSave: (params: { value; tableId; name; tableName? }) => Promise<any>; // 保存回调
}
// ref 暴露:{ show: () => void }  // 打开弹窗并初始化勾选
  • 渲染「定制列」Dialog,用 Checkbox.Group 列出所有列(由 dataSource 的 title/dataIndex 派生),勾选 = 显示。
  • 点「确定」时把未勾选的列拼成 value(dataIndex 逗号拼接)和 name(title 逗号拼接),连同 tableId、tableName(取自 Provider 的 getTableName)调 onSave;成功后调 onChange(id)。
import { SettingDialog } from '@chengkejie/form-page';
import { useRef } from 'react';

const ref = useRef<any>(null);
<SettingDialog
  ref={ref}
  id="my-table"
  dataSource={columns}            // 传完整列配置
  hiddenKeys={hiddenDataIndexes}  // 已隐藏列的 dataIndex
  container={containerEl}
  onSave={async (params) => { await api.saveTableKeys(params); }}
  onChange={(id) => reloadHidden(id)}
/>;
ref.current?.show();              // 打开弹窗

条件渲染组件(CompanySelect / ExtraDatePicker / SelectSearch / Section / CustomNode)

这些是 conditionProps 各类型对应的渲染组件,签名为 { field, data },其中 data 就是对应类型的条件条目。一般通过 condition 配置使用;单独使用时需自行传入 Fusion field 实例与条件条目:

| 组件 | data 类型 | 说明 | |------|------------|------| | CompanySelect | CompanySelectCondition | 公司搜索 Select,依赖 Provider 的 onSearchCompany/getCompanyInfo | | ExtraDatePicker | ExtraDatePickerCondition | 带快捷按钮的 日期/月/时间 选择器 | | SelectSearch | SelectSearchCondition | 远程搜索 Select,依赖 data.request(keyword) | | Section | SectionCondition | 双 Input 区间,支持 data.unit | | CustomNode | CustomNodeCondition | 渲染 data.node(field) |

import { Section } from '@chengkejie/form-page';
import { Field } from '@alifd/next';

const field = Field.useField();
<Section
  field={field}
  data={{ type: 'section', name: ['min', 'max'], options: [{}, {}], col: 6, unit: '元' }}
/>

完整示例

import { useRef } from 'react';
import { FormPage, FormPageProvider, FormPageRef, FormPageData } from '@chengkejie/form-page';
import '@chengkejie/form-page/dist/esm/styles/index.module.scss';

const fetchList = async (data: any, params: any) => {
  const res = await fetch(`/api/users?q=${data.keyword}&page=${params.current}&size=${params.pageSize}`).then(r => r.json());
  return { data: res.list, total: res.total };
};

export default function Page() {
  const ref = useRef<FormPageRef>(null);

  return (
    <FormPageProvider
      config={{
        fetchTableKeys: async () => [],
        onSaveTableKeys: async () => {},
      }}
    >
      <FormPage
        ref={ref}
        searchFn={fetchList}
        autoSearch
        rowSelectionFn={(keys) => console.log('selected', keys)}
        conditionProps={[
          { type: FormPageData.Type.Input,  name: 'keyword', label: '关键词', col: 6 },
          { type: FormPageData.Type.Select, name: 'status',  label: '状态', col: 6,
            options: { dataSource: [{ label: '启用', value: 1 }, { label: '停用', value: 0 }] } },
        ]}
        tableProps={{
          tableId: 'user-table',
          primaryKey: 'id',
          defaultPageSize: 20,
          columns: [
            { title: 'ID',   dataIndex: 'id',   width: 80,  lock: true },
            { title: '姓名', dataIndex: 'name', width: 160, ellipsis: true },
            { title: '邮箱', dataIndex: 'email', width: 220, ellipsis: true },
            { title: '状态', dataIndex: 'statusText', width: 100, align: 'center' },
            { title: '操作', dataIndex: 'op', cell: (v, i, r) => <a onClick={() => edit(r)}>编辑</a> },
          ],
        }}
        buttonProps={{ buttons: ['submit', 'reset', 'export', 'setting'] }}
        onReset={() => console.log('reset')}
      />
    </FormPageProvider>
  );
}

内部行为说明

以下为组件内部固定、非 prop 可配的行为,调用方知晓即可:

  • 调试钩子 window.field:组件挂载后会把当前 Fusion Field 实例挂到 window.field(FormPage.tsx:191),方便在浏览器控制台读取/调试表单值。生产环境也会挂载,介意可忽略该全局变量。
  • 表格形态:使用 Table.StickyLock(锁列吸附)+ fixedHeader;maxBodyHeight 按表格容器实测高度自动计算(减去表头高度,默认 44px)。非用户可配。
  • 排序:单列排序。内部维护 sort state,列上加 sortable: true 即参与排序;点击其它列会替换(而非叠加)当前排序。
  • 分页器样式:固定 pageSizeSelector="dropdown"、useFloatLayout、显示「共 X 条」。可由 pageSizeList 改选项、showPagination={false} 关闭。
  • 列宽拖拽:所有列自动设为 asyncResizable(可拖拽调宽),拖拽宽度保留在组件 state(300ms 防抖);ref.resetWidth() 重置为初始值。
  • 列显示/隐藏:tableId 对应的列中,dataIndex 命中 tbKeys(隐藏列表)即不渲染;持久化依赖 Provider 的 fetchTableKeys(读取)/onSaveTableKeys(保存)。
  • 按钮列宽度:操作按钮列自动占据本行剩余栅格宽度以紧贴最右侧;剩余空间按「模拟逐项摆放后的末行实际占用」计算(非总和取模,避免 flex 提前换行导致按钮列错位);条件正好填满整行(24 栅格)时按钮独占一行。
  • 条件对齐:条件项 textAlign 未显式设置时,按行内位置自动取 行首 left / 中间 center / 行尾 right。
  • 条件间距:条件行使用 24 栅格 + gutter=16(列内 padding,不影响百分比宽度与换行)。

开发 / 发布

# 安装依赖
npm i

# 开发模式(监听文件变化)
npm run dev

# 构建(输出 dist/esm、dist/cjs)
npm run build

# 代码检查
npm run lint

发布(公共 npm)

  1. npm 账号 chengkejie(包名 scope 即 @chengkejie)。
  2. 本机认证:npm login,或 npm config set //registry.npmjs.org/:_authToken <token>。

    ⚠️ 账号开了 2FA(Windows 通行密钥,不是 TOTP)。命令行发版会要 OTP 而通行密钥给不出 —— 需先把 npmjs.com 的 2FA 调成 "仅授权 / authorization only",或用 Classic Automation token(绕过 2FA)。

# 1. 改 package.json 的 version
# 2. 从 release 分支发布(dist 已构建;release 无 src,需跳过 prepublishOnly=father build)
npm publish --ignore-scripts

新包发布后有几分钟 CDN 读延迟,npm view 可能短暂 404,以 npm publish 输出 + @chengkejie/[email protected] 为准。

依赖

| 依赖 | 版本 | |------|------| | React | >=16.9.0 <19.0.0 | | React DOM | >=16.9.0 <19.0.0 | | @alifd/next | >=1.26.0 | | moment | >=2.24.0 | | ahooks | ^3.0.0 |

License

MIT