npm package: @minnee/pro-arco-table
Description: Enhanced Table component based on Arco Design with extended column types
Author: minnee
Latest version: 1.1.12

# Pro Arco Table

基于 Arco Design Vue 封装的增强型表格组件，提供搜索表单、工具栏、远程/本地数据请求、分页、滚动、可编辑单元格/行、类型安全的渲染与事件等能力。

## 安装与依赖

- 运行环境：Vue 3.5+、TypeScript 5+
- 对等依赖：@arco-design/web-vue、vue（请在宿主项目中安装）

```bash
npm i @minnee/pro-arco-table
# 对等依赖（如未安装）
npm i vue @arco-design/web-vue
```

## 快速使用

- 具名导出（仅支持具名导出）：

```ts
import { ProArcoTable } from '@minnee/pro-arco-table'
```

- 在示例项目/本地开发：

```ts
import { ProArcoTable } from '../../src/index'
```

- 全局注册（可选）：

```ts
import { createApp } from 'vue'
import ArcoVue from '@arco-design/web-vue'
import { install as installProArcoTable } from '@minnee/pro-arco-table'

const app = createApp(App)
app.use(ArcoVue)
installProArcoTable(app)
app.mount('#app')
```

- 样式
  - 从 v1.1.3 起，库构建改为“产出独立 CSS 文件”。宿主项目需要显式引入库样式：

    ```ts
    // main.ts 或组件入口
    import '@minnee/pro-arco-table/dist/style.css'
    // 同时确保引入 Arco 基础样式（示例）：
    import '@arco-design/web-vue/es/index.css'
    ```

  - 这样可避免生产环境下 CSP/时序导致的样式注入失败问题。

## 主要能力（概览）

- 内置搜索表单与工具栏，统一参数管理与触发请求
- 远程/本地模式：支持请求方法驱动的远程数据与本地静态数据
- 分页、滚动与数据总数联动（远程模式自动同步 total）
- 渲染类型与自定义渲染（依赖类型定义，安全约束）
- 行/单元格编辑模式（含保存/取消、校验、只读占位实现）
- 错误/空状态渲染与统一的请求状态回调

更多实现细节可参考：

- 入口与导出：`src/index.ts`
- 主组件：`src/table.vue`
- 类型定义：`src/types/index.ts`、`src/types/editable.ts`
- 常用工具：`src/utils/tools.ts`

## 工程化配置

- 构建：`vite.config.mts`（库模式，ESM/UMD 双产物；UMD 使用具名导出策略；独立 CSS 输出为 `dist/style.css`）
- 代码规范：`eslint.config.js`（Flat Config，TypeScript + Vue 规则）
- 类型：`tsconfig.json`（strict/无多余 emit，配合 vite-plugin-dts 生成类型声明）
- 提交钩子：`.husky/pre-commit`（lint-staged 自动修复）

## 开发与构建

```bash
# 本地开发（示例在 examples/ 下）
npm run dev

# 类型检查
npm run type-check

# 代码规范检查与自动修复
npm run lint

# 单元测试
npm test

# 构建库
npm run build
```

## 使用建议

- 仅具名导出：请使用 `import { ProArcoTable } from '@minnee/pro-arco-table'`
- UMD 使用（浏览器全局）：库名为 `ArcoTableEnhanced`，成员以具名属性暴露（例如 `window.ArcoTableEnhanced.ProArcoTable`）
- 详细的 Props/事件/类型请参考源码中的 TSDoc 注释与类型定义文件

## API 文档

### 表格配置（ProArcoTableProps）

| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|--------|
| data | 本地数据（与 requestMethod 互斥） | T[] | - |
| requestMethod | 远程数据请求方法（与 data 互斥） | (params?: SearchParams, extra?: { signal?: AbortSignal }) => Promise<RequestResponse<T>> | - |
| rowKey | 行主键字段 | string | 'id' |
| columns | 扩展的列配置集合 | ProArcoColumnData[] | - |
| requestParams | 额外的请求参数，会与分页/搜索参数合并 | SearchParams | - |
| paginationFieldNames | 分页字段映射：将内部 current/pageSize 映射为后端要求的字段名（例如 page/limit）；若同时配置 beforeRequest，会先执行 beforeRequest 转换再进行字段映射 | PaginationFieldNames | - |
| beforeRequest | 请求前转换请求参数（返回新参数） | (params: SearchParams) => SearchParams | - |
| afterRequest | 请求后转换请求结果（返回 { data, total }） | (resp: unknown) => RequestResponse<T> | - |
| polling | 轮询间隔（毫秒）；0/undefined 表示不开启 | number | - |
| autoRequest | 参数变化时自动请求数据 | boolean | true |
| onMountedRequest | 组件挂载后是否立即请求数据 | boolean | true |
| scroll | 表格的滚动属性配置，与 a-table 的 scroll 属性一致 | { x?: number | string; y?: number | string; minWidth?: number | string; maxHeight?: number | string; } | - |
| toolbarConfig | 工具栏配置：true/undefined 使用默认配置；false 不显示；对象为合并后的配置 | ToolbarConfig | boolean | { add: false, download: false } |
| searchConfig | 查询表单配置：true/undefined 使用默认配置；false 不显示；对象为合并后的配置 | SearchConfig | boolean | { show: true, layout: 'inline', span: 4,  searchText: '查询', resetText: '重置', showExpandButton: true, divider: true, resetConfig: { keepSearchParams: true } } |
| errorRenderer | 自定义错误渲染（优先级高于默认错误呈现） | (ctx: { error: unknown }) => VNodeChild | null | - |
| emptyRenderer | 自定义空状态渲染 | () => VNodeChild | null | - |
| autoHeightConfig | 自适应高度配置：true/undefined 使用默认配置；false 关闭自适应；对象为合并后的配置 | AutoHeightConfig | boolean | { getScrollContainer: undefined, minHeight: 240, extraOffset: 0 } |
| onRequestStateChange | 请求生命周期回调：loading/success/error | RequestStateCallback | - |

### 列配置（ProArcoColumnData）

| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|--------|
| title | 列头显示的标题（必填） | string | RenderFunction | - |
| dataIndex | 列对应的数据字段（必填） | string | - |
| downloadIndex | 下载使用的列索引，优先于 dataIndex | string | dataIndex |
| valueType | 值类型，驱动渲染与搜索/编辑控件类型 | ValueType | undefined ('input' 为默认) | 'input' |
| valueEnum | 枚举映射（仅 valueType=select 生效） | ValueEnum | - |
| search | 列级搜索配置：false 关闭；true 等价 {}；对象时类型随 valueType 收敛 | boolean | SearchConfigFor<V> | - |
| editable | 列级编辑配置：false 关闭；true 使用默认；对象支持 editValueType 精确约束 fieldProps | boolean | EditableConfigFor<V> | - |
| copyable | 是否支持双击复制单元格内容，若启用单元格编辑且双击的单元格可编辑，则优先触发编辑 | boolean | false |
| copyText | 自定义复制文本（优先于单元格显示文本） | string | - |
| show | 是否在表格中显示该列；为 false 时隐藏，但不影响查询表头渲染 | boolean | true |

### ValueType 与 FieldProps 映射

| ValueType | 说明 | 对应组件属性类型 |
|-----------|---------|-------------------|
| input | 输入框 | InputProps |
| textarea | 多行输入 | TextAreaProps |
| number | 数字输入 | InputNumberProps |
| select | 下拉选择（支持 valueEnum） | SelectProps |
| radio | 单选组（支持 valueEnum） | RadioGroupProps |
| cascader | 级联选择 | CascaderProps |
| date | 日期选择 | PickerProps |
| week | 周选择 | WeekPickerProps & CommonPickerProps |
| month | 月份选择 | MonthPickerProps & CommonPickerProps |
| quarter | 季度选择 | QuarterPickerProps & CommonPickerProps |
| year | 年份选择 | YearPickerProps & CommonPickerProps |
| dateTime | 日期时间选择 | PickerProps |
| time | 时间选择 | TimePickerProps |
| dateRange | 日期范围选择 | RangePickerProps |
| dateTimeRange | 日期时间范围选择 | RangePickerProps |
| timeRange | 时间范围选择 | TimePickerProps |

### 查询表单配置（SearchConfig）

| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|--------|
| show | 是否显示查询表单 | boolean | true |
| layout | 表单布局 | 'horizontal' | 'vertical' | 'inline' | 'inline' |
| span | 每行显示的表单项数量 | number | 4 |
| searchText | 查询按钮文本 | string | '查询' |
| resetText | 重置按钮文本 | string | '重置' |
| showExpandButton | 是否显示展开按钮（false 时不会折叠） | boolean | true |
| wrapperStyle | 查询表单最外层容器的样式 | CSSProperties | - |
| divider | 是否在搜索表单下方显示分割线 | boolean | true |
| resetConfig | 重置行为：当 keepSearchParams=true 时，重置后回填父层提供的初始搜索参数（:search-params），不保留交互过程中表单内部值；重置不影响 requestParams | { keepSearchParams?: boolean } | { keepSearchParams: true } |

### 列级搜索（column.search）

