@chengkejie/form-page
v1.2.0
Published
A configurable Form + Table + Pagination component for React with Fusion Design
Maintainers
Readme
@chengkejie/form-page
可配置的 React 搜索表单 + 表格 + 分页 一体化组件,基于 Fusion Design (@alifd/next) 和 ahooks。
本文档为完整 API 参考,供开发者和 AI 编码助手在调用本组件时查阅。所有 prop 名、类型、默认值均与源码
src/types.ts/src/FormPage.tsx一致。
目录
- 安装
- 快速开始
- FormPage Props
- tableProps 表格配置
- columns 列配置
- conditionProps 搜索条件配置
- buttonProps 按钮配置
- Ref 方法(FormPageRef)
- FormPageProvider 外部服务注入
- 子组件(独立导出)
- 完整示例
- 内部行为说明
- 开发 / 发布
安装
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返回值需符合ahooksuseFusionTable约定:{ 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)开启自动对齐。规则:
- 组件模拟 24 栅格逐项摆放(放不下整项换行),得到每个条件项的列起点偏移(0/6/12/18…);
- 列起点相同的项归为同一竖列(注意不是按
col值分组——col: 12的项从行首开始,起点为 0,与col: 6的行首项同组); - 组内取「未单独配
labelWidth的字符串标签」的最长字数(stringWidth:全角=1、半角两字=1,+0.5缓冲,向上取整)作为该竖列统一宽度; - 标签在统一宽度内右对齐——短标签左侧留白、右缘与组内最长标签对齐,使同一竖列的输入框起点连成一条竖线。
// 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)绑定到 FusionField,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:组件挂载后会把当前 FusionField实例挂到window.field(FormPage.tsx:191),方便在浏览器控制台读取/调试表单值。生产环境也会挂载,介意可忽略该全局变量。 - 表格形态:使用
Table.StickyLock(锁列吸附)+fixedHeader;maxBodyHeight按表格容器实测高度自动计算(减去表头高度,默认 44px)。非用户可配。 - 排序:单列排序。内部维护
sortstate,列上加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)
- npm 账号
chengkejie(包名 scope 即@chengkejie)。 - 本机认证:
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
