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

@maxax/sheet

v1.0.11

Published

Semantic Vue 3 + TypeScript source migration of the maxreport spreadsheet engine.

Readme

MaxSheet Vue 3 Core 插件库

@maxax/sheet 是 maxreport 表格设计器的 Vue 3 + TypeScript 核心插件。它把表格数据模型、Canvas 渲染、编辑交互、图层和打印导出等能力封装为 Vue 3 组件,并提供 51 个公开方法,方便现有系统迁移和二次开发。

本插件直接加载 TypeScript 引擎模块,不依赖旧版静态运行时、框架兼容层、隔离页面或额外脚本标签。

目录

适用范围

适合以下场景:

  • 在 Vue 3 系统中嵌入类 Excel 报表设计器。
  • 加载、编辑、保存 maxreport JSON 报表数据。
  • 构建只读报表和完整数据报表页面。
  • 管理图片、ECharts 图表、条码、二维码和富文本图层。
  • 将报表打印或导出为 PDF、图片等格式。
  • 从旧版全局调用 x.spreadsheet(...) 迁移到模块化 Vue 3 调用。

插件运行在浏览器环境,依赖 DOM、Canvas、window、document、ResizeObserver、XMLHttpRequest 和动态模块加载。服务端渲染时不要在服务端创建引擎实例,应仅在客户端挂载。

功能清单

表格编辑

  • 稀疏行列数据、自定义行高列宽、单元格文本和样式。
  • 边框、对齐、换行、字体、颜色、背景、数值和日期格式。
  • 合并、冻结、隐藏、固定打印头尾、动态合并和循环块。
  • 选区、拖拽、键盘移动、复制/剪切/粘贴、格式刷和自动填充。
  • 插入删除行列,并同步更新合并区、表达式引用和浮动图层位置。
  • 撤销/重做、查找替换、右键菜单、工具栏、筛选、排序和数据校验。

公式与格式

  • 聚合公式:SUM、AVERAGE、MAX、MIN、ROW、COUNTNZ。
  • 数据库公式:DBSUM、DBAVERAGE、DBMAX、DBMIN。
  • 日期时间、数学、字符串、条件、颜色和类型判断函数。
  • 数值、百分比、人民币、美元、欧元、年月日和日期时间格式。

图层

  • ECharts 图表创建、选择、更新、刷新和地图注册。
  • 图片、背景图、条码、二维码、富文本及单元格显示图层。
  • 文本、数字、金额、开关、单选、多选、下拉、树选择、日期、时间、地区、位置、用户、角色、部门、图片和文件上传组件。

打印导出与 Vue 集成

  • 纸张、横竖版、边距、清晰度、水印、当前页、选区、全表和多页打印。
  • PDF、图片、Excel、Word 相关入口和 Canvas 图层合成。
  • 全局插件或局部组件导入,Props 驱动配置和数据更新。
  • ready、error 与 14 个业务事件,组件 ref 代理 51 个引擎方法。
  • 显式提供 getInstance()、reload()、destroy() 和 ready 状态。

技术架构

Vue 应用
  ├─ app.use(MaxSheetVue3, pluginOptions)
  └─ <MaxSheet :data :options ...>
       ├─ createMaxSpreadsheet(host, options, locale, messages)
       │    └─ loadMaxSheetEngine() 动态加载 TypeScript 引擎
       │         └─ new MaxSpreadsheet(...)
       │              ├─ SheetData:数据、选区、历史、校验、公式、行列
       │              ├─ SpreadsheetView:DOM、Canvas、交互、工具栏、图层
       │              ├─ EChartsRenderer:图表
       │              └─ PrintExporter:打印、PDF、图片
       ├─ assertMaxSheetPublicApi() 检查 51 个公开方法
       ├─ bindMaxSheetEvents() 桥接引擎回调与 Vue 事件
       └─ defineExpose(Proxy) 通过组件 ref 代理引擎 API

Vue 组件负责挂载、响应式输入、事件桥接和生命周期;MaxSpreadsheet 是稳定 API 门面;具体数据和视图能力通过运行时依赖装配。引擎、ECharts、PDF 和截图能力按需动态加载。

安装与快速开始

环境要求

| 项目 | 要求 | | --- | --- | | Node.js | >= 20.19.0 | | Vue | >= 3.2.25 < 4 | | 浏览器 | 支持 ES2022、Canvas 和动态 import 的现代浏览器 | | 包格式 | ESM、UMD,自带 TypeScript 声明 |

安装

npm install @maxax/sheet

本地源码构建:

cd packages/sheet
npm install
npm run check
npm run build

在相邻业务项目中安装:

