Web 开发中用 H5DW 替代 DataWindow

H5DW 是什么

H5DW(HTML5 DataWindow)是 PowerBuilder DataWindow 的 JavaScript 实现,由 DataStore(数据处理)和 DataWindow(界面呈现)两个核心类组成,DataWindow 继承 DataStore,所有 DataStore 方法都能用。它保留了数据窗口的概念模型——带区、行流、公式、过滤排序、编辑回写、打印——只是把定义载体从 .srd 换成了浏览器能直接吃的形式。

npm install h5dw
<script src="http://www.satrda.com/h5dw/satreport.js"></script>
<link href="http://www.satrda.com/h5dw/satreport.css" rel="stylesheet">
<div id="datawindow" style="width:500px;height:400px"></div>
<script>
  let dw = new DataWindow('#datawindow');
</script>

不需要 Vue、不需要构建工具,纯 HTML 页面即可承载;也支持 ES Module / CommonJS / TypeScript。

DataWindow 报表可以直接加载

原样的数据窗口定义不用重写就能跑:

  • 数据窗口原样导入:引擎内置 PB 语法解析器,把 .srd 文本交给 parseDW() 就得到报表定义,赋给 dw.dataObject 即可渲染;ds.create("release 8; ...") 同样接受语法字符串。
  • 设计器里对应「导入数据窗口」入口:按 BOM / gb2312 读文件 → window.parseDW → 直接进画布编辑。
  • 服务端侧两条路都通:用 SatServer 在服务端把数据窗口语法解析成 H5DW 报表格式返回,或者把数据窗口原样下发给前端、由 H5DW 自己解析;自实现服务端只需返回报表 JSON 或语法文本。
  • 运行时改属性也沿用 PB 口径:describe() / modify() 用的是数据窗口属性串语法,dw.describe('colname.edit.style')、dw.describe('datawindow.table.sort') 都能取到值;setSQLSelect() / getSQLSelect() 兼容 setsqlselect 写法。
// 拿到 .srd 文本,直接装载
const res = await fetch('/h5dw/syntax?key=d_order');
dw.dataObject = parseDW(await res.text());   // 数据窗口语法 → 报表定义
await dw.retrieve();

界面可以一比一还原

同一份定义,画出来的东西是一致的:

  • 网格 + 带区的行流模型(header / detail / header.N / trailer.N / pageHeader / reportFooterN…),列宽、行高、合并区、冻结区、字体字号加粗、前后景色、边框线型、对齐与垂直对齐、format 显示格式,全部按定义绘制,而不是"套一层表格 CSS 近似一下"。
  • 浮动对象(图片、矩形/椭圆/线段/箭头、条码、二维码、图表、文本框)按锚点 (ri, ci, dx, dy) 精确定位,锚在 detail 的对象随行扩展;文本框支持内容超行时的行级伸展,伸展后下方整体下移,与 PB 的 band 自适应语义一致。
  • 屏幕、打印预览、实际打印、PDF、HTML、Excel 走同一套布局计算:分页断点、页眉页脚逐页重复、打印锚定页底的结果在各通道一致,导出 Excel 还能保留合并与原生文本框形状。

也就是说:老系统里那张凭证、那张处方笺在 PB 里长什么样,在浏览器里就是什么样,业务人员用类 Excel 的设计器改版式,会 Excel 就能改。

支持百万级数据

官方口径:100 万行数据流畅显示与编辑,支持百万级数据。做法是「计算全量、绘制局部」:

  • 布局与绘制只处理可视区 滚动时重新计算可见行范围,非可视行不参与绘制;
  • HTML 组件单元格与浮动对象采用虚拟创建:只为可视区域内的锚定行创建 DOM 实例,滚出可视区即销毁,DOM 数量不随行数线性增长;
  • 支持虚拟滚动,支持100万行级数据;
  • 公式结果缓存、分组求值缓存、行高缓存,避免滚动/重排时反复求值;

再加上多数据源(datasets)、区块循环(blocks)、子报表、增删查改、过滤排序、打印这些能力,一块画布就把「大数据网格 + 精细打印 + 自定义设计」三件事一起解决了。

主要语法对照

方法

