# H5DW
PB的数据窗口可以直接显示在Html,所见即所得支持增删查改等操作。流程如下:
- 在PB中导出数据窗口到特定的目录
- 通过框架的菜单管理添加菜单,并设置权限
- 配置数据窗口需要操作的数据库(可选)
说明: 示例默认连接satrda中配置的名称为erp的数据库, 如果需要连接其它数据库,修改server/plugins/erp/h5dw.js中的数据库名
# 导出数据窗口
- 在PB中选择要使用的数据窗口,编辑好后,找到数据窗口点右键,export
- 保存地址选择server/plugins/data/ 目录下
说明: 示例的数据窗口名称为d_test1,前端页面请求报表文件时,插件代码会查找该目录的报表
# 添加菜单
在菜单管理模块可以添加菜单,我们以添加一个显示数据窗口的菜单为例,演示如何添加菜单。
- 打开菜单管理,点击新增按钮,弹出增加菜单界面。分别选择菜单图标,输入菜单名称、显示排序、路由地址。
点击确定
- 在新增加的菜单行,点击该菜单右边的增加按钮,新增菜单。
在添加菜单界面录入如下:
组件路径
: amis
路由地址
: /data/page/testdw.js&dw=d_test1
提示:
组件路径
必须填amis
,表示加载amis配置。程序打开后,会加载/data/page/testdw.js
的配置界面, 可以自行参考amis文档进行界面配置,testdwjs
会根据传入的参数dw=d_test1
调用接口获取导出的d_test1数据窗口
刷新浏览器后可以看到左侧增加了菜单,点击菜单看到界面:

