JeeSite 框架学习笔记
目录
一、全局配置
1. 获取 Resource资源实际路径 DefaultResourceLoader
示例:
// 获取 Resource资源实际路径
ResourceLoader resourceLoader = new DefaultResourceLoader();
System.out.println(resourceLoader.getResource("classpath:static/font/").getURI());
参考链接:
java web获取配置文件路径.
java web获取配置文件路径_绝对路径、相对路径.
2. 配置前端 js css
配置前端 js css:
// resources\views\include\jslibs.html
// resources\views\include\csslibs.html
二、JAVA 处理 Word 和 打印
1. JAVA 处理 Word
参考链接:
Java多种方式动态生成doc文档.
POI设置 word文本下划线样式及文字底纹.
2. JAVA 打印
参考链接:
使用Java调用打印机进行打印(JPG、PDF和Word三种文件格式).
Java jacob调用打印机打印word文档.
3. Word转图片
// 测试:从本地读取word保存图片到本地
File fWord = new File("d:\\test.docx");
InputStream inStream = new FileInputStream(fWord);
List<BufferedImage> tempImg = WordAspose.wordToImg(inStream);
BufferedImage mergeImage = WordAspose.mergeImage(false, tempImg);
ImageIO.write(mergeImage, "jpg", new File("D:\\1.jpg"));
4. 前端 js 打印页面
// 打印选项设置
// 注意:如果写成 magin: 0mm; 可能会不起作用
// 此方法不适用IE内核的浏览器,IE 使用 pagesetup_null()
<style media="print">
@page {
margin-bottom: 0mm;
margin-top: 0mm
}
/*@page {*/
/* size: auto; !* auto is the initial value *!*/
/* margin: 0mm; !* this affects the margin in the printer settings *!*/
/*}*/
.noprint {display: none;}
</style>
// 页面打印
function doPrint() {
let bdhtml = window.document.body.innerHTML;
// 设置打印页面内容
window.document.body.innerHTML = bdhtml;
if (getExplorer() == "IE") {
pagesetup_null();
}
window.print();
}
// 获取浏览器类型
function getExplorer() {
let explorer = window.navigator.userAgent;
//ie
if (explorer.indexOf("MSIE") >= 0) {
return "IE";
}
//firefox
else if (explorer.indexOf("Firefox") >= 0) {
return "Firefox";
}
//Chrome
else if (explorer.indexOf("Chrome") >= 0) {
return "Chrome";
}
//Opera
else if (explorer.indexOf("Opera") >= 0) {
return "Opera";
}
//Safari
else if (explorer.indexOf("Safari") >= 0) {
return "Safari";
}
}
// 设置去除页眉页脚
function pagesetup_null() {
let hkey_root, hkey_path, hkey_key;
hkey_root = "HKEY_CURRENT_USER";
hkey_path = "\\Software\\Microsoft\\Internet Explorer\\PageSetup\\";
try {
let RegWsh = new ActiveXObject("WScript.Shell");
hkey_key = "header";
RegWsh.RegWrite(hkey_root + hkey_path + hkey_key, "");
hkey_key = "footer";
RegWsh.RegWrite(hkey_root + hkey_path + hkey_key, "");
} catch (e) {
}
}
打印页面中特定内容:
// 需要打印的页面内容
<div class="">
<!--startprint-->
<img src="${ctx}/management/v1/minio/file/view?path=${outBarnCertQuery.outCertPicUrl}">
<!--endprint-->
</div>
// 指定部分内容打印
function doPrint() {
let bdhtml = window.document.body.innerHTML;
// 用于截断字符串的内容,截取到指定的图片内容了
let sprnstr = "<!--startprint-->";
let eprnstr = "<!--endprint-->";
let prnhtml = bdhtml.substr(bdhtml.indexOf(sprnstr) + 17);
prnhtml = prnhtml.substring(0, prnhtml.indexOf(eprnstr));
// let printContent = $("#printConent")[0].innerHTML; //利用jquery获取打印内容;
// window.document.body.innerHTML = printContent;
// 设置打印页面内容
window.document.body.innerHTML = prnhtml;
if (getExplorer() == "IE") {
pagesetup_null();
}
window.print();
// 还原原有页面内容
window.document.body.innerHTML = bdhtml;
}
参考链接:
window.print():去除页眉页脚及网址.
三、Form 页面
1. 单列页面
a) label 和控件在一行
// 全部用 col-xs-*
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label col-xs-2" title="">
<span class="required ">*</span> ${text('编码')}:<i class="fa icon-question hide"></i></label>
<div class="col-xs-10">
<#form:input path="code" maxlength="30" class="form-control required"/>
</div>
</div>
</div>
</div>
b) 底部按钮与编辑框右对齐
// 使用 pull-right,再用 pr 或 mr 调节
<div class="row pr10">
<div class="pull-right mr5">
<button type="submit" class="btn btn-sm btn-primary" id="btnSubmit"><i class="fa fa-check"></i> ${text('保 存')}</button>;
<button type="button" class="btn btn-sm btn-default" id="btnCancel" onclick="closeCurrentLayer()"><i class="fa fa-reply-all"></i> ${text('关 闭')}</button>
</div>
</div>
2. 参数控制
a) 参数模板控制
// controller
@RequiresPermissions("management:baseinfo:enterprise:view")
@RequestMapping(value = "form")
public String form(Enterprise enterprise, Model model) {
enterprise.setIsEnabled(1L);
if (StringUtils.isBlank(enterprise.getEnterpriseId())) {
model.addAttribute("operation", "0");
} else {
model.addAttribute("operation", "1");
}
model.addAttribute("enterprise", enterprise);
return "modules/management/baseinfo/mgtEnterprise/enterpriseForm";
}
// form.html
<div class="col-xs-9">
<% if(operation != "0"){ %>
<#form:input path="creditCode" readonly="true" maxlength="20" class="form-control required"/>
<% }else{ %>
<#form:input path="creditCode" maxlength="20" onblur="checkCreditCode()" class="form-control required"/>
<% } %>
</div>
b) 利用实体的 isNewRecord 判断
${text(storageInPlan.isNewRecord ? '新增入库计划' : '编辑入库计划')}
<% if(storageInPlan.isNewRecord){ %>
<div class="form-unit">${text('基本信息')}</div>
<% } %>
3. select 控件 和 input 控件同一行无法对齐
a) select 控件 和 input 控件同一行无法对齐
// 1. 在 select 控件 和 input 控件外再包一层 div.col-xs-12 ,再微调一下 margin 和 padding
<div class="col-xs-12">
<div class="col-xs-6">
<div class="form-group" style="margin-left: -30px;">
<label class="control-label col-xs-4" title="">
<span class="required ">*</span> ${text('是否有劳动能力')}:<i class="fa icon-question hide"></i></label>
<div class="col-xs-8">
<% if (formType == "1"){ %>
<#form:input name="isLaborAbility" value="${regUnemployment.isLaborAbility == 0 ? '否' : '是'}" class="form-control" disabled="true"/>
<% } else { %>
<#form:select path="isLaborAbility" dictType="sys_yes_no" blankOption="true" blankOptionLabel="请选择" class="form-control"/>
<% } %>
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group" style="margin-left: -8px;">
<label class="control-label col-xs-4" title="">
<span class="required hide">*</span> ${text('专业技能证件')}:<i class="fa icon-question hide"></i></label>
<div class="col-xs-8 pr0">
<#form:input path="skillCertName" maxlength="64" class="form-control"/>
</div>
</div>
</div>
</div>
// 2. 设置 Select 控件的高度与 Input 控件一样
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-xs-4" title="">
<span class="required ">*</span> ${text('业务类型')}:<i class="fa icon-question hide"></i></label>
<div class="col-xs-8" style="height: 30px">
<#form:select path="clsInoutReasonId" items="${clsInoutReasonList!}" itemLabel="nameAbbr" itemValue="code" blankOption="true" data-placeholder="请选择" class="form-control required"/>
</div>
</div>
</div>
4. input 控件
a) input 控件中显示数组元素
// form
<div class="form-group">
<label class="control-label col-xs-4" title="">
<span class="required ">*</span> ${text('致残类型')}:<i class="fa icon-question hide"></i></label>
<div class="col-xs-8">
<% var cripItems = ["其他", "视力残疾", "听力残疾", "言语残疾", "力残疾", "肢体残疾", "精神残疾", "多重残疾"]; %>
<% if (formType == "1"){ %>
<#form:input name="typeCrip" value="${cripItems[regDisabler.typeCrip]}" class="form-control required" disabled="true"/>
<% } else { %>
<#form:select path="typeCrip" dictType="crip_type" blankOption="true" blankOptionLabel="请选择" class="form-control required"/>
<% } %>
</div>
</div>
b) input 控件中显示字典中的值
// form
<div class="form-group">
<label class="control-label col-xs-3" title="">
<span class="required ">*</span> ${text('建筑用途')}:<i class="fa icon-question hide"></i></label>
<div class="col-xs-9">
<% if (formType != "1"){ %>
<#form:select path="houseUsage" dictType="HOUSE_USAGE" blankOption="true" blankOptionLabel="请选择" class="form-control"/>
<% } else { %>
<% var tempHouseUsage2 = "商住两用"; %>
<% var tempHouseUsage3 = @DictUtils.getDictListJson('HOUSE_USAGE'); %>
<% var tempHouseUsage = @DictUtils.getDictLabel('msg_inner_content_level', '1', '普通'); %>
// 注意: @DictUtils.getDictLabel 的第二个参数是String类型,传入整型会报错
<#form:input name="houseUsage" value="${@DictUtils.getDictLabel('HOUSE_USAGE', resDoorAddr.houseUsage, ' ')}" class="form-control" disabled="true"/>
<% } %>
</div>
</div>
c) input 控件中自定义格式化
<#form:input name="field" value="${@DateUtils.formatDate(model.field,'yyyyMMdd')}" class="form-control "/>
<#form:input name="field" value="${@NumberUtils.formatNumber(model.field,'0.0')}" class="form-control "/>
d) input 控件中显示单位
<div class="form-group">
<label class="control-label col-xs-4" title="">
<span class="required hide">*</span> 应急资金投入:<i class="fa icon-question hide"></i></label>
<div class="col-xs-8">
<#form:input path="yjzjtr" maxlength="14" class="form-control digits"/>
<span style="position: absolute; top: 0; right: 15px; display: table-cell; white-space: nowrap; padding: 5px 10px;">元</span>
</div>
</div>
5. jQuery Validate自定义验证
// 注册验证方法
$(function(){
// 身份证号码验证
$.validator.addMethod("isIdCardNo", function(value, element) {
return this.optional(element) || (checkIdcard(value) == 'ok');
}, "请输入正确的身份证号码");
});
$("#inputForm").validate({
// 定义验证规则
rules: {
// householderIdnum : 控件ID
// isIdCardNo 验证方法名,[]: 参数列表(此处为空)
householderIdnum: {isIdCardNo:[]}
},
submitHandler: function (form) {
js.ajaxSubmitForm($(form), function (data) {
js.showMessage(data.message);
if (data.result == Global.TRUE) {
parent.refreshList();
closeCurrentLayer();
}
}, "json");
}
});
6. textarea 禁止自定义大小
// 添加 style="resize: none;"
<#form:textarea path="spYj" rows="8" maxlength="200" style="resize: none;" class="form-control"/>
7. 图片路径获取
// ${ctxStatic} 对应 resources/static 路径
// ${ctx}
// box-sizing: border-box 盒子模型:height = margin + padding + content;
<div id="alarmImageDiv" style="width: 100%; border: #e7e7e7 solid 1px; padding: 10px; box-sizing: border-box;" >
<img id="alarmImageBox" style="width: 100%;" src="${ctxStatic}/images/videoAlarm/personIn.png" alt="">
</div>
8. div onresize事件
// 在监控的过程中发现每次改变浏览器窗口的时候 onresize 事件都会触发两次(产生 的原因貌似比较复杂,网上没有定论,发现在最大化浏览器的时候,浏览器也会闪一下,有两次窗口大小的改变)。
// 解决方法: setTimeout()
// 解决触发两次的情况,实际浏览器的 resize 事件可能会触发 n 次,持续时间并不一定是 100 毫秒,例如用户拖动浏览器的边框,以下代码会执行多次 callback 事件;
$(function () {
window.onresize = function () {
var target = this;
if (target.resizeFlag) {
clearTimeout(target.resizeFlag);
}
target.resizeFlag = setTimeout(function() {
resizeWidget();
target.resizeFlag = null;
}, 800);
}
});
// gridData 控件调整大小有延时,设置一定的时间间隔才能获取到最新的宽高
function resizeWidget() {
$('#alarmImageDiv').height($('#gbox_dataGrid').height() - 20);
}
9. 去掉 comboTree的图标
<style>
/*去掉 comboTree的图标*/
.tree-folder{ width: 0px !important; }
.tree-file{ width: 0px !important; }
</style>
10. jQuery validator
a) validate 表单提交前二次验证数据的合法性
// 验证数据的合法性,不合法 return false;
$("#inputForm").validate({
submitHandler: function(form){
let zzValue = $("#zz").val();
if (zzValue == "" || zzValue == "请选择") {
js.alert("请选择组长", {icon: 7});
console.log(zzValue);
return false;
}
let zgbmValue = $("#zgbmCombo").val();
if (zgbmValue == "" || zgbmValue == "请选择") {
js.alert("请选择主管部门", {icon: 7});
console.log(zgbmValue);
return false;
}
$('#zgbmdm').attr('disabled', false);
js.ajaxSubmitForm($(form), function(data){
js.showMessage(data.message);
if(data.result == Global.TRUE){
js.getCurrentTabPage(function(window){
window.refresh()
closeCurrentLayer()
},false);
}
}, "json");
}
});
b) 添加自定义校验
// 中文字两个字节
jQuery.validator.addMethod("byteRangeLength", function(value, element, param) {
var length = value.length;
for(var i = 0; i < value.length; i++){
if(value.charCodeAt(i) > 127){
length++;
}
}
return this.optional(element) || ( length >= param[0] && length <= param[1] );
}, $.validator.format("请确保输入的值在{0}-{1}个字节之间(一个中文字算2个字节)"));
// 实用工具:用参数代替模板中的 {n}。
jQuery.format(template,argument,argumentN...)
// 邮政编码验证
jQuery.validator.addMethod("isZipCode", function(value, element) {
var tel = /^[0-9]{6}$/;
return this.optional(element) || (tel.test(value));
}, "请正确填写您的邮政编码");
// 固话和手机号码验证
$.validator.addMethod("telOrMobile", function(value, element) {
var tel = /^(((\d{3,4}-?)?[0-9]{7,8})|(1(3|4|5|6|7|8|9)\d{9}))$/g;
return this.optional(element) || (tel.test(value));
}, $.validator.messages.phone);
11. 输入范围在 {0} 到 {1} 之间的数值
// 输入范围在 0 到 9999.999999 之间的数值;
<#form:input path="safetyReserve" maxlength="11" type="number" min="0" max="9999.999999" style="width:173px" class="form-control required number"/>
12. 强行将字段更新为 null
// 增加 isUpdateForce = true
@Column(name="parent_id", attrName="parentId", label="父节点", comment="父节点", isUpdateForce = true),
13. 传到后端的值中多了逗号 “,”
// 问题原因: 表单中存在重名的控件
14. 刷新 list 页面
// 1. 通过 iframe 的name 属性获取对应的 window对象
<iframe id="storageInPlanListIframe" name="storageInPlanListIframe" class="ui-layout-content p0 b0 " src="${ctx}/plan/storageInPlan/list"></iframe>
js.getCurrentTabPage(function(contentWindow){
try {
contentWindow.storageInPlanListIframe.refresh();
} catch (e) {
console.log("reloadList error: ", e);
}
});
// 2.
js.getCurrentTabPage(function (contentWindow){
let dom = contentWindow.document.getElementById("mainIframe").contentWindow
updateDataGrid(dom.$("#dataGrid"),data.data);
});
// 3.
js.closeCurrentTabPage(function(contentWindow){
contentWindow[0].$('#dataGrid').dataGrid('refresh');
closeCurrentLayer();
});
四、DataGrid
1. 禁止拖拽表格列头
// 初始化DataGrid对象
$('#dataGrid').dataGrid({
searchForm: $("#searchForm"),
showRownum: false, // 是否显示行号
frozenCols: true, // 禁止拖拽表格列头
sortableColumn: false, // 是否允许列排序
columnModel: []
});
2. 多次表头出现多出一列
// 1. 设置 css 强调把多出的一列高度设置为 0
<style>
/*.jqg-first-row-header {display: none !important;}*/
.ui-jqgrid-hbox tr {height: 0px !important;}
</style>
// 初始化DataGrid对象
$('#dataGrid').dataGrid({
searchForm: $("#searchForm"),
columnModel: [],
// 2. 设置多级表头
groupHeaders: {
twoLevel:[
{startColumnName: 'dateUnemp', numberOfColumns: 8, titleText: '失业信息'},
{startColumnName: 'isReEmp', numberOfColumns: 8, titleText: '再就业信息'}
]
},
});
3. action 图标对齐
// 新增一个隐藏的图标
actions.push('<a href="javascript:0" title="${text(' ')}" style="visibility: hidden;"><i class="fa fa-eye"></i></a> ');
4. 最小高度设置 options.gridMinMeight
统一上下结构列表的样式:
- 上下结构的列表页, 下方Pannel高度统一:
带分页控件的,高度350px,
不带分页控件的,高度300px - 下方Panel内,dataGrid设置属性 gridMinMeight: 150
// 统一上下结构列表的样式
// 1. 布局 south__size: '350'
$('body').layout({
south__size: '350',
south__closable: false,
south__onresize: function () {
// 调整iframe的高度
var height = $("#parent").height();
$('#emerPersonFrame').height(height - 32);
}
});
// 2. Datagrid 最小高度设置: gridMinMeight: 150
// 初始化DataGrid对象
$('#dataGrid').dataGrid({
searchForm: $("#searchForm"),
showRownum: false, // 列排序
resizable: true, // 列宽固定
shrinkToFit: true,
sortableColumn: false,
gridMinMeight: 150,
columnModel: [
{header:'<span class="col-align-left">人员姓名</span>', name:'xm', index:'a.xm', width:150, align:"left"},
],
// 加载成功后执行事件
ajaxSuccess: function(data){
}
});
5. 下载文件处理
// dataGrid
actions.push('<a οnclick="downloadFile('+"'"+row.emerDealArchivesId+"'"+');" title="${text('下载资料')}"><i class="fa fa-download"></i></a> ');
// javascript
function downloadFile(emerDealArchivesId) {
js.ajaxSubmitForm($('#searchForm'), {
url:'${ctx}/biz/emer_deal/emerDealArchives/downFile?emerDealArchivesId='+emerDealArchivesId,
callback:function (data){
// 失败提示
if(data.result == Global.FALSE){
js.showMessage(data.message);
}
},
downloadFile:true
});
}
五、jQuery 应用
1. 属性选择器处理按钮
//刷新按钮触发事件
function refresh(){
$('button[type=submit]').click();
}
2. SELECT 控件操作
// 遍历所有选项
$('#parentCode option').each(function () {
console.log($(this).val());
})
// 默认选中项
$('#parentCode').val("654023101").select2();
// 打印选中选项
console.log($("#parentCode").find("option:selected").text());;
// data-ajax select 设置默认值
<#form:select path="transport"
data-ajax--url="${ctx}/biz/inv_manager/inventoryOutEmergency/queryResCar"
data-ajax--delay="500"
blankOption="true" data-placeholder="请输入车牌号"
class="form-control"/>
$('#transport').select2({data:[{id: data.numberCar, text: data.numberCar}]}).val(data.numberCar).select2();
3. ajax 请求数据
a) 415 错误
// 解决:
// 前端:
// contentType 与 data 都设置为JSON
// contentType: 'application/json; charset=UTF-8'
// JSON.stringify(data)
$.ajax({
type: "post",
async: false,
url: "${ctx}/emer_manager/emerPlanEnact/saveSelectEmerEvent",
contentType: 'application/json; charset=UTF-8',
data: JSON.stringify(data),
dataType: "json",
success: function (msg) {
console.log(msg);
},
error: function (obj) {
js.alert('系统发生异常!');
}
});
// 后台:
// 参数列表中加入@RequestBody注解
/**
* 保存应急事件数据
* url: "${ctx}/emer_manager/emerPlanEnact/saveSelectEmerEvent"
* param: emerEventId
* return: EmerPlan
*/
@RequiresPermissions("emer_manager:emerPlanEnact:edit")
@PostMapping(value = "saveSelectEmerEvent")
@ResponseBody
public String saveSelectEmerEvent(@RequestBody EmerEvent emerEvent) {
}
参考链接:
415错误及解决方法.
[Resolved org.springframework.web.HttpMediaTypeNotSupportedException: Content type.
六、IDEA 配置
1. 手动加的 lib 加入到 git
// 问题描述:
[ERROR] Failed to execute goal on project jeesite-cloud-module-management: Could not resolve dependencies for project com.jeesite:jeesite-cloud-module-management:war:4.3.3-SNAPSHOT: Could not find artifact aspose.words:aspose-words:jar:15.8.0 at specified path /home/tfs-agent/_work/2/s/modules/management/management/src/main/webapp/WEB-INF/lib/aspose-words-15.8.0.jar
// 解决方案:
// 1. 打开 .gitignore
// 2. 在最后新增一行
!**/webapp/WEB-INF/lib/*.jar
2. 快速查找和定位文件位置
3. run application报错
a) 报错信息:Error running Application. Command line is too long. Shorten the command line via JAR manifest or via a classpath file and rerun.
解决:
方案一:
方案二:
全局设置
-
由于方案一是当前项目设置,其他项目打开又没有了,所以可以设置全局,点击File→New Projects Settings→Run Configuration Templates for New Projects
-
点击springboot项目,跟方案一的配置一样。
参考链接:
IDEA报错Command line is too long.
IDEA报错Command line is too long.
4. DashBoard/ Services不显示端口号
解决方案:
打开启动项管理,把Enable JMX agent复选框选中,重新启动对应服务即可显示端口号。(IDEA 版本:2021.3.1)
5. 底部显示 service 窗口
a) View => Tool Windows => Service (快捷键: Alt + 8)
b) Service 窗口点 " + " 按钮 => Run Configuration Type +. Spring Boot
6. 快捷键
// 1. Surround with
CTRL+ALT+T
// eg.
//region Description
//endregion
// 2. 生成变量
CTRL+ALT+V
七、修改 JeeSite内置页面
1. 修改登录页面
a) 禁止登录页面出现滚动条
// 以下配置会出现纵向滚动条
/*.login-page {*/
/* background-image: url(${ctxStatic}/themes/login/bg.png);*/
/* background-size: 100% 100%;*/
/* background-repeat: no-repeat;*/
/* color: #FFFFFF;*/
/*}*/
.login-page {
min-height: 500px;
background-image: url(${ctxStatic}/themes/login/bg.png);
background-position: center center;
background-repeat: no-repeat;
/*background-attachment: fixed;*/ /* 底图固定:不随滚动条的滚动而滚动*/
/*background-size: cover;*/
overflow: hidden; /* 设置禁止出现滚动条 */
}
八、权限配置
1. 无需登录直接访问 url
需求:通过 http://localhost:8980/js/a/track/home/homeIndex 就可以直接打开首页。
注意点:通过 nacos 配置对应模块下的 jeesite-cloud-module-track.yml
# URI 权限过滤器定义(自定义添加参数时,请不要移除 ${adminPath}/** = user,否则会导致权限异常)
# 提示:填写过滤规则,请注意先后顺序,从上到下,先匹配先受用规则,匹配成功后不再继续匹配。
filterChainDefinitions: |
# 过滤掉这些接口(anno表示可匿名使用,可以理解为匿名用户或游客,即不登录就可以访问这个接口)
${adminPath}/track/home/** = anon
/inner/api/** = inner
/api/** = user
${adminPath}/** = user
# # URI 权限过滤器定义(以下参考,必须登录user可访问的地址和不需要登录anon可访问地址)
# filterChainDefinitions: |
# /ReportServer/** = user
# ${adminPath}/file/** = anon
# ${adminPath}/cms/* = anon
# ${adminPath}/cms/site/select = anon
# ${adminPath}/cms/site/* = anon
# ${adminPath}/cms/category/treeData = anon
# ${adminPath}/cms/category/* = anon
# ${adminPath}/cms/article/* = anon
# ${adminPath}/cms/link/* = anon
# ${adminPath}/sys/corpAdmin/treeData = anon
# ${adminPath}/${spring.application.name}/swagger/** = anon
# ${adminPath}/** = user
案例:
// 1. Nacos 的 jeesite-cloud-module-management.yml 增加
#Shiro 相关配置
shiro:
# URI 权限过滤器定义
filterChainDefinitions: |
${adminPath}/management/inoutsupervise/outBarnCertQuery/** = anon
/inner/api/** = inner
/api/** = user
${adminPath}/** = user
// 2. Controller 增加映射
// @RequiresPermissions("management:inoutsupervise:outBarnCertQuery:view") // 去掉权限
@RequestMapping(value = {"mIndex", ""})
public String mIndex(OutBarnCertQuery outBarnCertQuery, Model model) {
model.addAttribute("outBarnCertQuery", outBarnCertQuery);
return "modules/management/inoutsupervise/outBarnCert/mIndex";
}
// 3. 访问方式
http://localhost:8980/js/a/management/inoutsupervise/outBarnCertQuery/mIndex
九、Beetl
1. 注意点
a) 需要知道Java集合,数组长度,统一用虚拟属性~size来表示
// 需要知道Java集合,数组长度,统一用虚拟属性~size来表示
var list=[1,2,3];
var size = list.~size
b) for-in循环
// 1.
<%
for(user in userList){
print(userLP.index);
print(user.name);
}
%>
//第三行代码 userLP是Beetl隐含定义的变量,是一个ILoopStatus实例,能在循环体内使用。其命名规范是item名称后加上LP,他提供了当前循环的信息,如
// - **userLP.index **当前的索引,从1开始
// - userLP.dataIndex 索引,从0开始
// - **userLP.size **集合的长度
// - userLP.first 是否是第一个
// - userLP.last 是否是最后一个
// - userLP.even 索引是否是偶数
// - userLP.odd 索引是否是奇数
// 2.
// 对于渲染逻辑更为常见的是经典的for循环语句
<%
var a = [1,2,3];
// a.~size 是集合的大小,beetl 中的虚拟属性
for(var i=0;i<a.~size;i++){
print(a[i]);
}
%>
// 3.
// 不同于通常程序语言,如果没有进入循环体,则不需额外的处理,模板渲染逻辑更常见情况是如果没有进入循环体,还需要做点什么,因此,对于for循环来说,还有elsefor 用来表达如果循环体没有进入,则执行elsefor 后的语句
<%
var list = [];
for(item in list){
}elsefor{
print("未有记录");
}
%>
c) 安全输出
模板中还有两种情况会导致模板输出异常
- 有时候模板变量并不存在,这时候必须报错,如果简单忽略不输出(Velocity就是这样),很容易留坑
- 模板变量为null,但输出的是此变量的一个属性,如 ${user.wife.name}
// 安全输出指示符号: !
// 可以在!后增加一个常量(字符串,数字类型等),或者另外一个变量,方法,本地调用,作为默认输出,譬如:
// 如果user为null,或者user.wife为null,或者user.wife.name为null,输出单身
${user.wife.name!"单身"}
// 表示如果user为null,或者user. birthday为null,输出 System.constants.DefaultBir
${user.birthday!@System.constants.DefaultBir}
d) 调用Java方法与属性
可以通过符号@来表明后面表达式调用是java风格,可以调用对象的方法,属性
${@user.getMaxFriend(“lucy”)}
${@user.maxFriend[0].getName()}
${@com.xxxx.constants.Order.getMaxNum()}
// 内部类(包括枚举)访问同java一样,如User类有个内部枚举类Gender,访问是User$Gender
${@com.xxxx.User$Gender.MAN}
<%
var max = @com.xxxx.constants.Order.MAX_NUM;
var c =1;
var d = @user.getWife(c).getName();
%>
十、不同窗口之间交互
a) 三层及以上窗口交互:通过当前 layer 向上一层 layer 传递数据,窗口层次关系如下图:
// 方案一:利用 parent.window 获取目标窗口
// 调试技巧,在 F12调试窗口的控制台中输入 parent查看,在目标中添加类似aaaPlanEmerForm的function方便确认是否是目标窗口
parent.window[0].aaaPlanEmerForm();
// 方案二:利用 parent.layer
var index = parent.layer.getFrameIndex("layui-layer-iframe1");
var body = parent.layer.getChildFrame('body', index);
body.children().find('#xzcy').val('小组成员赋值'); // 小组成员赋值
%>
b) 同一 Tab 下的不同 window 之间交互,窗口之间的关系如下图:
// 1:获取 currentTabWindow,保存各窗口引用
// xxx1.html
let currentTabWindow;
js.getCurrentTabPage(function (w) {
// 将当前 window 保存到 currentTabWindow的属性中,这样就可以在同tab 的其他window中访问
w.mainListWindow = window;
currentTabWindow = w;
});
// xxx2.html
let currentTabWindow;
js.getCurrentTabPage(function (w) {
w.listWindow = window;
currentTabWindow = w;
});
// 2:利用 currentTabWindow 保存的窗口引用操作相应窗口
onSelectRow: function(id, isSelect, event) {
let rowData =$('#dataGrid').dataGrid('getRowData', id);
let mainListWindow = currentTabWindow.mainListWindow;
let listWindow = currentTabWindow.listWindow;
console.log("currentTabWindow = ", currentTabWindow);
console.log("mainListWindow = ", mainListWindow);
console.log("listWindow = ", listWindow);
},
%>
十一、使用经验总结:
1. 查看表单中的控件禁用:
// planEmerForm.html
$(function () {
$('#isEnabled').select2({minimumResultsForSearch: -1});
if ($('#zt').val() != "新的") {
$('#inputForm *').attr('disabled', true);
$('#btnSubmit').css('display', 'none');
$('#btnCancel').attr('disabled', false);
$('#zgbmCombo').combotree('disable');
}
});
2. ajax 请求:
a) JQuery:
// 使用示例
$.ajax({
type: "get",
async: false,
url: "${ctx}/biz/emerManager/emerPlan/getEmerPersonList",
data: {
'emerOrgId': emerOrgId
},
dataType: "json",
success: function (msg) {
console.log(msg);
},
error: function (obj) {
js.alert('系统发生异常!');
}
});
b) JeeSite:
/**
* AJAX 提交
* @param urlOrOpt 请求地址,或 AJAX 的选项(Opt为4.2.0新增)
* @param data 请求参数
* @param callback 请求 AJAX 后的回调方法
* @param dataType 返回数据类型(默认JSON)
* @param async 是否异步(默认true)
* @param message 调用 js.loading(message) 的提示信息。
* @usage js.ajaxSubmit('/js/a/index', function(data){ log(data) })
* @usage js.ajaxSubmit('/js/a/index', {param: 1}, function(data){ log(data) })
* @usage js.ajaxSubmit('/js/a/index', {param: 1}, function(data){ log(data) },
'json', true, '正在获取数据...')
* @usage js.ajaxSubmit({
url: '/js/a/index', data: {id: '123', name: '456'},
callback: function(data){ log(data) }, dataType: 'json',
async: true, message: '正在获取数据...'});
*/
js.ajaxSubmit(urlOrOpt, data, callback, dataType, async, message);
3. DataGrid 中 Action 按钮操作方式
// <a> + href=url
actions.push('<a href="${ctx}/biz/emer_manager/emerEvent/delete?emerEventId='+row.emerEventId+'" class="btnList" title="${text('删除应急事件信息')}" data-confirm="${text('确认要删除该应急事件信息吗?')}"><i class="fa fa-trash-o"></i></a> ');
// <a> + οnclick=fun()
actions.push('<a οnclick="addOrEditForm('+"'"+row.emerEventId+"'"+')" title="${text('编辑应急事件信息')}"><i class="fa fa-pencil"></i></a> ');
// <a> + href=javascript:fun()
actions.push('<a href="javascript:lookForm('+ "'" + row.emerEventId + "'" + ')" title="${text('查看应急事件信息')}"><i class="fa fa-eye"></i></a> ');
4. js.getCurrentTabPage 打开新页面窗口回调
// innerCallback 回调
// 获取百度点坐标
function getPoint(){
var jd = $("#jd").val();
var wd = $("#wd").val();
var dtlAddress = $("#dtlAddress").val();
var url = '${ctxPath}/biz/commonunit/baiduMapPoint';
if(isDefine(jd) && isDefine(wd)){
url = url+"?longitude="+jd+"&latitude="+wd+"&address="+dtlAddress;
}
js.getCurrentTabPage(function (contentWindow) {
contentWindow.layer.open({
type: 2,
title: '获取经纬度及地址',
maxmin: false,
resize :false,
scrollbar: false,
area: ['1000px', '705px'],
content: [url, 'no'],
innerCallback: function(index, layero, data){
if(typeof mapPointCallback == 'function'){
mapPointCallback(data);
}
return true;
}
});
});
}
5. 文本中加入汉字空格
// 英文空格:
// 汉字空格:  
<a href="#tabOther" aria-controls="tabOther" role="tab" data-toggle="tab"> 其他 </a>
n. temp
// Hello world
console.log("Hello world!");
十二、JavaScript 操作DOM:
1. 向 document中手动添加 style:
// mobilizationCapability/index.vue
// document中 append sytle
var style = this.$refs.mapcontainer.contentWindow.document.createElement("style");
style.type = "text/css";
style.innerHTML =
".custom-popup { width: 100%; height: 100%; padding: 5px; color: white; font-size: 16px; text-align: left; } .custom-popup2 { width: 100%; height: 100%; padding: 5px; color: yellow; font-size: 14px; text-align: left; }";
this.$refs.mapcontainer.contentWindow.document
.getElementsByTagName("head")
.item(0)
.appendChild(style);
// 使用
let pointid = this.mapEdit.addpoint(
res,
icon,
{
content: `<div class="custom-popup2"><div class="custom-popup">${res.ssqymc}</div><div>储备能力:200吨</div><div>储备物资:100吨</div><div>日生产能力:15吨</div><div>日加工能力:20吨</div><div>日配送能力:60吨</div><div>配送车辆数:5辆</div><div>响应能力:2小时</div></div>`,
},
)
十三、调试技巧:
1. 跟踪前端底层代码:
十四、DataGrid:
1. 同一页面根据 url 参数不同显示内容不同:
// url 配置:
// 登记 url:/biz/inv_manager/inventoryIn/index
// 审核 url:/biz/inv_manager/inventoryIn/index?type=submit
// index 页面中:
<iframe id="inventoryInMainFrame" name="inventoryInMainFrame"
class="ui-layout-content p0"
src="${ctx}/biz/inv_manager/inventoryIn/list?type=${parameter.type}"
></iframe>
// DataGrid
{header:'${text(" ")}', name:'actions', width:('${parameter.type}' == 'submit' ? 58 : 102), align: 'right', fixed:true, sortable:false, title:false, frozen: true,formatter: function(val, obj, row, act){
var actions = [];
if(row.statusIo=="待入库"){
//<% if(hasPermi('inv_manager:inventoryIn:edit') && parameter.type != 'submit'){ %>
actions.push('<a href="javascript:addOrEditForm('+ "'" + row.ivyInoutDtlId + "'" + ')" title="${text("编辑日常入库信息")}"><i class="fa fa-pencil"></i></a> ');
actions.push('<a href="${ctx}/biz/inv_manager/inventoryIn/del?ivyInoutDtlId='+row.ivyInoutDtlId+'" class="btnList" title="${text("删除日常入库信息")}" data-confirm="${text("确认要删除该日常入库信息吗?")}"><i class="fa fa-trash-o"></i></a> ');
actions.push('<a href="javascript:chouyang('+ "'" + row.ivyInoutDtlId + "'" + ')" title="${text("生成样品")}"><i class="fa fa-bitbucket"></i></a> ');
//<% } %>
//<% if(hasPermi('inv_manager:inventoryIn:check') && parameter.type == 'submit'){ %>
actions.push('<a href="javascript:checkFileSample('+ "'" + row.ivyInoutDtlId + "'" + ')" title="${text("提交日常入库信息")}"><i class="fa fa-check"></i></a> ');
//<% } %>
}
actions.push('<a href="javascript:viewForm('+ "'" + row.ivyInoutDtlId + "'"+')" title="${text("查看日常入库信息")}"><i class="fa fa-eye"></i></a> ');
return actions.join('');
}},
十五、自动生成:
1. 新增/修改 表单 自动生成 详设文档:
// 打印文档
$(document).ready(function(){
var regArray = ['email', 'mobile', 'phone', 'contactPhone', 'simplePhone', 'zipCode', 'idcard'];
var ctlList = [];
$('#inputForm label').each(function () {
ctlList.push({label: $(this).text().trim().replace('* ', '')});
});
// console.log(ctlList);
var ctlList2 = [];
$('#inputForm input, #inputForm select').each(function () {
if ($(this).attr('type') != 'hidden') {
var maxlength = $(this).attr('maxlength');
var isReg = false;
for (let i = 0; i < regArray.length; i++) {
if ($(this).hasClass(regArray[i])) {
isReg = true;
break;
}
}
var bSelectCtl = false;
if ($(this).is('select')) {
bSelectCtl = true;
}
if ($(this).attr('id') != 'cntUnitName' && $(this).attr('id') != 'weightUnitName') {
ctlList2.push({id: $(this).attr('id'),
maxlength: (maxlength != undefined ? ',长度'+maxlength+'位':''),
required: ($(this).hasClass('required') ? '必填' : '选填'),
bSelectCtl: (bSelectCtl ? ',下拉列表' : ''),
isReg: (isReg ? ',使用正则校验':'')});
}
}
});
// console.log(ctlList2);
if (ctlList.length != ctlList2.length) {
console.log("两个数组长度不一样,请手工核验!");
}
for (let i = 0; i < ctlList.length; i++) {
var output = i+1 + '. ' + ctlList[i].label + ctlList2[i].required + ctlList2[i].bSelectCtl + ctlList2[i].maxlength + ctlList2[i].isReg;
if (i == ctlList.length-1) output = output + '。'
else output = output + ';'
console.log(output);
}
});