npm install ../maxax/packages/sheet

也可在业务 package.json 中声明:

{
  "dependencies": {
    "@maxax/sheet": "file:../maxax/packages/sheet"
  }
}

本地安装前应先构建,因为包入口指向 dist。

全局注册

import { createApp } from 'vue'
import App from './App.vue'
import MaxSheetVue3 from '@maxax/sheet'
import '@maxax/sheet/style.css'

createApp(App)
  .use(MaxSheetVue3, {
    locale: 'zh-cn',
    defaultOptions: {
      domain: '/api',
      showToolbar: true,
      showGrid: true,
      showContextmenu: true,
      exportDisabled: false,
    },
  })
  .mount('#app')

自定义全局组件名:

import { createMaxSheetPlugin } from '@maxax/sheet'

const plugin = createMaxSheetPlugin({
  componentName: 'ReportDesigner',
  locale: 'zh-cn',
})

createApp(App).use(plugin).mount('#app')

createMaxSheetPlugin(defaults) 会与 app.use(plugin, options) 的配置合并,app.use 传入值优先。

局部组件完整示例

<script setup lang="ts">
import { ref } from 'vue'
import { MaxSheet } from '@maxax/sheet'
import '@maxax/sheet/style.css'
import type {
  MaxSheetComponentExpose,
  MaxSheetData,
  MaxSheetEngineInstance,
  MaxSheetOptions,
} from '@maxax/sheet'

const sheetRef = ref<MaxSheetComponentExpose | null>(null)
const data = ref<MaxSheetData>({
  name: 'sheet1',
  rows: {
    len: 100,
    0: { cells: { 0: { text: '姓名' }, 1: { text: '金额' } } },
    1: { cells: { 0: { text: '张三' }, 1: { text: '1280.50' } } },
  },
  cols: { len: 26, 0: { width: 160 }, 1: { width: 120 } },
  styles: [],
  merges: [],
  validations: [],
})

const options: MaxSheetOptions = {
  domain: '/api',
  showToolbar: true,
  showGrid: true,
  showContextmenu: true,
  exportDisabled: false,
  view: {
    height: () => window.innerHeight - 48,
    width: () => window.innerWidth,
  },
}

function handleReady(instance: MaxSheetEngineInstance) {
  console.log('MaxSheet 已就绪', instance.getData())
}

function save() {
  sheetRef.value?.trigger('save')
}
</script>

<template>
  <button :disabled="!sheetRef?.ready" @click="save">保存</button>
  <MaxSheet
    ref="sheetRef"
    :data="data"
    :options="options"
    height="calc(100vh - 48px)"
    :min-height="480"
    @ready="handleReady"
    @save="reportData => console.log(reportData)"
    @error="error => console.error(error)"
  />
</template>

直接创建引擎

import { createMaxSpreadsheet } from '@maxax/sheet/engine'
import '@maxax/sheet/style.css'

const host = document.querySelector<HTMLElement>('#sheet-host')
if (!host) throw new Error('找不到 #sheet-host')

const instance = await createMaxSpreadsheet(
  host,
  { domain: '/api', showToolbar: true, showGrid: true },
  'zh-cn',
)

instance.onSave(reportData => console.log(reportData))
instance.loadData({ name: 'sheet1', rows: { len: 100 } })

/engine 是不加载 Vue 组件入口的 ESM 子路径;根入口仍保留同名导出以兼容现有代码。 直接调用引擎时,销毁和 DOM 清理由宿主负责。一般页面优先使用 Vue 组件。

组件 API

Props

| Prop | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | options | MaxSheetOptions | {} | 引擎配置,与插件 defaultOptions 浅合并,组件值优先;深层变化会保留数据并安全重建实例。 | | data | MaxSheetData | undefined | 初始或更新后的报表数据。替换引用会由当前实例调用 loadData。 | | callbacks | MaxSheetCallbacks | {} | 14 个业务事件回调,会与 Vue 事件同时执行;替换对象或回调字段后立即生效,不重建实例。 | | locale | string | 插件值或 zh-cn | 当前语言,变化后保留数据并安全重建实例。 | | messages | Record<string, unknown> | 插件值 | 自定义语言包,深层变化后保留数据并安全重建实例。 | | width | string \| number | 100% | 外层宽度,数字转 px。 | | height | string \| number | 100% | 外层高度,数字转 px。 | | minHeight | string \| number | 320 | 最小高度。 | | loadingText | string | 正在加载报表设计器… | 动态加载提示。 | | autoResize | boolean | true | 容器变化时调用 render()。 | | theme | 'light' \| 'dark' \| 'auto' | 插件值或 light | 当前实例配色;auto 跟随系统配色。 |

