@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 代理引擎 APIVue 组件负责挂载、响应式输入、事件桥接和生命周期;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_onlineExcel 导入默认请求为 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)初始化、更新与销毁
初始化顺序:
- 组件挂载并显示加载层。
- 动态加载引擎和语言包。
- 创建
MaxSpreadsheet。 - 检查 51 个公开方法。
- 绑定事件并加载
data。 - 设置
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 buildcheck 执行类型、图表、公式、报表处理、运行时安全和源码迁移检查。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 中的模块。
限制与常见问题
重要限制
- 必须导入样式:
import '@maxax/sheet/style.css'。 - 纯多实例运行: 每个
MaxSheet实例使用自己的挂载根、数据 owner、打印状态、事件总线和 UI 控制器,不存在全局活动实例。 - 历史 DOM ID: 根节点和子应用保留兼容用的固定 ID,但所有内部查找均限定在实例挂载根内,同页可以安全使用多个实例。
- 自定义编辑可扩展: 按实例配置
customEditSaveFn可接入持久化或业务校验;不配置时仅更新本地数据。 - 扩展类型开放:
MaxSheetOptions/MaxSheetData接受额外字段,但后端是否支持需按项目协议确认。 - 历史 API 类型较宽: 图表、打印和业务配置常用
unknown,项目层应定义具体类型并校验。 - 部分返回值实际异步:
getSaveData()、getCanvas()、getLayerBase64()和部分打印流程应使用await。 updateLayerList不负责反序列化: 加载后端图层数据应调用loadData()。- 英文需自备语言包。
- 跨域图片会影响导出: 配置 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 前也会自动检查。