到这里已经完成了一个完整表单的功能,并实现了新增、删除、查询、保存等操作。不需要任何代码,非常简单。
# 前后端说明
前后端的实现都是通过插件进行实现的,不需要单独学习CSS和Html。 所有前后端代码都框架都已经集成,并不需要单独写,这里只是介绍前后端访问流程,有需要可以看。
# 前端实现
前端页面通过amis组件加载JSON实现。
当我们在菜单中指定组件路径:amis
时,会调用到模板文件/plugins/erp/data/page/template.html
,
该文件是将H5DW注册到amis中,一般不需要修改,大家有兴趣可以自行查看。
我们配置菜单时,指定了路由地址:/data/page/testdw.js&dw=d_test1
,该地址会加载文件
/plugins/erp/data/page/testdw.js
,代码如下:
(function () {
let amisJSON = {
"type": "page",
"initApi": "${apiurl}/h5dw/dataobject?key=${key}",
"data": {
"dataobject": null,
"showLoading": false,
"key": "d_test1",
"isDesign": true
},
"body": [{
"type": "spinner",
"show": true,
"overlay": true,
"size": "lg",
"visibleOn": "${showLoading}"
}, {
"type": "flex",
"direction": "column",
"justify": "flex-start",
"alignItems": "flex-start",
"style": {
"height": "100%"
},
"items": [{
"type": "button-toolbar",
"buttons": [{
"type": "button",
"label": "新增",
"style": {
"width": 5
},
"onEvent": {
"click": {
"actions": [{
"actionType": "custom",
"script": "page.dwMain.insertRow(0);"
}]
}
},
"icon": "fa fa-plus"
}, {
"type": "button",
"label": "删除",
"icon": "fa fa-plus",
"onClick": "page.dwMain.deleteRow(0);"
}, {
"type": "button",
"label": "查询",
"icon": "fa fa-firefox",
"onClick": "page.dwMain.retrieve();"
}, {
"type": "button",
"label": "保存",
"icon": "fa fa-firefox",
"onClick": "page.dwMain.update();"
}, {
"type": "button",
"label": "设计",
"icon": "fa fa-firefox",
"onClick": (e, props) => {
page.dwMain.design(props.data.isDesign);
props.data.isDesign = !props.data.isDesign;
},
}]
}, {
"type": "datawindow",
"style": {
"flexGrow": 1,
"margin-top": "3px"
},
"name": "dwMain",
"dataobject": "${dataobject}"
}]
}]
};
let amis = amisRequire('amis/embed');
if (window.dwKey) {
amisJSON.data.key = window.dwKey;
}
const config = window.baseConfig || parent.baseConfig;
amisScoped = amis.embed('#root', amisJSON, {data:{apiurl:config.api}});
})();
我们看到定义了H5DW对象的名称为dwMain
{
"type": "datawindow",
"style": {
"flexGrow": 1,
"margin-top": "3px"
},
"name": "dwMain",
"dataobject": "${dataobject}"
}
dataobject是变量通过接口获取
let amisJSON = {
"type": "page",
"initApi": "${apiurl}/h5dw/dataobject?key=${key}",
...
}
查询代码,调用了page.dwMain.retrieve()
,定义的datawindow名称都会加入到page变量下
{
"type": "button",
"label": "查询",
"icon": "fa fa-firefox",
"onClick": "page.dwMain.retrieve();"
}
保存代码
{
"type": "button",
"label": "保存",
"icon": "fa fa-firefox",
"onClick": "page.dwMain.update();"
}
onClick
中也可以加入自己的逻辑如
{
...
"onClick": (e, props) => {
let rtn = page.dwMain.update();
if (rtn === 1) {
amisLib.toast.info('成功');
}
}
}
# 后端实现
后台插件代码位置server/plugins/erp/h5dw.js
查询代码
// 1.通过前面传过来的报表key得到dataObject
// 2.通过dataStore的retrieve查询数据,结果通过Http返回给前端
retrieve(ctx,r,w) {
// console.log('retrieve');
let {key,args} = r.jsonBody;
try {
const d = getDW(ctx, key);
let sqlca = getDB();
let ds = new DataStore();
ds.setTransObject(sqlca);
ds.dataObject = d;
ds.retrieve();
w.write({status:0, "data":ds.data});
} catch (e) {
w.write({status:-1,msg:'' + e});
}
}
保存代码
// 1.通过前面传过来的报表key得到dataObject
// 2.通过dataStore的update保存,结果通过Http返回给前端
update(ctx,r,w) {
let {key,data} = r.jsonBody;
try {
let sqlca = getDB();
let ds = new DataStore();
ds.setTransObject(sqlca);
const d = getDW(ctx, key);
console.log('d',JSON.stringify(d));
ds.dataObject = d;
ds.setChanges(data);
sqlca.beginTrans();
let ret = ds.update();
if (ret === -1) {
sqlca.rollbackTrans();
w.write({"status":-1,msg:sqlca.sqlErrText});
} else {
sqlca.commitTrans();
w.write({"status":0});
}
} catch (e) {
w.write({status:-1,msg:'' + e});
}
}
查找和保存连接数据库是通过getDB得到sqlca对象来实现的
let sqlca = getDB();
getDB()
默认连接到erp数据库,连接其它数据库可以指定
let sqlca = getDB('mycon1');
其中的mycon1
指向需要操作的数据库
配置mycon1
数据库连接参考SatRDA后台的连接管理
查找报表是通过JS插件代码完成的,目录为server/plugins/erp/h5dw.js
,代码如下:
// 1.通过请求的key查找数据窗口
// 2.把数据窗口的sql语句table.retrieve,替换为key避免前端拿到sql造成安全性问题
dataobject(ctx,r,w) {
// console.log('dataobject');
const key = r.url.query.get("key");
try {
const d = getDW(ctx, key)
//做处理, 不把sql语句返回到客户端
d.table.retrieve = d.table.update = key;
// // let sqlca = getDB();
// // sqlca.execute(`waitfor delay '00:00:05:00'`);
w.write({status:0,msg:'ok',data:{dataobject:d}});
} catch (e) {
w.write({status:-1,msg:'' + e});
}
}
// 首先查找缓存中是否存在,如果存在缓存中,则直接从缓存取,
// 如果不在缓存中,根据条件判断加载/plugins/data目录下的数据窗口srd文件或者报表文件
function getDW(ctx, key, isParse = true) {
const od = ctx.memcacheGet('dataobject',key)
//const od = null; //调试时不缓存,修改后立即生效
let d = null;
if (od) {
d = JSON.parse(od);
} else {
let path = null;
if (isParse && key.indexOf('.') < 0) {
path = `./plugins/data/${key}.srd`;
} else {
path = `./plugins/data/${key}`
}
const f = satrda.fileOpen(path);
let syntax = '';
if (!isParse) {
const b = f.read();
syntax = satrda.decodeUtf8(b)
} else {
syntax = f.readAsText();
}
f.close();
if (isParse) {
d = parseDW(syntax);
} else {
d = JSON.parse(syntax);
}
ctx.memcacheSet('dataobject',key, JSON.stringify(d));
}
return d;
}