配置合并是浅合并。例如传入 options.row 会整体替换插件默认的 defaultOptions.row。

Props 的运行时更新分为三类:data 在现有实例中加载,callbacks 由事件桥接实时读取, width、height、minHeight、loadingText 和 autoResize 由组件直接响应。其余引擎构造 配置(options、locale、messages)变化时会销毁旧实例、保留当前 data 并安全重建。

ref 与基础事件

interface MaxSheetComponentExpose extends MaxSheetEngineInstance {
  readonly instance: MaxSheetEngineInstance | null
  readonly ready: boolean
  getInstance(): MaxSheetEngineInstance
  reload(): Promise<MaxSheetEngineInstance>
  destroy(): void
}

| 成员/事件 | 说明 | | --- | --- | | instance | 当前实例,未就绪或已销毁时为 null。 | | ready | 引擎是否可调用。 | | getInstance() | 返回实例;未就绪时抛出明确错误。 | | reload() | 创建新加载代次并重新初始化。 | | destroy() | 销毁引擎、卸载子应用并清空宿主节点。 | | @ready(instance) | API 检查、事件绑定和初始数据加载完成。 | | @error(error) | 初始化或自动重绘失败。 |

所有引擎方法应在 ready 后调用,否则会抛出“实例尚未就绪”错误。

配置项

MaxSheetOptions 保留扩展字段索引。除下表字段外,部署侧自定义配置也会原样传入引擎。

基础、尺寸和样式

| 字段 | 类型 | 引擎默认值 | 说明 | | --- | --- | --- | --- | | domain | string | http://127.0.0.1:8080/max-boot | 后端接口根地址,生产环境应显式配置。 | | showGrid | boolean | true | 显示网格。 | | showToolbar | boolean | true | 显示设计工具栏。 | | showReportSettings | boolean | false | 在桌面设计模式挂载内置“报表 / 单元格”配置面板。 | | showContextmenu | boolean | true | 启用右键菜单。 | | readOnly | boolean | false | 只读/查看模式。 | | showReadOnlyHeader | boolean | true | 只读模式是否显示顶部列头(A、B、C…)。非只读模式始终显示。 | | showReadOnlyIndex | boolean | true | 只读模式是否显示左侧行序号(1、2、3…)。非只读模式始终显示。 | | background | boolean \| string | false | 初始背景。 | | loopBlockLineColor | string | 空字符串 | 循环块边框色。 | | loopBlockLineDash | boolean | false | 循环块虚线。 | | view.height | () => number | 文档可视高度 | 引擎视口高度。 | | view.width | () => number | 文档可视宽度 | 引擎视口宽度。 | | row.len | number | 100 | 默认行数。 | | row.height | number | 25 | 默认行高。 | | row.minRowResizerHeight | number | 0 | 最小拖拽行高。 | | col.len | number | 26 | 默认列数。 | | col.width | number | 100 | 默认列宽。 | | col.indexWidth | number | 60 | 行号区宽度。 | | col.minWidth | number | 60 | 最小列宽。 | | style | MaxSheetCellStyle | 白底、左对齐、Arial 10px | 新单元格默认样式。 |

只读模式下,顶部列头和左侧行序号可以独立配置:

const options: MaxSheetOptions = {
  readOnly: true,
  showReadOnlyHeader: false,
  showReadOnlyIndex: true,
}

前端补全循环空白行

循环单元格可以设置 completeBlankStatus 和 completeBlankRow。ReportCellProcessor 会把当前数据集补到下一个指定倍数,整倍数不重复补行。补出的行复用循环模板的行高、 单元格样式和合并配置,后续内容及固定打印区域会同步后移。

import { ReportCellProcessor } from '@maxax/sheet/processor'

const template = {
  rows: {
    len: 20,
    2: {
      cells: {
        0: {
          text: '#{orders.code}',
          completeBlankStatus: 1,
          completeBlankRow: 10,
        },
      },
    },
  },
}

const report = new ReportCellProcessor().process(template, {
  orders: sevenOrders,
})
// 7 条真实数据 + 3 条空白模板行 = 10 条输出行

倍数必须是正整数。未启用、倍数无效或数据条数已经是整倍数时,不会增加空白行。

单元格分组配置

桌面设计模式开启 showReportSettings 后,右侧“单元格”面板会提供“分组配置”。 启用分组时,面板会在循环字段表达式中写入 group(...) 或 groupRight(...),并同步保存 aggregate: 'group' 与 direction: 'down' | 'right'。纵向分组会在预览中合并连续相同值, 横向分组保留 groupRight(...) 表达式并使用向右的设计器箭头。

