公司用了这个叫做jeecg的快速开发框架,我不知道有多少公司在用这个框架,园子里有的可以吱一声。个人觉得这框架唯一优势就是可以让不会ssh的人也能进行开发,只要你会J2SE,有web后台发开经验即可。

框架的优劣这里不做说明,但是官方文档真的写的很粗糙,很多时候需要自己额外添加一些功能的时候会有一点无处下手的感觉。接触了一段时间后,也踩了不少的坑,现在记录一下,以飨读者。

jeecg版本:3.7.1

Tips

前端

  1. 权限管理设置中,按钮权限需要对相应的按钮设置OperateCode字段,然后在后台菜单管理-页面权限控制中配置相应的规则,接着去角色管理中分配权限,注意checkbox选中状态下为显示该按钮(此处与文档中描述的相反!)。
  2. dgToolBar中对应的funname中的方法(例如add、update),都在curdtools_zh-cn.js文件中,写新的方法时可以去那里面复制。
  3. 针对于<t:datagrid>中的显示,<t:dgCol/>中如果有表示状态的字段,数据库可能存int,而显示需要中文,可以使用dictionary属性,如果对应的中文直接添加在系统后台的数据字典中(系统管理-数据字典),则直接dictionary=[字典名称];如果数据库中存在代码表,则dictionary=[表名,编码,显示文本]
  4. 针对于表单中的显示,状态选择可以使用下拉控件<t:dictSelect>,其中typeGroupCode属性填写数据字典名称。
  5. 文件上传推荐使用<t:webUploader>控件,具体代码见Snippets。t:webUploader是h5的,兼容性较好。
  6. <t:formvalid>表单中,需要手动提交表单,需要一个id为btn_sub的按钮。
  7. 表单页面中,设置input设置disabled="disabled" 后,该元素的内容不会提交表单,如果需要提交,但不可编辑,请使用readonly="readonly"
  8. 使用<t:dgOpenOpt>时注意,默认的openModeOpenWin,需要为其设置width和height,否则报错;OpenTab时则不需要设置。
  9. jeecg所有封装的控件的urlfont属性为图标设置,可以更换Font Awesome中的所有图标。

后台

  1. SpringMVC路由默认采用param形式,即xxController.do?getList曾经一度想改成xx/getList,尝试多次后失败,事实证明代码关联太强,不推荐修改。
  2. 数据表设计中如果包含添加人,添加时间的可以直接使用jeecg指定字段(create_time,create_by,create_name,update_time,update_by,update_name等),jeecg自带aop绑定,更新时会 自动赋值。具体查看DataBaseConstant.java和HiberAspect.java
  3. GUI代码生成器中,若pk为uuid,主键生成策略选择uuid,若为自增的id,则选择identity。
  4. GUI代码生成器中,form风格个人推荐选择div风格,使用表格时,Validform会有坑。
  5. GUI代码生成器中,推荐使用一对一关系来建表,需要一对多等别的关系时,可以添加注解来实现(@OneToMany,@ManyToOne
  6. 路由的全局拦截器文件为AuthInterceptor.java和SignInterceptor.java,在里面添加系统的拦截规则。
  7. 后台可以配置过滤器来解决全局跨域问题。代码见Snippets。
  8. 清理jeecg自带版本号和logo信息,注意他的国际化内容,文字信息均存在数据表t_s_muti_lang中,无法直接在源代码中搜索到。
  9. 定时任务有bug,暂未解决,存在实例化多次的情况。
  10. 事务处理,添加注解@Transactional(rollbackFor=Exception.class)
  11. t:datagrid查询问题,对时间查询,如果时间格式是年月日+时分秒,则无法查询,需要修改代码文件,将原来的区间格式由xxxbegin1、xxxend2改成xxxbegin1、xxxend2
  12. t:datagrid 查询问题,如果使用多对多的关系进行查询,直接对字段添加query=true,如果关系多于二级则无法查询。例如放款表中有一个借款表外键,借款表有一个用户表的外键。在显示的时候,显示字段的field=borrow.user.name,那么,就算设置了query=true,查询也是无效的。

Snippets

1.跨域过滤器

public class CorsFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
} @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.addHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Appkey");
filterChain.doFilter(servletRequest, servletResponse);
} @Override
public void destroy() {
}
}
<!-- web.xml中配置-->
<filter>
<filter-name>cors</filter-name>
<filter-class>cn.crenative.afloan.core.controller.CorsFilter</filter-class>
</filter> <filter-mapping>
<filter-name>cors</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

