JEECG上手使用总结

文章目录

  • 写在前面
    • 数据列表:Datagrid
    • 数据列表:AppendGrid
    • 数据列表:Online 报表配置
    • 用户选择框:UserSelectTag
      • 1. 效果图
      • 2. 实现代码
      • 3. 标签内部实现
    • 导出 Excel: @Excel 注解
      • 1. 前端代码
      • 2. 后端代码
    • 持久层注解: MiniDao

写在前面

由于之前的一个项目公司外包出去但是没有做好,于是公司打算自己做,使用的是 JEECG 框架,所以用这篇文章记录下使用这个框架的一些总结,刚开始只是随手记,遇到什么写什么,等内容差不多了再分类整理。

文章有点长,可以根据目录查看相应的模块。在文章的右侧工具栏可以快捷地查看目录:
JEECG上手使用总结_第1张图片

文章写得还不全, 如果没能解决问题, 可以移步 JEECG 官方文档, 不过官方文档格式有些乱。

数据列表:Datagrid

Datagrid是 JEECG 生成列表的通用解决方案之一,用户可以根据自己的需求自定义列表展示的效果:
JEECG上手使用总结_第2张图片
官网文档 对各个参数介绍很详细,这边主要就贴下整体代码(代码生成的页面和上图无关,上图代码在框架的UserController.java类的user()中):


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@include file="/context/mytags.jsp" %>
<t:base type="jquery,easyui,tools,DatePicker"/>
<div class="easyui-layout" fit="true">
 <div region="center" style="padding:0;border:0">
	 
 <t:datagrid name="userList" checkbox="true" title="用户管理"
 actionUrl="userController.do?datagrid&user.departId=${departId}" idField="id" sortName="user.createDate"
 rowStyler="gridRowStyler" autoLoadData="false"
 fitColumns="false" fit="true" singleSelect="true" queryMode="group">
 <t:dgCol title="主键" field="id" hidden="true" queryMode="single" width="120" />
 <t:dgCol title="姓名" field="user.name" queryMode="single" width="120" />
 <t:dgCol title="人员类型" field="user.userType" queryMode="single" dictionary="biz_user_type,code,name" width="120" />
 <t:dgCol title="出生日期" field="user.birthday" formatter="yyyy-MM-dd hh:mm:ss" queryMode="single" width="140" />
 <t:dgCol title="手机号" field="user.phone" queryMode="single" width="120" />
 
 <t:dgCol title="性别" field="user.sex" queryMode="single" dictionary="biz_sex_type,code,name" width="120" />
 <t:dgCol title="籍贯" field="user.birthplace" queryMode="single" dictionary="p_basic_place,id,name" width="120" />
 
 <t:dgCol title="年龄" field="user.age" queryMode="single" width="100" formatterjs="calcAge"/>
 t:datagrid>
 
 <div id="userListtb" style="padding: 3px; height: 27px;border-bottom:1px solid #D7D7D7">
 <div style="float: left;" class="searchColums">
 姓名:<input class="inuptxt ac_input" type="text" name="user.name" value="${name}">
 人员类型:<t:dictSelect field="user.userType" type="list"
 extendJson="{class:'form-control',style:'width:158px'}"
 datatype="*"
 typeGroupCode="biz_user_type"
 hasLabel="false" title="人员类型"/>
 出生日期:
 
 <input type="text" id="birthday_begin" name="user.birthday_begin" class="Wdate" value="${startTime}" autocomplete="off"
 style="width: 145px; height: 20px;" onclick="WdatePicker({dateFmt: 'yyyy-MM-dd',maxDate:'#F{$dp.$D(\'formDate_end\',{d:-1});}'});" /> ~
 <input type="text" id="birthday_end" name="user.birthday_end" class="Wdate" value="${endTime}" autocomplete="off"
 style="width: 145px; height: 20px; margin-right: 20px;" onclick="WdatePicker({dateFmt: 'yyyy-MM-dd',minDate:'#F{$dp.$D(\'formDate_begin\',{d:1});}'});" />
 div>
 <div align="right">
 <a href="#" class="easyui-linkbutton" iconCls="icon-search" onclick="userListsearch();">查询a>
 <a href="#" class="easyui-linkbutton" iconCls="icon-reload" onclick="searchReset('userList')">重置a>
 div>
 div>
 
 <div id="userListtb2" style="padding: 3px; height: 25px">
 <div style="float: left;">
 <a href="#" class="easyui-linkbutton" plain="true" icon="icon-chakan" onclick="masterView('userController.do?goView', 'userList', 'user.id');">查看a>
 <a href="#" class="easyui-linkbutton" plain="true" icon="icon-remove" onclick="masterDelete('userController.do?doBatchDel', 'userList', 'user.id');">删除a>
 div>
 div>
 div>
