一个不得不说的话题,通过近几年的发展,Web前端开发已经不是一个新有的岗位了,前端技术发展很是迅速,技术更新换代也很快,对于前端工程师来讲是一个很大的挑战“挣扎期”。javascript
从统计来看,中级前端的待遇是略高于中级后端的。这对于中小企业、创业公司来讲组建一个专有的前端团队仍是很一件很不容易的事情,无形中增长了人力成本, 话说招前端工程师简单,但能招聘到合适的前端工程师来讲,是一件很是不容易的事情。css
为了解决这个事情,中小企业、创业公司都在思考一个问题,作企业应用软件,若是不去组建专门的前端团队,能有一个很好的开发平台,很好的框架,让后端工程师具有一些基本的前端知识,就能够去作出很漂亮的界面就行了。html
这个想法很好,可是你会说,可能吗,会一点基础前端就能作好吗?专业的事情仍是有专业的人来作,先后端分离是趋势,表现逻辑分离意义很大。没有绝对完美的事情,尽管种种诱惑,惟心自问合适本身吗?我不否定,这还要针对产品、针对项目来选择解决方案。但JeeSite的中心思想是快速快发,快速交付,控制成本,对于一个想快速交付项目来讲不见得是一件好事。前端
重口难调,也许你不赞同这些见解,做者欢迎提问,固然你也能够将JeeSite彻底做为服务端代码,快速提供数据接口,自由实现或选型一套前端UI。java
好了,既然是奔着快速交付,控制成原本的,咱们就依这个角度去思考方案:web
Beetl模板语言相似JS语言和习俗,只须要将Beetl语言放入定界符号里便可,如默认的是<% %>,那JeeSite是怎样选择的呢:ajax
设想定义:<% %>算法
优势:jsp标准定界符,比较容易被理解,是后端运行的语法json
缺点:html后缀的模板,不能和html标签混用,不然IDE会提示语法错误后端
设想定义:@ 回车符
优势:简单
缺点:多行beetl语法时,比较麻烦,而且会出现多余的不少空格空行
设想定义:[ ]、[@ ]、[@ ]
有点:能够和html混用
缺点:与js的[]冲突,必须使用\转义
**设想定义:[@ @]、[# #]、# #、@ @**
优势:基本没有标示符冲突
缺点:感官、阅读、写法稍微差点
设想定义:<!--# -->、<!--: -->
优势:使用html注释,没有标示符冲突
缺点:使用IDE高亮时,没有写代码的感受,像是在写注释
最终选择:<% %>
通过上述分析,最终仍是回归默认,使用jsp标准定界符<%%>来做为模板语言的定界符,一方同比较容易被理解,明确是后端运行的语法,另外一方面冲突少,边界比较好界定
<% layout('/layouts/default.html', {title: '菜单管理', libs: ['validate'], bodyClass: ''}){ %> <div class="main-content"> <div class="box box-main"> <div class="box-header with-border"> <div class="box-title"> <i class="fa icon-book-open"></i> 菜单管理 </div> <div class="box-tools pull-right"> <button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button> </div> </div> <div class="box-body"> </div> <div class="box-footer"> </div> </div> </div> <% } %>
调用默认布局 /layouts/default.html,自动引入页面头部和尾部内容,经过参数设置要加载的css和js类库,参数以下:
title参数: 设置页面的标题名字
libs参数: 设置页面要引入的css和js类库,支持类库以下:
默认引入:layer、select二、WdatePicker
bodyClass参数: 设置body的class属性值
如下工具类可经过@类型快速调用,如:${@Global.getConfig('key')}
如下是Beetl函数及扩展函数
数据类型格式化
日期格式化:
Today is ${date,dateFormat="yyyy-MM-dd"} Today is ${date,dateFormat} 若是date为日期类型可简写: ${date,“yyyy-MM-dd”}
数值格式化:
Salary is ${salary,numberFormat="##.##"}
生成一个form标签,支持指定model属性,相似SpringMVC的<form:form modelAttribute=""/>标签的属性,自动给表单内的控件绑定属性值 <#form:form id="inputForm" model="${user}" action="${ctx}/sys/user" method="POST" class="form-horizontal"> 表单内容 </#form:form> 支持上传文件: <#form:form id="inputForm" model="${user}" action="${ctx}/sys/user" method="POST" enctype="multipart/form-data" class="form-horizontal"> 表单内容 </#form:form>
控件属性:
var p = { // 标签参数 id: id!, // 表单ID model: model!, // 绑定Model对象,例如:${user!} action: action!, // 表单请求地址 method: method!, // 请求方法,默认 post enctype: enctype!, // 发送以前进行数据编码,上传文件时指定:multipart/form-data };
自动绑定form:form上指定的model下的userName属性,相似SpringMVC的<form:input path=""/>标签的属性 <#form:input path="userName" maxlength="100" class="form-control required "/> 日期格式化: <#form:input path="userName" maxlength="100" dataFormat="date" class="form-control required "/> 数值格式化: <#form:input path="userName" maxlength="100" dataFormat="number" class="form-control required "/> 不自动绑定,把path改成name就能够: <#form:input name="userName" value="${user.userName}" maxlength="100" class="form-control required "/>
控件属性:
var p = { // 标签参数 id: id!, // 元素ID,若是不填写,则与name相同 path: path!, // 绑定form上model中属性的值 name: name!, // 元素名称,不填写 value: value!, // 元素值 type: type!'text', // 元素的类型,默认text dataFormat: dataFormat!'', // 数据格式化,支持以下值: // date: 日期; // datetime: 日期时间; // number: 数值类型,保留2位小数 };
根据字典类型设置下拉数据: <#form:select path="userType" dictType="sys_user_type" class="form-control required " /> 增长一个空白选项: <#form:select path="roleType" dictType="sys_role_type" blankOption="true" class="form-control " /> 多选下拉列表: <#form:select path="roleType" dictType="sys_role_type" multiple="true" class="form-control " /> 手动设置下拉框值,相似SrpingMVC的<form:options items="" itemLabel="" itemValue=""/>标签的属性: <#form:select path="moduleCodes" items="${moduleList}" itemLabel="moduleName" itemValue="moduleCode" class="form-control required" />
控件属性:
var p = { // 标签参数 id: id!, // 元素ID,若是不填写,则与name相同 path: path!, // 绑定form上model中属性的值 name: name!, // 元素名称,不填写 value: value!, // 元素值 dictType: dictType!, // 字典类型,从字典里获取,自动设置items、itemLabel、itemValue items: items![], // 列表数据,可接受对象集合,如:List<DictData> itemLabel: itemLabel!'', // 指定列表数据中的什么属性名做为option的标签名 itemValue: itemValue!'', // 指定列表数据中的什么属性名做为option的value值 multiple: multiple!'false', // 是否为多选框 blankOption: @ObjectUtils.toBoolean(blankOption!false), // 是否默认有个空白选择项目 };
相似<#form:select/>标签的使用方法 <#form:radio path="menuType" dictType="sys_menu_type" class="form-control required" />
控件属性:
var p = { // 标签参数 id: id!, // 元素ID,若是不填写,则与name相同 path: path!, // 绑定form上model中属性的值 name: name!, // 元素名称,不填写 value: value!, // 元素值 dictType: dictType!, // 字典类型,从字典里获取,自动设置items、itemLabel、itemValue items: items!([]), // 列表数据,可接受对象集合,如:List<DictData> itemLabel: itemLabel!'', // 指定列表数据中的什么属性名做为option的标签名 itemValue: itemValue!'', // 指定列表数据中的什么属性名做为option的value值 };
相似<#form:select/>标签的使用方法,后台接受moduleCodes为字符串,选择多个自动使用“,”分隔,相比SpringMVC必须是List方便的多 <#form:checkbox path="moduleCodes" items="${moduleList}" itemLabel="moduleName" itemValue="moduleCode" class="form-control required" /> 生成一个复选框按钮,后台接受replaceFile为Global.YES或Global.NO值: <#form:checkbox path="replaceFile" label="是否替换现有文件" class="form-control"/>
控件属性:
var p = { // 标签参数 id: id!, // 元素ID,若是不填写,则与name相同 path: path!, // 绑定form上model中属性的值 name: name!, // 元素名称,不填写 value: value!, // 元素值 dictType: dictType!'', // 字典类型,从字典里获取,自动设置items、itemLabel、itemValue items: items!([]), // 列表数据,可接受对象集合,如:List<DictData> itemLabel: itemLabel!'', // 指定列表数据中的什么属性名做为option的标签名 itemValue: itemValue!'', // 指定列表数据中的什么属性名做为option的value值 label: label!, // 只有一个复选按钮的状况下设置 };
<#form:textarea path="remarks" rows="3" maxlength="200" class="form-control"/>
控件属性:
var p = { // 标签参数 id: id!, // 元素ID,若是不填写,则与name相同 path: path!, // 绑定form上model中属性的值 name: name!, // 元素名称,不填写 value: value!, // 元素值 };
<#form:hidden path="menuCode" />
控件属性:
var p = { // 标签参数 id: id!, // 元素ID,若是不填写,则与name相同 path: path!, // 绑定form上model中属性的值 name: name!, // 元素名称,不填写 value: value!, // 元素值 type: type!'hidden', // 元素的类型,默认hidden };
封装layer+zTree实现树结构选择组件,使用场景如:部门选择,行政区划选择,栏目列表选择等
<#form:treeselect id="parent" title="上级菜单" name="parent.id" value="${menu.parent.id!}" labelName="parent.menuName" labelValue="${menu.parent.menuName!}" url="${ctx}/sys/menu/treeData?excludeCode=${menu.menuCode}&sysCode=${menu.sysCode}&isShowCode=2" class="" allowClear="true" canSelectRoot="true" canSelectParent="true"/>
组件属性:
var p = { // 标签参数 id: id!, // 元素ID name: name!, // 隐藏域名称 value: value!, // 隐藏域值 labelName: labelName!, // 标签框名称 labelValue: labelValue!, // 标签框值 class: class!'', // 标签框的CSS类名 placeholder: placeholder!, // 标签框的预期值的提示信息 dataMsgRequired: thisTag.attrs['data-msg-required'], // 必填错误提示信息 btnClass: btnClass!, // 标签框后面的按钮CSS类名 title: title!'选项', // 对话框标题 boxWidth: boxWidth!300, // 对话框宽度,默认300像素 boxHeight: boxHeight!400, // 对话框高度,默认400像素 url: url!, // 树结构,数据源地址 [{id, pid, name}] readonly: @ObjectUtils.toBoolean(readonly!false), // 是否只读模式 allowInput: @ObjectUtils.toBoolean(allowInput!false), // 是否容许label框输入 allowClear: @ObjectUtils.toBoolean(allowClear!true), // 是否容许清空选择内容 checkbox: @ObjectUtils.toBoolean(checkbox!false), // 是否显示复选框,是否支持多选,若是设置canSelectParent=true则返回父节点数据 expandLevel: @ObjectUtils.toInteger(expandLevel!(-1)), // 默认展开层次级别(默认:若是有1个根节点,则展开一级节点,不然不展开) canSelectRoot: @ObjectUtils.toBoolean(canSelectRoot!false), // 能够选择跟节点 canSelectParent: @ObjectUtils.toBoolean(canSelectParent!false), // 能够选择父级节点 returnFullName: @ObjectUtils.toBoolean(returnFullName!false), // 是否返回全路径,包含全部上级信息,以 returnFullNameSplit 参数分隔 returnFullNameSplit: returnFullNameSplit!'/', // 是否返回全路径,的分隔符,默认“/” };
<#form:iconselect path="menuIcon" class=""/>
组件属性:
var p = { // 标签参数 id: id!, // 元素ID,若是不填写,则与name相同 path: path!, // 绑定form上model中属性的值 name: name!, // 元素名称,不填写 value: value!, // 元素值 class: class!'', // 隐藏域和标签框的CSS类名 };
<#form:validcode name="validCode" isRequired="true" isRemote="true" />
组件属性:
var p = { id: id!name, // 验证码输入框ID name: name!, // 验证码输入框名称(必填) isRequired: @ObjectUtils.toBoolean(isRequired!true), // 是否必填,默认必填 dataMsgRequired: thisTag.attrs['data-msg-required'], // 必填错误提示信息 isRemote: @ObjectUtils.toBoolean(isRemote!true), // 是否支持实时远程验证 dataMsgRemote: thisTag.attrs['data-msg-remote'], // 必填错误提示信息 isLazy: @ObjectUtils.toBoolean(isLazy!false), // 是否懒加载验证码图片,原noRefresh参数 };
<#form:listselect id="userSelect" title="用户" url="${ctx}/sys/user/userSelect?userType=${role.userType}" allowClear="false" checkbox="true" itemCode="userCode" itemName="userName"/>
组件属性:
var p = { // 标签参数 id: id!, // 元素ID path: path!, // 绑定form上model中属性的值 name: name!, // 隐藏域名称 value: value!, // 隐藏域值 labelPath: labelPath!, // 绑定form上model中属性的值 labelName: labelName!, // 标签框名称 labelValue: labelValue!, // 标签框值 class: class!'', // 标签框的CSS类名 placeholder: placeholder!, // 标签框的预期值的提示信息 dataMsgRequired: thisTag.attrs['data-msg-required'], // 必填错误提示信息 btnClass: btnClass!, // 标签框后面的按钮CSS类名 title: title!'选项', // 对话框标题 boxWidth: boxWidth!'$(top.window).width() - 100', // 对话框宽度 boxHeight: boxHeight!'$(top.window).height() - 100', // 对话框高度 url: url!, // 树结构,数据源地址 [{id, pid, name}] readonly: @ObjectUtils.toBoolean(readonly!false), // 是否只读模式 allowInput: @ObjectUtils.toBoolean(allowInput!false), // 是否容许label框输入 allowClear: @ObjectUtils.toBoolean(allowClear!true), // 是否容许清空选择内容 checkbox: @ObjectUtils.toBoolean(checkbox!false), // 是否显示复选框,是否支持多选,若是设置canSelectParent=true则返回父节点数据 itemCode: itemCode!, // 选择后结果集中的Code属性名,返回到隐藏域的值 itemName: itemName!, // 选择后结果集中的Name属性名,返回到输入框的值 };
一、文件上传: <#form:fileupload id="upload1" bizKey="${user.id}" bizType="user_upload1" uploadType="all" class="required" readonly="false"/> 后台代码:FileUploadUtils.saveFileUpload(user.getId(), "user_upload1"); 二、图片上传: <#form:fileupload id="upload2" bizKey="${user.id}" bizType="user_upload2" uploadType="image" class="required" readonly="false"/> 后台代码:FileUploadUtils.saveFileUpload(user.getId(), "user_upload2"); 三、返回路径: <#form:fileupload id="upload3" returnPath="true" filePathInputId="upload3Path" fileNameInputId="upload3Name" uploadType="image" readonly="false" maxUploadNum="3" isMini="false"/> <#form:input name="upload3Path" class="form-control"/> <#form:input name="upload3Name" class="form-control"/>
组件属性:
var p = { // 标签参数 id: id!, // 元素ID bizKey: bizKey!, // 业务表的主键值(与附件关联的业务数据) bizType: bizType!, // 业务表的上传类型(全网惟一,推荐格式:实体名_上传类型,例如,文章图片:article_photo) returnPath: @ObjectUtils.toBoolean(returnPath!false), // 是不是返回文件路径到输入框(默认false),可将路径直接保存到某个字段里 filePathInputId: filePathInputId!, // 设置文件URL存放的输入框的ID,当returnPath为true的时候,返回文件URL到这个输入框 fileNameInputId: fileNameInputId!, // 设置文件名称存放的输入框的ID,当returnPath为true的时候,返回文件名称到这个输入框 uploadType: uploadType!'', // 上传文件类型:all、file、image、media,若不设置,则自动根据上传文件后缀获取 class: class!'', // 标签框的CSS类名,设置 required 加入必填验证 readonly: @ObjectUtils.toBoolean(readonly!false), // 是否只读模式,只读模式下为查看模式,只容许下载 allowSuffixes: allowSuffixes!'', // 容许上传的后缀,前台的限制,不能超越file.*AllowSuffixes的设置,例如:.jpg,.png, maxUploadNum: @ObjectUtils.toInteger(maxUploadNum!300), // 多文件下容许最多上传几个,默认300个,设置-1表明不限制 imageMaxWidth: @ObjectUtils.toInteger(imageMaxWidth!1024), // 图片压缩,最大宽度(uploadType为image生效),设置-1表明不作任何处理 imageMaxHeight: @ObjectUtils.toInteger(imageMaxHeight!768), // 图片压缩,最大宽度(uploadType为image生效),设置-1表明不作任何处理 isLazy: @ObjectUtils.toBoolean(isLazy!false), // 设置为ture须要点击上传按钮才上传文件,不然选择后就直接上传 isMini: @ObjectUtils.toBoolean(isMini!false), // 是不是精简上传窗口,无边距,无边框 preview: preview!'', // 是否显示预览按钮,接受参数:weboffice };
<img id="avatarImg" class="profile-user-img img-responsive img-circle" src="${@user.getAvatarUrl().replaceFirst('/ctxPath', ctxPath)}"> <#form:imageclip name="imageBase64" btnText="修改头像" btnClass="btn-block" imageId="avatarImg" imageDefaultSrc="${ctxStatic+'/images/user1.jpg'}" circle="true"/> 后台代码: // 若是设置了头像,则保存头像 if (StringUtils.isNotBlank(imageBase64)){ if ("EMPTY".equals(imageBase64)){ user.setAvatar(StringUtils.EMPTY); }else{ String imageUrl = "avatar/"+user.getUserCode() +"."+FileUtils.getFileExtensionByImageBase64(imageBase64); String fileName = Global.getUserfilesBaseDir(imageUrl); FileUtils.writeToFileByImageBase64(fileName, imageBase64); user.setAvatar(Global.USERFILES_BASE_URL + imageUrl); } }
组件属性:
var p = { // 标签参数 id: id!, // 元素ID,若是不填写,则与name相同 path: path!, // 绑定form上model中属性的值 name: name!, // 元素名称,不填写 value: value!, // 元素值 class: class!'', // 隐藏域的CSS类名 btnText: btnText!'选择图片', // 按钮的名字 btnClass: btnClass!'', // 按钮的CSS类名 imageId: imageId!'', // 裁剪后base64返回到img的id imageDefaultSrc: imageDefaultSrc!'', // 图片默认地址,清除后使用地址 circle: circle!'false', // 是否圆形图片 };
<#form:ueditor name="text" maxlength="10000" height="200" class="required" simpleToolbars="false" readonly="false" outline="false"/>
组件属性:
var p = { // 标签参数 id: id!, // 元素ID,若是不填写,则与name相同 path: path!, // 绑定form上model中属性的值 name: name!, // 元素名称,不填写 value: value!, // 元素值 class: class!'', // 标签框的CSS类名,设置 required 加入必填验证 maxlength: maxlength!'', // 编辑器最大输入字数,为空表明无限制 height: height!'200', // 编辑器的高度,默认200 simpleToolbars: @ObjectUtils.toBoolean(simpleToolbars!false), // 是不是简单的工具条 readonly: @ObjectUtils.toBoolean(readonly!false), // 是否只读模式 outline: @ObjectUtils.toBoolean(outline!false), // 大纲视图 options: options!'', // UE附加选项,逗号隔开。 };
/** * 输出日志 */ log(msg); /** * 输出错误日志 */ error(msg); /** * URL 编码 */ js.encodeUrl(url); /** * URL 解码 */ js.decodeUrl(url); /** * 获得 IE 版本,若是是IE返回:IE版本号,不然返回:false * if (js.ie && js.ie <= 8){ alert('浏览器版本太低') } */ js.ie; /** * 安全取值,复杂类型或嵌套类型时,取不到属性中的属性时不抛出异常 * js.val(jsonObj, 'user.office.name'); */ js.val(jsonObj, attrName); /** * 返回HashCode惟一值(默认忽略大小写) * @param str 要获取的字符串HashCode值 * @param caseSensitive 是否大小写敏感(默认false) * @usage js.hashCode(str); */ js.hashCode(str, caseSensitive); /** * 异步加载文件,loadFile v1.0 * js.loadFile(file文件路径, callback成功回调, error失败回调) * js.loadFile('js/test.js',function(){},function(data){}); * js.loadFile(['js/test.js','css/test.css'],function(){},function(data){}); */ js.loadFile(file, callback, error); /** * 打开一个Window窗体 */ js.windowOpen(url, name, width, height); /** * 关闭当前Window窗体 */ js.windowClose(); /** * 给URL地址添加参数,若是原来有参数则用&前缀,若是没有则用?前缀 */ js.addParam(url, params); /** * 获取URL地址的参数 */ js.getParam(paramName, url); /** * 查看Object的内容,手机调试用 * @param obj */ js.alertObj(obj); /** * 获取字典标签 * js.getDictLabel(${@DictUtils.getDictListJson('sys_menu_type')}, val, '未知', true) */ js.getDictLabel(dictListJson, value, defaultValue, inCss); /////////////////////////////////////// message dialog /** * 显示加载框 * @param message 加载框提示信息 * @param ignoreMessageIfExists 若是已经有加载框如今,则忽略message信息的设置 * @usage js.loading('正在保存...'); */ js.loading(message, ignoreMessageIfExists); /** * 关闭加载框 * @param timeout 关闭延迟时间 * @param forceClose 是否强制关闭 * @usage js.closeLoading(1000, true); */ js.closeLoading(timeout, forceClose); /** * 获得layer对话框对象 * js.layer.msg(); */ js.layer; /** * 显示提示框 * @param message 提示消息 * @param title 提示标题 * @param type 提示类型(success、error、warning、info) * @param timeout 自动关闭毫秒(默认4000毫秒) */ js.showMessage(message, title, type, timeout); /** * 显示错误提示框 */ js.showErrorMessage(responseText); /** * 关闭提示框 */ js.closeMessage(); /** * 提示对话框 * @param message 提示消息 * @param options 对话框选项 * @param closed 对话框关闭回调方法 * @usage js.alert('你好!', function(){}) * @usage js.alert('你好!', {icon: 2}, function(){}) */ js.alert(message, options, closed); /** * 确认对话框 * @param message 确认信息 * @param urlOrFun 确认后的跳转的地址,或调用的方法 * @param data 若是urlOrFun是地址,该参数是调用地址的参数信息 * @param callback 执行ajax的回调方法,若是为空,则直接经过location=urlOrFun跳转。 * @param dataType 返回数据类型(默认json) * @param async 是否异步(默认true) * @param loadingMessage 调用loading(loadingMessage)的提示信息。 * @usage js.confirm('确认删除吗?', '$ctx/biz/delete?id=123', function(data){alert('删除成功')}, 'json', true, '正在删除...'); * @usage js.confirm('确认删除吗?', '$ctx/biz/delete', {id: '123'}, function(data){alert('删除成功')}, 'json', true, '正在删除...'); * @usage js.confirm('确认删除吗?', function(data){alert('删除成功')}); */ js.confirm(message, urlOrFun, data, callback, dataType, async, loadingMessage); /////////////////////////////////////// js template /** * 根据js模板生成代码,使用laytpl引擎 * @param id 模板ID * @param data 模板数据(可选) * @param callback 若是填写,则为异步渲染 * @usage * 模板格式: <sc ript id="dempTpl" type="text/template">//<!-- * 这里写模块内容... * //--></sc ript> * 调用方法: js.template('dempTpl', data); * 模版语法: * 输出一个普通字段,不转义html: {{ d.field }} * 输出一个普通字段,并转义html: {{= d.field }} * JavaScript脚本: {{# JavaScript statement }} */ js.template(id, data, callback); /////////////////////////////////////// ajax form /** * AJAX 提交 * js.ajaxSubmit('/sys/user/save', {param: 1}, function(data){}) */ js.ajaxSubmit(url, data, callback, dataType, async, message); /** * AJAX 提交表单(支持文件上传) * js.ajaxSubmitForm($(form), function(data){}) */ js.ajaxSubmitForm(formJqueryObj, callback, dataType, async, message); /////////////////////////////////////// string /** * String两边去空格 */ js.trim(str); /** * String的startWith */ js.startWith(str, start); /** * String的endWith */ js.endWith(str, end); /** * 截取字符串,区别汉字和英文 */ js.abbr(name, maxLength); /////////////////////////////////////// number /** * 格式化数值 * @param num 待格式化的树 * @param cent 保留小数位数 * @param isThousand 是否进行千分位格式化 */ js.formatNumber(num, cent, isThousand); /** * 金额格式化(千位符,小数四舍五入)金额每隔三位加一个逗号 * @param s 要格式化的数值 * @param n 小数位数 */ js.formatMoney(s, n); /** * 数值前补零 */ js.numberPad(num, n); /////////////////////////////////////// date /** * 日期格式化 * @param date 日期 new Date() * @param f 格式化字符串 yyyy-MM-dd HH:mm:ss */ js.formatDate(date, f); /** * 字符串转为日期 * @param date */ js.parseDate(date); /** * 日期加减 * @param date * @param dadd 天数 */ js.addDate(date, dadd); /** * 快速选择日期方法 * @param type 1今日,2本周,3本月,4本季度,5上月 * @param beginDateId 开始日期控件的ID * @param endDateId 结束日期控件的ID */ js.quickSelectDate(type, beginDateId, endDateId); /////////////////////////////////////// cookie /** * cookie 操做 * @param name Cookie名称 * @param value Cookie值,填写表示设置,不填写表示获取 * @parma options:{expires:7} 若是是数字,则expires单位为天。 */ js.cookie(name, value, options); /////////////////////////////////////// tabPage /** * 获得TabPage对象 */ js.tabPage; /** * 初始化TAB页面 * @param id */ js.initTabPage(id, options); /** * 添加TAB页面 * @param $this 点击的对象 * @param title 提示标题 * @param url 访问的路径 * @param closeable 是否有关闭按钮 * @param refresh 打开后是否刷新从新加载 */ js.addTabPage($this, title, url, closeable, refresh); /** * 获取当前TAB页面 * @param currentTabCallback(contents, contentWindow) 当前页面回调方法,传入当前tab页面的contents和contentWindow */ js.getCurrentTabPage(currentTabCallback); /** * 获取当前页面的上一个TAB页面,并激活上级页面 * @param preTabCallback(contents, contentWindow) 传入上一个tab页面的contents和contentWindow * @param isCloseCurrentTab 是否关闭当前页签 */ js.getPrevTabPage(preTabCallback, isCloseCurrentTab); /** * 关闭当前TAB页面,并激活上级页面 * @param preTabCallback(contents, contentWindow) 关闭前的回调方法,传入上一个tab页面的contents和contentWindow */ js.closeCurrentTabPage(preTabCallback);
数据表格是一个必不可少的元素,在选择这个选型的时候尝试了不少开源组件,最终选择jqGrid,只是由于它接近经典思惟,用着还算顺手,最主要的是碰见什么问题均可以自行解决和修复问题,有人说jqGrid很差看,这不要紧这彻底而已自行编写CSS改造它,下面看看一个简单的例子:
<#form:form id="searchForm" model="${config}" action="${ctx}/sys/config/listData" method="post" class="form-inline " data-page-no="${parameter.pageNo}" data-page-size="${parameter.pageSize}" data-order-by="${parameter.orderBy}"> 参数名称:<#form:input path="configName" maxlength="100" class="form-control" /> 参数键名:<#form:input path="configKey_like" maxlength="100" class="form-control" /> <button type="submit" class="btn btn-primary btn-sm">查询</button> <button type="reset" class="btn btn-default btn-sm">重置</button> </#form:form> <table id="dataGrid"></table> <div id="dataGridPage"></div>
// 初始化DataGrid对象 $('#dataGrid').dataGrid({ // 查询数据表单 searchForm: $('#searchForm'), // 设置数据表格列 columnModel: [ {header:'参数名称', name:'configName', index:'a.config_name', width:200, formatter: function(val, obj, row, act){ return '<a href="${ctx}/sys/config/form?id='+row.id+'" class="btnList" data-title="编辑参数">'+val+'</a>'; }}, {header:'参数键名', name:'configKey', index:'a.config_key', width:200}, {header:'参数键值', name:'configValue', sortable:false, width:260, classes:"nowrap"}, {header:'操做', name:'actions', width:100, sortable:false, title:false, formatter: function(val, obj, row, act){ var actions = []; <% if(hasPermi('sys:config:edit')){ %> actions.push('<a href="${ctx}/sys/config/form?id='+row.id+'" class="btnList" title="编辑参数"><i class="fa fa-pencil"></i></a> '); <% } %> <% if(hasPermi('sys:config:delete')){ %> actions.push('<a href="${ctx}/sys/config/delete?id='+row.id+'" class="btnList" title="删除参数" data-confirm="确认要删除该参数吗?"><i class="fa fa-trash-o"></i></a> '); <% } %> return actions.join(''); }} ], // 加载成功后执行事件 ajaxSuccess: function(data){ } });
是否是比你使用foreach方便的多,封装后名字叫dataGrid,这只是展现了冰山一角,它支持全部jqGrid参数,即简化了代码编写,又不失功能