- 搜索对象类型：boolean | SearchConfigFor<V>
  - false：关闭该列的搜索能力
  - true：启用默认搜索配置（等价于 {})
  - 对象：根据列的 valueType 收敛具体字段类型
- searchIndex：自定义该搜索项的表单字段名；未设置时回退到列的 dataIndex；提交参数以该字段为键
- 注意：请求参数的转换仅通过 props.beforeRequest 统一处理；列级不再支持 transform；子组件会把原始表单值透传，最终由父组件请求管线转换

### 列级 fieldSlots（搜索输入插槽）

- 作用：在不改动组件整体的情况下，对输入组件的局部区域进行定制（例如 Input 的前后缀、Select 的下拉项/空态/页眉页脚、Date/Time 选择器的图标/额外内容等）。
- 配置位置：列的 search.fieldSlots，类型会依据列的 valueType 精确约束。
- 实现说明：由于 Vue 3 模板不支持对象式 `v-slots` 的直接传递，本库通过遍历 `fieldSlots` 并使用 `v-slot:[name]` 动态生成具名插槽，同时提供通用 `SlotRenderer` 以支持函数式插槽渲染上下文。
- 类型映射（节选，详见 `src/types/index.ts` 中的定义）：
  - InputSlots：prefix/suffix/prepend/append，插槽参数为 `{ fieldProps }`
  - InputNumberSlots：minus/plus/prefix/suffix，插槽参数为 `{ fieldProps }`
  - SelectSlots：extra/header/footer/prefix/option/label/empty/search-icon/arrow-icon，插槽参数随具体插槽而定（例如 `option`/`label` 为 `{ option }`）
  - CascaderSlots：prefix/label/option/empty/search-icon/loading-icon/arrow-icon，插槽参数随具体插槽而定（例如 `label`/`option` 为 `{ data }`；其他为 `{ fieldProps }`）
  - DatePickerSlots：prefix/suffix-icon/extra/cell，插槽参数分别为 `{ fieldProps }`、`{ fieldProps }`、`{ fieldProps }`、`{ value, date, isCurrentMonth }`
  - TimePickerSlots：prefix/suffix-icon/extra，插槽参数为 `{ fieldProps }`
- 使用示例（简要）：

```ts
columns: [
  {
    title: '名称',
    dataIndex: 'name',
    valueType: 'input',
    search: {
      fieldSlots: {
        prefix: ({ fieldProps }) => '👤',
        suffix: ({ fieldProps }) => '✓',
      },
    },
  },
  {
    title: '状态',
    dataIndex: 'status',
    valueType: 'select',
    valueEnum: {
      0: { label: '未启用' },
      1: { label: '启用' },
    },
    search: {
      fieldSlots: {
        option: ({ option }) => option.label,
        empty: () => '暂无选项',
      },
    },
  },
]
```

- 注意事项：
  - 插槽名需与类型映射严格匹配，否则不会生效（静默忽略）。
  - 插槽函数的参数签名需与映射一致；IDE 将给出类型提示。
  - 区间类组件（dateRange/dateTimeRange/timeRange）当前不提供自定义 `cell` 插槽；可通过外部渲染实现更复杂需求。
  - textarea/radio 暂不支持插槽透传。

### 工具栏配置（ToolbarConfig）

| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|--------|
| add | 新增按钮配置；false 不显示 | { text?: string; onClick: (e?: Event) => void } | false |
| download | 下载按钮配置；false 不显示；undefined 使用默认配置 | DownloadConfig | false | undefined | false |

### 下载配置（DownloadConfig）

| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|--------|
| fileName | 下载的文件名称 | string | - |
| pageSize | 每次请求多少条数据 | number | 200 |
| maxRowsPerFile | 单个导出文件的最大行数（不含表头），超出自动分卷 | number | 1000000 |
| onProgress | 下载进度回调（仅远程下载实时更新） | (info: { downloaded: number; total: number; percent: number; part: number; page: number; pageSize: number }) => void | - |
| throttleMs | 每页请求之间的节流时间（毫秒） | number | - |
| retryCount | 请求失败的重试次数（指数退避） | number | 0 |
| retryDelayBaseMs | 重试的基础延迟（毫秒）；实际延迟为 base * 2^attempt | number | 500 |
| afterDownload | 下载完成后的回调 | () => void | - |

### 返回参数（RequestResponse）

| 参数 | 说明 | 类型 |
|------|------|------|
| data | 数据列表 | T[] |
| total | 总条数 | number |

### 请求生命周期与转换