const template = {
  rows: {
    2: {
      cells: {
        0: {
          text: '#{orders.group(region)}',
          aggregate: 'group',
          direction: 'down',
        },
        1: {
          text: '#{orders.groupRight(category)}',
          aggregate: 'group',
          direction: 'right',
        },
      },
    },
  },
}

groupRight 不会触发纵向合并;它用于标记同一循环字段沿横向扩展。关闭分组时,面板会还原 为普通的 #{dataset.field} 表达式并清除分组属性。

纵向分组还可以在同一面板中启用“分组依据”并设置“小计文本”。普通数据列会显示“聚合方式”, 支持无、求和、最大值、最小值、平均值和计数,并通过 maxreport 兼容的 funcname 字段保存。 分组依据仅在当前单元格为纵向分组时显示,改为横向分组后会清除不再适用的小计配置。

const verticalSubtotalCell = {
  text: '#{orders.group(region)}',
  aggregate: 'group',
  direction: 'down',
  subtotal: 'groupField',
  subtotalText: '合计',
}

const verticalSubtotalValueCell = {
  text: '#{orders.amount}',
  funcname: 'SUM',
}

横向分组中的明细值使用 dynamic(...)。例如月份、省份和销售额组成三行模板时,可分别写成 #{pro.groupRight(month)}、#{pro.groupRight(province)} 和 #{pro.dynamic(sales)}。处理器会从 dynamic(...) 自动识别横向区域并按数据条数向右展开,不需要额外配置循环块。

const options = {
  style: {
    bgcolor: '#ffffff',
    align: 'left',
    valign: 'middle',
    textwrap: false,
    strike: false,
    underline: false,
    color: '#0a0a0a',
    font: {
      name: 'Microsoft YaHei',
      size: 10,
      bold: false,
      italic: false,
    },
  },
}

上传、打印和导出

| 字段 | 类型 | 默认值/行为 | 说明 | | --- | --- | --- | --- | | uploadUrl | string | /maxreport/upload | 图片上传路径,与 domain 拼接。 | | uploadExcelUrl | string | /maxreport/importExcel | Excel 导入路径,与 domain 拼接。 | | viewLocalImage | string \| function | /maxreport/img | 图片查看前缀;当前图层主要按字符串使用。 | | downLoadImage | function | 失败占位实现 | 下载/转换图片,跨域打印导出时重要。 | | printPaper | Record<string, unknown>[] | [] | 自定义纸张。 | | pdfCompress | boolean | true | PDF 压缩。 | | exportDisabled | boolean | true | 禁用部分导出入口;需要导出时设为 false。 | | exportExcelPattern | string | undefined | Excel 导出模式兼容字段。 | | exportTimeout | number | undefined | 导出超时兼容字段。 |

Sheet 操作回调

工作表新增、删除、重命名和排序会先更新本地页签;配置了对应回调时,再通知宿主执行持久化。未配置回调不会阻断本地操作,回调返回值也不参与本地流程。

const options = {
  addSheetFn(sheet) {
    console.log('新增', sheet)
  },
  deleteSheetFn(sheet) {
    console.log('删除', sheet)
  },
  renameSheetFn(sheet, previousName) {
    console.log('重命名', previousName, '→', sheet.name)
  },
  reorderSheetFn(sheets) {
    console.log('新顺序', sheets.map(sheet => sheet.id))
  },
}

downLoadImage 兼容返回结构:

type DownloadImage = (imageUrl: string) => Promise<{
  data: { success: boolean; result: string }
}>

自定义纸张:

const options = {
  printPaper: [
    { title: '小票', size: [79, 200] },
    { title: 'A7', size: [80, 50] },
  ],
}

插件级配置

| 字段 | 默认值 | 说明 | | --- | --- | --- | | componentName | MaxSheet | 全局注册组件名。 | | locale | zh-cn | 默认语言。 | | theme | light | 所有实例的默认配色,可被组件 theme Prop 覆盖。 | | messages | undefined | 默认语言包。 | | defaultOptions | {} | 所有实例的默认引擎配置。 |

数据格式

type MaxSheetRecord = Record<string, unknown>
type MaxSheetData = MaxSheetRecord | MaxSheetRecord[]

报表 JSON 是开放结构,常见核心字段如下:

