Jeecg踩坑不完全指南
公司用了这个叫做jeecg的快速开发框架,我不知道有多少公司在用这个框架,园子里有的可以吱一声。个人觉得这框架唯一优势就是可以让不会ssh的人也能进行开发,只要你会J2SE,有web后台发开经验即可。
框架的优劣这里不做说明,但是官方文档真的写的很粗糙,很多时候需要自己额外添加一些功能的时候会有一点无处下手的感觉。接触了一段时间后,也踩了不少的坑,现在记录一下,以飨读者。
jeecg版本:3.7.1
Tips
前端
- 权限管理设置中,按钮权限需要对相应的按钮设置
OperateCode
字段,然后在后台菜单管理-页面权限控制中配置相应的规则,接着去角色管理中分配权限,注意checkbox选中状态下为显示该按钮(此处与文档中描述的相反!)。 dgToolBar
中对应的funname
中的方法(例如add、update),都在curdtools_zh-cn.js
文件中,写新的方法时可以去那里面复制。- 针对于
<t:datagrid>
中的显示,<t:dgCol/>
中如果有表示状态的字段,数据库可能存int,而显示需要中文,可以使用dictionary
属性,如果对应的中文直接添加在系统后台的数据字典中(系统管理-数据字典),则直接dictionary=[字典名称]
;如果数据库中存在代码表,则dictionary=[表名,编码,显示文本]
- 针对于表单中的显示,状态选择可以使用下拉控件
<t:dictSelect>
,其中typeGroupCode
属性填写数据字典名称。 - 文件上传推荐使用
<t:webUploader>
控件,具体代码见Snippets。t:webUploader是h5的,兼容性较好。 - 在
<t:formvalid>
表单中,需要手动提交表单,需要一个id为btn_sub
的按钮。 - 表单页面中,设置input设置
disabled="disabled"
后,该元素的内容不会提交表单,如果需要提交,但不可编辑,请使用readonly="readonly"
- 使用
<t:dgOpenOpt>
时注意,默认的openMode
为OpenWin
,需要为其设置width和height,否则报错;OpenTab
时则不需要设置。 - jeecg所有封装的控件的
urlfont
属性为图标设置,可以更换Font Awesome中的所有图标。
后台
- SpringMVC路由默认采用
param
形式,即xxController.do?getList
曾经一度想改成xx/getList
,尝试多次后失败,事实证明代码关联太强,不推荐修改。 - 数据表设计中如果包含添加人,添加时间的可以直接使用jeecg指定字段
(create_time,create_by,create_name,update_time,update_by,update_name等)
,jeecg自带aop绑定,更新时会 自动赋值。具体查看DataBaseConstant.java和HiberAspect.java
。 - GUI代码生成器中,若pk为uuid,主键生成策略选择uuid,若为自增的id,则选择identity。
- GUI代码生成器中,form风格个人推荐选择div风格,使用表格时,Validform会有坑。
- GUI代码生成器中,推荐使用一对一关系来建表,需要一对多等别的关系时,可以添加注解来实现(
@OneToMany,@ManyToOne
) - 路由的全局拦截器文件为
AuthInterceptor.java和SignInterceptor.java
,在里面添加系统的拦截规则。 - 后台可以配置过滤器来解决全局跨域问题。代码见Snippets。
- 清理jeecg自带版本号和logo信息,注意他的国际化内容,文字信息均存在数据表
t_s_muti_lang
中,无法直接在源代码中搜索到。 - 定时任务有bug,暂未解决,存在实例化多次的情况。
- 事务处理,添加注解
@Transactional(rollbackFor=Exception.class)
。 t:datagrid
查询问题,对时间查询,如果时间格式是年月日+时分秒,则无法查询,需要修改代码文件,将原来的区间格式由xxxbegin1、xxxend2
改成xxxbegin1、xxxend2
。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);
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踩坑不完全指南的更多相关文章
- 『OGG 02』Win7 配置 Oracle GoldenGate Adapter Java 踩坑指南
上一文章 <__Win7 配置OGG(Oracle GoldenGate).docx>定下了 两个目标: 目标1: 给安装的Oracle_11g 创建 两个用户 admin 和 root ...
- Spring WebSocket踩坑指南
Spring WebSocket踩坑指南 本次公司项目中需要在后台与安卓App间建立一个长连接,这里采用了Spring的WebSocket,协议为Stomp. 关于Stomp协议这里就不多介绍了,网上 ...
- Microsoft SQL Server on Linux 踩坑指南
微软用 SQL Server 在 2016 年的时候搞了一个大新闻,宣传 Microsoft ❤️ Linux 打得一众软粉措手不及.但是这还是好事情,Linux 上也有好用的 SQL Server ...
- 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 ...
- 新书推荐《再也不踩坑的Kubernetes实战指南》
<再也不踩坑的Kubernetes实战指南>终于出版啦.目前可以在京东.天猫购买,京东自营和当当网预计一个星期左右上架. 本书贴合生产环境经验,解决在初次使用或者是构建集群中的痛点,帮 ...
- 树莓派4B踩坑指南 - (15)搭建在线python IDE
今天想在树莓派上自己搭一个在线的python IDE,于是找到了一篇教程--Fred913大神的从头开始制作OJ-在线IDE的搭建 自己尝试动手做了一下, 还是发现不少细节需要注意, 记录在此 如果不 ...
- 正则表达式 test 踩坑指南
正则表达式 test 踩坑指南 test 只能使用一次,第二次返回的是错误结果! reg = /edg|edge/g; /edg|edge/g reg.test(`edg`) true reg.tes ...
- Taro 开发踩坑指南 (小程序,H5, RN)
Taro 开发踩坑指南 (小程序,H5, RN) css taro 如何展示多行文本省略号 https://www.cnblogs.com/xgqfrms/p/12569057.html UI 设计稿 ...
- 小程序 & taro 踩坑指南
小程序 & taro 踩坑指南 微信开发者工具, 不支持 react bug https://github.com/NervJS/taro/issues/5042 solution just ...
随机推荐
- Struts2学习笔记整理(二)
这里是重点. Action接口 struts2 的Action可以是POJO 为了让用户开发的Action更加规范struts2提供了一个Action接口 ActionSupport基类 Struts ...
- Nginx优点
1.高并发响应性能非常好,官方Nginx处理静态文件并发5w/s 2.反向代码性能非常强(可用于负载均衡) 3.内存和cpu占比率低(为Apache的1/5-1/10); 4.对后端服务有健康检查功能 ...
- Android开发之常见事件响应方式
常见的事件有 (1)单击事件 onClickListener (2)长按事件 onLongClickListener (3)滑动事件 onTouchListener (4)键盘事件 onKeyLi ...
- iOS 中Block以及Blocks的使用,闭包方法调用
OC: -(void)dataWithUrl:(NSString*)string AndId:(NSInteger)id returnName:(void(^)(NSString*name))back ...
- Python学习日记:day9--------函数
初识函数 1,自定义函数 s ='内容' #自定义函数 def my_len():#自定义函数没有参数 i =0 for k in s: i+=1 print(i) return i #返回值 my_ ...
- linux下PHP后台配置极光推送问题
一.composer.json配置注意空格 按照极光推送官网所述,在composer.json下写入: "require": { "jpush/jpush": ...
- 如何在yarn上运行Hello World(一)
1.YARN是什么 YARN (Yet Another Resource Negotiator,另一种资源协调者) 是hadoop上的一种资源调度器,它是一个通用资源管理系统,可以为上层应用提供统一 ...
- 非常详细的 Docker 学习笔记-转载
文章链接 一.Docker 简介 Docker 两个主要部件: Docker: 开源的容器虚拟化平台 Docker Hub: 用于分享.管理 Docker 容器的 Docker SaaS 平台 -- ...
- lesson - 6 Linux下磁盘管理
1. 查看磁盘或者目录的容量df 查看磁盘各分区使用情况 不加参数以k为单位 df -i inode数,df -h 以G或者T或者M df -m 以M单位显示 du 查看目录或者文 ...
- 1.Nginx服务应用
Nginx服务应用 Nginx的优点和作用 Nginx是一款高性能的HTTP和反向代理的服务器软件,还是一个IMAP/POP3/SMTP(邮件)代理服务器! Nginx在功能实现上都采用模块化结构设计 ...