2.文件上传

    @RequestMapping(params = "doUpload", method = RequestMethod.POST)
@ResponseBody
public AjaxJson doUpload(MultipartHttpServletRequest request,
String path) {
logger.info("后台上传文件");
AjaxJson j = new AjaxJson();
String fileName = null;
String ctxPath = request.getSession().getServletContext().getRealPath(path);
File file = new File(ctxPath);
if (!file.exists()) {
file.mkdir();// 创建文件根目录
}
Map<String, MultipartFile> fileMap = request.getFileMap();
for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
MultipartFile mf = entity.getValue();// 获取上传文件对象
fileName = mf.getOriginalFilename();// 获取文件名
String fileExt = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
String newFileName = df.format(new Date()) + "_" + new Random().nextInt(1000) + "." + fileExt;
String savePath = file.getPath() + "/" + newFileName;// 上传后的文件绝对路径
System.out.println("上传后路径:" + savePath);
File savefile = new File(savePath);
try {
// String imageUrl = "http://" + request.getServerName() + ":" + request.getLocalPort() + request.getContextPath() + path + "/" + newFileName;
String imageUrl = request.getContextPath() + path + "/" + newFileName;
logger.info("输出路径:" + imageUrl);
mf.transferTo(savefile);
j.setObj(imageUrl);
} catch (IOException e) {
e.printStackTrace();
}
}
j.setMsg("上传成功");
return j;
}
 <t:webUploader url="upload.do?doUpload&path=[相对路径]" name="[数据库字段]" extensions="" auto="true" pathValues="${后端set的attribute名称}"/>
<!-- eg:-->
<t:webUploader url="upload.do?doUpload&path=/upload/afloan/users/attachment" name="credentialPhoto" extensions="" auto="true" pathValues="${attachmentPage.credentialPhoto}"/>

3.dictSelect

  <t:dictSelect field="credentialType" type="select" defaultVal="${attachmentPage.credentialType}" typeGroupCode="attachment" hasLabel="false"/>

4.全局表单元素的隐藏

$(":input").attr("disabled", "true");
$('select').attr('disabled', true);

5.添加一个提示的窗口

 layer.open({
title: title, //弹窗title
content: content, //弹窗内容
icon: 7,
yes: function (index) {
//回调函数
},
btn: ['确定', '取消'],
btn2: function (index) {
layer.close(index);
}
});

6.选择datagrid中选中的行。