| 字段 | 说明 | | --- | --- | | name | 工作表名称。 | | rows | 稀疏行和单元格数据,行列索引从 0 开始。 | | cols | 列数和单列宽度。 | | styles | 单元格引用的样式表。 | | merges | 合并区域,如 A1:B2。 | | validations | 数据校验。 | | freeze | 冻结位置,如 A1。 | | imgList / chartList | 图片和图表图层。 | | barcodeList / qrcodeList | 条码和二维码图层。 | | displayConfig | 单元格显示层配置。 | | printConfig | 纸张、边距、方向、清晰度和水印。 | | recordSubTableOrCollection | 主从表、子表和集合区域。 |

const report = {
  name: 'sheet1',
  rows: {
    len: 100,
    0: {
      height: 32,
      cells: {
        0: { text: '销售报表', style: 0, merge: [0, 2] },
      },
    },
    1: {
      cells: {
        0: { text: '月份' },
        1: { text: '销售额', format: 'rmb' },
        2: { text: '=SUM(B3:B12)' },
      },
    },
  },
  cols: { len: 26, 0: { width: 120 }, 1: { width: 140 } },
  styles: [
    { align: 'center', valign: 'middle', font: { bold: true, size: 16 } },
  ],
  merges: ['A1:C1'],
  validations: [],
}

loadData() 会保留扩展字段。保存时会清理负数临时索引和末尾无业务意义的空白行列,并把图层合并到最终报表 JSON。

事件与回调

每个业务事件可以通过 Vue 事件或 callbacks 订阅:

<MaxSheet
  :callbacks="{ save: handleSave, 'select-chart': handleSelectChart }"
  @save="handleSaveEvent"
  @select-chart="handleSelectChartEvent"
/>

callbacks 先执行,随后发出 Vue 事件。回调返回值会返回引擎;Vue 事件不参与返回值。

| 事件名 | 注册方法 | 常见参数/用途 | | --- | --- | --- | | change | change | 最新报表数据。 | | save | onSave | 已清理并合并图层的报表数据。 | | add-chart | onAddChart | 当前单元格 left/top/row/col。 | | upload-excel | onUploadExcel | Excel 上传响应。 | | export-excel-all | onExportExcelAll | 当前设置。 | | export-big-data-excel | onExportBigDataExcel | 当前设置。 | | loop-block-choose-db-code | onLoopBlockChooseDbCode | 内置弹窗保存后通知数据库列表与已选主数据源。 | | select-chart | onSelectChart | 当前图层/图表配置。 | | chart-delete | onChartDelete | 图层 ID。 | | click-chart | onClickChart | ECharts 点击事件。 | | chart-db-click | onChartDbClick | 图表数据库交互。 | | setting-event | onSettingEvent | 设置动作名与可选数据。 | | cell-express | onCellExpress | 当前单元格表达式。 | | validate | onValidate | 来源与单元格校验信息。 |

setting-event 常见动作包括 clickcell、batch-size-setting、find-replace、background、print-setting、view-setting、export-config、pdf、export_img、iText_pdf、export-word、print_all 和 print_table。导出菜单已有插件内置实现,相关事件保留用于埋点、通知或宿主接管;通过 callbacks 或引擎事件注册的回调返回 false 时,插件不会继续执行默认导出。

报表栏导出菜单不需要页面编写下载逻辑:

| 菜单项 | 插件默认行为 | | --- | --- | | Excel | 下载保留合并、行列尺寸和常用样式的标准 .xlsx 文件。 | | 大数据 Excel | 下载 UTF-8 .csv 文件,减少浏览器内存占用。 | | Word | 下载保留表格布局、合并和常用样式的标准 .docx 文件。 | | PDF | 打开浏览器 PDF 打印流程,文本保持可选择。 | | PDF 图像 | 将完整表格与可视图层合成为图片并下载 .pdf。 | | 图片 | 将完整表格与可视图层下载为 .png。 |

打印和导出直接使用当前已加载的完整报表。请在调用 loadData() 前取得全部记录并生成报表。

引擎 API

插件通过 MaxSheetEngineInstance 暴露 51 个稳定方法。

数据、保存和基础控制

| 方法 | 说明 | | --- | --- | | initSheetList(data) | 初始化多工作表列表;桌面端更新底栏,移动只读端更新移动视图。 | | loadData(data, reset?, preserve?) | 加载报表,返回 this;preserve 常用于切换工作表。 | | getData() | 获取当前报表数据。 | | validate() | 校验错误集合为空时返回 true。 | | change(callback) | 注册变更回调,返回 this。 | | onSave(callback) | 注册保存回调,返回 this。 | | trigger(action) | 触发动作,当前明确支持 trigger('save')。 | | getSaveData() | 收集图层并生成保存数据;当前实现是异步流程,建议 await。 | | tip(message, type?) | 显示引擎提示。 | | render() | 重新渲染表格。 | | updateEditor(value) | 更新当前编辑器内容。 | | destroy() | 幂等释放事件、观察器、定时器、图层、图表、子应用和宿主 DOM。 |