目的 PowerBuilder H5DW
装载定义 dw.Object.DataObject = "d_order" dw.dataObject = 报表JSON / parseDW(srd文本)
检索 dw.Retrieve(ll_rc) await dw.retrieve(arg1, arg2)
更新 dw.Update() await dw.update()
行数 dw.RowCount() dw.rowCount()
取值 dw.GetItemString(1,"name") dw.getItem(1, 'name')
赋值 dw.SetItem(1,"name","x") dw.setItem(1, 'name', 'x')
插/删行 dw.InsertRow(0) / dw.DeleteRow(1) dw.insertRow(0) / dw.deleteRow(1)
当前行 dw.GetRow() / dw.SetRow(2) dw.getRow() / dw.setRow(2)
过滤 dw.SetFilter("…") + dw.Filter() dw.setFilter("name like '小%' and age > 18") + dw.filter()
排序 dw.SetSort("name A") + dw.Sort() dw.setSort("name A, price D") + dw.sort()(A 升 D 降)
查找 dw.Find("price>100", 1, 10) dw.find('price>100', 1, 10)
求值 dw.Evaluate("price*qty") dw.evaluate("price * quantity", 1)(对第 1 行)
取属性 dw.Describe("d.id.Color") dw.describe('id.color')
改属性 dw.Modify("d.id.Color='255'") dw.modify("…")
改 SQL dw.SetSQLSelect(s) dw.setSQLSelect(sql) / dw.getSQLSelect()
共享数据 ds2.ShareData(ds1) dw.shareData(other)(+ shareDataOff())
子数据窗口 dw.GetChild("dw", ds) dw.getChild(name)
状态标志 dw.SetItemStatus(...) dw.setItemStatus(row, col, buffer, status) / getItemStatus(...) / resetUpdate()
缓冲区行操作 RowsCopy / RowsMove / RowsDiscard rowsCopy() / rowsMove() / rowsDiscard()
选择行 dw.SelectRow(1, TRUE) dw.selectRow(1, true) / isSelected(1) / getSelectedRows()
提交变更 dw.GetChanges(blb) dw.getChanges() / dw.setChanges(data)
接受编辑文本 dw.AcceptText() dw.acceptText() / dw.setText(value)
清屏 dw.Reset() dw.reset()
打印 dw.Print() dw.print({ paperSize: 'A4', orientation: 'portrait' })

行号一律从 1 开始,列可以用名称字符串,也可以用数字索引。

事件

PowerBuilder H5DW
ItemChanged 事件 dw.on('ItemChanged', (evt, row, cell, text) => {})
Clicked dw.on('Clicked', (evt, row, cell) => {})
RowFocusChanged dw.on('RowFocusChanged', (evt, row) => {})
DoubleClick dw.on('DoubleClicked', ...)
EDNEdited / 键事件 EditChanged / KeyDown / ButtonClicked / CellRender / DropDownSelected / FilterChanged
事件写在 PB 脚本里 报表级脚本挂 dataObject.eventScript,自定义函数挂 dataObject.functions;on_EventName 命名自动注册

主动派发用 dw.fire('ItemChanged', ...),解绑用 dw.off(name, fn)。

公式与绑定

用途 PowerBuilder H5DW
取字段 t1.name(列表达式) #{name}
拼接 '客户:' + company_name #{'客户:' + CompanyName}
运算 price * qty #{price * qty}
条件 if(price>=100,'高价','低价') #{if(价格 >= 100, '高价', '低价')}
分组汇总 sum(amt for group 1) #{sum(amt for group 1)}
计数/平均/极值 count / avg / max / min #{count(城市 for group 3)}、#{avg(价格 for group 2)}、#{max(金额 for group 1)}
全局汇总 Sum(amt For All) #{sum(amt for all)}
交叉表汇总 — #{crosstabsum('amount')},动态列标题 @字段名
多数据源字段 子数据窗口取数 #{key1.name}(具名数据源加 key 前缀,主数据源不加前缀)

表达式里字段名、if()、mid() 等函数与 PB 一致,dw.evaluate() 可对任意行单独求值。

更多功能

  1. 支持拖拽控件和Excel两种方式编辑,所见即所得
  2. 支持导入导出Excel模板

在线示例

在线演示

一句话总结

概念是原来那套概念,定义可以原样导进来,界面按原样画出来,百万行也照刷照改——想给老数据窗口找个 Web 落点,H5DW 走的就是这条最短的路。

posted @ 2026-10-12 15:54  pcwe2002  阅读(4)  评论(0)    收藏  举报