kendoGrid动态列的实现-高级查询结果展示优化过程
高级查询功能是针对所有流程及表单数据的查询入口,作为工作流报表的暂时性替代功能,对于领导和相关流程的业务单位来说十分重要。高级查询功能提供了包括了流程名称、发起人、发起人所属部门等的基本条件查询以及可用作查询条件(是否可用作查询条件在列定义中设置,选中某个表单后,可供选择的列自动展示,并且根据列定义中的数据格式提供不同的查询方式,比如模糊匹配、比如日期的区间查询等)的高级条件查询,通过组合查询条件得到想要的查询结果。
由于高级查询一直存在分布的问题,究其原因主要是前端的展示与后台的分布难以协同,oaGrid或者原生的kendoGrid均无法展现动态列(这个问题网上确实没有例子,有gridView可以实现动态列的范例)。高级查询先后经历无分页(用<table>标签拼接)、伪前端分页等一系列演化。
高级查询的权限控制等在前面的博文中有介绍,本文主要介绍针对高级查询结果的前后端协同的分页实现。下面介绍一下目前的实现逻辑以及整个优化的历程。该功能的需求最初由我定义,形成需求后下发给工程师R具体实现,关于条件组合、sql拼接我们暂时不介绍,当时作为雏形的结果展示是一种一次查询出所有结果,然后采用<table>标签动态接拼展示动态的查询结果。下面是最初的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
/**
* 动态创建table
* */
function createDyTable(showColumn, columnName, resultList) {
//组装table表头
var showColumns = showColumn;
var title = "<tr>" ;
var value = "" ;
$( '#dyTable' ).empty();
if (columnName.length > 0 &&
showColumn.length > 0) {
$.each(columnName, function (index, items) {
title += "<th>" + items + "</th>" ;
});
title += "<th>操作</th>" ;
title += "</tr>" ;
$( '#dyTable' ).append(title);
$.each(resultList, function (idx, key) {
value = "<tr>" ;
$.each(showColumn, function (index, items) {
var rowValue = key[ '' + "f_" + items + '' ];
value += "<td style='word-break:break-all;width:300px;'>" + rowValue + "</td>" ;
});
var id = key[ 'f_id' ];
var operation = "<td style='word-break:break-all;width:200px;'>" ;
operation += "<span class='operate_detail'></span><a onclick=\"querySeniorDetailById(\'" + id + "\');return false;\" href=\"javascript:void(0)\">查看详情</a> " ;
operation += "<span class='operate_history'></span><a onclick=\"querySeniorHistoryById(\'" + id + "\');return false;\" href=\"javascript:void(0)\">查看历史</a> " ;
operation += "</td>" ;
value += operation;
value += "</tr>" ;
$(' #dyTable').append(value);
});
}
};
|
以上的js比较简单,第一、从高级查询条件中获得表单信息;第二、循环遍历一行一行地写进<table>。
由于开发这一版本的时候正是OA一期上线的关键时期,R对我说实现分布的功能对于kendoGrid是不可能的,而当时我们所有的表格的展示用的都是基于kendoGrid的oaGrid,熟悉kendoGrid和js的主管Z也说kendoGrid实现动态列的分布是不行的。当时刚刚担任主管的我本着“评价个冰箱,自己一定也得会制冷”、“要求别人做到的,自己先做到”,而当时的我刚从上家公司C/S开发上转过来不久,对BS结构以及js等前端的开发技术并不了解,所以只有暂时接受这种根本没有分页的方案。
但这个一直是我关注的心结之一,不久我将该任务分配给了高中毕业,但热衷技术、态度端正、经总监推荐、总经理还是副总裁特批进来的L,结果仍然是以grid无法实现动态列且前后端分页而不了了之。
再接下来,新入职的G,熟悉js但对grid并不了解。我当时按照固有的思路,即实现前后端分页是不行的。在当时的OA的原项目经理、后来转做技术总监的徐总指导下,在这个固有的思路指引下,开发了基于原生kendoGrid的页面的前端分页。该实现并不是真正的分页查询,只是将所有数据查询后,在前端的分页展示。但这个方案以及其部分实现确实为我后来解决oaGrid的分页提供了一种很好的思路。我们看一下这个阶段的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
function newCreateDyTable(showColumn, columnName, resultList) {
$( "#dyTable" ).html( "" );
var provinces = ""
//组装table表头
var showColumns = showColumn;
if (columnName.length > 0 &&
showColumn.length > 0) {
var clumnss = [];
$.each(columnName, function (index, items) {
clumnss.push({
field: showColumn[index],
title: items,
width: 150
});
});
clumnss.push({
title: "流程状态" ,
field: "f_lczt" ,
width : 150
});
var opertion = []
clumnss.push({
title: "操作" ,
field: "opertion" ,
template: function (id) {
var operation = "" ;
operation += "<span class='operate_detail'></span><a onclick=\"querySeniorDetailById(\'" + id.opertion + "\');return false;\" href=\"javascript:void(0)\">查看详情</a> " ;
operation += "<span class='operate_history'></span><a onclick=\"querySeniorHistoryById(\'" + id.opertion + "\');return false;\" href=\"javascript:void(0)\">查看历史</a> " ;
return operation;
},
width : 150
});
$.each(resultList, function (idx, key) {
provinces += "{ " ;
$.each(showColumn, function (index, items) {
var rowValue = key[' ' + "f_" + items + ' '];
provinces+="\""+items+"\""+":"+"\""+rowValue+"\",";
});
if(""!=provinces&&null!=provinces){
var id = key[' f_id '];
var f_lczt = key[' f_lczt '];
provinces+= "\"f_lczt\""+":"+"\"" +f_lczt + "\",";
provinces+= "\"opertion\""+":"+"\"" + id + "\"";
}
provinces += " },"
});
}
if(""!=provinces&&null!=provinces){
provinces = provinces.substring(0, provinces.length-1);
}
provinces = "[" + provinces+"]";
var reg = /(\r\n)/g;
provinces.replace(reg, " ")
var dateobj = eval(' ( ' + provinces + ' )');
$( "#dyTable" ).kendoGrid({
dataSource: {
data: dateobj,
pageSize: 10
},
selModel: true ,
pageable: {
input: true ,
numeric: false ,
messages: {
display: "{0} - {1} 共 {2} 条数据" ,
empty: "没有数据" ,
page: "页" ,
of: "/ {0}" ,
itemsPerPage: "条每页" ,
first: "第一页" ,
previous: "前一页" ,
next: "下一页" ,
last: "最后一页" ,
refresh: "刷新"
}
},
columns: clumnss
});
}
|
这一版本的相校初始版本一是实现了用户直接感观的分页,二是表格样式比较美观。
这一版本的设计中实际已经组装出了oaGrid所需的columns,其设计思路剑走偏锋,对columns的组装从查询的高级条件中直接获取,比我之前设想的从后台组装columns更简单、易行且更符合需求(从后台组装实际无法判断哪些字段是用户期望展示的,虽然可以将状态\id等写死在判断代码中)。
通过第二版本引出了关于前台应用oaGrid分页的思路,前端的代码如下,这一版本相当简单,保持了代码风格的统一,即查询结果使用oaGrid展示及分页:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
$( '#myToDoTaskGrid' ).oaGrid({
beforeload: function (operation) {
var processDefineVo = {dataMap:{
seniorQueryInfo:seniorQueryInfo,
showColumn:showColumn
},seniorQueryBaseDto:seniorQueryBaseDto};
operation.processDefineVo = processDefineVo;
},
title: '待办任务查询结果' ,
selModel: true ,
url : '../workflow/querySeniorInfo.action' ,
columns : clumnss
});
|
参考一般模块的grid分页的逻辑,其grid的列是固定的,分页逻辑的采用了com.gaochao.platform.orm.mybatis.PagingIntercept,对所有的query方法进行分页,如传进的参数有start和limit,就将start和limit设置进来,如果没有,则默认使用limit=2147483647,offset=0,为普通查询语句添加分页逻辑。在oaGrid.js中有page\skip用来传递页码和页数,然后进行查询。
经过查看gc.oaTools.js中的oaGird实现,在第L608
1
2
3
4
5
|
schema : {
model : dataModel,
data : 'pageResult.result'
,
total : 'pageResult.count'
|
}并结合网上的关于dendoGrid的范例model的格式即为我们使用一般模块使用oaGrid时前端传入的columns数组,再反观data和total即是我们com.gaochao.platform.entity.PageResult中的两个属性,由此我们得到一种方案,即columns不由前端传入而改为重写PageResult加入dataModel,并在后端实现dataModel的拼接(拼接会查询数据库,可能影响效率)。
第二种方案:很多关于mybatis的分页方案是采用了所谓的Interceptor,对某些数据库操作进行拦截然后处理,我们的数据范围就是使用的com.gaochao.oa.module.com.dependence.server.service.impl.DataruleHandler,这是一种职责链的设计模式。网上提供的插件,也就是interceptor方案我们的系统里已有,即是对所有一般使用oaGrid的后端统一的分页处理:com.gaochao.platform.orm.mybatis.PagingIntercept,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
@Intercepts ({ @Signature (type = Executor. class , method = "query" , args = {
MappedStatement. class , Object. class , RowBounds. class , ResultHandler. class }) })
public class PagingIntercept implements Interceptor {
private PagingDialect pagingDialect;
public Object intercept(Invocation invocation) throws Throwable {
//query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[ 0 ];
Object parameter = args[ 1 ];
RowBounds rowBounds = (RowBounds) args[ 2 ];
int offset = rowBounds.getOffset();
int limit = rowBounds.getLimit();
if (pagingDialect.supportsPaging() && (offset != RowBounds.NO_ROW_OFFSET || limit != RowBounds.NO_ROW_LIMIT)) {
BoundSql boundSql = ms.getBoundSql(parameter);
String sql = boundSql.getSql().trim();
sql = pagingDialect.getPagingSql(sql, offset, limit);
offset = RowBounds.NO_ROW_OFFSET;
limit = RowBounds.NO_ROW_LIMIT;
args[ 2 ] = new RowBounds(offset, limit);
BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), sql, boundSql.getParameterMappings(), boundSql.getParameterObject());
copyMetaParameters(boundSql, newBoundSql);
MappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql));
args[ 0 ] = newMs;
}
return invocation.proceed();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this );
}
public void setProperties(Properties properties) {
String className = (String) properties.get( "dialect" );
if (StringUtils.isBlank(className)) {
throw new MyBatisException( "分页方言 不能为空." );
}
Class<?> dialectClass = null ;
try {
dialectClass = ClassUtils.forName(className);
} catch (Exception e) {
throw new MyBatisException(e);
}
pagingDialect = (PagingDialect) BeanUtils.instantiate(dialectClass);
}
/**
* 复制BoundSql的MetaParameter
* <br>------------------------------<br>
* @param lhs
* @param rhs
*/
private void copyMetaParameters(BoundSql lhs, BoundSql rhs) {
for (ParameterMapping map : lhs.getParameterMappings()) {
String key = map.getProperty();
Object value = lhs.getAdditionalParameter(key);
if ( null != value) {
rhs.setAdditionalParameter(key, value);
}
}
}
// see: MapperBuilderAssistant
private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
Builder builder =
new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.keyGenerator(ms.getKeyGenerator());
builder.keyProperty(ms.getKeyProperty());
// setStatementTimeout()
builder.timeout(ms.getTimeout());
// setStatementResultMap()
builder.parameterMap(ms.getParameterMap());
// setStatementResultMap()
builder.resultMaps(ms.getResultMaps());
builder.resultSetType(ms.getResultSetType());
// setStatementCache()
builder.cache(ms.getCache());
builder.flushCacheRequired(ms.isFlushCacheRequired());
builder.useCache(ms.isUseCache());
return builder.build();
}
public static class BoundSqlSqlSource implements SqlSource {
private BoundSql boundSql;
public BoundSqlSqlSource(BoundSql boundSql) {
this .boundSql = boundSql;
}
public BoundSql getBoundSql(Object parameterObject) {
return boundSql;
}
}
}
|
如前所述,当我们有start、limit时,该方法使用start\limit进行分页,如果没有传入,相当于不分页;该拦截针对的所有query方法
1
2
|
@Intercepts ({ @Signature (type = Executor. class , method = "query" , args = {
MappedStatement. class , Object. class , RowBounds. class , ResultHandler. class }) })
|
我们的高级查询也是已有拦截的,只是因为我们没有传入start\limit,默认不分页;另外G在做前端伪分页时采用了拼接columns的方法将columns传入,但在接下来的处理是查询所有,然后在前端使用原生的kendogrid进行类似table的动态拼接即newCreateDyTable方法。这种方法是将结果一次查出,分页展示。结合PagingIntercept和G的columns拼接方法,我们只需要简单修改高级查询的查询逻辑,传入start、limit等分页元素,前端完全复用一般模块的gird。这种分页是后端的分页,且不用增加太多的代码。可以肯定,在同样的sql和数据量的情况下,这种分页要比不分页或前端分页给用户的体验更佳,等待时间更短。
kendoGrid动态列的实现-高级查询结果展示优化过程相关推荐
- java回顾:MyBatis参数、sql片段、动态sql、高级查询
目录 一.MyBatis参数 SqlSessiong工具类 1.映射文件配置-入参 1.1 parameterType入参 1.2 单个入参,变量名任意定义: 1.3 多个入参,解决方案: 1.4 p ...
- 解答网友提问:如何构建动态表达式实现高级查询服务
上次我们介绍了"一秒创建高级查询服务".前天,有网友在公众号后台问我,怎么使用动态表达式: 我想应该是客户提出了更高的要求,查询的条件不仅限于大于.小于,更加多样化,需要动态组合成 ...
- 高级查询(1.连接查询(对列的扩展)2.联合查询(对行的扩展)3.子查询)
高级查询 1.连接查询(对列的扩展) 第一种形式: select * from Info,Nation #会形成笛卡尔积 select * from Info,Nation where Info.Na ...
- 2.4.3 Mybatis 高级查询, 复杂映射, 返回主键, 动态SQL if, set, foreach, 核心配置文件深入,plugins标签, 多表查询, 嵌套查询
目录 Mybatis 复杂映射&配置文件深入 一 Mybatis高级查询 1.1 ResutlMap属性 1.2 多条件查询(三种) 1.3 模糊查询 二 Mybatis映射文件深入 2.1 ...
- 动态创建Lambda表达式实现高级查询
需求简介 最近这几天做的东西总算是回归咱的老本行了,给投资管理项目做一个台账的东西,就是类似我们的报表.其 中有一个功能是一个高级查询的需求,在查询条件方面大概有7.8个查询条件.需求就是如果一个条件 ...
- MySQL列数不确定查询_MySQL的高级查询
高级查询: 1.连接查询 指把2张表或者多张表之间做一个连接,所有数据放在一个表里显示.适用于有外键关系的2张表或多张表.如果没有加外键关系,但是这2张表有联系,也可以加连接查询. sele ...
- ABP 多模块关联查询、分组统计、列转行、Vue 复合表头动态列
本文记录了一次使用abp Core5 ,vue 开发复杂报表的经历. 0.需求概述 业务需求是:统计一个化工厂车队形式记录数据中异常停车的报表,维度可以按照车俩.驾驶员两个维度进行统计,统计的元素有: ...
- es查询两列相减大于某个值的数据_elasticsearch 高级查询
高级查询 子条件查询 (特定字段查询所指特定值) 复合条件查询 (以一定的逻辑组合子条件查询) 一.子条件查询 子条件查询分为 query context.filter context 1.query ...
- ORACLE---Unit04: SQL(高级查询)
---(重点复习:子查询.分页查询.decode) --- SQL(高级查询) --- 子查询 --- 子查询是嵌套在其它SQL语句当中的,目的是为嵌套的SQL提供数据,以便其执行. ---查看谁的工 ...
- MySQL高级 —— 查询性能优化
引言 承接<MySQL高级 -- 高性能索引>,本篇博客将围绕<高性能MySQL(第三版)>第六章内容进行总结和概括. 与索引的部分一样,SQL优化也是广大程序员深入MySQL ...
最新文章
- Science | 以功能为核心的蛋白质设计
- java linux urlencode_iOS urlEncode编码解码(非过时方法,已解决)
- python的xlwt模块的常用方法
- C语言实用算法系列之学生管理系统_单向链表内操作_提取排序规则
- HUST软工1506班第2周作业成绩公布
- 【转载】世界各地对BI的应用状况
- 时间同步绝对是一个大问题
- 乐迪机器人正确操作_乐迪智能早教机器人好用吗 乐迪智能早教机器人使用测评...
- 最好用的OCR实时翻译工具:Bob for Mac
- 把你的 Mac 从 Catalina 降级回 Mojave 系统的避坑指南
- 手把手教你把ASP.NET项目发布到服务器上(没学会随时来打我)
- 中小企业如何选择OA协同办公产品?最全的对比都在这里了
- 先决条件(一)问题定义和需求分析
- HTML期末作业-我的家乡网页作业
- linux中mv、cp、rm分别是什么意思
- 线上线下协同发展,020 商业解析
- win2008系统 安装hplaserj1010打印机驱动程序
- 理解pem pfx文件
- 微信小程序实现锚点跳转
- php提取域名字符串,由字符串,提取完整子域名的方法 -php
热门文章
- 【VBA编程实例】 如何导出百度云盘的目录
- 通过图像的指针读取图像
- MFC学习笔记(1)
- 《剑指offer》面试题46、47、49
- ERDAS 安装完成之后如何打开软件新建工程
- 教务管理系统C++全部代码
- java中web错误返回码,Java-Web3j Transfer.sendFunds()返回错误“天然气...
- Kotlin实战【三】表示与选择
- java集合框架中迭代器的作用_JAVA集合框架:Iterator迭代器
- sstv解码_SSTV通联活动:ISS国际空间站2020年1月30日和1月31日