async function saveDirectly() {
  const instance = sheetRef.value?.getInstance()
  if (!instance) return

  const reportData = await instance.getSaveData()
  await fetch('/api/reports/123', {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(reportData),
  })
}

trigger('save') 走 onSave/save 事件;getSaveData() 只返回数据,适合宿主主动提交。

事件注册方法

| 方法 | 对应事件 | 返回值 | | --- | --- | --- | | onAddChart(callback) | add-chart | this | | onUploadExcel(callback) | upload-excel | this | | onExportExcelAll(callback) | export-excel-all | this | | onExportBigDataExcel(callback) | export-big-data-excel | this | | onLoopBlockChooseDbCode(callback) | loop-block-choose-db-code | this | | onSelectChart(callback) | select-chart | this | | onChartDelete(callback) | chart-delete | this | | onClickChart(callback) | click-chart | this | | onChartDbClick(callback) | chart-db-click | this | | onSettingEvent(callback) | setting-event | this | | onCellExpress(callback) | cell-express | this | | onValidate(callback) | validate | this |

组件已自动注册这些方法并转发事件。使用 <MaxSheet> 时优先使用 Vue 事件或 callbacks,再次直接注册可能覆盖组件内部桥接回调。

单元格和表达式

| 方法 | 说明 | | --- | --- | | setExpData(data) | 设置数据库表达式值集合。 | | setSelectCellExpress(expression) | 将表达式写入当前选中单元格并重绘。 | | cellText(row, column, value?) | 不传值时读取,传值时写入;索引从 0 开始,运行时列也接受 A/B/...。 | | cellProp(row, column, value?) | 字符串参数读取属性,对象参数批量写属性。 |

const sheet = sheetRef.value?.getInstance()
if (sheet) {
  sheet.cellText(0, 0, '标题')
  sheet.cellText(1, 1, '=SUM(B3:B10)')
  const title = sheet.cellText(0, 0)
  const format = sheet.cellProp(1, 1, 'format')

  sheet.cellProp(1, 1, {
    format: 'rmb',
    decimalPlaces: 2,
    autoHeight: true,
  })
  console.log({ title, format })
}

图表和图层

| 方法 | 说明 | | --- | --- | | addChart(type, config?, data?) | 添加图表。 | | registerMap(name, geoJson) | 异步加载 ECharts 并注册地图。 | | refreshChart(data?) | 刷新当前选中图表。 | | updateChart(id, config) | 按 ID 更新图表。 | | updateChartBackground(id, background) | 更新图表背景。 | | refreshChartData(id, data) | 更新图表系列数据。 | | updateLayerOffset(id, offset) | 更新图层偏移。 | | updateChartExtData(id, data) | 更新图表数据库/坐标轴扩展信息。 | | getAllChartInfo() | 返回图表 ID、名称和可选轴/数据库摘要。 | | getLayerList() | 异步取得运行时图层;有图表时最多等待约 4.8 秒挂载。 | | updateLayerList(layers) | 替换运行时图层,只接受内部 SpreadsheetLayer 实例。 | | getLayerBase64() | 异步获取所有图层 Base64。 |

const geoJson = await fetch('/maps/china.json').then(response => response.json())
await sheetRef.value?.registerMap('china', geoJson)

打印、PDF 和 Canvas

| 方法 | 说明 | | --- | --- | | getPrintCanvas(options?) | 异步取得多页打印 Canvas 流程结果。 | | getLayerBase64() | 获取图层 Base64 集合。 | | exportPdf(options?) | 按当前打印配置导出 PDF。 | | printByQuery(query, options?) | 打印已加载的完整报表。 | | updatePrintConfig(config) | 更新纸张、方向、边距、清晰度、水印并重绘。 | | getCanvas() | 异步合成完整表格与图层,当前实现返回 Data URL。 |

报表栏“打印全部记录”会先调用可选的 options.getRpQueryParam(),再执行 printByQuery(query, 'table'),随后通过 setting-event 的 print_table 动作回传同一份查询参数。

async function exportPng() {
  const dataUrl = await sheetRef.value?.getCanvas()
  if (typeof dataUrl !== 'string') return
  const link = document.createElement('a')
  link.href = dataUrl
  link.download = 'report.png'
  link.click()
}

sheetRef.value?.updatePrintConfig({
  paper: 'A4',
  width: 210,
  height: 297,
  marginX: 10,
  marginY: 10,
  layout: 'landscape',
  definition: 2,
  watermarkShow: true,
  watermarkText: '内部资料',
  watermarkColor: '#E71414',
  rotationAngle: -25,
  fontsize: 20,
})

