对于企业应用具有一个功能强大的表格Grid组件,无论对于开发和用户体验来说,都能起到事半功倍的效果。经过多个项目经验积累,框架选择了目前主流的JQuery jqGrid插件。 该Grid组件功能非常丰富强大,除了一些分页、多字段排序等基础功能外,还包括诸如TreeGrid、SubGrid、AdvancedSearch、Group、ColumnChooser等众多高级特性,具体可参考其官方示例jqGrid Demo。
框架层面一方面从前端对组件进行了封装增强,使开发过程只需简单基础配置定义即可拥有强大的表格组件功能;另一方面与Java后端进行充分整合,封装简化实现基本无需或很少量编码即可实现大量复杂常见的非功能需求功能:
以下对框架集成Grid组件的一些典型功能进行截图示意介绍,由于框架还在不断完善过程中,以下截图可能和最新版本有所差异,但基本不影响功能使用介绍。
在不同的功能页面,工具条操作按钮有所差异,包含一系列图标按钮、下拉列表、右键菜单等组合,具体含义可鼠标移动上方查看提示信息了解功能含义。
以下大致按照从左至右的顺序,介绍一下主要操作按钮:
下拉按钮组: 提供一些基础功能和业务功能列表,与右键菜单基本是同步的;下拉按钮组列表一般会有个基本的分隔划分原则:无需选取的按钮一组,单选行项的业务功能一组,多选行项的业务功能一组。
查询按钮:点击弹出高级组合查询窗口,在此窗口可实现复杂的嵌套组合条件查询;
刷新按钮:点击强制表格重新加载数据;
常用功能按钮:紧随下拉按钮组之后是一些常用功能按钮,可以定制化把一些常用的业务功能按钮从下拉按钮组挑选出来放置于此方便用户快速直接点击;
Inline Edit 添加 按钮:进入新增行项模式;
Inline Edit 编辑 按钮:选中一行项点击进入修改编辑模式或者在选中状态回车亦可进入编辑模式,编辑完成后点击按钮或回车保存后自动进入下一行编辑模式,便于快速连续编辑数据;
Inline Edit 保存 按钮:编辑模式下,点击提交当前编辑数据,或键盘Enter回车同样效果;
Inline Edit 取消 按钮:编辑模式下,点击取消当前编辑数据,或键盘ESC退出键同样效果;
开启拖放移动操作模式: 在一些如菜单的典型父子关系的表格页面,点击对应操作按钮后可以鼠标自由拖拽形式调整各节点父子关系和同级节点相对排序,操作便捷高效。具体详见随后专门章节说明。
其余一些操作按钮在"更多操作"下拉菜单列表:
设定显示列和顺序:可以实现对列顺序调整、显示/隐藏列控制等,支持两种操作方式:
直接表头鼠标右键点击,弹出列表勾选即可
对于一个企业系统,数据查询需求无处不在,作为一个基础框架,一方面需要向用户提供功能强大,交互友好的查询支持可以极大提升用户体验,另一方面又需要考虑开发层面尽量抽取封装以简化开发人员重复的,繁琐的数据组装等代码编写也非常关键,同时还能保证设计实现的规范一致性。
框架基本打通前端UI到后端数据访问的动态参数/分页/排序整个环节,基本只需要少量的按照参数名称规则,对于Grid表格数据查询基本不用任何编码,即可实现非常灵活而强大的动态数据访问支持。基本流程是把Web前端传入的按照约定的参数集合转换为结构化查询对象,然后传入业务层组装为基于JPA Criteria语法的Spring JPA Data识别的动态查询对象,最终由Hibernate转换为底层数据库查询SQL语句。
组件上方的快速过滤查询区域,可以实现对列数据方便快速的过程查询,点击左侧小三角可以切换过滤条显示控制。 支持常见的三种输入类型:
除此,点击各查询输入组件左侧的操作符链接即可快速切换查询类型,各列会根据列数据类型或用户定义动态显示可选的查询类型:
点击弹出高级组合查询窗口,在此窗口可实现复杂的嵌套组合条件查询;
一般的应用系统涉及大量父子关系的递归树形结构数据对象,框架结合Adjacent List Model 与 Nested Set Model两种模型定义树形结构对象, 可以有效的支持父子关系、层级关系、包含关系、相邻关系等不同纬度业务逻辑实现,同时前端Grid组件与后端数据处理结合,虽然还受限于一些各组件整合集成成熟度存在一些使用限制, 但是基本来说已经比较完整的支持实现了各种典型的层级数据的直接拖放操作,诸如父子关系调整,同级节点相对排序调整等,大幅提升了操作效率和用户体验。
两种模型的结合,不仅从数据管理操作层面提升了便捷,另外一个层面对数据查询和统计的功能支持和性能优化会起到一个更好的底层支持, Adjacent List Model单纯的parent关联要实现一些诸如区间、族谱、路径等查询统计需要大量的表JOIN操作,结合Nested Set Model后就可以转换为一些高效的数字区间处理,大幅提升查询效率。
当然,两种模式既然都存在就必要各有优缺点,主要还是需要结合业务需求,分析数据更新和查询的各自频率、数据量、统计纬度等特点,选择合适的数据模型。具体两种模型概念细节,请自行网上搜索了解。
目前框架表格操作存在的一些限制说明如下:
除了基本的数据列表展示,对于企业应用系统还有一种很常见的分组聚合统计,例如需要把明细库存信息按照库存地/商品,销售单明细按照商品毛利/订单毛利/销售员利润等不同维度进行分组汇总统计显示。
一般情况此类非常规分页查询数据访问,首先会想到采用JQL/HQL或Native SQL形式编写自由而复杂连表风组聚合统计,但是一个此种方式一个比较繁琐的问题就是不便于以简单统一的方式处理动态参数的组装,还有排序分页之类的特性支持,以及返回集合数据的组装。
为了简化此类需求的开发,框架做了一个基于JPA Criteria的进一步封装,开发层面只需Web层简单传入需要分组和聚合规则(典型的加减乘除以及case when和as语法支持等)的属性列表,即可返回与之对应便于用于JSON序列化的Map结构数据,最方便的是即便是聚合属性也能像常规的对象属性查询一样支持动态参数/分页/排序等特性。
@MetaData(value = "按库存地汇总库存量")
public HttpHeaders findByGroupStorageLocation() {
setModel(findByGroupAggregate("commodity.id", "commodity.sku", "commodity.barcode", "commodity.title",
"storageLocation.id", "sum(curStockQuantity)", "sum(salingTotalQuantity)",
"sum(purchasingTotalQuantity)", "sum(stockThresholdQuantity)", "sum(availableQuantity) as sumAvailableQuantity"));
return buildDefaultHttpHeaders();
}
@MetaData(value = "按商品汇总库存量")
public HttpHeaders findByGroupCommodity() {
setModel(findByGroupAggregate("commodity.id", "commodity.sku", "commodity.barcode", "commodity.title",
"sum(curStockQuantity)", "sum(salingTotalQuantity)", "sum(purchasingTotalQuantity)",
"sum(stockThresholdQuantity)", "sum(availableQuantity)"));
return buildDefaultHttpHeaders();
}
url : WEB_ROOT + '/biz/stock/commodity-stock!findByGroupStorageLocation',
colModel : [ {
label : '商品主键',
name : 'commodity.id',
hidden : true,
hidedlg : true,
editable : true
}, {
label : '商品编码',
name : 'commodity.sku',
width : 80,
align : 'center'
}, {
label : '商品名称',
name : 'commodity.title',
width : 200,
align : 'left'
}, {
label : '库存地',
name : 'storageLocation.id',
width : 150,
stype : 'select',
editoptions : {
updatable : false,
value : Biz.getStockDatas()
}
}, {
label : '当前库存量',
name : 'sum(curStockQuantity)',
width : 60,
editable : true,
formatter : 'number'
}, {
label : '销售锁定库存',
name : 'sum(salingTotalQuantity)',
width : 60,
editable : true,
formatter : 'number'
}
@MetaData(value = "销售商品毛利统计", comments = "由于可能出现完全赠品类型的0销售额订单,需要引入case when判断处理否则会出现除零错误")
public HttpHeaders findByGroupCommodity() {
setModel(findByGroupAggregate("commodity.id", "commodity.sku", "commodity.title",
"max(case(equal(amount,0),-1,quot(diff(amount,costAmount),amount))) as maxProfitRate",
"min(case(equal(amount,0),-1,quot(diff(amount,costAmount),amount))) as minProfitRate",
"sum(diff(amount,costAmount)) as sumProfitAmount", "sum(amount)", "sum(quantity)",
"case(equal(sum(amount),0),-1,quot(sum(diff(amount,costAmount)),sum(amount))) as avgProfitRate"));
return buildDefaultHttpHeaders();
}
url : WEB_ROOT + "/biz/sale/sale-delivery-detail!findByGroupCommodity",
colModel : [ {
name : 'commodity.id',
hidden : true,
hidedlg : true
}, {
label : '商品编码',
name : 'commodity.sku',
width : 50,
align : 'center'
}, {
label : '商品名称',
name : 'commodity.title',
width : 200,
align : 'left'
}, {
label : '最低毛利率',
name : 'minProfitRate',
width : 40,
formatter : 'percentage'
}, {
label : '最高毛利率',
name : 'maxProfitRate',
width : 40,
formatter : 'percentage'
}, {
label : '平均毛利率',
name : 'avgProfitRate',
width : 50,
formatter : 'percentage'
}, {
label : '总计销售数量',
name : 'sum(quantity)',
width : 50,
formatter : 'number'
}, {
label : '总计销售金额',
name : 'sum(amount)',
width : 50,
formatter : 'currency'
}, {
label : '总计毛利金额',
name : 'sumProfitAmount',
width : 50,
formatter : 'currency'
} ]
/**
* 分组聚合统计,常用于类似按账期时间段统计商品销售利润,按会计科目总帐统计等
*
* @param groupFilter 过滤参数对象
* @param pageable 分页排序参数对象,TODO:目前有个限制未实现总记录数处理,直接返回一个固定大数字
* @param properties 属性集合,判断规则:属性名称包含"("则标识为聚合属性,其余为分组属性
* 属性语法规则:sum = + , diff = - , prod = * , quot = / , case(condition,when,else)
* 示例:
* sum(amount) as xyz
* count(id) as xyz
* sum(diff(amount,costAmount)) as xyz
* min(case(equal(amount,0),-1,quot(diff(amount,costAmount),amount))) as xyz
* case(equal(sum(amount),0),-1,quot(sum(diff(amount,costAmount)),sum(amount))) as xyz
* @return Map结构的集合分页对象
*/
@Transactional(readOnly = true)
public Page<Map<String, Object>> findByGroupAggregate(GroupPropertyFilter groupFilter, Pageable pageable, String... properties)
具体可访问示例应用体验这些特性效果。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。