公司用了這個叫作jeecg的快速開發框架,我不知道有多少公司在用這個框架,園子裏有的能夠吱一聲。我的以爲這框架惟一優點就是可讓不會ssh的人也能進行開發,只要你會J2SE,有web後臺發開經驗便可。html
框架的優劣這裏不作說明,可是官方文檔真的寫的很粗糙,不少時候須要本身額外添加一些功能的時候會有一點無處下手的感受。接觸了一段時間後,也踩了很多的坑,如今記錄一下,以饗讀者。前端
jeecg版本:3.7.1java
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
的按鈕。disabled="disabled"
後,該元素的內容不會提交表單,若是須要提交,但不可編輯,請使用readonly="readonly"
<t:dgOpenOpt>
時注意,默認的openMode
爲OpenWin
,須要爲其設置width和height,不然報錯;OpenTab
時則不須要設置。urlfont
屬性爲圖標設置,能夠更換Font Awesome中的全部圖標。param
形式,即xxController.do?getList
曾經一度想改爲xx/getList
,嘗試屢次後失敗,事實證實代碼關聯太強,不推薦修改。(create_time,create_by,create_name,update_time,update_by,update_name等)
,jeecg自帶aop綁定,更新時會 自動賦值。具體查看DataBaseConstant.java和HiberAspect.java
。@OneToMany,@ManyToOne
)AuthInterceptor.java和SignInterceptor.java
,在裏面添加系統的攔截規則。t_s_muti_lang
中,沒法直接在源代碼中搜索到。@Transactional(rollbackFor=Exception.class)
。t:datagrid
查詢問題,對時間查詢,若是時間格式是年月日+時分秒,則沒法查詢,須要修改代碼文件,將原來的區間格式由xxxbegin一、xxxend2
改爲xxxbegin一、xxxend2
。t:datagrid
查詢問題,若是使用多對多的關係進行查詢,直接對字段添加query=true
,若是關係多於二級則沒法查詢。例如放款表中有一個借款表外鍵,借款表有一個用戶表的外鍵。在顯示的時候,顯示字段的field=borrow.user.name
,那麼,就算設置了query=true
,查詢也是無效的。1.跨域過濾器web
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.全局表單元素的隱藏api
$(":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中選中的行。app
var rowsData = $('#' + id).datagrid('getSelections'); //獲取具體的字段名,推薦第二種取值形式,若是使用一對多,name字段名可能長這樣(user.id),使用第一種方式會報錯 console.log(rowData[0].fieldname) console.log(rowData[0]['fieldname')
7. 選擇datagrid中選中的行cors
// 在方法中添加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
,別的字段同理。