var rowsData = $('#' + id).datagrid('getSelections');
//获取具体的字段名,推荐第二种取值形式,如果使用一对多,name字段名可能长这样(user.id),使用第一种方式会报错
console.log(rowData[0].fieldname)
console.log(rowData[0]['fieldname')

7. 选择datagrid中选中的行

// 在方法中添加index,控件会自动添加选择的行号
<t:dgFunOpt title="删除" funname="deleteOne()" urlclass="ace_button" urlfont="fa-trash-o"/> function deleteOne(index) {
console.log(index);![Alt text](./popup.gif) var row = $("#usersList").datagrid('getData').rows[index];
}

8.添加一个新的标签页

//function addOneTab(subtitle, url, icon),该方法定义在curdtools_zh-cn.js中
function openAuditTab(id, mobile) {
addOneTab("用户" + mobile + "的档案", "userInfo?userInfo&mode=claim&userId=" + id);
}

9.popup,弹框选择相应的记录,并回调到父页面。

//设置表单内容
function setUser(obj, rowTag, selected) {
if (selected == '' || selected == null) {
alert("请选择");
return false;
} else {
var str = "";
var name = "";
var idNo = "";
$.each(selected, function (i, n) {
str += n.mobile;
name += n.realName;
idNo += n.idcardNo;
});
$("input[id='" + rowTag + ".mobile']").val(str);
$("input[id='" + rowTag + ".realName']").val(name);
$("input[id='" + rowTag + ".idcardNo']").val(idNo);
return true;
}
} /**
* 弹出popup窗口获取
* @param obj
* @param rowTag 行标记
* @param code 动态报表配置ID
*/
function selectUser(obj, rowTag) {
if (rowTag == null) {
alert("popup参数配置不全");
return;
}
console.log($('#mobile').val());
var inputClickUrl = basePath + "/users?userSelect";
if (typeof (windowapi) == 'undefined') { //页面弹出popup
$.dialog({
content: "url:" + inputClickUrl,
zIndex: getzIndex(),
lock: true,
title: "选择客户",
width: 1000,
height: 300,
cache: false,
ok: function () {
iframe = this.iframe.contentWindow;
var selected = iframe.getSelectRows(); //重要,此处获取行数据
return setUserMobile(obj, rowTag, selected);
},
cancelVal: '关闭',
cancel: true //为true等价于function(){}
});
} else { //popup内弹出popup
$.dialog({
content: "url:" + inputClickUrl,
zIndex: getzIndex(),
lock: true,
title: "选择客户",
width: 1000,
height: 300,
parent: windowapi, //设置弹出popup的openner
cache: false,
ok: function () {
iframe = this.iframe.contentWindow;
var selected = iframe.getSelectRows(); //重要,此处获取行数据
return setUserMobile(obj, rowTag, selected);
},
cancelVal: '关闭',
cancel: true //为true等价于function(){}
});
}
}
<!-- 方法绑定 -->
<input class="inputxt" onclick="selectUser(this,'user');" placeholder="点击选择客户" id="user.mobile"
name="appUser.mobile" value="${borrowInfoPage.appUser.mobile}"/>

10.一对多关系的使用

具体例子:借款订单(afl_borrow_info)中存在用户表(afl_user)外键,通过user_id关联。

@Entity
@Table(name = "afl_borrow_info", schema = "")
@DynamicUpdate(true)
@DynamicInsert(true)
@SuppressWarnings("serial")
public class BorrowInfoEntity implements java.io.Serializable {
private UsersEntity appUser; @ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "user_id")
public UsersEntity getAppUser() {
return appUser;
} public void setAppUser(UsersEntity appUser) {
this.appUser = appUser;
}
}

关联之后,所有的查询(service层)和页面渲染(jsp),均不再使用user_id而是使用appUser.id,别的字段同理。

Jeecg踩坑不完全指南的更多相关文章

  1. 『OGG 02』Win7 配置 Oracle GoldenGate Adapter Java 踩坑指南

    上一文章 <__Win7 配置OGG(Oracle GoldenGate).docx>定下了 两个目标: 目标1: 给安装的Oracle_11g 创建 两个用户 admin 和 root ...

  2. Spring WebSocket踩坑指南

    Spring WebSocket踩坑指南 本次公司项目中需要在后台与安卓App间建立一个长连接,这里采用了Spring的WebSocket,协议为Stomp. 关于Stomp协议这里就不多介绍了,网上 ...

  3. Microsoft SQL Server on Linux 踩坑指南

    微软用 SQL Server 在 2016 年的时候搞了一个大新闻,宣传 Microsoft ❤️ Linux 打得一众软粉措手不及.但是这还是好事情,Linux 上也有好用的 SQL Server ...

  4. C# -- HttpWebRequest 和 HttpWebResponse 的使用 C#编写扫雷游戏 使用IIS调试ASP.NET网站程序 WCF入门教程 ASP.Net Core开发(踩坑)指南 ASP.Net Core Razor+AdminLTE 小试牛刀 webservice创建、部署和调用 .net接收post请求并把数据转为字典格式

    C# -- HttpWebRequest 和 HttpWebResponse 的使用 C# -- HttpWebRequest 和 HttpWebResponse 的使用 结合使用HttpWebReq ...

  5. 新书推荐《再也不踩坑的Kubernetes实战指南》

      <再也不踩坑的Kubernetes实战指南>终于出版啦.目前可以在京东.天猫购买,京东自营和当当网预计一个星期左右上架. 本书贴合生产环境经验,解决在初次使用或者是构建集群中的痛点,帮 ...

  6. 树莓派4B踩坑指南 - (15)搭建在线python IDE

    今天想在树莓派上自己搭一个在线的python IDE,于是找到了一篇教程--Fred913大神的从头开始制作OJ-在线IDE的搭建 自己尝试动手做了一下, 还是发现不少细节需要注意, 记录在此 如果不 ...

  7. 正则表达式 test 踩坑指南

    正则表达式 test 踩坑指南 test 只能使用一次,第二次返回的是错误结果! reg = /edg|edge/g; /edg|edge/g reg.test(`edg`) true reg.tes ...

  8. Taro 开发踩坑指南 (小程序,H5, RN)

    Taro 开发踩坑指南 (小程序,H5, RN) css taro 如何展示多行文本省略号 https://www.cnblogs.com/xgqfrms/p/12569057.html UI 设计稿 ...

  9. 小程序 & taro 踩坑指南

    小程序 & taro 踩坑指南 微信开发者工具, 不支持 react bug https://github.com/NervJS/taro/issues/5042 solution just ...