div>
<script type="text/javascript">
 window.onload = function () {
 userListsearch();
 };
script>

当页面初始化后框架会自动生成一个 name + search()的函数,如果 autoLoadData 属性为 true 则页面加载后就会自动调用该函数查询数据并展示出来,如果为false则需要手动调用该函数查询数据,可以看到「查询」按钮也是绑定该函数。

该框架在生成页面的时候会生成一些函数和样式,所以框架中大部分页面的问题都可以考虑从已经生成的页面上去查看代码,寻找解决方案。

后端查询数据接口 userController.do?datagrid 按照一定的规范进行编写,命名统一是datagrid

UserController.java

/**
 * easyui AJAX 请求数据
 */
@RequestMapping(params = "datagrid")
public void datagrid(UserEntity user, HttpServletRequest request, HttpServletResponse response, DataGrid dataGrid) {
 userService.datagrid(user, request.getParameterMap(), dataGrid);
 TagUtil.datagrid(response, dataGrid);
}

UserService.java

// UserEntity 需要对应到数据库中的表
public void datagrid(UserEntity user, Map<String, String[]> parameterMap, DataGrid dataGrid){
 CriteriaQuery cq = new CriteriaQuery(UserEntity.class, dataGrid);
 //查询条件组装器
 org.jeecgframework.core.extend.hqlsearch.HqlGenerateUtil.installHql(cq, user, parameterMap);
 try{
 //自定义追加查询条件
 }catch (Exception e) {
 throw new BusinessException(e.getMessage());
 }
 cq.add();
 this.getDataGridReturn(cq, true);
}

UserEntity.java

@Entity
@Table(name = "p_basic_user", schema = "")
public class UserEntity implements Serializable {
	属性...
}

数据列表:AppendGrid

AppendGrid 是 JEECG 实现数据列表的又一解决方案,相比 DataGrid 这种列表可以实现动态增加行、删除行、编辑行功能,可以满足特定的业务需求,效果图如下:
JEECG上手使用总结_第3张图片

这个模块主要是引用了开源的 AppendGrid 插件, 可以看看 官方示例,不知道是不是用的人不多,遇到问题的时候很少能找到解决方案

这里就以上图为例,演示下大致的实现逻辑,有些地方会用注释加以说明(这里由于长度限制省略部分代码):

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@include file="/context/mytags.jsp" %>

<html lang="zh-CN">
<head>
 <meta charset="utf-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <t:base type="jquery,bootstrap,bootstrap-form,common-form,DatePicker"/>