| 参数 | 说明 | 类型 |
|------|------|------|
| beforeRequest | 请求前转换参数（返回新 SearchParams） | (params: SearchParams) => SearchParams |
| afterRequest | 请求后转换响应（返回 { data, total }) | (resp: unknown) => RequestResponse<T> |
| onRequestStateChange.state | 请求阶段 | 'loading' | 'success' | 'error' |
| onRequestStateChange.context.originalParams | 转换前的原始参数 | SearchParams |
| onRequestStateChange.context.params | 转换后的最终参数 | SearchParams |
| onRequestStateChange.context.response | 标准响应（成功时存在） | RequestResponse<T> | undefined |
| onRequestStateChange.context.error | 错误对象（失败时存在） | unknown |
| onRequestStateChange.context.fromPolling | 是否来源于轮询 | boolean | undefined |

### 表格方法（ProArcoTableMethods）

| 方法名 | 说明 | 类型 |
|--------|------|------|
| fetchData | 手动请求数据；早退场景返回 null | () => Promise<RequestResponse | null> |
| reload | 重新拉取数据；resetPage 为 true 时先重置到第一页 | (resetPage?: boolean) => void |
| resetAndReload | 清空搜索条件并重置到第一页后拉取 | () => void |
| startEditable | 开始编辑（智能路由 行/单元格） | (rowKey: string, columnKey: string, record?: T) => Promise<boolean> | (rowKey: string | string[], record?: T | T[]) => Promise<boolean> |
| saveEditable | 保存编辑（行/单元格） | (rowKey?: string | string[]) => Promise<boolean> | (rowKey: string, columnKey: string) => Promise<boolean> |
| cancelEditable | 取消编辑（行/单元格） | (rowKey?: string | string[]) => Promise<boolean> | (rowKey: string, columnKey: string) => Promise<boolean> |

### 编辑配置（EditableConfig）

| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|--------|
| type | 编辑类型：单元格/行 | 'cell' | 'row' | 'row' |
| trigger | 触发方式：外部调用/双击 | 'external' | 'dblclick' | 'external' |
| multiple | 多行编辑（仅行编辑有效） | boolean | false |
| saveText | 保存按钮文本 | string | '保存' |
| cancelText | 取消按钮文本 | string | '取消' |
| saveOnBlur | 失焦自动保存（单元格编辑） | boolean | true |
| validateOnBlur | 失焦自动校验（行/单元格） | boolean | true |
| disableOtherActionsWhenEditing | 编辑时禁用其它操作（分页/筛选/导出等） | boolean | false |
| onSave | 保存回调（通过列级 FormItem 校验后触发） | (newRecord: T, oldRecord: T, rowKey: string, rowIndex: number) => void | Promise<void> | - |
| onCancel | 取消回调（撤销变更后触发） | (rowKey: string, record: T, rowIndex: number) => void | Promise<void> | - |
| onChange | 字段变化回调（仅编辑中） | (rowData: T, field: string, value: unknown, rowKey: string, rowIndex: number) => void | - |

### 可编辑表格方法（EditableTableMethods）

| 方法名 | 说明 | 类型 |
|--------|------|------|
| isEditing | 检查是否处于编辑状态（全局/行/单元格） | (rowKey?: string, dataIndex?: string) => boolean |
| getRowsData | 获取整表或指定行数据 | (rowKey?: string) => T \| T[] \| undefined |
| setRowData | 设置行数据（浅合并第一层） | (rowKey: string, data: Partial<T>) => void |
| validate | 校验指定行或全部编辑中行 | (rowKeys?: string[]) => Promise<boolean> |
| startRowEditable | 开始行编辑（支持多行） | (rowKey: string \| string[], record?: T \| T[]) => Promise<boolean> |
| saveRowEditable | 保存行编辑（全部或指定行） | (rowKey?: string \| string[]) => Promise<boolean> |
| cancelRowEditable | 取消行编辑（全部或指定行） | (rowKey?: string \| string[]) => Promise<boolean> |
| startCellEditable | 开始单元格编辑 | (rowKey: string, columnKey: string, record?: T) => Promise<boolean> |
| saveCellEditable | 保存单元格编辑 | (rowKey: string, columnKey: string) => Promise<boolean> |
| cancelCellEditable | 取消单元格编辑 | (rowKey: string, columnKey: string) => Promise<boolean> |
| isRowEditing | 指定行是否处于编辑 | (rowKey?: string) => boolean |
| isCellEditing | 指定单元格是否处于编辑 | (rowKey?: string, columnKey?: string) => boolean |
| getRowData | 便捷获取单行数据 | (rowKey: string) => T \| undefined |
| getFieldPath | 生成内部字段路径（rowKey + dataIndex） | (rowKey: string, dataIndex: string) => string |

## 许可协议

MIT