背景、视图和尺寸

| 方法 | 说明 | | --- | --- | | setBackground(background) | 设置表格背景。 | | setViewButton(config) | 更新报表栏显示和按钮。 | | updateOptions(key, value) | 更新一个运行时配置并重绘。 | | watchScreenInnerWidth(callback) | 设置宽度回调,立即执行并返回结果。 | | updateCellSize({ ri, ci, width?, height? }) | 设置指定行高/列宽,同步关联图层。 | | batchUpdateCellSize({ width?, height? }) | 对当前选区批量设置尺寸。 |

sheetRef.value?.updateCellSize({ ri: 0, ci: 0, width: 180, height: 36 })

API 完整性检查

import {
  assertMaxSheetPublicApi,
  MAX_SHEET_PUBLIC_METHODS,
} from '@maxax/sheet'

const instance = sheetRef.value?.getInstance()
if (instance) {
  assertMaxSheetPublicApi(instance)
  console.log(MAX_SHEET_PUBLIC_METHODS.length) // 51
}

后端与宿主能力接入

上传协议与鉴权

图片上传默认请求:

POST {domain}{uploadUrl}
Content-Type: multipart/form-data
file: <文件>
fileName: <原始文件名>
biz: excel_online

Excel 导入默认请求为 POST {domain}{uploadExcelUrl}。每个实例从自己的 options.token 读取鉴权值;存在 token 时会同时设置 X-Access-Token 和 token 请求头。预期响应:

{
  "success": true,
  "message": "/files/example.png"
}

自定义单元格编辑回调

带有 customEditConf 的单元格会在只读视图中显示自定义编辑入口。默认保存会直接更新当前实例的本地单元格,不依赖 HTTP;如需持久化或执行业务校验,可配置 options.customEditSaveFn。同步或异步回调成功后更新单元格,回调抛错时弹窗保持打开并提示失败。

const options: MaxSheetOptions = {
  async customEditSaveFn(payload) {
    // 可选:由宿主决定是否请求接口、写入状态或只做校验。
    await saveCustomCell(payload)
  },
}

回调参数包含 rowIndex、columnIndex、value、eventParams、datetime、params 和原始 configuration。回调属于单个 MaxSheet 实例,不会跨实例共享。

多页打印与全量导出

先从后端取得全部数据,再用 ReportCellProcessor 生成完整报表并调用 loadData()。打印和导出直接使用这份报表;纸张分页由打印配置和 pagingRow 控制。

国际化与生命周期

国际化

插件内置并导出 zhCN:

import { zhCN } from '@maxax/sheet'

自定义语言包:

createApp(App).use(MaxSheetVue3, {
  locale: 'en',
  messages: {
    toolbar: { save: 'Save' },
    validation: { required: 'Required' },
  },
})

也可给单个组件传 locale 和 messages。属性变化后引擎会切换语言并重绘。当前内置 en 回退到中文,完整英文界面需要业务方提供完整消息。

直接创建带独立语言上下文的引擎:

import { createMaxSpreadsheet } from '@maxax/sheet/engine'

const instance = await createMaxSpreadsheet(host, options, 'en', englishMessages)

初始化、更新与销毁

初始化顺序:

  1. 组件挂载并显示加载层。
  2. 动态加载引擎和语言包。
  3. 创建 MaxSpreadsheet。
  4. 检查 51 个公开方法。
  5. 绑定事件并加载 data。
  6. 设置 ready = true,发出 ready。

组件监听 data 引用,不做深度监听。推荐替换引用:

data.value = structuredClone(nextData)

或主动加载:

sheetRef.value?.loadData(nextData)

重新初始化:

const nextInstance = await sheetRef.value?.reload()

reload() 使用加载代次隔离,旧异步初始化不会覆盖新实例。destroy() 和组件自动卸载会清理引擎、表格 UI、底部栏、ResizeObserver、动画帧和宿主 DOM。

源码、构建与发布