head>
<body>
<div class="container">
 <div class="panel-body">
 
 <t:formvalid formid="formObj" dialog="true" layout="table" tiptype="6"
 action="storagePlanController.do?doAdd" styleClass="form-horizontal" >
 <input type="hidden" name="id" value='${user.id}'>
 <div class="row">
 <div class="bt-item col-xs-12">
 <div class="row">
 <div class="col-xs-1 bt-label required-img">供货商:div>
 <div class="col-xs-2 bt-content">
 
 <input id="supplierName" name="supplierName" type="text" value='${supplierName}' class="form-control Wpopup"
 datatype="*" ignore="checked" onclick="popupClick(this,'id,name','supplierId,supplierName','basic_supplier_select')"/>
 <input id="supplierId" name="supplierId" type="hidden" value='${supplierId}'>
 div>
 <div class="col-xs-1 bt-label required-img">日期:div>
 <div class="col-xs-2 bt-content">
 
 <input id="formDate" name="formDate" type="text"
 class="form-control Wdate" onClick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss',isShowToday:true})" datatype="date" ignore="checked"
 value="'${formDate}' type='date' pattern='yyyy-MM-dd HH:mm:ss'/>"/>
 div>
 单据号...
 制单人...
 div>
 div>
 <div class="bt-item col-xs-12">
 <div class="row">
 <div class="col-xs-1 bt-label required-img">审核:div>
 <div class="col-xs-2 bt-content">
 
 <t:userSelect title="审核选择" datatype="*"
 selectedNamesInputId="auditUserName"
 selectedIdsInputId="auditUserId"
 userNamesDefalutVal="${auditUserName}"
 userIdsDefalutVal="${auditUserId}"
 windowWidth="1000px" windowHeight="600px" />
 div>
 验收...
 仓管...
 财务...
 div>
 div>
 div>

 <div id="detail_tab" class="tab-wrapper">
 
 <ul class="nav nav-tabs" role="tablist">
 
 <li role="presentation" class="active" tab-ajax-url="storagePlanController.do?datagridDetail&field=id,itemEntity.itemCategoryName,itemEntity.id,itemEntity.code,itemEntity.name,itemEntity.specification,itemEntity.unitName,unitPrice,batchNo,quantity,sumAmount&rows=50&user.id=${user.id}">
 <a href="#con-wrapper0" role="tab" data-toggle="tab">采购计划物品a>
 li>
 
 <li role="presentation" id="tabAddData">
 <button id="addDetailBtn" type="button" class="btn btn-default"><span class="glyphicon glyphicon-plus" aria-hidden="true">span>button>
 li>
 ul>
 
 <div class="tab-content">
 <div id="con-wrapper0" role="tabpanel" class="tab-pane active table-responsive">
 <table id="tblAppendGrid" class="table">table>
 div>
 div>
 div>
 t:formvalid>
 div>
div>
<script src="plug-in/layer/layer.js">script>
<script src="plug-in/jquery-plugs/AppendGrid.js">script>
<script type="text/javascript">
 var myAppendGrid = null;
 var detailList = [];

 $(document).ready(function () {
 // 1. 初始化 AppendGrid 对象
 myAppendGrid = new AppendGrid($.extend({}, AppendGridGlobalParam, {
 element: "tblAppendGrid",
 idPrefix: "storagePlanDetailList",
 maxRowsAllowed: 5,
 maxNumRowsReached: function () {
 // Show an alert message
 alert('You cannot add more than 5 rows!');
 },
 // columns: 设置字段, 如果必填, 增加 datatype 属性 (可以是任意值)
 columns: [
 { name: "id", display: "id", type: "hidden" },
 { name: "itemId", display: "itemId", type: "hidden" },
 { name: "itemCategoryName", display: "类别", type: "readonly", ctrlClass: "form-control input-sm", ctrlAttr: { disabled: true } },
 { name: "itemName", display: "名称", type: "readonly", ctrlClass: "form-control input-sm", ctrlAttr: { disabled: true } },
 { name: "specification", display: "规格", type: "readonly", ctrlClass: "form-control input-sm", ctrlAttr: { disabled: true } },
 { name: "unitName", display: "单位", type: "readonly", ctrlClass: "form-control input-sm", ctrlAttr: { disabled: true } },
 { name: "batchNo", display: "批次", type: "text", ctrlClass: "form-control input-sm", ctrlAttr: { "datatype": "*" } },
 { name: "quantity", display: "数量", type: "number", ctrlClass: "form-control input-sm", ctrlAttr: { "datatype": "num" }, events: {
 change: function(e) {// 自定义处理函数, 在值发生变化后触发, 还有 click 事件
 // 获取当前行的下标, 从 0 开始
 var rowIdx = myAppendGrid.getRowIndex(e.uniqueIndex);
 // 获取当前行中的字段值
 var quantity = myAppendGrid.getCtrlValue("quantity", rowIdx);
 var unitPrice = myAppendGrid.getCtrlValue("unitPrice", rowIdx);
 // 设置当前行中某个字段值
 myAppendGrid.setCtrlValue("sumAmount", rowIdx, (quantity * unitPrice).toFixed(3));
 }
 }
 },
 { name: "unitPrice", display: "单价", type: "text", ctrlClass: "form-control input-sm", ctrlAttr: { "datatype": "num" }},
 { name: "sumAmount", display: "金额", type: "text", ctrlClass: "form-control input-sm", ctrlAttr: { disabled: true } }
 ]
 }));

 // 2. 初始化: 查询列表数据, 并动态添加到列表中
 initDetail();
 });

 function initDetail() {
 var tabHead = $("#detail_tab li:first");
 // 通过这个地址查询列表数据
 var url = tabHead.attr("tab-ajax-url");
 $.get(url, {}, function (data) {
 detailList = [];
 // 组装数据
 $.each( data.rows, function(i, n){
 var dataMap = {};
 dataMap.id = n['id'];
 dataMap.itemId = n['itemEntity.id'];
 dataMap.itemCategoryName = n['itemEntity.itemCategoryName'];
 dataMap.itemCode = n['itemEntity.code'];
 dataMap.itemName = n['itemEntity.name'];
 dataMap.specification = n['itemEntity.specification'];
 dataMap.unitName = n['itemEntity.unitName'];
 dataMap.unitPrice = n['unitPrice'];
 dataMap.batchNo = n['batchNo'];
 dataMap.quantity = n['quantity'];
 dataMap.sumAmount = (n['quantity'] * n['unitPrice']).toFixed(3);
 detailList.push(dataMap);
 });

 // 将数据添加到列表中
 myAppendGrid.appendRow(detailList);
 });
 }

