# H5DW

PB的数据窗口可以直接显示在Html,所见即所得支持增删查改等操作。流程如下:

  1. 在PB中导出数据窗口到特定的目录
  2. 通过框架的菜单管理添加菜单,并设置权限
  3. 配置数据窗口需要操作的数据库(可选)

说明: 示例默认连接satrda中配置的名称为erp的数据库, 如果需要连接其它数据库,修改server/plugins/erp/h5dw.js中的数据库名

# 导出数据窗口

  1. 在PB中选择要使用的数据窗口,编辑好后,找到数据窗口点右键,export
  2. 保存地址选择server/plugins/data/ 目录下

说明: 示例的数据窗口名称为d_test1,前端页面请求报表文件时,插件代码会查找该目录的报表

# 添加菜单

在菜单管理模块可以添加菜单,我们以添加一个显示数据窗口的菜单为例,演示如何添加菜单。

  1. 打开菜单管理,点击新增按钮,弹出增加菜单界面。分别选择菜单图标,输入菜单名称、显示排序、路由地址。 web 点击确定
  2. 在新增加的菜单行,点击该菜单右边的增加按钮,新增菜单。 web 在添加菜单界面录入如下: web

组件路径 : amis 路由地址 : /data/page/testdw.js&dw=d_test1

提示: 组件路径必须填amis,表示加载amis配置。程序打开后,会加载/data/page/testdw.js的配置界面, 可以自行参考amis文档进行界面配置,testdwjs会根据传入的参数dw=d_test1调用接口获取导出的d_test1数据窗口

刷新浏览器后可以看到左侧增加了菜单,点击菜单看到界面:

web

到这里已经完成了一个完整表单的功能,并实现了新增、删除、查询、保存等操作。不需要任何代码,非常简单。

# 前后端说明

前后端的实现都是通过插件进行实现的,不需要单独学习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;
}