新 Excel 导入工具设计¶
前端组件¶
Excel 导入可视化模板设计器(Vite + Vue3 + Pinia)整体设计方案。
工程路径:
web/obpm-designer-excelimp
当前版本:0.1.0
构建命令:npm run dev(开发) /npm run build(ESM/UMD 库)
方案概述¶
项目定位¶
打造一款可拖拽、可配置、可撤销重做、可独立 ESM 导出的 Excel 导入模板可视化设计组件。支持业务人员可视化配置 Excel 表头映射、数据校验规则、字段转换规则,无需编码即可生成标准导入模板;组件支持独立打包为 ESM 模块,可被任意 Vue3 项目、Vite 项目、第三方工程直接引入使用。
核心能力¶
- 可视化拖拽:拖拽配置 Excel 导入字段、排序、表头映射关系
- 模板定义:支持设置必填、数据类型、校验规则、填写方式、格式、默认值、字段转换
- 导出目标:Sheet 级支持导出到表单或数据库表,并绑定模块/表单或数据源/目标表
- 多级表头:列标签支持多行叠加、横向合并(colspan)、纵向合并(rowspan)及完整样式配置
- 主从结构:支持多 Sheet 分组、主/从 Sheet 配置;从表字段通过
relation与主表字段建立一对多关联 - 操作回溯:撤销/重做(SVG 图标按钮)、清空当前 Sheet 画布(橡皮擦图标)
- 模板能力:弹窗查看/编辑 JSON 模板;配置变更通过
change事件通知宿主持久化;Excel 导入模板文件由**服务端**按TemplateJson生成(见「Excel 导出接口(后端)」) - 开箱即用:ESM 独立组件打包,无侵入,支持第三方项目按需引入
- 状态统一:Pinia 全局管理设计器状态,数据可持久化、可外部监听
技术栈选型¶
| 类别 | 选型 | 说明 |
|---|---|---|
| 构建工具 | Vite 8 | 极速构建、原生 ESM 支持 |
| 核心框架 | Vue 3 + Composition API + <script setup> |
— |
| 状态管理 | Pinia | 简洁轻量化、支持状态快照,适配撤销重做 |
| 拖拽引擎 | vue3-dnd + react-dnd-html5-backend | 标准 HTML5 拖拽 |
| 样式方案 | SCSS + scoped 组件样式 | 画布表格样式见 preview-table.scss |
| 打包方案 | Vite Library Mode | ESM + UMD 双格式,npm run build |
页面布局¶
整体采用「顶部工具栏 + 左 / 中 / 右三栏」的经典设计器布局:左侧取字段、中间排模板、右侧配属性。
顶部工具栏
┌──────────────────────────────────────────────────────────────────────┐
│ [模板名] [模板描述] │ [撤销][重做][清空] │ JSON模板 │
└──────────────────────────────────────────────────────────────────────┘
- 模板名称 / 描述:当前导入模板的名称与说明,可就地编辑。
- 操作回溯(SVG 图标按钮,
title悬停提示): - 撤销:回退上一步操作
- 重做:恢复已撤销的操作
- 清空(橡皮擦图标):清空**当前 Sheet** 的字段与列标签,保留 Sheet 绑定配置及其他 Sheet
- JSON 模板:打开弹窗,在多行文本框中查看/编辑完整
TemplateJson;**取消**关闭不保存,**应用**校验 JSON 后加载到画布。
页签 / 字段库
每个 Sheet 页签对应一个导入分组(主 / 从),其左侧面板包含页签配置与可用字段列表:
┌─────────────────────────────────────┐
│ 左 · 页签 / 字段库 │
│ ┌ 页签配置 ────────────────────┐ │
│ │ 页签名称:[ 主表 ] │ │
│ │ 导 出 到:[ 表单 ▾ ] │ │ ← 表单 | 数据库表
│ │ ── 表单模式 ──────────────── │ │
│ │ 所属模块:[ 销售模块 ▾ ] │ │
│ │ 表 单:[ 订单表单 ▾ ] │ │
│ │ ── 数据库表模式 ──────────── │ │
│ │ 数 据 源:[ 主数据库 ▾ ] │ │
│ │ 目 标 表:[ orders ▾ ] │ │
│ └──────────────────────────────┘ │
│ ┌ 可用字段列表 ────────────────┐ │
│ │ ☑ ITEM_订单号 订单号 │ │
│ │ ☑ ITEM_客户 客户名称 │ │
│ │ ☐ ITEM_金额 订单金额 │ │
│ │ ☐ ITEM_日期 下单日期 │ │
│ │ (勾选 / 拖拽到画布) │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────┘
- 页签名称:Sheet 页签名(对应导出 Excel 的 Sheet 名,最长 31 字符),可自定义修改。
- 导出到:下拉选择导入目标类型,
form(表单)或database(数据库表),存储于ExcelSheet.exportTarget,默认form。 - 表单模式(
exportTarget === 'form'): - 所属模块:下拉选择,数据来源
hostRequestBridge.getModules()。 - 表单:下拉选择,数据来源
hostRequestBridge.getModuleForms(moduleId);选定后通过getFormSchema()拉取可绑定字段。 - 数据库表模式(
exportTarget === 'database'): - 数据源:下拉选择,数据来源
hostRequestBridge.getAppDataSources(appId)。 - 目标表:下拉选择,数据来源
hostRequestBridge.getDataSourceTables(appId, dataSourceId)。 - 字段库可选通过
getDataSourceTableSchema(appId, dataSourceId, tableId)拉取表字段(FakeHostApi已提供 Mock)。 - 可用字段列表:来自所选表单或目标表的可绑定字段(
name= 绑定 Key,label= 显示名),支持勾选或拖拽到中间画布。 - 切换表单 / 目标表且 Sheet 已有绑定字段时,弹出确认并清空画布;切换「导出到」且已有绑定字段时不允许切换。
- 组件 Prop
appId用于拉取数据源与目标表;未传时 Mock 环境使用demo_app。 - 未注入
hostRequestBridge时使用内置FakeHostApi(utils/hostApi.ts)。
模板画布
画布表格分为 设计辅助行(仅设计器内展示,不导出 Excel)与 导出内容行:
┌───────────────────────────────────────────────┐
│ 中 · 设计画布 │
│ ┌ Sheet 页签─────────────────────────┐ │
│ │ 主表 │ 订单明细 │ + 新增 │ │
│ ├────────────────────────────────────┤ │
│ │ thead(不导出) │ │
│ │ 行1 · 字段名 │订单号│客户名称│… │ ← 可拖拽排序 │
│ │ 行2 · 列字母 │ A │ B │ C │ │
│ ├────────────────────────────────────┤ │
│ │ tbody(导出内容) │ │
│ │ L1 列标签行 │ 订单号 │ 客户名称(合并)│ │
│ │ L2 列标签行 │ (纵向) │ 订单金额 │… │ │
│ │ 数据行 │ │ │ │ │
│ └────────────────────────────────────┘ │
│ [ + 列标签行 ] │
└───────────────────────────────────────────────┘
- 字段名行(thead 第 1 行):绿色表头,展示各列字段名称,支持拖拽排序(
CanvasColumn.vue)。 - 列字母行(thead 第 2 行):展示 A / B / C… 列标识,辅助对齐。
- 列标签行(tbody):多级表头,可点击选中并在右侧配置属性;支持
+ 列标签行新增层级。 - 数据行(tbody 最后一行):占位 / 示例数据预览,可配置行高(pt,默认 15)与是否填充默认值。
列名 / 列标签 / 数据行属性
画布中的单元格按类型分为 列名 / 列标签 / 数据行 / Sheet 四类,选中不同类型时右侧属性面板自动切换:
| 选中类型 | 属性面板 | 可配置项 |
|---|---|---|
| 列名(Column) | FieldSetting.vue · 列信息 |
字段名称、绑定 Key、主从关联(从 Sheet 字段)、必填、数据类型、校验规则、填写方式、格式、默认值 |
| 列标签(Label) | LabelSetting.vue · 列标签属性 |
标签文本、所在行级、字体大小(pt,默认 11)、字重、斜体、文字装饰、文字/背景颜色、对齐方式、横向合并跨度(colspan)、纵向合并跨度(rowspan) |
| 数据行(Data Row) | SheetSetting.vue · 数据行属性 |
行高(pt,默认 15)、是否示例数据行 |
| Sheet / 未选中 | SheetSetting.vue · Sheet 属性 |
Sheet 类型(主/从) |
列标签支持 多行(多级表头)、横向合并 与 纵向合并,可组合使用:
A列 B列 C列
L1 ┌──────── 订单号 ──┬──── 客户名称 ────┐ ← A 纵向合并 2 行;B+C 横向合并
L2 │ (纵向合并) │ 订单金额 │ (C列标签) │
数据行│ │ │ │ ← 导出时填充默认值(可选)
└──────────────────┴──────────┴──────────┘
列标签默认行为
- 拖入 / 勾选字段时,自动在 L1 创建列标签,文本与字段名称相同。
- 点击「+ 列标签行」时,为当前 Sheet 所有列在该层级创建默认标签(文本 = 各字段名)。
- 字段排序、增删列时,
labelRow.ts同步维护columnIndex。 - 新建标签的默认样式见
DEFAULT_LABEL_STYLE(labelRow.ts):
| 属性 | 默认值 |
|---|---|
fontSize |
11(pt) |
fontWeight |
normal |
fontStyle |
normal |
textDecoration |
none |
color |
#2e7d32 |
backgroundColor |
#e8f5e9 |
数据行默认行为
- 新建 Sheet 时
dataRow.height默认为 15(pt),与全局config.excelRowHeight一致。 - 设计器画布预览使用 pt 作为行高单位;服务端导出 Excel 时写入 POI
Row.setHeightInPoints(与config.excelRowHeight/dataRow.height同构,默认 15 pt)。
区域与组件对应
| 区域 | 组件 | 说明 |
|---|---|---|
| 顶部工具栏 | Toolbar.vue |
模板信息编辑、操作回溯(SVG 图标)、JSON 模板弹窗 |
| 页签 / 字段库 | DragFieldList.vue |
页签配置 + 导出到(表单/数据库表)+ 模块/表单或数据源/目标表 + 可用字段列表 |
| Sheet 页签 | SheetTabs.vue |
多 Sheet 切换、新增 / 删除(主从模式) |
| 模板画布 | DragCanvas.vue |
表格网格、列标签行、数据行 |
| 列拖拽 | CanvasColumn.vue |
字段名列拖拽排序 |
| 字段项 | FieldItem.vue |
左侧字段列表项 |
| 右侧 · 列信息 | FieldSetting.vue |
字段属性配置(含主从关联) |
| 右侧 · 列标签 | LabelSetting.vue |
列标签样式与合并配置 |
| 右侧 · Sheet/数据行 | SheetSetting.vue |
Sheet 主从属性、数据行配置 |
整体架构设计¶
架构分层¶
- 视图层:设计器主页面、拖拽画布、字段/标签配置面板、操作工具栏
- 组件层:可复用拖拽组件、表单配置组件、按钮操作组件
- 状态层:Pinia Store 统一管理模板数据、选中状态、操作快照
- 工具层:标签行渲染、模板序列化、撤销重做快照、宿主 API 桥接
核心数据流¶
用户拖拽/配置操作 → 触发组件事件 → Pinia 更新状态 + recordSnapshot()
→ 视图响应更新 → serializeTemplate() 生成 TemplateJson → 触发 change 事件 / 支持 JSON 导入导出
模块拆分¶
| 模块 | 文件 | 职责 |
|---|---|---|
| 拖拽核心 | CanvasColumn.vue、FieldItem.vue |
字段拖拽、排序、放置 |
| 模板配置 | FieldSetting.vue、LabelSetting.vue、SheetSetting.vue |
字段/标签/Sheet 属性(主从关联在 FieldSetting) |
| 操作回溯 | snapshot.ts、deepClone.ts |
状态快照、撤销重做 |
| 标签行逻辑 | labelRow.ts |
合并覆盖判断、渲染单元格构建、列索引同步 |
| 模板序列化 | serialize.ts |
TemplateJson 序列化/反序列化、旧版 relationField 迁移、detailKey 同步 |
| 字段工具 | excel.ts |
从表单字段创建 ExcelFieldItem(createFieldFromFormField) |
| 打包集成 | index.ts、vite.config.ts |
ESM 组件打包、对外 API |
核心功能详细设计¶
可视化拖拽设计¶
- 从字段库拖拽或勾选新增字段到模板画布
- 画布内字段名行自由拖拽排序(
vue3-dnd),实时更新列顺序并同步标签columnIndex - 支持单个字段删除(同步删除该列全部标签)
- 支持清空当前 Sheet 画布(字段 + 标签)
模板字段配置规则¶
每个模板字段(ExcelFieldItem)支持完整配置项,可序列化存储为模板 JSON:
| 配置项 | 字段 | 说明 |
|---|---|---|
| 字段 ID | id |
前端唯一标识(UUID) |
| 字段名称 | name |
Excel 展示表头名称 |
| 绑定字段 Key | key |
后端映射字段唯一标识(对应表单 / 表字段 name) |
| 是否必填 | required |
导入校验时非空判断;服务端导出 Excel 时影响数据验证 allowBlank |
| 数据类型 | dataType |
见「列信息配置」 |
| 校验规则 | validationRule |
none / numberRange / dateRange / textLength / regex,默认 none |
| 文本长度 / 数值范围 | lengthLimit |
[min, max],用于文本长度或数值范围校验 |
| 日期范围 | dateRange |
[start, end] ISO 日期,用于 date / datetime |
| 时间范围 | timeRange |
[start, end] HH:mm,用于 time |
| 正则校验 | pattern / patternTip |
自定义正则及错误提示 |
| 填写方式 | fillMode |
input(输入)/ select(下拉选择),默认 input |
| 下拉选项 | selectOptions |
fillMode === 'select' 时的候选项列表 |
| 数值格式 | numberFormat |
数值类型 Excel 格式串 |
| 日期格式 | dateFormat |
日期类型 Excel 格式串 |
| 时间格式 | timeFormat |
时间类型 Excel 格式串 |
| 日期时间格式 | datetimeFormat |
日期时间类型 Excel 格式串 |
| 默认值 | defaultValue |
示例数据行填充值 |
| 字段转换规则 | transform |
Record<string, string> |
| 主从关联 | relation |
MasterDetailRelation,仅从 Sheet 字段有效,表示子表字段与主表字段的对应关系 |
列信息配置(数据类型 / 校验 / 填写方式 / 格式)¶
列信息(FieldSetting.vue)按分组展示:主从关联(从 Sheet 且 enableMasterDetail 时)、数据类型、校验规则、填写方式、格式、其他(默认值)。表单项采用上下结构(.form-group 标签在上、控件在下)。
数据类型(dataType)
| 类型 | 值 | 说明 |
|---|---|---|
| 文本 | string |
通用字符串 |
| 数值 | number |
配合数值范围、数值格式 |
| 日期 | date |
配合日期范围、日期格式 |
| 时间 | time |
配合时间范围、时间格式 |
| 日期时间 | datetime |
配合日期时间范围、日期时间格式 |
| 手机号 | phone |
可选正则校验 |
| 邮箱 | email |
可选正则校验 |
| 身份证 | idCard |
可选正则校验 |
校验规则(validationRule,非必填,默认 none)
| 校验类型 | 值 | 适用类型 | 配置字段 |
|---|---|---|---|
| 无 | none |
全部 | 服务端导出时不写入 Excel 数据验证(下拉填写方式除外) |
| 数值范围 | numberRange |
数值 | lengthLimit = [min, max] |
| 日期范围 | dateRange |
日期 | dateRange = [start, end] |
| 时间范围 | dateRange |
时间 | timeRange = [start, end](HH:mm) |
| 日期时间范围 | dateRange |
日期时间 | dateRange = [start, end](含时间) |
| 文本长度 | textLength |
文本 | lengthLimit = [min, max] |
| 正则 | regex |
文本 / 手机号 / 邮箱 / 身份证 | pattern + patternTip;后三者选中正则时可使用内置默认公式 |
填写方式(fillMode)
| 方式 | 值 | 说明 |
|---|---|---|
| 输入 | input |
单元格自由输入(默认) |
| 下拉选择 | select |
单元格以下拉框填写,需配置 selectOptions(每行一项) |
格式(按数据类型分别配置;服务端导出 Excel 时写入单元格 DataFormat)
数值格式(numberFormat):
| 格式串 | 含义 |
|---|---|
#,##0;-#,##0 |
千分位整数(正数 ; 负数) |
#,##0;[Red]-#,##0 |
千分位整数,负数显示红色 |
#,##0.00;-#,##0.00 |
千分位两位小数 |
#,##0.00;[Red]-#,##0.00 |
千分位两位小数,负数显示红色 |
日期格式(dateFormat):
| 格式串 | 含义 |
|---|---|
yyyy/m/d |
年/月/日 |
[DBNum1][$-804]yyyy"年"m"月"d"日" |
中文数字年月日 |
[DBNum1][$-804]yyyy"年"m"月" |
中文数字年月 |
时间格式(timeFormat):
| 格式串 | 含义 |
|---|---|
mm:ss.0 |
分:秒.十分位秒 |
h:mm |
时:分 |
h:mm:ss |
时:分:秒 |
日期时间格式(datetimeFormat):
| 格式串 | 含义 |
|---|---|
yyyy/m/d h:mm |
年/月/日 时:分 |
yyyy-mm-dd hh:mm:ss |
年-月-日 时:分:秒 |
[DBNum1]为中文小写数字格式,$-804为简体中文区域代码。
选项常量定义于 components/ExcelDesigner/fieldOptions.ts。
列标签配置规则¶
列标签存储于 ExcelSheet.labelRows: ColumnLabelCell[]:
| 配置项 | 字段 | 说明 |
|---|---|---|
| 标签 ID | id |
UUID |
| 列索引 | columnIndex |
对应 fieldList 中的列位置(0-based) |
| 行级 | rowLevel |
L1、L2…(1-based) |
| 标签文本 | text |
显示内容 |
| 对齐 | align |
left / center / right |
| 横向合并 | colspan |
默认 1,最大不超过剩余列数 |
| 纵向合并 | rowspan |
默认 1,最大不超过当前行级到底部标签行 |
| 样式 | style |
LabelCellStyle 对象:见下表;服务端映射为 JsonCellStyle |
LabelCellStyle 字段(前端 → 服务端 JsonCellStyle / ExcelTemplateStyleHelper):
| 前端字段 | 类型 | 说明 |
|---|---|---|
fontSize |
number |
字号(pt),默认 11 |
fontWeight |
string | number |
字重,如 "700" / "bold" |
fontStyle |
string |
斜体:"italic"(优先于 fontItalic) |
fontItalic |
boolean |
斜体(兼容字段) |
textDecoration |
string |
"underline" / "line-through" |
color |
string |
前景色,如 "#2e7d32" |
backgroundColor |
string |
背景色,如 "#e8f5e9" |
合并渲染规则(labelRow.ts):
isLabelPositionCovered():判断某(rowLevel, columnIndex)是否被上方单元格的 rowspan/colspan 覆盖。buildLabelRowRenderCells():构建单行可见单元格列表,跳过被覆盖位置,保留columnIndex供导出定位。normalizeLabelCell():写入时校验 colspan/rowY 不越界。
主从结构导入设计¶
- 导入字段按 Sheet 分组,设计器以多 Sheet 页签组织
- 主 Sheet(
sheetType: 'master'):业务主体,一行一条主记录 - 从 Sheet(
sheetType: 'detail'):业务明细,一行一条从记录,通过字段级relation与主 Sheet 字段建立关联 - 同一模板仅允许一个主 Sheet;切换某 Sheet 为主时,其余自动降为从 Sheet
- 主从模式通过 Prop
enableMasterDetail开启,默认关闭
关联字段(MasterDetailRelation)
主从关联配置在 列信息(FieldSetting.vue)中完成,而非 Sheet 属性。每个从 Sheet 字段可独立配置与主表字段的映射:
| 字段 | 说明 |
|---|---|
masterKey |
主表字段 key(下拉选项来自主 Sheet 的 fieldList) |
detailKey |
子表字段 key(即当前列字段的 key,保存时自动同步) |
配置界面:
- 子表字段:只读展示当前列名称与 Key
- 主表字段:下拉选择主 Sheet 中要对齐的字段;清空表示取消关联
显示条件:enableMasterDetail === true 且当前 Sheet 为从 Sheet 且主 Sheet 已有字段。
序列化示例(从 Sheet 某一列):
兼容迁移(serialize.ts):
- 旧版模板在从 Sheet 上存
relationField(仅主表字段 Key),加载 / 序列化时自动迁移为字段级relation - 迁移策略:写入该从 Sheet 第一个尚未配置
relation的字段(若全部已配置则写入第一个字段) - 序列化时同步
relation.detailKey与字段key,保证双向映射一致
从 Sheet 绑定继承:
- 从 Sheet 的「导出到」「数据源」继承自主 Sheet(
DragFieldList.vue只读展示) - 各从 Sheet 可独立配置目标表 / 表单字段列表,主从关联在各列的
relation上分别维护
表单 / 数据库字段获取(设计阶段)¶
表单模式(exportTarget === 'form'):
hostRequestBridge.getModules()→ 模块列表hostRequestBridge.getModuleForms(moduleId)→ 表单列表hostRequestBridge.getFormSchema(moduleId, formId)→ 可绑定字段(name/label)
数据库表模式(exportTarget === 'database'):
hostRequestBridge.getAppDataSources(appId)→ 数据源列表hostRequestBridge.getDataSourceTables(appId, dataSourceId)→ 目标表列表-
hostRequestBridge.getDataSourceTableSchema?(appId, dataSourceId, tableId)→ 表字段(可选,用于字段库) -
组件 Prop
hostRequestBridge可选;本地开发 / 演示使用FakeHostApi(utils/hostApi.ts) - 组件 Prop
appId传入应用 ID;Mock 默认demo_app - 主从结构下,各 Sheet 独立配置绑定关系并拉取字段
撤销 / 重做功能设计¶
实现方案(Pinia 状态快照 + deepClone):
- 撤销栈
undoStack、重做栈redoStack,默认最大 20 步(PropmaxStep可配置) - 每次增删改配置后调用
recordSnapshot(),快照包含templateName、templateDesc、sheets、activeSheetId - 使用
deepClone(JSON 序列化)替代structuredClone,兼容 Pinia Proxy
可回溯操作:字段拖拽排序、新增/删除字段、属性修改、标签增删改、Sheet 操作、模板加载、清空画布。
JSON 模板弹窗¶
工具栏「JSON模板」按钮打开 JsonTemplateModal.vue:
| 项 | 说明 |
|---|---|
| 展示 | 多行 textarea,等宽字体,打开时自动填入当前模板的格式化 JSON(exportTemplateJson()) |
| 取消 | 关闭弹窗,不修改画布 |
| 应用 | 校验 JSON 格式 → loadTemplate()(自动 ensurePrimaryLabelRow()、迁移旧版 relationField)→ 触发 change 事件;格式错误时在弹窗内提示 |
| 用途 | 查看/复制当前模板、粘贴外部 JSON 批量导入、手工微调模板结构 |
模板能力总览¶
| 能力 | 实现 | 说明 |
|---|---|---|
| 查看/编辑 JSON | Toolbar → JsonTemplateModal |
弹窗内多行文本框展示与编辑 TemplateJson |
| 导出 Excel | — | 由宿主调用服务端 export-excel 接口生成(见「Excel 导出接口(后端)」) |
| 持久化 | change 事件 |
配置变更时返回 TemplateJson,由宿主写入 jsonTemplate 等后端字段 |
Excel 模板生成规则(服务端)¶
前端设计器仅维护
TemplateJson;.xlsx文件由后端ExcelTemplateBuilder生成,规则如下。HTTP 入口见「方式二:服务端按 jsonTemplate 生成」。
导出范围(每个 Sheet):
- ✅ 列标签行(L1 ~ Ln)
- ✅ 数据行区域(自数据行起向下 **500 行**预置格式单元格,
TEMPLATE_FORMAT_ROW_COUNT) - ❌ 字段名行、列字母行(仅设计器辅助,不写入 Excel)
样式(ExcelTemplateStyleHelper + Apache POI):
- 列标签:来自
LabelSetting配置的字体、颜色、背景、对齐、边框;字号写入 POIFont.setFontHeightInPoints(pt) - 数据列:按字段类型写入
numberFormat/dateFormat/timeFormat/datetimeFormat(通过resolveNumFmt) - 数据行行高取自
sheet.dataRow.height(pt)或全局config.excelRowHeight(默认 15);预置格式行沿用数据行行高 - 空白单元格:补全边框
单元格格式与值(ExcelTemplateBuilder):
- 数值 / 日期 / 时间 / 日期时间类型写入 Excel 数字单元格,并设置对应
DataFormat - 示例默认值按类型解析为 Excel 序列值(日期序列、时间小数等)
- 空白数据单元格保留数值型以便 Excel 应用格式
数据验证(ExcelTemplateBuilder):
- 由 POI 直接写入
<dataValidations> validationRule === 'none'时不写入校验(填写方式为下拉选择时仍写入 list 验证)- 支持:数值范围、日期/时间/日期时间范围、文本长度、正则(含手机号/邮箱/身份证内置公式)、下拉列表
- 日期范围:使用
=AND(A1>=DATE(y,m,d), A1<=DATE(...))公式,兼容yyyy-MM-dd与yyyy/MM/dd等输入格式 - 验证范围:数据行至 Excel 最大行(1048576)
合并处理(ExcelTemplateBuilder):
- 根据各标签的
colspan/rowspan写入合并区域 - 使用
LabelRenderCell.columnIndex定位单元格,避免纵向合并导致列索引偏移
数据行内容:sheet.dataRow.isExample === true 时,首行数据单元格填充各字段 defaultValue(按类型解析),其余预置行为空。
状态管理设计(Pinia)¶
Store¶
单一 Store useExcelDesignStore(store/excelDesign.ts)。
核心 State¶
// 模板基础信息
templateName: string
templateDesc: string
sheets: ExcelSheet[]
// 拖拽 / 选中
activeSheetId: string | null
dragCurrentItem: ExcelFieldItem | null
dragPlaceholderIndex: number
selection: SelectionState // type: column | label | dataRow | sheet | null
// 操作快照
undoStack: ExcelStateSnapshot[]
redoStack: ExcelStateSnapshot[]
maxSnapshot: number // 默认 20
// 全局配置
config: DesignerConfig
// 字段库(当前 Sheet 表单 schema)
availableFields: FormField[]
核心类型定义¶
interface MasterDetailRelation {
masterKey: string // 主表字段 key
detailKey: string // 子表字段 key
}
interface ExcelSheet {
id: string
name: string
exportTarget?: ExportTarget // 'form' | 'database',默认 'form'
moduleId: string
formId: string
dataSourceId?: string // exportTarget === 'database'
targetTableId?: string // exportTarget === 'database'
sheetType: 'master' | 'detail'
fieldList: ExcelFieldItem[]
labelRows: ColumnLabelCell[]
dataRow: DataRowConfig
}
interface ExcelFieldItem {
id: string
key: string
name: string
required: boolean
dataType: DataType // string | number | date | time | datetime | phone | email | idCard
validationRule?: ValidationRule // none | numberRange | dateRange | textLength | regex,默认 none
lengthLimit?: [number, number]
dateRange?: [string, string]
timeRange?: [string, string]
pattern?: string
patternTip?: string
fillMode?: FillMode // input | select,默认 input
selectOptions?: string[]
numberFormat?: NumberFormat
dateFormat?: DateOnlyFormat
timeFormat?: TimeFormat
datetimeFormat?: DateTimeFormat
defaultValue?: string
transform?: Record<string, string>
relation?: MasterDetailRelation // 主从关联,仅从 Sheet 字段有效
}
interface LabelCellStyle {
fontSize?: number
fontWeight?: string | number
fontStyle?: string // 'italic'
fontItalic?: boolean
textDecoration?: string // 'underline' | 'line-through'
color?: string
backgroundColor?: string
}
interface ColumnLabelCell {
id: string
columnIndex: number
rowLevel: number
text: string
align: 'left' | 'center' | 'right'
colspan: number
rowspan: number
style?: LabelCellStyle
}
interface DataRowConfig {
height: number // 行高(pt),默认 15
isExample: boolean
style?: LabelCellStyle | string // 对象或 JSON 字符串;服务端解析为 JsonCellStyle
}
interface DesignerConfig {
showRequiredMark: boolean // 默认 true
openValidateTip: boolean // 默认 true
excelRowHeight: number // 全局行高(pt),默认 15;非数据行及 dataRow.height 为空时的兜底
}
interface TemplateJson {
templateName: string
templateDesc: string
sheets: ExcelSheet[]
config: DesignerConfig
}
interface ExcelStateSnapshot {
templateName: string
templateDesc: string
sheets: ExcelSheet[]
activeSheetId: string | null
timestamp: number
}
interface HostRequestBridge {
getModules(): Promise<ModuleItem[]>
getModuleForms(moduleId: string): Promise<FormItem[]>
getFormSchema(moduleId: string, formId: string): Promise<FormField[]>
getAppDataSources(appId: string): Promise<DataSourceItem[]>
getDataSourceTables(appId: string, dataSourceId: string): Promise<DataSourceTableItem[]>
getDataSourceTableSchema?(appId: string, dataSourceId: string, tableId: string): Promise<FormField[]>
}
核心 Actions¶
| 分类 | Actions |
|---|---|
| Sheet | addSheet、deleteSheet、updateSheet、setActiveSheet |
| 主从 | setSheetType |
| 字段 | addField、addFieldFromForm、deleteField、updateField(含 relation.detailKey 同步)、sortField |
| 标签 | updateLabelCell、addLabelRow |
| 数据行 | updateDataRow |
| 选中 | setSelection、setAvailableFields |
| 快照 | recordSnapshot、undo、redo、clearSnapshot |
| 模板 | loadTemplate、saveTemplate、exportTemplateJson、clearCanvas |
Getters¶
activeSheet:当前激活 SheetcanUndo/canRedotemplateJson:实时序列化结果masterSheet:主 Sheet 引用
项目目录结构¶
web/obpm-designer-excelimp/
├── src/
│ ├── components/
│ │ └── ExcelDesigner/
│ │ ├── index.vue # 入口组件(DndProvider 包裹)
│ │ ├── Toolbar.vue # 工具栏(SVG 图标 + JSON 模板弹窗)
│ │ ├── JsonTemplateModal.vue # JSON 模板查看/编辑弹窗
│ │ ├── ConfirmModal.vue # 通用确认弹窗
│ │ ├── DragFieldList.vue
│ │ ├── DragCanvas.vue
│ │ ├── CanvasColumn.vue # 字段名列拖拽
│ │ ├── FieldItem.vue
│ │ ├── FieldSetting.vue
│ │ ├── fieldOptions.ts # 列信息选项常量
│ │ ├── LabelSetting.vue
│ │ ├── SheetSetting.vue
│ │ ├── SheetTabs.vue
│ │ └── constants.ts
│ ├── store/
│ │ └── excelDesign.ts
│ ├── utils/
│ │ ├── excel.ts # 从表单字段创建 ExcelFieldItem
│ │ ├── labelRow.ts # 标签行渲染 / 合并 / 列索引同步
│ │ ├── preview.ts # columnLetter 等
│ │ ├── snapshot.ts
│ │ ├── serialize.ts
│ │ ├── validate.ts
│ │ ├── deepClone.ts
│ │ └── hostApi.ts # FakeHostApi
│ ├── types/
│ │ └── index.ts
│ ├── style/
│ │ ├── index.scss
│ │ └── preview-table.scss
│ ├── index.ts # 库模式入口
│ ├── App.vue
│ └── main.ts
├── vite.config.ts
└── package.json
Vite 打包 & ESM 组件导出¶
打包配置¶
// vite.config.ts(mode === 'lib' 时生效)
build: {
lib: {
entry: 'src/index.ts',
name: 'ExcelImportDesigner',
fileName: (format) => `excel-import-designer.${format}.js`,
formats: ['es', 'umd'],
},
rollupOptions: {
external: ['vue', 'pinia', 'vue3-dnd', 'react-dnd-html5-backend'],
},
}
package.json 导出¶
{
"name": "obpm-designer-excelimp",
"main": "./dist/excel-import-designer.umd.js",
"module": "./dist/excel-import-designer.es.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/excel-import-designer.es.js",
"require": "./dist/excel-import-designer.umd.js",
"types": "./dist/index.d.ts"
}
}
}
对外导出(src/index.ts)¶
export { ExcelImportDesigner, useExcelDesignStore }
export * from './types'
export * from './utils/excel'
export * from './utils/serialize'
export * from './utils/validate'
export * from './utils/hostApi'
export default ExcelImportDesigner
第三方引用示例¶
<script setup lang="ts">
import ExcelImportDesigner, { useExcelDesignStore, FakeHostApi } from 'obpm-designer-excelimp'
import type { TemplateJson } from 'obpm-designer-excelimp'
const bridge = new FakeHostApi() // 或宿主实现的 HostRequestBridge
function handleChange(template: TemplateJson) {
// 配置变更时持久化 template(如 debounce 后 PUT excelconfigs)
}
</script>
<template>
<ExcelImportDesigner
:host-request-bridge="bridge"
app-id="my_app_id"
:enable-master-detail="true"
@change="handleChange"
/>
</template>
const store = useExcelDesignStore()
store.undo()
store.exportTemplateJson()
// Excel 模板导出:宿主调用服务端 export-excel 接口
组件属性与事件(对外 API)¶
Props¶
| Prop | 类型 | 默认 | 说明 |
|---|---|---|---|
templateJson |
TemplateJson |
— | 初始模板,支持回显 |
disabled |
boolean |
false |
禁用编辑 |
maxStep |
number |
20 |
最大撤销步数 |
showToolbar |
boolean |
true |
是否展示工具栏 |
defaultFields |
FormField[] |
— | 预留:预设字段(当前未使用) |
enableMasterDetail |
boolean |
false |
启用主从多 Sheet 模式 |
hostRequestBridge |
HostRequestBridge |
FakeHostApi |
宿主模块/表单/数据源/字段 API |
appId |
string |
— | 应用 ID,拉取数据源与目标表;Mock 默认 demo_app |
Emits¶
| 事件 | payload | 说明 |
|---|---|---|
change |
TemplateJson |
配置变更(含编辑、撤销/重做、JSON 模板应用等) |
undo |
— | 撤销后触发 |
redo |
— | 重做后触发 |
样式与兼容性¶
- 组件使用
scopedSCSS,画布表格复用preview-table.scss .form-group表单项为上下结构(标签在上、控件在下,index.scss)- 兼容 Vue 3.3+、Pinia 2+/3+、Vite 8+
- Peer Dependencies:
vue、pinia、vue3-dnd
已知限制与后续规划¶
| 项 | 状态 | 说明 |
|---|---|---|
| 模板预览弹窗 | 已移除 | 早期 TemplatePreview 组件已删除,以画布实时预览为准 |
| 必填标识 | 配置已有 | config.showRequiredMark 由服务端导出时追加 * |
| 校验提示 | 部分实现 | 服务端导出已写入数据验证;config.openValidateTip 全局开关待统一 |
核心亮点¶
- 完全解耦可复用:独立 ESM/UMD 组件,宿主 API 桥接
- 多级表头完备:colspan + rowspan 组合合并,画布预览与服务端导出规则一致
- 完整操作回溯:Pinia 快照 + deepClone,覆盖标签与字段全部操作
- 模板能力完备:JSON 模板弹窗编辑 + 服务端 Excel 模板生成,Excel 仅含业务表头与数据行
- TypeScript 全量支持:类型完整导出
- FakeHostApi 开箱调试:无需宿主即可本地开发验证
设计时态后端实现(Java)¶
Excel 导入后端分 设计时态(obpm-designer)与 运行时态(obpm-runtime),领域模型与持久化逻辑在 obpm-core。前端组件库(obpm-designer-excelimp)通过 hostRequestBridge 桥接,不直接访问后端;obpm-designer-vue3 负责实现桥接层,将 TypeScript 接口映射为下方 HTTP 端点。
模块职责¶
| 模块 | 职责 |
|---|---|
obpm-designer |
设计时 REST 控制器:Excel 导入配置 CRUD、导入模板导出、模块/表单/数据源元数据 |
obpm-core |
IMPMappingConfigDesignTimeService:配置持久化;ExcelMappingDiagram / ImpExcelToDoc:运行时 XML 解析与导入执行;ExcelImportRuntimeService / JsonImportProvider / ImportPlanBuilder:JSON 模板运行态导入;ExcelTemplateExportService / ExcelTemplateBuilder / ExcelTemplateStyleHelper:按 jsonTemplate 生成导入模板 |
obpm-common |
HttpDownloadHelper:导出响应头(中文文件名 RFC 5987 编码) |
obpm-runtime |
视图 Excel 导入/校验动作入口;视图数据导出 Excel;导入样例模板下载;ExcelImportRuntimeController:运行时动态导入模板导出 |
obpm-designer-vue3 |
旧版 iframe 画布(public/excelHtml)及高级工具列表页(ExcelConf.vue);新版设计器桥接(ObpmExcelHostBridge)待接入 |
存储模型¶
复用现有 IMPMappingConfigVO(cn.myapps.core.common.model.excelimport.IMPMappingConfigVO),文件系统持久化,挂在应用(parentId = applicationId)下。
| 字段 | 用途 |
|---|---|
id |
配置 ID,对应视图动作 impmappingconfigid |
name |
配置名称(应用内唯一) |
description |
配置说明 |
applicationid / parentId |
所属应用 ID |
templateType |
模板类型:空 / EXCEL_XML 为旧画布;EXCEL_JSON 为新版 JSON |
xml |
旧版画布映射 XML(ExcelMappingDiagram 序列化,CDATA 存储) |
jsonTemplate |
新版设计器完整 JSON(TemplateJson,CDATA 存储) |
templatePath |
上传的 Excel 样例模板路径,前台导入时可下载参考 |
持久化路径遵循 IMPMappingConfigVO.getPath(),保存在 storage/workspace/{applicationId}/excelconfig/ 下(ModelSuffix.EXCEL_IMPORT_CFG_*)。
// obpm-core/.../IMPMappingConfigVO.java
public static final String TYPE_EXCEL_XML = "EXCEL_XML";
public static final String TYPE_EXCEL_JSON = "EXCEL_JSON";
@XmlElement
private String templateType;
@XmlJavaTypeAdapter(CDataAdapter.class)
private String xml;
@XmlJavaTypeAdapter(CDataAdapter.class)
private String jsonTemplate;
@XmlElement
private String templatePath;
public boolean isExcelJsonTemplate() { ... }
public boolean isExcelXmlTemplate() { ... }
读取兼容:getXml() 自动将历史包名 cn.myapps.core.dynaform.dts.excelimport 替换为 cn.myapps.runtime.dynaform.dts.excelimport,保证升级后旧配置仍可解析。
统一约定¶
Base URL¶
| 服务 | 端口 | 基础路径 |
|---|---|---|
| 设计器 | 8082 |
http://{host}:8082{contextPath}/api/designtime |
contextPath 由 myapps.context-path.designer 配置,默认可为空(如 /designer)。
响应格式¶
与现有设计器 API 一致,返回 Resource 对象:
旧 ExcelImp 实现(XML 画布)¶
状态:已实现
Controller:cn.myapps.designtime.dts.excelimport.config.controller.ExcelConfigsController
Service:cn.myapps.core.designtime.dts.excelimport.config.service.IMPMappingConfigDesignTimeService/IMPMappingConfigDesignTimeServiceImpl
DAO:cn.myapps.core.common.dao.excelimport.FileSystemIMPMappingConfigDAO
基础路径:/api/designtime/applications/{applicationId}/excelconfigs
IMPMappingConfigDesignTimeService 继承标准 DesignTimeService<IMPMappingConfigVO>,通过 DesignTimeServiceManager.impMappingConfigDesignTimeService() 获取实例,并注册于 DesignTimeServiceFactory。
类结构¶
classDiagram
class ExcelConfigsController {
+doGetExcelList()
+doGetExcelDetailed()
+doCreateForm()
+doUpdateForm()
+doDeleteForm()
+exportExcelByConfigId()
+exportExcelByBody()
}
class IMPMappingConfigDesignTimeService {
+query()
+findById()
+save()
+update()
+delete()
+mergeOnUpdate()
+normalizeBeforeSave()
+validateBeforeSave()
}
class IMPMappingConfigVO {
+templateType
+xml
+jsonTemplate
+templatePath
+isExcelJsonTemplate()
+isExcelXmlTemplate()
}
class ExcelMappingDiagram {
+MasterSheet
+DetailSheet
+Column
+Relation
}
ExcelConfigsController --> IMPMappingConfigDesignTimeService
IMPMappingConfigDesignTimeService --> IMPMappingConfigVO
ExcelMappingDiagram --> IMPMappingConfigVO : xml 序列化
旧版 XML 映射结构¶
设计器 iframe(obpm-designer-vue3/public/excelHtml)将画布导出为 ExcelMappingDiagram XML,运行时由 Factory.trnsXML2Dgrm(xml) 反序列化:
| XML 元素 | 说明 |
|---|---|
ExcelMappingDiagram |
根节点,含 description、needRollback 等 |
MasterSheet |
主工作表,绑定主表单 |
DetailSheet |
子工作表,绑定子表单 |
Column |
列映射:fieldName、primaryKey、valueScript、validateRule |
Relation |
工作表与列的关联线 |
接口清单¶
GET /{applicationId}/excelconfigs¶
获取当前应用下的 Excel 导入配置列表。
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
name |
string |
否 | 按名称模糊查询 |
pageNo |
int |
否 | 页码,默认 1 |
linesPerPage |
int |
否 | 每页条数,默认 10 |
响应 data:DataPackage<IMPMappingConfigVO>。
GET /{applicationId}/excelconfigs/{excelConfigId}¶
获取配置详情。旧版配置返回 xml + templatePath;新版配置(templateType = EXCEL_JSON)返回 jsonTemplate + templatePath,xml 为空。
实现:IMPMappingConfigDesignTimeService.findById。
POST /{applicationId}/excelconfigs¶
新建 Excel 导入配置。
请求体:IMPMappingConfigVO JSON。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
name |
string |
是 | 配置名称(应用内唯一) |
description |
string |
否 | 配置说明 |
templateType |
string |
否 | 空 / EXCEL_XML 为旧版;EXCEL_JSON 为新版 |
xml |
string |
否 | 旧版画布 XML(templateType 非 EXCEL_JSON 时使用) |
jsonTemplate |
string |
条件 | templateType = EXCEL_JSON 时必填,完整 TemplateJson 字符串 |
templatePath |
string |
否 | 样例 Excel 模板路径 |
服务端分配 id(Sequence.getDesignTimeSequence()),强制 applicationid / parentId 为路径中的应用 ID。
保存流程:normalizeBeforeSave → doSaveValidate(名称唯一)→ validateBeforeSave(JSON 格式校验)。
响应 data:{ "id": "..." }。
PUT /{applicationId}/excelconfigs/{excelConfigId}¶
更新配置,请求体字段同 POST,id 取自路径。
更新合并:mergeOnUpdate 在请求体未携带 jsonTemplate / xml / templatePath / description 时,自动保留已有值,避免部分更新清空模板内容。
保存流程:mergeOnUpdate → normalizeBeforeSave → doSaveValidate → validateBeforeSave → update。
DELETE /{applicationId}/excelconfigs¶
批量删除,请求体为 ID 数组 string[]。
前端接入(旧版)¶
| 场景 | 实现 |
|---|---|
| 高级工具列表 | obpm-designer-vue3/src/components/AdvancedTool/ExcelConf.vue |
| 画布设计 | iframe 加载 public/excelHtml/excelHtmlTem.html |
| API 封装 | AdvancedToolAPI.js → getExcelConfList / updataExcelConf / deleteExcelConfList |
| 视图动作绑定 | ViewApi.js 拉取配置列表,视图 Activity.impmappingconfigid 关联配置 |
新 ExcelImp 兼容设计(JSON 模板)¶
状态:已实现(设计时态)
在不破坏旧版xml配置的前提下,为新版obpm-designer-excelimp的TemplateJson提供独立存储字段,模式参考打印设计器Report.printTemplate+templateType。
IMPMappingConfigVO 扩展¶
/** 旧版 iframe 画布 XML 映射 */
public static final String TYPE_EXCEL_XML = "EXCEL_XML";
/** 新版 JSON Excel 导入设计器模板 */
public static final String TYPE_EXCEL_JSON = "EXCEL_JSON";
/** 模板类型:空或 EXCEL_XML 表示旧画布 XML;EXCEL_JSON 表示新版 JSON */
@XmlElement
private String templateType;
/** 新版设计器完整 JSON(templateName + sheets + config),CDATA 存储 */
@XmlJavaTypeAdapter(CDataAdapter.class)
private String jsonTemplate;
public boolean isExcelJsonTemplate() { return TYPE_EXCEL_JSON.equals(templateType); }
public boolean isExcelXmlTemplate() { return templateType 为空或 TYPE_EXCEL_XML; }
templateType |
读写字段 | 设计器 |
|---|---|---|
空 / EXCEL_XML |
xml + templatePath |
旧 iframe 画布 |
EXCEL_JSON |
jsonTemplate + templatePath(可选,由服务端 export-excel 生成后上传) |
obpm-designer-excelimp |
IMPMappingConfigDesignTimeService 扩展方法¶
| 方法 | 说明 |
|---|---|
mergeOnUpdate(existing, incoming) |
更新时合并已有配置;请求体未携带的 jsonTemplate / xml / templatePath / description 保留原值 |
normalizeBeforeSave(vo) |
EXCEL_JSON 时清空 xml;EXCEL_XML(或空)时清空 jsonTemplate 并补全 templateType |
validateBeforeSave(vo) |
EXCEL_JSON 时校验 jsonTemplate 非空且为合法 JSON;不支持的 templateType 抛出校验异常 |
兼容策略:
- 旧配置无
templateType,运行时继续走vo.getXml()→Factory.trnsXML2Dgrm。 - 新配置
templateType = EXCEL_JSON时,xml留空;保存/加载仅读写jsonTemplate。 ExcelConfigsController的 POST/PUT 路径不变,请求体增加templateType/jsonTemplate即可;doSaveValidate仍校验name唯一。- 应用概览 PDF(
ExcelImportConfigOverview)已区分 JSON / XML 模板类型展示。 - 运行时 JSON 解析器已实现:
TemplateJson.sheets→ImportPlan,与旧ImpExcelToDoc并列,在ActivityRunTimeServiceImpl内按templateType分支。
TemplateJson 与旧 XML 概念映射¶
新版 TemplateJson |
旧版 XML |
|---|---|
sheets[].sheetType: 'master' |
MasterSheet |
sheets[].sheetType: 'detail' + fieldList[].relation(masterKey ↔ detailKey) |
DetailSheet + Relation(工作表与列关联线) |
sheets[].fieldList[](name / key / 校验 / 默认值) |
Column(fieldName / validateRule / valueScript) |
sheets[].labelRows[](多级表头) |
旧版无等价结构,运行时按列序读取 |
sheets[].exportTarget(form / database) |
旧版仅支持表单绑定 |
hostRequestBridge 映射(设计态)¶
新版组件 HostRequestBridge 所需元数据 API 大部分复用现有端点,无需为 Excel 导入单独新建模块/数据源接口:
| 前端方法 | HTTP 端点 | 说明 |
|---|---|---|
getModules() |
GET /api/designtime/applications/{appId}/modules?parentId={appId} |
复用 ModuleDesignTimeController |
getModuleForms(moduleId) |
GET /api/designtime/applications/{appId}/modules/{moduleId}/forms |
复用 FormController |
getFormSchema(moduleId, formId) |
GET /api/designtime/applications/{appId}/print/form-schema?moduleId=&formId= |
复用 PrintDesignTimeController.getFormSchema(返回 name / label 字段列表) |
getAppDataSources(appId) |
GET /api/designtime/applications/{appId}/datasources |
复用 DataSourceController |
getDataSourceTables(appId, dsId) |
GET /api/designtime/applications/{appId}/datasources/metadatas?datasourceId={dsId}&subNodes=isTables |
复用元数据树接口,取表节点 |
getDataSourceTableSchema(appId, dsId, tableId) |
GET /api/designtime/applications/{appId}/datasources/{dsId}/forms/{tableId}/info |
复用 DataSourceController.getTable(列名 / 类型) |
| 加载配置 | GET /api/designtime/applications/{appId}/excelconfigs/{id} |
回显 jsonTemplate(新)或 xml(旧) |
| 保存配置 | PUT /api/designtime/applications/{appId}/excelconfigs/{id} |
写入 templateType=EXCEL_JSON + jsonTemplate |
| 新建配置 | POST /api/designtime/applications/{appId}/excelconfigs |
同上 |
| 导出 Excel 模板(服务端,已保存配置) | GET /api/designtime/applications/{appId}/excelconfigs/{id}/export-excel |
下载文件名取 IMPMappingConfigVO.name;见「Excel 导出接口(后端)」 |
| 预览导出 Excel 模板(服务端) | POST /api/designtime/applications/{appId}/excelconfigs/export-excel |
请求体含 name + jsonTemplate 或完整 TemplateJson;文件名优先 name,否则 templateName |
| 上传样例模板 | POST /api/designtime/applications/{appId}/uploads?path=... |
回写 templatePath |
obpm-designer-vue3 中计划新增 ObpmExcelHostBridge(命名参考 ObpmPrintHostBridge),实现 HostRequestBridge 并注入 @myapps/obpm-designer-excelimp 的 ExcelImportDesigner。
保存时请求体示例(新版):
{
"name": "订单导入模板",
"description": "主从订单导入",
"templateType": "EXCEL_JSON",
"jsonTemplate": "{\"templateName\":\"订单导入模板\",\"templateDesc\":\"...\",\"sheets\":[...],\"config\":{...}}",
"templatePath": "/uploads/excel/order-template.xlsx"
}
运行时态¶
Excel 导入运行态由 obpm-runtime 的 ActivityController 提供 HTTP 入口,核心导入逻辑在 obpm-core(ImpExcelToDoc / AbstractImportProvider)。前台视图「导入 Excel」动作(ActivityType.EXCEL_IMPORT = 27)通过 Activity.impmappingconfigid 关联设计态配置,上传的 Excel 文件路径由前台传入。
统一约定¶
Base URL¶
| 服务 | 端口 | 基础路径 |
|---|---|---|
| 运行时 | 8083 |
http://{host}:8083{contextPath}/api/runtime |
contextPath 由 myapps.context-path.runtime 配置,默认可为空(如 /obpm)。
响应格式¶
与现有运行时 API 一致,返回 Resource 对象。导入成功时 data 为国际化结果字符串;失败时 errcode = 4001,data 为错误信息数组($$ 分隔的多条校验错误)。
前台调用流程¶
sequenceDiagram
participant V as excel_upload.vue
participant U as 文件上传
participant A as ActivityController
participant S as ActivityRunTimeServiceImpl
V->>V: 下载样例模板(Activity.excelTemplate)
V->>U: 上传 .xls/.xlsx,获得 path
opt 校验
V->>A: POST validationExcel
A->>S: validationExcel()
end
V->>A: POST importExcel(轮询 readProcess)
A->>S: improtExcel()
S-->>V: 导入结果 / 进度
- 视图加载时,
ViewController将Activity.excelTemplate(来自IMPMappingConfigVO.templatePath)加密后下发,供前台下载样例模板。 - 用户选择 Excel 文件,经
FrontFileAndImageUploadServlet或POST /api/runtime/upload(actionType=excelImport)上传,得到服务器相对路径path。 - 可选先调用 校验 接口;校验通过后再调用 导入 接口。
- 导入过程中前台每 500ms 轮询
readProcess获取进度;完成后删除临时 Excel 文件。
已实现(XML 配置)¶
状态:已实现
Controller:cn.myapps.runtime.activity.controller.ActivityController
Service:cn.myapps.runtime.activity.service.ActivityRunTimeServiceImpl
基础路径:/api/runtime
类结构¶
classDiagram
class ActivityController {
+importExcel()
+validationExcel()
+readProcess()
+readValidateProcess()
}
class ActivityRunTimeServiceImpl {
+improtExcel()
+validationExcel()
}
class ExcelImportRuntimeServiceImpl {
+importExcel()
+validationExcel()
}
class JsonImportProvider {
+creatDocument()
+validationDocument()
}
class ImpExcelToDoc {
+creatDocument()
+validationDocument()
}
class AbstractImportProvider {
+creatDocument()
+validationDocument()
}
class Factory {
+trnsXML2Dgrm(xml)
}
ActivityController --> ActivityRunTimeServiceImpl
ActivityRunTimeServiceImpl --> ExcelImportRuntimeServiceImpl : EXCEL_JSON
ActivityRunTimeServiceImpl --> Factory : EXCEL_XML
ActivityRunTimeServiceImpl --> ImpExcelToDoc : EXCEL_XML
ExcelImportRuntimeServiceImpl --> JsonImportProvider
ImpExcelToDoc --> AbstractImportProvider
接口清单¶
POST /{applicationId}/views/{viewId}/activities/importExcel¶
执行 Excel 数据导入。
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
excelImportTime |
string |
是 | 导入批次时间戳,用于进度缓存键 |
请求体(Content-Type: application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
impmappingconfigid |
string |
是 | Excel 导入配置 ID |
path |
string |
是 | 已上传 Excel 的服务器相对路径 |
actId |
string |
是 | 视图动作 ID |
parentId |
string |
否 | 主从导入时的父文档 ID |
isRelate |
string |
否 | 是否关联导入("true" / "false") |
exparams |
object |
否 | 扩展参数(appId、formId、docid 等) |
请求体示例
{
"impmappingconfigid": "__impConfig001",
"path": "/uploads/excel/20260710/order.xlsx",
"actId": "__actImport001",
"parentId": "",
"exparams": {
"appId": "sOZu9kthmxyP8qQfq0e",
"formId": "form_order",
"docid": "",
"parentId": "",
"isRelate": false
}
}
实现:ActivityController.importExcel → 初始化进度缓存 → ActivityRunTimeServiceImpl.improtExcel → IMPMappingConfigDesignTimeService.doView → 按 templateType 分支:
- EXCEL_JSON:ExcelImportRuntimeServiceImpl.importExcel → JsonImportProvider.creatDocument
- EXCEL_XML / 空:Factory.trnsXML2Dgrm(vo.getXml()) → ImpExcelToDoc.creatDocument → DocumentProcess.doCreateOrUpdate4ExcelImport
成功响应:errcode = 0,data 含 cn.myapps.runtime.dynaform.dts.excelimport.success.total.imported 国际化键及导入行数。
失败响应:errcode = 4001,errmsg = "导入出错",data 为 $$ 分隔的错误行数组。
POST /{applicationId}/views/{viewId}/activities/validationExcel¶
导入前校验 Excel 数据(不写入文档)。
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
excelValidateTime |
string |
是 | 校验批次时间戳 |
请求体:与 importExcel 相同。
实现:ActivityRunTimeServiceImpl.validationExcel → 按 templateType 分支:
- EXCEL_JSON:ExcelImportRuntimeServiceImpl.validationExcel → JsonImportProvider.validationDocument
- EXCEL_XML / 空:ImpExcelToDoc.validationDocument → ValidationDocumentImprotProvider
GET /importExcel/readProcess¶
轮询导入进度。
查询参数:excelImportTime
响应 data
| 字段 | 说明 |
|---|---|
excelImportCount |
已处理行数 |
excelRowCount |
总行数 |
importExcelResult |
最终结果(导入完成后写入) |
GET /validateExcel/readProcess¶
轮询校验进度,字段同上(缓存键前缀为 excelValidate*)。
Service 层处理¶
ActivityRunTimeServiceImpl 在 improtExcel / validationExcel 内按 vo.isExcelJsonTemplate() 分支:
JSON 路径(EXCEL_JSON):
ExcelImportRuntimeService jsonService = new ExcelImportRuntimeServiceImpl();
result = jsonService.importExcel(vo, excelPath, user, params, applicationId, viewId);
// 或 validationExcel(...)
XML 路径(EXCEL_XML 或 templateType 为空,保持不变):
ExcelMappingDiagram em = Factory.trnsXML2Dgrm(vo.getXml());
ImpExcelToDoc imp = new ImpExcelToDoc(excelPath, em);
result = imp.creatDocument(em, excelPath, user, params, applicationId);
// finally: 删除临时 Excel 文件
| 类 | 职责 |
|---|---|
JsonImportProvider |
JSON 模板:读 Excel、校验、主从写入、进度上报 |
JsonExcelReader / JsonFieldValidator / JsonFieldValueConverter |
JSON 路径解析与字段处理 |
JsonFormImporter / JsonDatabaseImporter |
表单 / 数据库表写入 |
XLSDocumentImprotProvider / XLSXDocumentImprotProvider |
XML 路径:按 .xls / .xlsx 解析工作簿 |
AbstractImportProvider |
XML 路径:遍历行、执行值脚本/校验脚本、主从写入、进度上报 |
ValidationDocumentImprotProvider |
XML 路径:仅校验、不写库 |
前台封装¶
| 文件 | 方法 | 说明 |
|---|---|---|
obpm-runtime-web/portal/vue3/src/api.js |
importExcel |
type=excelimport → importExcel;否则 → validationExcel |
| 同上 | importExcelProgress |
轮询 /runtime/importExcel/readProcess |
excel_upload.vue |
performExport |
组装 impmappingconfigid / path / actId / exparams |
新版 JSON 配置(已实现)¶
状态:已实现(设计态
jsonTemplate存储 + 运行态 JSON 导入 + 服务端模板导出)
策略:复用现有ActivityController三端点,前台excel_upload.vue与api.js无需改动;在ActivityRunTimeServiceImpl内按IMPMappingConfigVO.templateType分支到ExcelImportRuntimeService。命名约定:前端 TypeScript 接口为
TemplateJson;后端 Java 模型类为JsonTemplate(cn.myapps.core.runtime.dynaform.dts.excelimport.json.model),二者结构同构。
类结构¶
classDiagram
class ActivityController {
+importExcel()
+validationExcel()
}
class ActivityRunTimeServiceImpl {
+improtExcel()
+validationExcel()
}
class ExcelImportRuntimeService {
+importExcel(vo, excelPath, user, params)
+validationExcel(vo, excelPath, user, params)
}
class ExcelImportRuntimeServiceImpl {
+createProvider(vo) JsonImportProvider
}
class JsonImportProvider {
+creatDocument()
+validationDocument()
}
class JsonExcelReader {
+readAllSheets(excelPath, plan)
}
class JsonFormImporter {
+importRow()
}
class JsonDatabaseImporter {
+importRow()
}
class ImpExcelToDoc {
+creatDocument()
}
ActivityController --> ActivityRunTimeServiceImpl
ActivityRunTimeServiceImpl --> ExcelImportRuntimeService : EXCEL_JSON
ActivityRunTimeServiceImpl --> ImpExcelToDoc : EXCEL_XML
ExcelImportRuntimeServiceImpl ..|> ExcelImportRuntimeService
ExcelImportRuntimeServiceImpl --> JsonImportProvider
JsonImportProvider --> JsonExcelReader
JsonImportProvider --> JsonFormImporter
JsonImportProvider --> JsonDatabaseImporter
| 类 | 模块 | 职责 |
|---|---|---|
ExcelImportRuntimeService |
obpm-core |
接口:importExcel / validationExcel |
ExcelImportRuntimeServiceImpl |
obpm-core |
解析 jsonTemplate、构建 ImportPlan、创建 JsonImportProvider |
JsonImportProvider |
obpm-core |
实现 ImportProvider;复用 AbstractImportProvider 进度缓存键;调度读 Excel 与写入 |
TemplateJsonParser |
obpm-core |
jsonTemplate 字符串 → JsonTemplate;解析 labelRows[].style、dataRow.style |
ImportPlanBuilder |
obpm-core |
JsonTemplate → ImportPlan(Sheet 绑定、列索引、主从关联、数据起始行) |
ImportPlan |
obpm-core |
运行时中间结构 |
JsonExcelReader |
obpm-core |
POI 读取 Excel,跳过标签行,按列序提取数据行 |
JsonFieldValidator |
obpm-core |
必填、数据类型、下拉、数值/日期/文本长度/正则校验 |
JsonFieldValueConverter |
obpm-core |
字段类型转换(number/date/time/datetime 等) |
JsonFormImporter |
obpm-core |
exportTarget=form:校验后调用 DocumentProcess.doCreateOrUpdate4ExcelImport |
JsonDatabaseImporter |
obpm-core |
exportTarget=database:JDBC insert/update(主键存在则更新) |
ActivityRunTimeServiceImpl 分支改造¶
IMPMappingConfigVO vo = process.doView(applicationId, impmappingconfigid);
if (vo.isExcelJsonTemplate()) {
ExcelImportRuntimeService jsonService = new ExcelImportRuntimeServiceImpl();
if (isValidate) {
return jsonService.validationExcel(vo, excelPath, user, params, applicationId, viewId);
}
return jsonService.importExcel(vo, excelPath, user, params, applicationId, viewId);
}
// 旧版 XML 路径(保持不变)
ExcelMappingDiagram em = Factory.trnsXML2Dgrm(vo.getXml());
ImpExcelToDoc imp = new ImpExcelToDoc(excelPath, em);
return imp.creatDocument(em, excelPath, user, params, applicationId);
JSON 导入处理流程¶
- 加载配置:
doView读取IMPMappingConfigVO,isExcelJsonTemplate()为真时取jsonTemplate。 - 解析模板:
TemplateJsonParser反序列化为JsonTemplate(sheets+config)。 - 构建导入计划:
- 按
sheets[].exportTarget区分表单(form)与数据库表(database)写入目标; sheetType: 'master'对应主记录;'detail'通过fieldList[].relation(masterKey↔detailKey)关联主 Sheet;- 列定位:跳过
labelRows标签行,从首个数据行起按fieldList列序读取(支持多级表头后的固定数据起始行)。 - 逐行处理(复用
AbstractImportProvider进度上报): - 字段类型转换(
fieldType/numberFormat/dateFormat等); - 校验规则(
validationRule、validationMin/Max、regexPattern、下拉列表); - 默认值与
transform字段转换; - 主键唯一性(
isPrimaryKey)→ 决定 create 或 update。 - 写入:表单模式调用
DocumentProcess.doCreateOrUpdate4ExcelImport;数据库模式直接 JDBC 写入目标表(新增)。 - 清理:与 XML 路径相同,finally 删除临时 Excel 文件。
XML 与 JSON 运行时差异¶
| 能力 | XML 旧版 | JSON 新版 |
|---|---|---|
| 配置来源 | vo.getXml() → ExcelMappingDiagram |
vo.getJsonTemplate() → JsonTemplate |
| 表头定位 | Relation 关联工作表与 Column |
labelRows 行级 + fieldList 列索引 |
| 主从结构 | MasterSheet / DetailSheet |
sheetType + relation 字段 |
| 字段校验 | validateRule iScript |
validationRule 内置规则 + 可扩展脚本 |
| 值处理 | valueScript iScript |
transform 映射 + 可扩展脚本 |
| 数据库表导入 | 不支持 | exportTarget = 'database' |
| 多级表头 | 不支持 | labelRows colspan/rowspan 定位数据行 |
| HTTP 端点 | ActivityController 现有三端点 |
相同,无需新 Controller |
错误与进度¶
JSON 路径**复用** AbstractImportProvider 的 MemoryCacheUtil 键(EXCELIMPORTCOUNT、EXCELIMPORTROWCOUNT、EXCELIMPORTRESULT 等),保证 readProcess 轮询逻辑不变。错误消息格式与 XML 路径一致(工作表名 {Row}[行号]: 错误详情),多条以 $$ 拼接返回。
Excel 导出接口(后端)¶
平台中与 Excel 相关的「导出」分两类,本章节说明后端 HTTP 入口(设计时态 + 运行时态):
| 类型 | 场景 | 产出物 | 典型调用方 |
|---|---|---|---|
| 导入模板导出 | 设计器配置完成 / 前台导入前 | 带表头、校验、示例行的**空白**导入模板 | 服务端 export-excel、excel_upload.vue |
| 视图数据导出 | 视图列表页「导出 Excel」按钮 | 当前视图查询结果的 .xlsx 数据文件 |
excel_export.vue |
导入模板导出与视图数据导出**职责不同**:前者服务「填表再导入」流程;后者服务「把已有数据导出备份/分析」流程(
ActivityType.EXPTOEXCEL = 16)。
设计时态 · 导入模板导出¶
方式一:上传样例模板(已实现)¶
宿主将服务端生成的 Excel(或用户自备模板)上传至服务器,路径写入 IMPMappingConfigVO.templatePath,供运行时前台下载参考。推荐在宿主监听 change 并持久化时,先调用 export-excel 获取二进制流,再上传至 uploads(ObpmExcelHostBridge 联动方案见方式二末尾)。
| 项 | 说明 |
|---|---|
| Controller | cn.myapps.designtime.common.controller.UploadDesignTimeController |
| 端点 | POST /api/designtime/applications/{applicationId}/uploads |
| 表单字段 | file(MultipartFile[]) |
| 查询参数 | path:存储子目录(旧版 Excel 配置 iframe 传 path=/exceltemplate 等) |
响应 data(JSONArray):
保存 Excel 配置时,将 filePath 写入 templatePath 字段(PUT/POST excelconfigs)。
旧版 iframe 上传页:obpm-designer-vue3/public/excelHtml/upload.html → /{contextPath}/designtime/applications/{appId}/uploads。
方式二:服务端按 jsonTemplate 生成(已实现)¶
状态:已实现
Controller:cn.myapps.designtime.dts.excelimport.config.controller.ExcelConfigsController
Service:cn.myapps.core.runtime.dynaform.dts.excelimport.json.ExcelTemplateExportService/ExcelTemplateExportServiceImpl
Builder:cn.myapps.core.runtime.dynaform.dts.excelimport.json.ExcelTemplateBuilder(Apache POIXSSFWorkbook)
Style:cn.myapps.core.runtime.dynaform.dts.excelimport.json.ExcelTemplateStyleHelper
响应头:cn.myapps.common.util.HttpDownloadHelper(obpm-common)
目标:设计器保存、批量生成、服务端预览、自动同步templatePath等场景,由 Java 按TemplateJson生成.xlsx(规则见上文「Excel 模板生成规则(服务端)」)。
端点
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/api/designtime/applications/{applicationId}/excelconfigs/{excelConfigId}/export-excel |
按已保存配置导出(exportExcelByConfigId) |
POST |
/api/designtime/applications/{applicationId}/excelconfigs/export-excel |
按请求体预览导出(exportExcelByBody,无需先保存) |
下载文件名(无 filename 查询参数;Controller 内 toXlsxFilename() 自动补 .xlsx):
| 端点 | 文件名来源 |
|---|---|
| GET 按配置导出 | IMPMappingConfigVO.name |
| POST 预览导出 | 请求体 name → 否则 TemplateJson.templateName → 否则 excel-template.xlsx |
| 运行时动态导出 | IMPMappingConfigVO.name |
POST 请求体:完整 TemplateJson JSON 对象;亦支持 { "name": "...", "jsonTemplate": "..." } 包装形式。
响应:Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;Content-Disposition 通过 HttpDownloadHelper 设置(filename + filename*=UTF-8'' 百分号编码,支持中文);响应体为二进制流(非 Resource JSON)。
实现
| 类 | 模块 | 职责 |
|---|---|---|
ExcelConfigsController.exportExcelByConfigId / exportExcelByBody |
obpm-designer |
HTTP 入口,解析下载名、写流 |
ExcelImportRuntimeController.exportExcel |
obpm-runtime |
运行时动态导出入口 |
ExcelTemplateExportService |
obpm-core |
接口:exportFromConfig / exportFromTemplateJson |
ExcelTemplateExportServiceImpl |
obpm-core |
加载配置、解析 JSON、调用 Builder |
ExcelTemplateBuilder |
obpm-core |
构建工作簿、合并、数据验证 |
ExcelTemplateStyleHelper |
obpm-core |
标签/数据单元格样式、字段 numFmt |
TemplateJsonParser |
obpm-core |
jsonTemplate → JsonTemplate(含 JsonCellStyle) |
HttpDownloadHelper |
obpm-common |
附件响应头与中文文件名编码 |
生成规则(与上文「Excel 模板生成规则(服务端)」一致):
- 每个
sheets[]对应一个 POISheet,名称取sheet.name(最长 31 字符); - 写入
labelRows标签行 + 合并区域(colspan/rowspan);无labelRows时用字段name生成默认表头(showRequiredMark时追加*); - 标签样式:读取
labelRows[].style(LabelCellStyle),经ExcelTemplateStyleHelper.createLabelStyle写入字体/颜色/背景/对齐/边框; - 数据行样式:读取
dataRow.style(对象或 JSON 字符串),与字段numberFormat/dateFormat等合并; - 自数据行起预置 **500 行**格式单元格(
ExcelTemplateBuilder.TEMPLATE_FORMAT_ROW_COUNT); - 数据行行高(pt,默认 15)写入 POI
Row.setHeightInPoints; - 标签字号(pt,默认 11)写入 POI 字体
setFontHeightInPoints; - 数据起始行与导入一致:
sheet.getDisplayLabelLevelCount()(见ImportPlanBuilder); - 数据类型:number/date/time/datetime 空白单元格保留数值型(
setBlank())并设置对应DataFormat;示例行defaultValue按类型解析为序列值; - 数据验证(
validationRule === 'none'时不写入,fillMode === 'select'下拉除外): - 下拉:
selectOptions(超长或含逗号时写入隐藏 sheet_validation_lists); - 数值范围 / 文本长度:
lengthLimit; - 日期/日期时间范围:
dateRange,使用 Excel 公式DATE()比较(兼容yyyy-MM-dd/yyyy/MM/dd等输入格式); - 时间范围:
timeRange,使用TIME()公式; - 正则 / 内置:
regex规则下 phone/email/idCard 使用自定义公式; - 必填:
required=true时allowBlank=false;config.openValidateTip时使用patternTip作为错误提示; - 不写入设计器辅助行(字段名行、列字母行)。
与持久化联动(可选):ObpmExcelHostBridge 在收到 change 并写入配置时,可先调用 export-excel,再将生成文件上传至 uploads,自动回写 templatePath。
classDiagram
class ExcelConfigsController {
+exportExcelByConfigId()
+exportExcelByBody()
}
class ExcelTemplateExportService {
+exportFromConfig(appId, configId)
+exportFromTemplateJson(jsonTemplate)
}
class ExcelTemplateStyleHelper {
+createLabelStyle()
+createDataStyle()
+resolveNumFmt(field)
}
class HttpDownloadHelper {
+setExcelAttachmentHeaders()
}
class ExcelTemplateBuilder {
+buildWorkbook(template) XSSFWorkbook
+TEMPLATE_FORMAT_ROW_COUNT
}
class ExcelImportRuntimeController {
+exportExcel()
}
ExcelConfigsController --> ExcelTemplateExportService
ExcelImportRuntimeController --> ExcelTemplateExportService
ExcelConfigsController --> HttpDownloadHelper
ExcelImportRuntimeController --> HttpDownloadHelper
ExcelTemplateExportService --> ExcelTemplateBuilder
ExcelTemplateBuilder --> ExcelTemplateStyleHelper
运行时态 · 导出相关接口¶
运行时态包含两条独立链路:导入样例模板下载(服务导入流程)与**视图数据导出**(EXPTOEXCEL 动作)。
导入样例模板下载(已实现)¶
前台「下载模板」默认下载设计态已保存的 templatePath 静态文件;当 templatePath 为空且配置为 EXCEL_JSON 时,可改调 GET .../excelimport/{configId}/export-excel 动态生成(见下文)。
| 类 | 端点 | 说明 |
|---|---|---|
DownloadController |
GET/POST /api/runtime/file/isFileExisted |
校验模板文件是否存在 |
DownloadController |
GET/POST /api/runtime/file/download |
下载文件流 |
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
filepath |
string |
是 | 文件相对路径(运行时加密传输) |
filename |
string |
否 | 下载显示名 |
applicationId |
string |
否 | 应用 ID(/resources 路径时需解析 workspace) |
调用链:ViewController 下发 Activity.excelTemplate(IMPMappingConfigVO.templatePath,按用户加密)→ excel_upload.vue → getFileExisted → templateDownload(api.js)→ DownloadController.doFileDownload。
POST /obpm/api/runtime/file/download?filename=订单导入模板.xlsx&filepath={encryptedPath}&applicationId={appId}
响应为二进制文件流。
视图数据导出 Excel(已实现)¶
状态:已实现
Controller:cn.myapps.runtime.activity.controller.ActivityController
Service:ActivityRunTimeServiceImpl.exportExcel
核心构建:cn.myapps.core.common.model.view.ExcelFileBuilder(Apache POISXSSFWorkbook)
动作类型:ActivityType.EXPTOEXCEL(16)
与 Excel **导入**配置无直接耦合;按视图列定义与查询条件导出当前数据。
POST /{applicationId}/views/{viewId}/activities/exportExcel¶
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
actId |
string |
是 | 导出按钮动作 ID |
filename |
string |
是 | 文件名(无扩展名;优先使用视图 description) |
excelExportTime |
string |
是 | 导出批次时间戳,用于进度缓存 |
isExpSub |
string |
否 | 是否导出子表 |
parentId |
string |
否 | 主从视图父文档 ID |
isRelate |
string |
否 | 是否关联导出 |
请求体
| 字段 | 类型 | 说明 |
|---|---|---|
selectColumns |
string |
逗号分隔的列 ID,空则导出全部可见列 |
selectDocIds |
string[] |
勾选导出的文档 ID;空数组表示按查询条件导出全部 |
items |
object |
查询条件键值(常用查询 / 高级查询表单值) |
fixedColumn |
string |
冻结列 ID(可选) |
请求体示例
{
"selectColumns": "col_orderNo,col_customer,col_amount",
"selectDocIds": ["doc001", "doc002"],
"items": {
"ITEM_状态": "已审核"
}
}
响应:直接写入 HttpServletResponse 输出流,Content-Type: application/x-download,附件名 {filename}.xlsx(非 JSON Resource)。
实现:ActivityController.exportExcel → 初始化 ExcelFileBuilder 进度缓存 → ActivityRunTimeServiceImpl.exportExcel → expDocToExcel → ExcelFileBuilder.buildSheet(view, params, ids) → toExcelFile() → 流式输出后删除临时文件。
ExcelFileBuilder 能力摘要:按视图列配置导出字段值;支持图片字段内嵌图、下拉选项脚本列、按 Column.excelVisible 过滤、主子表关联导出、导出过程进度写入 MemoryCacheUtil。
GET /exportExcel/readProcess¶
轮询视图数据导出进度。
查询参数:excelExportTime
响应 data
| 字段 | 说明 |
|---|---|
excelExportCount |
已导出条数 |
excelRowCount |
总条数(为 0 时前台按 100% 处理) |
exportExcelResult |
完成后写入的结果路径 |
前台封装:api.js → exportExcel / exportExcelProgress;excel_export.vue 每 500ms 轮询。
运行时按 jsonTemplate 动态生成导入模板(已实现)¶
状态:已实现
Controller:cn.myapps.runtime.dts.excelimport.controller.ExcelImportRuntimeController
Service:复用cn.myapps.core.runtime.dynaform.dts.excelimport.json.ExcelTemplateExportService(obpm-core共享)
当配置为EXCEL_JSON且templatePath为空或需强制刷新时,运行态按jsonTemplate即时生成导入模板,避免依赖设计态上传文件。
端点
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/api/runtime/{applicationId}/excelimport/{configId}/export-excel |
按 impmappingconfigid 生成并下载;applicationId / configId 支持运行时加密 ID |
下载文件名:IMPMappingConfigVO.name + .xlsx(无查询参数)。
响应:与设计态导出相同,为 .xlsx 二进制流(非 JSON Resource);响应头由 HttpDownloadHelper 设置。
实现:ExcelImportRuntimeController.exportExcel → DesUtil.decryptTextByUserId 解密路径参数 → IMPMappingConfigDesignTimeService.findById 加载配置 → 校验 isExcelJsonTemplate() 且 jsonTemplate 非空 → toXlsxFilename(config.getName()) → ExcelTemplateExportService.exportFromTemplateJson 写流。
错误:配置不存在、jsonTemplate 为空或非 EXCEL_JSON 时抛出 OBPMValidateException(400)。
与样例下载的关系:
| 条件 | 行为 |
|---|---|
templatePath 非空 |
优先 file/download 下载静态文件(现有逻辑) |
templatePath 为空且 jsonTemplate 非空 |
调用 export-excel 动态生成 |
| 均为空 | 返回 400,提示未配置模板 |
导出接口对照总表¶
| 端点 | 时态 | 状态 | 产出 | 主要类 |
|---|---|---|---|---|
POST .../uploads |
设计时 | 已实现 | 上传样例模板 → templatePath |
UploadDesignTimeController |
GET .../excelconfigs/{id}/export-excel |
设计时 | 已实现 | 由已保存 jsonTemplate 生成 .xlsx |
ExcelConfigsController.exportExcelByConfigId |
POST .../excelconfigs/export-excel |
设计时 | 已实现 | 由请求体 TemplateJson 预览生成 .xlsx |
ExcelConfigsController.exportExcelByBody |
POST .../file/download |
运行时 | 已实现 | 下载 templatePath 样例 |
DownloadController |
GET .../excelimport/{id}/export-excel |
运行时 | 已实现 | 动态生成导入模板 | ExcelImportRuntimeController |
POST .../activities/exportExcel |
运行时 | 已实现 | 视图查询结果 .xlsx |
ActivityController + ExcelFileBuilder |
GET .../exportExcel/readProcess |
运行时 | 已实现 | 视图导出进度 | ActivityController |