script>
body>
html>

数据列表:Online 报表配置

先看官方介绍:

报表配置可以通过在线配置,无需编写代码生成数据报表页面。JEECG Online 报表开发是一种配置式的报表开发模式,省去了报表的开发工作,将报表的开发简化为 SQL + 配置的形式。

意思就是在网页上进行配置,然后直接调用就可以生成一张简单的列表页面,如下图中 2 所示:
JEECG上手使用总结_第4张图片
上图是点击加号后弹出配置好的弹窗,要实现这种效果要分两步走,第一步配置报表的基本信息,第二部在前端页面中引用报表,接下来以上图例子来实现以下。

首先是报表配置,官网介绍的很清楚了,见 Online 数据报表配置,这里简单讲一下:
JEECG上手使用总结_第5张图片
配置好以后,就在前端页面中编写弹窗代码,先写一个「+」按钮:

<li role="presentation" id="tabAddData">
	<button id="addBtn" type="button" class="btn btn-default">
		<span class="glyphicon glyphicon-plus" aria-hidden="true">span>
	button>
li>

绑定 onclick 事件,事件中进行弹窗:

// 定义 click 事件
$('#addBtn').bind('click', function(){
 var departId = $("input[name=departId]").val();
 if (isEmpty(departId)) {
 tip("请先选择部门。");
 return false;
 }
 // 这里的 'sys_user_select' 就是报表的编码
 popupClick2('sys_user_select', departId);
 return false;
});