随机推荐

  1. 自学Zabbix3.8.1.2-可视化Visualisation-Graphs自定义图表

    自学Zabbix3.8.1.2-可视化Visualisation-Graphs自定义图表 自定义图表,如名称所示,提供定制功能.虽然简单的图形可以很好地查看单个项目的数据,但是它们不提供配置功能.因此 ...

  2. SDWebimage如何获取缓存大小以及清除缓存

    sdwebimage如何获取缓存大小以及清除缓存 1.找到SDImageCache类 2.添加如下方法: - (float)checkTmpSize {    float totalSize = 0; ...

  3. Android开发——使用LitePal开源数据库

    前言:之前使用Android内置的数据库,感觉一大堆SQL语句,一不小心就错了,很难受,学习了这个LItePal的开源数据库,瞬间觉得Android内置的数据库简直是垃圾般的存在 LitePal Gi ...

  4. wordpress登录、修改、删除、查看代码记录

    wordpress 登录,新增.修改.删除.查看,页面代码如下 package info.itest.www; import static org.junit.Assert.*; import org ...

  5. 框架原理第三讲,RTTCreate,运行时类型创建.(以MFC框架讲解)

    框架原理第三讲,RTTCreate,运行时类型创建.(以MFC框架讲解) 通过昨天的讲解,我们已经理解了运行时类型识别是什么. 比如  CObject * pthis = (Cobject *)Cre ...

  6. mvc中signalr实现一对一的聊天

    Asp.net MVC中实现即时通讯聊天的功能.前几天刚写了一片基础入门的教程,今天就来实现一下使用signaIr实现一对一的聊天的功能,对于这种场景也是即时通讯最基本功能.好吧废话不多说.先来看一下 ...

  7. ArcGIS API for JavaScript 4.3学习笔记[新] AJS4.3和AJS3.20新特性

    今天"ArcGIS极客说"公众号推送了这两个大版本的更新,吓得我赶紧撸了一篇新博客. 这里就不写代码验证了,作为新特性小节简单介绍一下!~ AJS 4.3 1. 更强大的Featu ...

  8. bzoj 3626: [LNOI2014]LCA

    Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q ...

  9. iptables 命令详解

    转载:http://blog.chinaunix.net/uid-26495963-id-3279216.html 一:前言 防火墙,其实说白了讲,就是用于实现Linux下访问控制的功能的,它分为硬件 ...

  10. 点击按钮显示隐藏DIV,点击DIV外面隐藏DIV

    点击按钮显示隐藏DIV,点击DIV外面隐藏DIV 注意:此方法对touch事件不行,因为stopPropagation并不能阻止touchend的冒泡 <style type="tex ...