zan-grid
v1.2.1
Published
Client-side enterprise grid for Vue 3
Downloads
396
Readme
zan-grid
面向 Vue 3 的客户端企业表格组件,聚焦“本地行模型 + 企业交互能力”的主线场景。
当前主线是客户端实用企业版。
rowModelType="server"、serverDatasource等历史入口只保留实验/兼容能力,本阶段不扩展服务端分组、服务端透视、服务端聚合、服务端全量导出。
适用范围
- 中后台列表页、运营表、配置表、报表明细页
- 前端本地排序、筛选、分页、聚合、分组、透视
- 需要复制粘贴、范围填充、查找、条件格式、迷你图等类 Excel 交互
- 需要把网格选区或客户端透视结果接到图表
不适合:
- 强依赖完整 Server Row Model 的大数据仓库型网格
- 必须由服务端主导分组、透视、聚合、全量导出的场景
安装
npm install zan-gridimport { Grid } from 'zan-grid'
import 'zan-grid/style'快速开始
<script setup lang="ts">
import { ref } from 'vue'
import { Grid, type ColumnDef, type GridApi } from 'zan-grid'
import 'zan-grid/style'
const gridRef = ref<GridApi | null>(null)
const columns: ColumnDef[] = [
{ field: 'id', header: 'ID', width: 90, type: 'number', sortable: true },
{ field: 'name', header: '名称', width: 180, filterable: true },
{ field: 'price', header: '单价', width: 120, type: 'number', aggFunc: 'sum' },
{ field: 'createdAt', header: '创建时间', width: 160, type: 'date' }
]
const rows = ref([
{ id: 1, name: '苹果', price: 12, createdAt: '2026-06-01 10:00:00' },
{ id: 2, name: '香蕉', price: 18, createdAt: '2026-06-02 09:30:00' }
])
</script>
<template>
<Grid
ref="gridRef"
:columns="columns"
:rows="rows"
:height="480"
row-selection="multiple"
:show-row-numbers="true"
/>
</template>核心设计
1. 列定义和行数据解耦
columns负责列能力和渲染方式rows只负责数据- 排序、筛选、聚合、导出是否按数值列处理,取决于
ColumnDef.type,不是取决于你用了什么编辑器
2. 列值类型和编辑器是两回事
这一点最容易踩坑:
type: 'number'决定数值排序、数字筛选、聚合、Excel 数值导出cellEditor/valueParser决定用户怎么编辑和提交值- 如果一列实际是金额、数量、积分,请明确设成
type: 'number'
示例:
const columns: ColumnDef[] = [
{
field: 'originalPrice',
header: '原价',
type: 'number',
editable: true,
valueParser: value => Number(value || 0)
}
]3. 主线是客户端行模型
rowModelType默认是client- 客户端模式下,排序、筛选、聚合、分组、透视、状态恢复能力最完整
server模式只保留兼容入口,不作为当前主线能力承诺
ColumnDef 关键字段
export interface ColumnDef {
field: string
header?: string
headerGroup?: string | string[]
kind?: 'data' | 'rowNumber' | 'selection' | 'groupTree' | 'treeToggle' | 'detailToggle'
width?: number
hide?: boolean
pinned?: 'left' | 'right'
sortable?: boolean
filterable?: boolean
editable?: boolean
type?: 'text' | 'number' | 'date'
aggFunc?: 'sum' | 'avg' | 'count' | 'min' | 'max' | 'custom'
aggFormula?: string
formatter?: (value: any, row: any) => string
cellRenderer?: Component
cellEditor?: Component
valueParser?: (value: any, row: any, column: ColumnDef) => any
conditionalFormat?: ColumnConditionalFormatConfig
sparkline?: boolean | GridSparklineOptions
}常用字段说明:
field:数据字段名,必须唯一header:表头文案type:text / number / date,会影响排序、筛选、聚合、导出headerGroup:顶层到叶子表头分组路径pinned:左/右固定列formatter:把原始值格式化为展示文本cellRenderer:自定义渲染cellEditor:自定义编辑器valueParser:编辑结果写回前的解析器aggFunc:页脚聚合和分组聚合方式aggFormula:当aggFunc === 'custom'时的公式conditionalFormat:条件格式sparkline:列内迷你图
Grid Props
最常用的输入属性:
columns: ColumnDef[]rows: any[]height?: string | numberrowHeight?: numberrowSelection?: 'single' | 'multiple' | booleanshowRowNumbers?: booleanshowHeaderFilters?: boolean:是否渲染表头下方筛选行,默认falsepagination?: { enabled?: boolean; pageSize?: number; pageSizeOptions?: number[] }toolbar?: boolean | GridToolbarConfigsideBar?: boolean | GridSideBarConfigstatusBar?: boolean | GridStatusBarConfigquickFilterText?: stringadvancedFilterModel?: GridAdvancedFilterModelbatchEdit?: booleanrowGroupFields?: string[]pivotFields?: string[]treeData?: booleantreeChildrenField?: stringmasterDetail?: booleandetailField?: stringdetailColumns?: MasterDetailColumnDef[]rowDrag?: boolean | GridRowDragOptionsgetRowId?: (row: any) => string | numbercustomContextMenuItems?: (context) => GridCustomContextMenuItem[]customContextMenuMode?: 'append' | 'replace'onCustomContextMenuItemClick?: (key, context) => void
Grid Emits
rowClick(data, event)cellClick(data, column, event)selectionChange(selectedRows)rangeSelectionChange()sortChange(sortModel)filterChange(filterModel)batchEditCommit(result)batchEditRollback(result)updateRows(rows)
常见配置
工具栏 / 侧栏 / 状态栏
<Grid
:columns="columns"
:rows="rows"
:toolbar="{ panels: ['columns', 'filters', 'aggregates'], exports: ['csv', 'excel'], compact: true }"
:side-bar="{ panels: ['columns', 'filters', 'groups', 'aggregates'], defaultPanel: 'columns' }"
:status-bar="{ panels: ['rowCount', 'selectedRows', 'page', 'numericSummary'] }"
/>关闭入口:
toolbar={false}sideBar={false}或sideBar={{ panels: [] }}statusBar={false}
分页
<Grid
:columns="columns"
:rows="rows"
:pagination="{ enabled: true, pageSize: 50, pageSizeOptions: [20, 50, 100] }"
/>行拖拽
<Grid
:columns="columns"
:rows="rows"
:row-drag="{ enabled: true, managed: true, showHandle: true }"
/>筛选
列筛选模型
gridRef.value?.setFilterModel([
{ field: 'name', type: 'text', value: 'apple' },
{ field: 'price', type: 'number', value: { operator: 'gt', value: 100 } }
])高级筛选
gridRef.value?.setAdvancedFilterModel({
relation: 'and',
conditions: [
{ field: 'name', operator: 'contains', value: 'apple' },
{ field: 'price', operator: 'between', value: 100, valueTo: 300 }
]
})说明:
setFilterModel()和advancedFilterModel会按AND叠加- 数值/日期列会启用条件筛选,文本列默认集合/文本筛选
quickFilterText是独立的全局快速筛选入口
编辑和批量编辑
常用能力:
- 普通单元格编辑
- 自定义编辑器
- 撤销 / 重做
- 批量编辑缓冲
- 范围填充
- 剪贴板复制 / 剪切 / 粘贴
API:
undoLastEdit()redoLastEdit()startBatchEdit()commitBatchEdit()rollbackBatchEdit()refreshCells()
示例:
gridRef.value?.startBatchEdit()
const result = gridRef.value?.commitBatchEdit()
console.log(result?.committed, result?.changeCount)聚合、分组、透视
基础聚合
const columns: ColumnDef[] = [
{ field: 'amount', header: '金额', type: 'number', aggFunc: 'sum' },
{ field: 'count', header: '数量', type: 'number', aggFunc: 'count' },
{ field: 'ratio', header: '转化率', type: 'number', aggFunc: 'custom', aggFormula: 'amount/count' }
]说明:
sum / avg / min / max / count / custom- 非数值列默认只允许
count / custom - 自定义聚合优先复用已算好的其他聚合结果,不重复遍历所有行
分组 / 透视
gridRef.value?.setRowGroupFields(['department'])
gridRef.value?.setPivotFields(['month'])说明:
- 主线只保证客户端分组和客户端透视
getPivotChartData()返回客户端透视后的图表候选数据
条件格式和 Sparkline
条件格式
const columns: ColumnDef[] = [
{
field: 'amount',
header: '金额',
type: 'number',
conditionalFormat: {
type: 'dataBar',
color: '#3b82f6'
}
}
]迷你图
const columns: ColumnDef[] = [
{
field: 'trend',
header: '趋势',
sparkline: {
type: 'area',
width: 92,
height: 24,
showLastValue: true
}
}
]右键菜单扩展
const buildContextMenuItems = context => [
{ key: 'refresh', label: '刷新' },
{ key: 'export', label: '导出', children: [
{ key: 'exportCsv', label: '导出 CSV', action: 'exportCsv' },
{ key: 'exportExcel', label: '导出 Excel', action: 'exportExcel' }
] }
]<Grid
:columns="columns"
:rows="rows"
:custom-context-menu-items="buildContextMenuItems"
custom-context-menu-mode="replace"
:on-custom-context-menu-item-click="handleCustomMenuClick"
/>说明:
append:在内置菜单前追加自定义项replace:完全替换为自定义菜单- 支持多级子菜单、禁用项、分隔线
导出
CSV
gridRef.value?.exportCsv({
filename: 'grid.csv',
includeHiddenColumns: false,
includeColumnGroups: true
})Excel
gridRef.value?.exportExcel({
filename: 'grid.xlsx',
includeHiddenColumns: false,
includeColumnGroups: true,
freezeHeader: true,
applyBasicStyles: true
})说明:
- Excel 导出会保留数值列类型
- 支持列组表头、冻结表头、基础样式
状态保存与恢复
const state = gridRef.value?.getState()
gridRef.value?.applyState(state)GridState 包含:
- 列宽、顺序、隐藏、固定、聚合、条件格式
- 排序、筛选、高级筛选、快速筛选
- 分组字段、透视字段、折叠状态
- 树节点和明细行展开状态
- 侧栏状态
- 选中行集合
Grid API
最常用 API:
setRows(rows)getRows()updateRow(rowId, data)removeRow(rowId)addRow(data, index?)getSelectedRows()setSelectedRows(rows)setSortModel(model)setFilterModel(model)setAdvancedFilterModel(model)getAdvancedFilterModel()setQuickFilter(text)setColumnOrder(fields)setColumnWidth(field, width)getSelectedRangeData()getPivotChartData()find(text, direction?)getState()applyState(state)
Tree Data
<Grid
:columns="columns"
:rows="rows"
tree-data
tree-children-field="children"
:show-row-numbers="true"
/>相关 API:
expandTreeRow(rowId)collapseTreeRow(rowId)toggleTreeRow(rowId)
Master Detail
<Grid
:columns="columns"
:rows="rows"
:master-detail="true"
detail-field="detail"
:detail-columns="detailColumns"
:detail-row-height="136"
/>相关 API:
expandDetailRow(rowId)collapseDetailRow(rowId)toggleDetailRow(rowId)
与 zan-grid-charts 配合
getSelectedRangeData()可直接交给zan-grid-chartsgetPivotChartData()可把客户端透视结果转成图表候选数据
如果你要在弹层中展示范围图表,推荐搭配:
zan-gridzan-grid-chartszan-layer
性能建议
- 超大数据量优先使用客户端虚拟滚动,不要在
cellRenderer里做重逻辑 - 数值列显式声明
type: 'number',避免筛选、聚合、导出走文本链路 - 自定义编辑器尽量保持无副作用,把写回逻辑放到
valueParser或外层事件里 - 自定义聚合公式优先引用已有聚合列,避免把业务公式塞回逐行遍历
边界与限制
server模式不是当前主线,不承诺完整企业能力treeData、masterDetail、客户端分组树列不建议混用masterDetail会关闭虚拟行渲染,不适合超大数据量- Sparkline 和复杂
cellRenderer同列共存时,自定义渲染器优先
开发命令
npm run dev
npm run test
npm run test:ui
npm run build
npm run acceptance:smoke