function popupClick(pcode, departId) {
	// 该地址格式固定, 可以在后面添加参数如 depart_id
 var url = "cgReportController.do?popup&id=" +pcode + "&depart_id="+departId;
 $.dialog({
 content: "url:"+url,
 zIndex: getzIndex(),
 lock : true,
 title:$.i18n.prop('common.select'),
 width:800,
 height: 400,
 parent:windowapi,
 cache:false,
 // 定义弹窗中确认按钮的事件
 ok: function() {
 	// 获取弹窗中选择的行
 iframe = this.iframe.contentWindow;
 var selected = iframe.getSelectRows();
 if (selected == '' || selected == null ){
 alert($.i18n.prop('common.select.please'));
 return false;
 }

 // 获取当前所以行的关联 ID
 var rows = myAppendGrid.getAllValue();
 var cascadeIds = "";
 if (!isEmpty(rows)) {
 for (var i = 0; i < rows.length; i++) {
 cascadeIds += rows[i]["cascadeId"] + ",";
 }
 }

			// 本来到这里就差不多了, 可以拿到选择到的行的所有信息, 可以根据自己的需要写回页面
			// 下面这些是结合 AppendGrid 动态添加行的代码, 如果没有用到可以忽略
 var isSelected = false;
 $.each(selected, function(i, n) {
 if (cascadeIds.indexOf(n['request_detail_id']) !== -1) {
 isSelected = true;
 }
 });

 if (isSelected) {
 tip("该人员已存在,请重新选择");
 } else {
 detailList = [];
 $.each( selected, function(i, n) {
 if (isEmpty(parseInt(n['name']))) {
 tip("数据问题");
 return true;
 }
 var dataMap = {};
 dataMap.id = "";
 dataMap.cascadeId = n['request_detail_id'];
 dataMap.name = n['name'];
 dataMap.age = n['age'];
 dataMap.phone = n['phone'];
 dataMap.address = n['address'];
 dataMap.sex = n['sex'];
 detailList.push(dataMap);
 });

 myAppendGrid.appendRow(detailList);
 }
 return true;
 },
 cancelVal: $.i18n.prop('dialog.close'),
 cancel: true
 });
}

到这里就已经完成了一个基本的报表配置和使用了。

用户选择框:UserSelectTag

1. 效果图

点击标签后弹出用户选择弹窗,该弹窗数据来源 t_s_user
JEECG上手使用总结_第6张图片

2. 实现代码

在使用时需要先引入 JEECG 自定义标签:

<%@ taglib prefix="t" uri="/easyui-tags"%>
<div class="col-xs-1 bt-label required-img">审核:div>
<div class="col-xs-2 bt-content">
	 
 <t:userSelect title="审核选择" datatype="*"
 selectedNamesInputId="auditByName"
 selectedIdsInputId="auditBy"
 userNamesDefalutVal="${auditByName}"
 userIdsDefalutVal="${auditBy}"
 windowWidth="1000px" windowHeight="600px" />
 div>

3. 标签内部实现

这是 JEECG 自定义的标签,如下图:
JEECG上手使用总结_第7张图片
对应的处理该标签的 Java 类是标签中的UserSelectTag类,该类继承了 JSP 包中的TagSupport类,并重写了父类的方法。在doEndTag()中将end()组装的页面进行输出,该页面作用是弹窗,定义「确定」按钮绑定的事件
JEECG上手使用总结_第8张图片
这里还有一个userUrl参数, 通过它就可以获取弹窗内显示的具体内容, 也就是人员选择页面。

导出 Excel: @Excel 注解

JEECG 框架导入导出表格主要是通过@Excel注解来完成的, 注解作用在属性上, 这里说下具体使用步骤。

1. 前端代码

前端部分主要就是写一个导出按钮, 有两种写法:


<div id="storageOutListtb2" style="padding: 3px; height: 25px">
 <div style="float: left;">
 <a href="#" class="easyui-linkbutton" plain="true" icon="icon-putout"
 onclick="ExportXls('storageStoreController.do?exportXls', 'storageStoreList','id')">导出a>
 div>
div>


<t:datagrid name="storagePlanList" checkbox="true" ...>
 <t:dgCol title="主键" field="id" hidden="true" queryMode="single" width="120" />
 <t:dgCol title="单据号" field="formNo" queryMode="single" query="true" width="120" />
 ...
 <t:dgToolBar title="导出" icon="icon-putout" onclick="\"masterExport('storageStoreController.do?exportXls', 'storageStoreList', 'id');\"" funname="ExportXls" />
 t:datagrid>

生成的按钮如图:
JEECG上手使用总结_第9张图片
两种方法中的onclick绑定了两种方法, 它们的实现都代码基本一致, 在common.js中; 方法都需要有三个入参, 第一个入参是进行导出处理的 URL, 第二个参数是datagrid列表的name属性值, 第三个参数是传递到后端的参数名。

2. 后端代码

后端代码主要分三步:

  • 对需要导出的字段使用@Excel进行注解
  • 查询需要导出的数据
  • 将 Excel 表格的信息组装并导出