core/
├─ src/
│  ├─ index.ts                  包公开入口
│  ├─ plugin.ts                 Vue 插件与默认配置合并
│  ├─ types.ts                  公开配置、事件和 54 方法类型
│  ├─ components/
│  │  ├─ MaxSheet.vue            生命周期、事件和 ref 代理
│  │  ├─ designer/              报表设置面板
│  │  └─ shell/                 底部栏、移动视图、报表 UI
│  ├─ engine/
│  │  ├─ index.ts               引擎懒加载和实例工厂
│  │  ├─ public-api.ts          54 方法清单与运行时检查
│  │  ├─ events.ts              14 个业务事件桥接
│  │  ├─ max-spreadsheet.ts      稳定公开 API 门面
│  │  ├─ spreadsheet-engine.ts  SheetData 和引擎装配
│  │  ├─ domain/                行列、选区、合并、历史、校验
│  │  ├─ extends/               打印与导出命令扩展及公共流程
│  │  ├─ view/                  交互、工具栏、图层和视图
│  │  ├─ canvas/                Canvas 绘制
│  │  ├─ chart/                 ECharts 渲染
│  │  └─ print/                 打印、PDF 和图片导出
│  ├─ locales/                  语言包
│  └─ styles/                   样式和本地图标
├─ scripts/
│  ├─ verify-source-migration.mjs  迁移和 API 校验
│  └─ clean-declarations.mjs       清理私有声明
├─ dist/                        构建产物
├─ package.json
└─ vite.config.ts

关键职责:

  • MaxSheet.vue:合并配置、创建引擎、桥接事件、代理 API、管理错误和生命周期。
  • max-spreadsheet.ts:定义稳定 54 方法门面,隔离具体数据和视图实现。
  • spreadsheet-engine.ts:创建 SheetData、装配视图,并为每个实例绑定独立运行时上下文。
  • public-api.ts:公开 API 单一清单和运行时断言。
  • verify-source-migration.mjs:阻止 API 缺失、Vue 2 compat、CommonJS 和遗留生成代码重新进入核心源码。

开发、校验和构建

npm run dev
npm run check
npm run test:e2e
npm run build

check 执行类型、图表、公式、报表处理、运行时安全和源码迁移检查。test:e2e 使用 Chromium 验证多实例语言隔离、键盘操作、重载、销毁和监听器释放。build 先校验,再由 Vite 输出共享 chunk 的多入口 ESM、兼容 UMD、CSS,随后生成声明并清理私有声明。

| 产物 | 说明 | | --- | --- | | dist/max-sheet.js | ESM 入口。 | | dist/max-sheet.umd.cjs | UMD/CommonJS 入口。 | | dist/engine.js | ./engine ESM 子路径,不加载 Vue 组件入口。 | | dist/processor.js | ./processor ESM 子路径,只公开报表数据处理器。 | | dist/maxsheet-vue3.css | 完整样式。 | | dist/index.d.ts | 类型入口。 | | dist/*.js | 按需异步 chunk。 |

包公开根入口、./engine、./processor 和 ./style.css。不要依赖 dist 内部 chunk 文件名,也不要深度导入未出现在 package.json#exports 中的模块。

限制与常见问题

重要限制

  1. 必须导入样式: import '@maxax/sheet/style.css'。
  2. 纯多实例运行: 每个 MaxSheet 实例使用自己的挂载根、数据 owner、打印状态、事件总线和 UI 控制器,不存在全局活动实例。
  3. 历史 DOM ID: 根节点和子应用保留兼容用的固定 ID,但所有内部查找均限定在实例挂载根内,同页可以安全使用多个实例。
  4. 自定义编辑可扩展: 按实例配置 customEditSaveFn 可接入持久化或业务校验;不配置时仅更新本地数据。
  5. 扩展类型开放: MaxSheetOptions/MaxSheetData 接受额外字段,但后端是否支持需按项目协议确认。
  6. 历史 API 类型较宽: 图表、打印和业务配置常用 unknown,项目层应定义具体类型并校验。
  7. 部分返回值实际异步: getSaveData()、getCanvas()、getLayerBase64() 和部分打印流程应使用 await。
  8. updateLayerList 不负责反序列化: 加载后端图层数据应调用 loadData()。
  9. 英文需自备语言包。
  10. 跨域图片会影响导出: 配置 CORS 或通过 downLoadImage 转为可绘制资源。

页面空白或样式错乱

确认导入 CSS,并确保父容器有明确高度。height="100%" 只有在父级高度确定时有效。

ref 提示实例未就绪

把调用放到 ready 事件后,或先判断 sheetRef.value?.ready。

修改 data 后未刷新

组件监听引用而不是深层字段。替换整个引用,或直接调用 loadData(nextData)。

点击保存没有发请求

插件只整理数据并发出 save,不会自动写入业务接口。请在 @save 或 callbacks.save 中调用后端 API。

上传返回 401

检查当前实例的 domain、上传路径和 token 选项,以及后端接受的 token/租户请求头。

如何确认没有缺少旧版 API

cd core
npm run check

或调用 assertMaxSheetPublicApi(instance);组件在 ready 前也会自动检查。