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

zan-grid

v1.2.1

Published

Client-side enterprise grid for Vue 3

Readme

zan-grid

面向 Vue 3 的客户端企业表格组件,聚焦“本地行模型 + 企业交互能力”的主线场景。

当前主线是客户端实用企业版。rowModelType="server"serverDatasource 等历史入口只保留实验/兼容能力,本阶段不扩展服务端分组、服务端透视、服务端聚合、服务端全量导出。

适用范围

  • 中后台列表页、运营表、配置表、报表明细页
  • 前端本地排序、筛选、分页、聚合、分组、透视
  • 需要复制粘贴、范围填充、查找、条件格式、迷你图等类 Excel 交互
  • 需要把网格选区或客户端透视结果接到图表

不适合:

  • 强依赖完整 Server Row Model 的大数据仓库型网格
  • 必须由服务端主导分组、透视、聚合、全量导出的场景

安装

npm install zan-grid
import { 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:表头文案
  • typetext / number / date,会影响排序、筛选、聚合、导出
  • headerGroup:顶层到叶子表头分组路径
  • pinned:左/右固定列
  • formatter:把原始值格式化为展示文本
  • cellRenderer:自定义渲染
  • cellEditor:自定义编辑器
  • valueParser:编辑结果写回前的解析器
  • aggFunc:页脚聚合和分组聚合方式
  • aggFormula:当 aggFunc === 'custom' 时的公式
  • conditionalFormat:条件格式
  • sparkline:列内迷你图

Grid Props

最常用的输入属性:

  • columns: ColumnDef[]
  • rows: any[]
  • height?: string | number
  • rowHeight?: number
  • rowSelection?: 'single' | 'multiple' | boolean
  • showRowNumbers?: boolean
  • showHeaderFilters?: boolean:是否渲染表头下方筛选行,默认 false
  • pagination?: { enabled?: boolean; pageSize?: number; pageSizeOptions?: number[] }
  • toolbar?: boolean | GridToolbarConfig
  • sideBar?: boolean | GridSideBarConfig
  • statusBar?: boolean | GridStatusBarConfig
  • quickFilterText?: string
  • advancedFilterModel?: GridAdvancedFilterModel
  • batchEdit?: boolean
  • rowGroupFields?: string[]
  • pivotFields?: string[]
  • treeData?: boolean
  • treeChildrenField?: string
  • masterDetail?: boolean
  • detailField?: string
  • detailColumns?: MasterDetailColumnDef[]
  • rowDrag?: boolean | GridRowDragOptions
  • getRowId?: (row: any) => string | number
  • customContextMenuItems?: (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-charts
  • getPivotChartData() 可把客户端透视结果转成图表候选数据

如果你要在弹层中展示范围图表,推荐搭配:

  • zan-grid
  • zan-grid-charts
  • zan-layer

性能建议

  • 超大数据量优先使用客户端虚拟滚动,不要在 cellRenderer 里做重逻辑
  • 数值列显式声明 type: 'number',避免筛选、聚合、导出走文本链路
  • 自定义编辑器尽量保持无副作用,把写回逻辑放到 valueParser 或外层事件里
  • 自定义聚合公式优先引用已有聚合列,避免把业务公式塞回逐行遍历

边界与限制

  • server 模式不是当前主线,不承诺完整企业能力
  • treeDatamasterDetail、客户端分组树列不建议混用
  • masterDetail 会关闭虚拟行渲染,不适合超大数据量
  • Sparkline 和复杂 cellRenderer 同列共存时,自定义渲染器优先

开发命令

npm run dev
npm run test
npm run test:ui
npm run build
npm run acceptance:smoke