接下来使用一个完整的例子来说明, 首先是对需要导出的字段添加@Excel注解:

public class SysUser implements Serializable {
 /**id*/
 private String id;
 /**登录账号 */
 @Excel(name = "登录账号", width = 15)
 private String username;
 /**真实姓名*/
 @Excel(name = "真实姓名", width = 15)
 private String realname;
 /**生日*/
 @Excel(name = "生日", width = 15, format = "yyyy-MM-dd")
 private Date birthday;
 /**性别(1:男 2:女)*/
 @Excel(name = "性别", width = 15,dicCode="sex")
 private Integer sex;
 /**状态 (1:正常 2:冻结)*/
 @Excel(name = "状态", width = 15,replace={"正常_1","冻结_0"})
 private Integer status;

	// 忽略 getter、setter 方法
}

不需要导出的不用加注解,注解的各个属性说明可以参照 JEECG Excel 注解说明。

接着在 service 层查询需要导出的数据, 可以直接使用 SQL 进行查询或者 JEECG 统一的datagrid()查询:

/**
 * 入参说明参考下面第三点里的代码便知
 */
public void datagrid(SysUser user, Map<String, String[]> parameterMap, DataGrid dataGrid) {
 CriteriaQuery cq = new CriteriaQuery(SysUser.class, dataGrid);
 //查询条件组装器
 org.jeecgframework.core.extend.hqlsearch.HqlGenerateUtil.installHql(cq, SysUser, parameterMap);
 try {
 //自定义追加查询条件
 String birthday= StringUtil.getRequestParamValue(parameterMap, "birthday");

 cq.createAlias("sysUser", "sysUser");
 if (StringUtils.isNotBlank(birthday)) {
 cq.ge("sysUser.birthday", DateUtils.parseDate(birthday, "yyyy-MM-dd"));
 }
 cq.add();
 } catch (Exception e) {
 throw new BusinessException(e.getMessage());
 }
 cq.add();

 this.getDataGridReturn(cq, true);
 }

最后在 controller 层将所有数据组装起来:

	/**
 * 导出 Excel
 */
 @RequestMapping(params = "exportXls")
 public String exportXls(SysUser user, HttpServletRequest request, DataGrid dataGrid, ModelMap map) {
 	// 1. 查询数据, 对象的属性必须带有 @Excel 注解, 否则无法导出
 userService.datagrid(user, request.getParameterMap(), dataGrid);
 List<SysUser> dataList = dataGrid.getResults();
 
		// 2. 将基本信息组装起来
 String format = org.apache.tools.ant.util.DateUtils.format(currentTimeMillis(), "yyyy-MM-dd");
 map.put(NormalExcelConstants.FILE_NAME, "用户表"+format);
 // 要导出的对象类信息
 map.put(NormalExcelConstants.CLASS, SysUser.class);
 String tableName = "用户表";
 map.put(NormalExcelConstants.PARAMS, new ExportParams(tableName, "导出人:" + ResourceUtil.getSessionUser().getUserName(),
 "导出信息"));
 map.put(NormalExcelConstants.DATA_LIST, dataList);

		// 3. 跳转导出
 return NormalExcelConstants.JEECG_EXCEL_VIEW;
 }

不能将Map类型作为数据来导出, 因为缺少@Excel注解, 框架无法解析

持久层注解: MiniDao

用来注解 Dao 层接口的自定义注解, 如
JEECG上手使用总结_第10张图片
该注解对应的注解处理器是MiniDaoHandler.java, 被注解所标注接口中的方法如果有加@Sql注解则查询用的 SQL 语句就在其属性中, 若只有@ResultType注解, 则需要创建一个名字为接口名_方法名.sql的 SQL 文件, 如:
JEECG上手使用总结_第11张图片
这里有一个getDepartAuthGroupList()方法, 则它所对应的文件是DepartAuthGroupDao_getDepartAuthGroupList.sql, 文件中只有简单的一句查询语句:
JEECG上手使用总结_第12张图片

你可能感兴趣的:(JEECG上手使用总结)