Extjs 学习笔记1
学习笔记
目 录
1 ExtJs 4
1.1 常见错误处理 4
1.1.1 多个js文件中有相同的控件,切换时无法正常显示 4
1.1.2 Store的使用方法 4
1.1.3 gridPanel单元格添加按钮或链接,响应函数 4
1.1.4 对界面中控件进行处理的方法 8
1.1.5 Ajax异步请求处理的方法 8
1.1.6 Store的load()与add()方法的区别 10
1.1.7 控件操作规则 11
1.1.8 Msg消息框事件处理 11
1.1.9 Gridpanel所列的数据记录集获取 12
1.1.10 ColumnModel()的应用 12
1.1.11 PagingToolbar控件的翻页处理 13
1.1.12 ExtJs中打开新的网页 14
1.1.13 动态更新TextField的fieldLabel 15
1.1.14 IE浏览器显示界面出错 16
1.1.15 向后台发送请求时出错 17
1.1.16 创建gridpanel时的错误 18
1.1.17 查询结果以excel形式输出 19
2 Hibernate 21
2.1 多表查询 21
2.1.1 查询语句 21
2.1.2 获取多表join返回的多个对象 21
2.1.3 动态生成grid表格头部 22
2.1.4 对加载成功的store数据处理 22
2.1.5 事务处理 24
2.1.5.1 中文乱码问题?? 25
2.2 常见错误处理 26
2.2.1 Transcation not successful started 26
2.2.2 对于Hibernate中的对象转为json,最终转为string返回客户端 26
2.2.2.1 对于返回单个对象的处理方法: 26
2.2.2.2 对于对象集合的处理方法 27
2.2.2.3 会话对象的应用 27
2.2.2.4 创建并保存包含其它外键关联对象 28
2.2.2.5 排序的通用实现方法 29
2.2.2.6 获取上传文件的方法 30
2.2.2.7 sessionFactory类无法创建时排错方法 31
2.2.2.8 上传文件的处理方法 32
2.2.2.9 Java类自定义的数据类型使用方法 38
1 ExtJs
1.1 常见错误处理
1.1.1 多个js文件中有相同的控件,切换时无法正常显示
检查一下这些控件的属性id是否相同,最好分配一个全局的id。
1.1.2 Store的使用方法
//指定批次的职位类型记录store
PMS.OffcialExam.StatisticsReport.score.positionTypeStore = new Ext.data.JsonStore({
url:'./PositionInfo',
baseParams: {action:'getPositionAndType',data:'{batchId:0}'},
root : 'data',
totalProperty : 'count',
successProperty : 'success',
fields : PMS.OffcialExam.StatisticsReport.score.positionAndTypeRecord,
listeners:{
'beforeload':function(){
msgTip = Ext.MessageBox.show({
title:'提示',
width : 250,
msg:'报表统计信息刷新中,请稍后......'
});
}
}
});
其中PMS.OffcialExam.StatisticsReport.score.positionAndTypeRecord是程序中定义的store记录结构对象
1.1.3 gridPanel单元格添加按钮或链接,响应函数
function showUrl(_values)
{
return "<button title='' value='' class=''>增加</button>"+" "+"<a href='#' onclick=''>修改</a>"+" "+"<a href='#' onclick=''>删除</a>";
}
改为按钮时的代码
// return "<button title='' value='' class=''>生成</button>"+" "+"<button title='' value='' class=''>分配准考证</button>";
不论renderer中生成的是链接,还是其他的内容比如button,都是通用的,代码如下
首先给grid添加一个cellclick事件的响应函数
grid.on('cellclick', grid.onCellClick, grid);
响应函数中做如下处理
onCellClick: function (grid, rowIndex, columnIndex, e) {
if (e.getTarget().innerHTML === '查看') { //借助事件的target来判断,这里是链接可以这样判断,其他方式亦可
var record = grid.getStore().getAt(rowIndex); // Get the Record
var fieldName = grid.getColumnModel().getDataIndex(columnIndex); // Get field name
var data = record.get(fieldName);//这个data就是点击的单元格的数据了,一定有用的
if (fieldName == 'this_column') {
//如果是这一列,做这个事
}
if (fieldName === 'that_column') {
//如果是另外一列,做另外的事
}
}
}
这个方法,不仅可以处理链接,简化一下就是处理某个单元格点击,onCellClick的作用域还是grid,这个是重点,onCellClick还是在grid这个组件之内,而没有产生全局调用,另外点击onclick的响应函数,只能接收字面参数,想传递一个对象是不可能的,如果应用上述方法,即可解决
代码例子:
//渲染器 (生成,分配准考证)
function opeatorRender()
{
return "<button title='' value='' class=''>生成</button>"+" "+"<button title='' value='' class=''>分配准考证</button>";
};
PMS.OffcialExam.ExamSiteManage.assignExamRoom.siteAssign_cm = new Ext.grid.ColumnModel([msm,
{header: '考区号', dataIndex:'examAreaCode'},
{header: '考区名称', dataIndex:'examAreaName'},
{header: '考点号', dataIndex: 'examSiteCode'},
{header: '考点名称', dataIndex: 'examSiteName'},
{header: '考点地址', dataIndex: 'examSiteAddress'},
{header: '考场数量', dataIndex: 'examRoomAmount'},
{header: '使用考场数', dataIndex: 'applyExamRoomNumber'},
{header: '每考场人数', dataIndex: 'actualPersonNumber'},
{header: '是否分配准考证', dataIndex: 'isMakeRegisteration'},
{header:'操作',dataIndex:'work',sortable:true,renderer:opeatorRender}
]);
PMS.OffcialExam.ExamSiteManage.assignExamRoom.siteAssign_store = new Ext.data.JsonStore({
url : './ExamRoomAssignInfo',
baseParams: {action:'getExamSiteAssignData'},
root : 'data',
totalProperty : 'count',
successProperty : 'success',
fields : PMS.OffcialExam.ExamSiteManage.assignExamRoom.assignListRecord
/*[{name : 'examSiteInfo_Id'},
{name : 'examAreaCode'},
{name : 'examAreaName'},
{name : 'examSiteCode'},
{name : 'examSiteName'},
{name : 'examSiteAddress'},
{name : 'examRoomAmount'},
{name : 'applyExamRoomNumber'},
{name : 'actualPersonNumber'},
{name : 'isMakeRegisteration'}
]*/
});
PMS.OffcialExam.ExamSiteManage.assignExamRoom.siteAssign_store.load();
PMS.OffcialExam.ExamSiteManage.assignExamRoom.examDistributeList = new Ext.grid.GridPanel({
title : '考场分配列表',
border : false,
hidden : true,
autoScroll: true,
viewConfig: {
forceFit: true
},
height : 150,
store : PMS.OffcialExam.ExamSiteManage.assignExamRoom.siteAssign_store,
cm : PMS.OffcialExam.ExamSiteManage.assignExamRoom.siteAssign_cm,
sm: msm,
loadMask: {
msg : '加载中....'
},
bbar: new Ext.PagingToolbar({
pageSize : 10,
store : PMS.OffcialExam.ExamSiteManage.assignExamRoom.siteAssign_store,
displayInfo: true,
displayMsg : '显示{0}-{1}条,共{2}条',
emptyMsg : '没有记录',
items:['-',{
text : '删除所选',
iconCls: 'delete'
},'-',{
text: '分配准考证号',
handler: function() {
for(var i = 0; i < PMS.OffcialExam.ExamSiteManage.assignExamRoom.mainPanel.items.length; i++){
PMS.OffcialExam.ExamSiteManage.assignExamRoom.mainPanel.items.items[i].hide();
}
//显示后重新布局一次
PMS.OffcialExam.ExamSiteManage.assignExamRoom.queryDefine.show().collapse();
PMS.OffcialExam.ExamSiteManage.assignExamRoom.admissionCardInfo.show();
}
}]
}),
listeners:{
'cellclick':function(grid, rowIndex, columnIndex, e){ //单击含生成,分配准考证操作的单元格响应函数
if (e.getTarget().innerHTML === '生成') { //根据事件的target判断是哪种操作
var record = grid.getStore().getAt(rowIndex); // 获得所选记录
var fieldName = grid.getColumnModel().getDataIndex(columnIndex); // Get field name
var data = record.get(fieldName);//这个data就是点击的单元格的数据了,一定有用的
var roomrecords = PMS.OffcialExam.ExamSiteManage.assignExamRoom.examDetailGrid.getStore().getRange();
for(var j = 0;j<roomrecords.length;j++){
PMS.OffcialExam.ExamSiteManage.assignExamRoom.doSave(roomrecords[j]);
}
alert("cellclick result"+fieldName);
}
if(e.getTarget().innerHTML === '分配准考证'){
var fieldName = grid.getColumnModel().getDataIndex(columnIndex); // Get field name
alert("cellclick add result"+fieldName);
}
}
}
});
1.1.4 对界面中控件进行处理的方法
例子:将生成按钮置为不可用
(按钮id为PMS.OffcialExam.ExamSiteManage.assignExamRoom.genButton)
Ext.getCmp('PMS.OffcialExam.ExamSiteManage.assignExamRoom.genButton').disable();
1.1.5 Ajax异步请求处理的方法
发送请求,以及数据,同时要求返回对象(集合):
var para = {};
para.data = "{tTExamSiteInfo_id:" + grid.getStore().getAt(rowindex).get('examSiteInfo_Id') + "}";
para.action = 'getExamSiteDetailData';
PMS.OffcialExam.ExamSiteManage.assignExamRoom.examDetailList_store.load({params:para,
callback:function(records, options, success){
if(success){
PMS.OffcialExam.ExamSiteManage.assignExamRoom.examDetailGrid.show();
}
},
scrop:store
});
发送请求,以及数据,但只要求返回处理结果,如插入,修改或删除记录处理:
function(abatchid,areaid,siteid){
var para = {};
para.data = "{tTExamBatch_id:" + abatchid + ",tTExamAreaCode_id:" + areaid + ",tTExamSiteInfo_id:" + siteid+ "}";
para.action = "getRegisterationNumber";
Ext.Ajax.request({
url:'./ExamRoomAssignInfo',
params:para,
success:function(response,config){
var result = Ext.decode(response.responseText);
if(!result.success){
Ext.Msg.show({
title:'提示',
msg:'准考证分配失败',
modal:true,
icon:Ext.Msg.ERROR,
buttons:Ext.Msg.OK
});
}else{
Ext.Msg.show({
title:'提示',
msg:'准考证分配成功',
modal:true,
icon:Ext.Msg.INFO,
buttons:Ext.Msg.OK
});
//在此后可再加发新请求
}
},
failure:function(){
Ext.Msg.show({
title:'错误',
msg:'连接服务器出错',
modal:true,
icon:Ext.Msg.ERROR,
buttons:Ext.Msg.OK
});
}
});
};
1.1.6 Store的load()与add()方法的区别
定义好store结构后,可通过定义url, baseParams,而后利用load()直接装载store中的数据记录;如果使用add()方法,需要首先定义record结构,并创建record记录对象(空对象),而对该对象的各属性赋值,而后调用add(record)方可。
使用add()方法的例子:
var localrecords = [];
var siteRecord = new PMS.OffcialExam.ExamSiteManage.assignExamRoom.examSiteRecord({
id:'',
tTExamBatch_id:'',
tTExamSiteInfo_id:'',
examSiteCode:'',
examSiteName:''
});
siteRecord.set('id',records[0].get('id'));
siteRecord.set('tTExamBatch_id',records[0].get('tTExamBatch_id'));
siteRecord.set('tTExamSiteInfo_id',records[0].get('tTExamSiteInfo_id'));
siteRecord.set('examSiteCode',records[0].get('examSiteCode'));
siteRecord.set('examSiteName',records[0].get('examSiteName'));
localrecords[0] = siteRecord;
PMS.OffcialExam.ExamSiteManage.assignExamRoom.examSiteInfoStore.add(localrecords);
1.1.7 控件操作规则
对于容器(gridpanel)中的控件(button未单独定义,textfield单独定义)操作时,若该控件未单独定义,则需要通过Ext.getCmp(‘控件id’)进行,例如,设置id为assignButton某个button为不可用:
Ext.getCmp('assignButton').disable();
反之,则可直接操作,如:
examSiteDetail_areaNameField = new Ext.form.TextField({
anchor:'90%',
fieldLabel : '考区名称'
});
读取其中的值:examSiteDetail_areaNameField.getValue();
1.1.8 Msg消息框事件处理
Ext.Msg.show({
title:'提示',
msg:result.message,
icon:Ext.Msg.INFO,
buttons:Ext.Msg.OK,
fn:function(btn){
//单击“确定”按钮后,要处理的代码
}
});
1.1.9 Gridpanel所列的数据记录集获取
在编程中,有时需要在容器(gridpanel)对象之外对其中的数据进行处理,可以通过容器(gridpanel)对象中的store对象直接读取其中的记录或具体字段。例如,下面例子是从某个gridpanel对象的store对象(PMS.OffcialExam.ScoreManage.scoreImport.store)读取记录或字段:
//获取成绩处理信息列表中指定职位的所有记录个人id
PMS.OffcialExam.ScoreManage.scoreImport.getSelectedPersonId = function()
{
var selectedRecordId = "";
var datas= PMS.OffcialExam.ScoreManage.scoreImport.store.data;
//将所选的记录ID组合成以“=”分隔的字符串
for(var i = 0;i < datas.length;i++){
selectedRecordId += (String)(datas.items[i].data.pId) ;
if(i < datas.length-1){
selectedRecordId += "=";
}
}
return selectedRecordId;
};
1.1.10 ColumnModel()的应用
控件gridpanel的表格列(字段)定义方式有两种,一种直接定义列模式对象中,另一种则是先定义记录类型,再将该记录类型赋于列模式对象。
1. 直接定义方式
PMS.OffcialExam.ScoreManage.generationInterviewList_cm =
new Ext.grid.ColumnModel([
PMS.OffcialExam.DataInitalize.examSubject.sm,
{header:'id',dataIndex:'id',sortable:true,align:'center',hidden:true},
{header : '科目类别',align:'center',sortable:true,dataIndex : 'subjectType',renderer:function(value){ if (value == '1') {return "公共科目";} else {return "专业科目"; } }},
{header : '科目代码',align:'center',sortable:true, dataIndex : 'subjectCode'},
{header : '科目名称', align:'center',sortable:true,dataIndex : 'subjectName'},
{header : '考试时间(分钟)', align:'center',sortable:true,dataIndex : 'examTimeLength'},
{header : '备注', align:'center',sortable:true,dataIndex : 'content'}
1.1.11 PagingToolbar控件的翻页处理
控件PagingToolbar的下一页按钮被单击时,将调用方法doLoad()加载下一页的数据,具体用法:
1.定义gridpanel控件
generationInterviewList.listing = new Ext.grid.GridPanel({
title : String.format(PMS.constant.panelTitle,'面试人员列表'),
height:190,
collapsible : true,
titleCollapse: true,
buttonAlign : 'center',
store : generationInterviewList.store,
cm : generationInterviewList_cm,
sm : generationInterviewList.sm,
bbar : generationInterviewList.bbar,
viewConfig : {
forceFit : true //自动填充
},
getPageData: function(bbar){ //重写getPageData方法,获取分页工具条活动页的页号等数据
var total = bbar.store.getTotalCount();
return {
total: total,
activePage: total != 0 && bbar.cursor == 0 ? 1 : Math.ceil(bbar.cursor / bbar.pageSize),
pages: total !=0 && total < bbar.pageSize ? 1 : Math.ceil(total / bbar.pageSize)
};
}
2.定义PagingToolbar控件
GenerationInterviewList.bbar = new Ext.PagingToolbar({
displayMsg : '显示{0} - {1}条,共{2}条',
emptyMsg : '没有记录',
pageSize : 5,//一页显示5条
store : generationInterviewList.store,
displayInfo : true,
listeners : {
'change' : function(bbar, pageData){
if(pageData.activePage>1){
var grid = bbar.ownerCt;
var store = grid.getStore();
grid.getSelectionModel().selectFirstRow();
var start = pageData.activePage;
}
}
},
3.在程序需要的地方调用getPageData方法,获取当前页对象,即可人工触发下一页事件
//调用gridpanel中的getPageData获取包含当前活动页页号等属性的对象
var d = generationInterviewList.listing.getPageData(generationInterviewList.bbar);
//分页工具条向后翻页,加载下一页的数据 generationInterviewList.bbar.doLoad(Math.min(d.pages, d.activePage + 1));
1.1.12 ExtJs中打开新的网页
在前端界面中打开新的网页,具体用法:
//ExtJs界面的事件处理中调用打开新网页
listeners: {
'click' : function() {
var username = 'admin';
var password = 'pass';
openPostWindow("/yjExamB/ExamSituationInfo?action=loginCheck", username, password);
}
//打开新页面的方法
function openPostWindow(url, username, password)//, name)
{
var str_form_head = '<form name="tempForm" action="' + url + '" method="post" style="display:none">';
var str_form_foot = '</form>';
var str_form_f1 = '<input type="hidden" name="username" value=' + username + ' />';
var str_form_f2 = '<input type="hidden" name="password" value=' + password + ' />';
var str_javascript_execute = '<script type="text/javascript">document.tempForm.submit();</script>';
var arr_data = new Array();
arr_data.push('<h3>Please wait ...</h3>');
arr_data.push(str_form_head);
arr_data.push(str_form_f1);
arr_data.push(str_form_f2);
arr_data.push(str_form_foot);
arr_data.push(str_javascript_execute);
var oWin = window.open('');
oWin.document.write(arr_data.join(''));
return oWin;
1.1.13 动态更新TextField的fieldLabel
动态更改TextField的fieldLabel方法时,因ExtJs本身未提供这个方法,可用Firebug来查看ExtJs的TextField在运行时被解析成什么样的HTML标签,然后用Dom操作来修改它的标题。 例如:
var textField = new Ext.form.TextField({
fieldLabel: "输入框标题",
id: "textfieldId"
});
上面的一个TextField在运行时被解析成的HTML为:
<div tabindex="-1" class="x-form-item " id="ext-gen117">
<label class="x-form-item-label" for="textfieldId" id="ext-gen118">输入框标题:</label>
<div style="padding-left: 105px;" id="x-form-el-netprice1" class="x-form-element">
<input type="text" name="textfieldId" id="textfieldId" autocomplete="off" class=" x-form-text x-form-field x-form-num-field" title="">
</div>
<div class="x-form-clear-left"></div>
</div>
从上面的HTML可以看出,输入框(text)和它的标题(label)的Dom关系是:label是text的叔叔 ,所以,我们可以使用如下代码来更改它的标题:
document.getElementById("textfieldId").parentNode.previousSibling.innerHTML ="输入框标题改变了:"; (1)
其中previousSibling属性:可返回某节点之前紧跟的节点(处于同一树层级)
ExtJs中每个控件本身也可以获取其DOM对象,语句(1)也可写为:
Ext.getCmp("textfieldId").el.dom.parentNode.previousSibling.innerHTML ="输入框标题改变了:";
1.1.14 IE浏览器显示界面出错
浏览器显示出错,如图:
问题:在items:[]定义中对象定义的个数与所描述的不对应,即items[a,b,]表示有三个控件项,但实际只定义了二个,将会在IE8、9等浏览器运行时,报出如上图错误
1.1.15 向后台发送请求时出错
问题:发送请求时的参数对象未正确设置,如:
//报考职位代码store
PEXAM.ExamSiteManage.assignExamRoom.positionStore = new Ext.data.JsonStore({
proxy: new Ext.data.HttpProxy({url:'./PositionInfo'}),
baseParams: {action: 'getPositionCodeByBatch'},
idProperty: 'id',
root : 'data',
totalProperty : 'count', //getCount(); totalCount getTotalCount()
successProperty : 'success',
fields : PEXAM.ExamSiteManage.assignExamRoom.positionRecord
});
//报考职位数据加载
var para = {};
var data1 = {};
data1.abatchid = PEXAM.Common.currentBatch.abid;
para.data = Ext.encode(data1);
PEXAM.ExamSiteManage.assignExamRoom.positionStore.removeAll();
PEXAM.ExamSiteManage.assignExamRoom.positionLocalStore.removeAll();
PEXAM.ExamSiteManage.assignExamRoom.positionStore.load({params : para,
callback: function(records,options,success){
if(success)
if(records.length>0)
PEXAM.ExamSiteManage.assignExamRoom.positionLocalStore.add(records);
}
});
若红色字体部分的参数未加入,或是未定义para都会出现上述错误
1.1.16 创建gridpanel时的错误
若创建gridpanel使用的自动扩展列,
this.gridRoomList = {
xtype : 'grid',
id : 'selectSite_roomlist_grid',
flex : 1,
//height : this.parent.center.getInnerHeight()-80,
columns : this.columnModelRoomList,
store : this.storeRoomList,
bbar : this.pagingToolbarRoomList,
loadMask : true,
stripeRows : true,
autoExpandColumn : 'roomlist_1_col', //自动扩展列
ancestor : this,
listeners : {
rowdblclick : function(grid, rowIndex) {
var record = grid.getStore().getAt(rowIndex);
if (!record){
}
},
rowcontextmenu : Ext.emptyFn
}
};
若未能同时在columnmodel中定义这个列,则会出现以下错误提示:
this.config[a] is undefined
this.mainBody is undefined
解决办法:
给autoExpandColumn对应的属性在ColumnModel中的定义加个id属性,如下:
{id:’name’,header: “name”,width: 75, sortable: true, dataIndex: ‘name’},
应该是该列没有定义或者Id错误
1.1.17 查询结果以excel形式输出
若要求查询后直接生成一个excel表,在前端可用脚本生成表单,用隐藏字段传送参数,用代码方式提交表单到后台,前端实现代码如下:
this.pagingToolbarStatisticSignup = {
xtype : 'paging',
id : 'statistic_signup_pagingbar',
ancestor : this,
store : this.storeStatisticSignup,
pageSize : 2000000,
displayInfo : true,
items: [
{
text: '导出到Excel文件',
iconCls:'button_icon_add',
ancestor: this,
handler: function(btn, e){
var params = {};
var combo_value = Ext.getCmp('statistic_signup_exambatch_combo').getValue();
var fields = this.ancestor.selectedFields;
if (!combo_value) {
Ext.Msg.alert('操作错误', '请首先选择统计批次.');
return;
}
if(fields==''){
Ext.Msg.alert('操作错误', '请选择统计选项.');
return;
}
//脚本生成表单
params.param1 = combo_value;
params.param2 = fields;
params.param3 = "excel"; //淡出到EXCEL
var frm = document.createElement("form");
frm.action = 'Statistic';
frm.method = 'post';
var action = document.createElement("input");
action.setAttribute('name', 'action');
action.setAttribute('type', 'hidden');
action.setAttribute('value', 'signup');
frm.appendChild(action);
var data = document.createElement("input");
data.setAttribute('name', 'data');
data.setAttribute('type', 'hidden');
data.setAttribute('value', Ext.util.JSON.encode(params));
frm.appendChild(data);
document.body.appendChild(frm);
frm.submit();
document.body.removeChild(frm);
}
}
]
};
参考网站:
http://www.myext.cn
2 Hibernate
2.1 多表查询
2.1.1 查询语句
select e.id,e.signUpTime,e.auditStatus,e.auditResult,e.auditTime,e.paymentStatus,e.paymentTime,e.regPrintStatus,e.regPrintTime,p.personName,i.positionCode,i.positionName from TExamSituationInfo e inner join e.tTPersonInfo_id p inner join e.tTPositionInfo_id i where e.tTExamBatch_id = " + aBatchId + " and p.personID = '"+ c[2] +"' and i.positionCode = '"+ c[0] +"'"; //当前考试批次,职位代码,身份证为组合条件具体需求描述
与SQL SERVER中不同:
select e.id,p.id,i.id from (TExamSituationInfo e inner join TPersonInfo p on e.tTExamBatch_id_id =4 and e.tTPersonInfo_id_id = p.id and p.personID = 'p002') inner join TPositionInfo i on e.tTPositionInfo_id_id = i.id and i.positionCode ='001'
2.1.2 获取多表join返回的多个对象
List dl =null;
HibernateUtil.beginSession(session);
Query query = session.createQuery("from TExamSituationInfo e inner join e.tTPersonInfo_id p inner join e.tTPositionInfo_id i " +
"where e.tTExamBatch_id = " + aBatchId);
//因查询将返回多个对象,list()不强制转换为某个hibernate
dl = query.list();
Iterator<Object[]> iter = dl.iterator(); //迭代器返回的是对象数组
//数组中包含了三个hibernate对象
TExamSituationInfo d1 = (TExamSituationInfo)d[0]; //数组中的第一个元素
TPersonInfo d2 = (TPersonInfo)d[1]; //数组中的第二个元素
TPersonInfo d3 =(TPositionInfo)d[2]; //数组中的第三个元素
d1.getAuditStatus() //通过hibernate对象的get 方法获取该对象中属性
2.1.3 动态生成grid表格头部
//创建头部字段对象
var headerObject1 ={};
headerObject1.header ='序号',
headerObject1.dataIndex = 'sequence';
var headerObject2 ={};
headerObject2.header ='报考系统代码',
headerObject2.dataIndex = 'systemCode';
var headerObject3 ={};
headerObject3.header ='职位代码',
headerObject3.dataIndex = 'positionCode';
var headerObject4 ={};
headerObject4.header ='职位名称',
headerObject4.dataIndex = 'positionName';
var headerObject5 ={};
headerObject5.header ='用人单位',
headerObject5.dataIndex = 'unitName';
var headerObject6 ={};
headerObject6.header ='计划录用人数',
headerObject6.dataIndex = 'recruitingNumbers';
//将所有字段对象加入数组
var arrys = [];
arrys.push(headerObject1);
arrys.push(headerObject2);
arrys.push(headerObject3);
arrys.push(headerObject4);
arrys.push(headerObject5);
arrys.push(headerObject6);
//创建空的列模式
PMS.OffcialExam.StatisticsReport.position.cm =
new Ext.grid.ColumnModel([]);
//将对象数组加入列模式
PMS.OffcialExam.StatisticsReport.position.cm.setConfig(arrys,true);
2.1.4 对加载成功的store数据处理
当客户端通过load()方法加载数据到store后,无法即可获取所加载的数据,其原因是未判断异步加载是否已完成,若对其加以判断,则可对加载的数据即可处理。
//定义加载数据store
PMS.OffcialExam.StatisticsReport.position.store = new Ext.data.JsonStore({
url : './PositionInfo',
root : 'data',
totalProperty : 'count',
successProperty : 'success',
fields : [
{name : 'sequence'},
{name : 'systemCode'},
{name : 'positionCode'},
{name : 'positionName'},
{name : 'unitCode'},
{name : 'unitName'},
{name : 'recruitingNumbers'}
]
});
//定义统计信息记录类型(保存从store中得到的数据,用于动态加载新store)
PMS.OffcialExam.StatisticsReport.position.staticstRecord = Ext.data.Record.create([
{name : 'sequence'},
{name : 'systemCode'},
{name : 'positionCode'},
{name : 'positionName'},
{name : 'unitName'},
{name : 'recruitingNumbers'}
]);
//定义动态加载信息列表store
PMS.OffcialExam.StatisticsReport.position.listStore = new Ext.data.JsonStore({
proxy : new Ext.data.MemoryProxy({}),
reader: new Ext.data.ArrayReader({},
PMS.OffcialExam.StatisticsReport.position.staticstRecord)
});
var obtainRecords = [];
//统计选项
PMS.OffcialExam.StatisticsReport.position.condition = new Ext.form.FormPanel({
title : '录用职位计划统计选项',
border : false,
collapsible : true,//可收缩
height : 275,
layout : {
type : 'hbox',
align : 'stretch',
padding : '20px 10px 0px 30px'
},
defaults : {
border : false
},
items : [//其它控件代码(略)
xtype : 'button',
text : '统计',
handler : function() {
var para = {};
para.data = "{aBatchId:4}";
para.action = 'positionStatisticsJson'; PMS.OffcialExam.StatisticsReport.position.store.load({
params: para,
//加载后执行的回调函数中success属性可判断是否加载完毕
callback: function(records, options, success){
if(success) obtainRecords = records;
},
scrop: store
});
}
//其它控件代码(略)
//将返回的记录集 用于动态加载的数据显示
PMS.OffcialExam.StatisticsReport.position.hh = function(){
PMS.OffcialExam.StatisticsReport.position.listStore.removeAll();
PMS.OffcialExam.StatisticsReport.position.listStore.add(obtainRecords);
};
2.1.5 事务处理
在一个DAO类中,若类中的方法A调用方法B,且方法A中有启动事务,则被方法B中不可重新启动事务,否则会出现“transaction not started”错误,即事务无法正常启动。
public List<ISituationExamResult> getSituationPositionScoreData(Long batch) throws Exception{
try{
HibernateUtil.beginSession(session);
Query query = session.createQuery("select p.id from TPositionInfo p inner join p.tTPositionType_id t where p.tTExamBatch_id.id =:p1 and p.positionCode =:p2 and t.positionTypeCode =:p3 ");
……
this.getById(); //调用本类中的方法
……
}catch(){
}
return null;
}
public void getById() throws Exception {
try{
HibernateUtil.beginSession(session); //再次启动事务,调用时将报错
Query query = session.createQuery("select * from ……");
……
}
因此
getById()方法中应去掉事务启动和提交的语句HibernateUtil.beginSession(session);和if(commit) HibernateUtil.commitSession(session);
同时为了避免其它类调用此方法,去未启动事务,可将此方法设置为内部方法。
2.1.5.1 中文乱码问题??
store带参数加载数据,因为ext中的ajax用的是UTF-8,所以直接传的话会出现乱码,我们可以用Extjs的encodeURIComponent先把中文编码,然后在后台用java的java.net.URLDecoder再解码就可以解决,示例如下:
stroe加载:
store.baseParams={
euser:encodeURIComponent(euser)
batName:encodeURIComponent(batName) //编码
};
store.load();
后台解码:
if(!Util.isEmpty(euser))
euser=URLDecoder.decode(euser,"UTF-8"); //解码转中文
if(!Util.isEmpty(batName)) batName=URLDecoder.decode(batName,"UTF-8"); //解码转中文
2.2 常见错误处理
2.2.1 Transcation not successful started
当一个类中方法A中有beginSession(session),
commitTranscation(session), rollbacktranscation(session),即应用了事务,此时在方法A中调用方法B,而方法B 也有应用事务,且与方法A在同一个类中,将会出现上述错误。
解决方法:可将方法B中不必再使用事务了,或是将方法B放到另一个类中。
2.2.2 对于Hibernate中的对象转为json,最终转为string返回客户端
2.2.2.1 对于返回单个对象的处理方法:
IExamRoomAssignInfo i =new IExamRoomAssignInfo();
……//对于i对象赋值
JsonObject jo = new JsonObject();
if(i != null){
jo.addProperty("success", true);
JsonObject data = (JsonObject)gson.toJsonTree(i);//得到单个对象的jsonobject
jo.add("data", data);
}else{
jo.addProperty("success", false);
jo.addProperty("message", "数据不存在");
}
String result = gson.toJson(jo);
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
PrintWriter pw = response.getWriter();
pw.print(result);
2.2.2.2 对于对象集合的处理方法
List<IExamRoomAssignInfo> il = new ArrayList<IExamRoomAssignInfo>();
……//对il集合赋值
JsonObject jo = new JsonObject();
if(il != null){
jo.addProperty("success", true);
JsonArray data = (JsonArray)gson.toJsonTree(il);
jo.add("data", data);
}else{
jo.addProperty("success", false);
jo.addProperty("message", "数据不存在");
}
String result = gson.toJson(jo);
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
PrintWriter pw = response.getWriter();
pw.print(result);
在获取data时,若返回的是对象,或字符串时将如下处理:
////JsonObject data = (JsonObject)gson.toJsonTree(i);//得到单个对象的jsonobject
////String data = gson.toJson(flag);//得到单个对象的jsonobject
2.2.2.3 会话对象的应用
当处理多个相关表的数据操作时,可将先一个会话对象传至下一个DAO对象中,可将相关表的处理汇集在同一个会话事务中,否则会报出错误信息:illegal attempted a proxy with two session。
try{
HibernateUtil.beginSession(session);
//查询指定考点的所有考场
Query query = session.createQuery("from TExamRoomAssignInfo e where e.tTExamBatch_id.id =:p1 " +
"and e.tTExamAreaCode_id.id =:p2 and e.tTExamSiteInfo_id.id =:p3");
query.setLong("p1", aBatchId);
query.setLong("p2", areaId);
query.setLong("p3", siteId);
//写入TRegisterationAssignInfo
DAORegisterationAssignInfo daodr = null;
try{
daodr = new DAORegisterationAssignInfo(session);
//保存准考证号分配信息,变更相关表的准考证分配状态
daodr.saveRegisterationAssignData(r);
}catch(Exception e){
throw e;
}finally{
if(daodr != null) daodr.close();
}
2.2.2.4 创建并保存包含其它外键关联对象
当一个对象中有外键,在创建这个对象并对其所属性赋值时,其外键属性的赋值方式为:
原对象的外键属性((外键类名)session.get(外键类.class,外键对象ID))
例如,
public void UpdatePracticeScoreScore(IExamResultInfo i)throws Exception
{
TExamResultInfo r = new TExamResultInfo();
TExamSubject tes1 = daos.getPracticeID();
ContentClone.copy(i, r, exclude);
r.setIsAbsent("否");
r.setIsPublic("否");
r.setScore(i.practiceScore);
r.settTExamBatch_id(tac.gettTExamBatch_id());
r.settTPersonInfo_id((TPersonInfo)session.get(TPersonInfo.class, i.tTPersonInfo_id));
r.settTPositionInfo_id((TPositionInfo) session.get(TPositionInfo.class, i.tTPositionInfo_id));
r.settTExamSubject_id(tes1);
if(i.practiceScore!=null) session.save(r);
……
2.2.2.5 排序的通用实现方法
Collections的排序方法sort(),可以通过实现其接口comparator来自定义比较器,而且通过使用泛型实现更为通用的方法
import java.util.Comparator;
import java.util.Collections;
import java.util.List;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
public class ListSortUtil<T> {
@SuppressWarnings({ "unchecked", "rawtypes" })
public void Sort(List<T> list, final String method, final String sortType){
Collections.sort(list, new Comparator() {
public int compare(Object a ,Object b){
int ret = 0;
try{
//method的参数类型,此时该方法无参数,如果有则写为某个具体对象
Class[] param_1 = {};
Object[] param_2 = {};
Method m1 = ((T)a).getClass().getMethod(method, param_1);
Method m2 = ((T)b).getClass().getMethod(method, param_1);
if(sortType != null&&sortType.equals("desc")){
ret = m2.invoke(((T)b), param_2).toString().compareTo(m1.invoke(((T)a), param_2).toString());
}else if(sortType.equals("asc")){
ret = m1.invoke(((T)a), param_2).toString().compareTo(m2.invoke(((T)b),param_2).toString());
}
}catch(NoSuchMethodException me){
me.printStackTrace();
}catch(IllegalAccessException le){
le.printStackTrace();
}catch(InvocationTargetException ie){
ie.printStackTrace();
}
return ret;
}
});
}
}
排序的方法的调用,如list是一个IProduct 类集合对象,则对于list的价格(getPrice())降序排序可写为:
ListSortUtil< IProduct > slist = new ListSortUtil< IProduct >();
Slist.Sort(list,”getPrice”,”desc”)
2.2.2.6 获取上传文件的方法
//获取文件流
@SuppressWarnings("rawtypes")
private InputStream getFile(HttpServletRequest request, HttpServletResponse response) throws IOException{
//文件较大时,可存于临时目录
// 创建文件处理工厂,它用于生成FileItem对象。
DiskFileItemFactory difactory = new DiskFileItemFactory();
//设置缓存大小,如果上传文件超过缓存大小,将使用临时目录做为缓存。
difactory.setSizeThreshold(1024 * 1024);
// 设置处理工厂缓存的临时目录,此目录下的文件需要手动删除。
String dir = this.getServletContext().getRealPath("/");
File filedir = new File(dir + "filetemp");
if (!filedir.exists())
filedir.mkdir();
difactory.setRepository(filedir);*/
// 设置文件实际保存的目录
String userdir = dir + "files";
File fudir = new File(userdir);
if (!fudir.exists())
fudir.mkdir();
// 创建request的解析器,它会将数据封装到FileItem对象中。
ServletFileUpload sfu = new ServletFileUpload(difactory); */
//若一般文件,可直接读取
// 创建request的解析器,它会将数据封装到FileItem对象中。
FileItemFactory factory = new DiskFileItemFactory();
// 创建request的解析器,它会将数据封装到FileItem对象中。
ServletFileUpload sfu = new ServletFileUpload(factory);
// 解析保存在request中的数据并返回list集合
List list = null;
try {
list = sfu.parseRequest(request);
} catch (FileUploadException e) {
e.printStackTrace();
}
for (Iterator it = list.iterator(); it.hasNext();) {
FileItem fi = (FileItem) it.next();
if (!fi.isFormField()) {//不是表单字段数据
return fi.getInputStream();
}
}
return null;
}
2.2.2.7 sessionFactory类无法创建时排错方法
在sessionFactory创建类中设置出错信息输出,可快速查找到出错的具体问题。
public class HibernateUtil {
private static SessionFactory sessionFactory;
static {
try {
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
ex.printStackTrace(); //显示问题的输出语句
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
// Alternatively, you could look up in JNDI here
return sessionFactory;
}
public static void shutdown() {
// Close caches and connection pools
getSessionFactory().close();
}
public static Session getSession()
{
Session session = sessionFactory.openSession();
return session;
}
public static void beginSession(Session session)
{
session.getTransaction().begin();
}
public static void commitTransaction(Session session)
{
session.getTransaction().commit();
}
public static void rollbackTransaction(Session session)
{
Transaction tx = session.getTransaction();
if (tx.isActive())
tx.rollback();
}
}
2.2.2.8 上传文件的处理方法
在上传文件时,前端要采用上传文件组件,后台则需要以文件流方式读取。
1、前端:
OEXAM.SystemCodeManage.major.upLoadFileText = new Ext.form.TextField({
id : 'excel_upload',
inputType : 'file', //输入类型
fieldLabel : String.format(OEXAM.constant.labelTextCss,' Excel文件路径'),
allowBlank:false,
blankText:"输入不可为空",
blankText:'上传文件不能为空'
});
//专业信息导入form
OEXAM.SystemCodeManage.major.importForm = new Ext.form.FormPanel({
id:'import_form',
fileUpload : true, //文件上传组件
items : [OEXAM.SystemCodeManage.major.upLoadFileText
/*{
inputType : 'file',
fieldLabel : String.format(OEXAM.constant.labelTextCss,' Excel文件路径'),
allowBlank:false,
blankText:"输入不可为空",
xtype : 'textfield',
blankText:'上传文件不能为空'
}*/],
buttons : [{
text : String.format(OEXAM.constant.buttonTextCss,"确定"),
iconCls: 'yes',
handler : function (){
if(!Ext.getCmp("import_form").getForm().isValid()){
Ext.Msg.show({
title:'温馨提示',
msg:'您还未选择文件!',
modal:true,
icon : Ext.Msg.INFO,
buttons: Ext.Msg.OK
});
return;
}
var para = {};
para.action = 'importExcel';
Ext.getCmp("import_form").getForm().submit({ //提交方式
clienValidation:true,
waitMsg:'正在上传,请稍候',
waitTitle:'提示:',
url:'./MajorInfo',
params :para,
method:'POST',
// timeout : 2000,
success:function(form,action){
var res = action.result;
if(res.success){
if(res.message == 1){
Ext.Msg.show({
title:'提示',
msg:'专业信息导入成功',
modal:true,
icon:Ext.Msg.INFO,
buttons:Ext.Msg.OK
});
Ext.getCmp('import_form').getForm().reset();
OEXAM.SystemCodeManage.major.importWin.hide();
if(OEXAM.SystemCodeManage.major.listing.store.data.items != ""){
OEXAM.SystemCodeManage.major.majorList_store.removeAll();
}
OEXAM.SystemCodeManage.major.form.getForm().reset();
OEXAM.SystemCodeManage.major.store.reload(); //重新加载查询条件中的专业信息
}else if(res.message == 0){
Ext.Msg.show({
title:'提示',
msg:'专业信息导入失败,请与系统管理员联系。',
modal:true,
icon:Ext.Msg.INFO,
buttons:Ext.Msg.OK
});
}else if(res.message == -1){
Ext.Msg.show({
title:'提示',
msg:'请上传.xls格式的文件。',
modal:true,
icon:Ext.Msg.INFO,
buttons:Ext.Msg.OK
});
OEXAM.SystemCodeManage.major.upLoadFileText.setValue('');
}
}
},
failure:function(action,form){
Ext.MessageBox.show({
title : '服务器错误',
msg : action.result.message,
buttons : Ext.MessageBox.OK,
icon : Ext.MessageBox.ERROR
});
}
});
}
},{
text : String.format(OEXAM.constant.buttonTextCss,'重置'),
handler : function(){
Ext.getCmp('import_form').getForm().reset();
}
}]
});
2、后台
通过servlet中的protected void handleMultipartRequest(HttpServletRequest request, HttpServletResponse response) throws IOException方法得到前端请求。代码如下:
(1)如果是读取文本类文件
protected void handleMultipartRequest(HttpServletRequest request, HttpServletResponse response) throws IOException
{
// 创建文件处理工厂,它用于生成FileItem对象。
FileItemFactory factory = new DiskFileItemFactory();
// 创建request的解析器,它会将数据封装到FileItem对象中。
ServletFileUpload sfu = new ServletFileUpload(factory);
// 解析保存在request中的数据并返回list集合
List<FileItem> list = null;
HashMap<String, String> hmpara = new HashMap<String, String>();
try {
list = sfu.parseRequest(request); //得到请求中的上传文件列表
} catch (FileUploadException e) {
this.logAndOutprintException(e, request, response);
return;
}
InputStream ins = null;
for (Iterator<FileItem> it = list.iterator(); it.hasNext();) {
FileItem fi = (FileItem) it.next();
if (!fi.isFormField()) {//不是表单字段数据
ins = fi.getInputStream(); //得到上传文件流
hmpara.put(fi.getFieldName(), fi.getName()); //得到文件上传文件框控件的标记,上传文件名
}else{
hmpara.put(fi.getFieldName(), fi.getString("utf-8")); //得到表单中action
}
}
String action = hmpara.get("action");
if("importExcel".equals(action)){
importExcel(ins, hmpara, request,response);
}else
{
this.logAndOutprintException(new Exception(CommonData.ERROR_ACTION), request, response);
}
return;
}
(2)如果是读取图片文件,可以字节数组方式得到,以便与数据库中图片字段相对应
protected void handleMultipartRequest(HttpServletRequest request, HttpServletResponse response) throws IOException
{
request.setCharacterEncoding("utf-8");
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> items = null;
try {
items = upload.parseRequest(request);
}
catch(Exception e)
{
this.logAndOutprintException(e, request, response);
return;
}
Iterator<FileItem> iter = items.iterator();
HashMap<String, String> hms = new HashMap<String, String>();
HashMap<String, byte[]> hmb = new HashMap<String, byte[]>(); //定义存放图片的数据结构
while (iter.hasNext())
{
FileItem item = (FileItem)iter.next();
if (item.isFormField())
{
hms.put(item.getFieldName(), item.getString("utf-8"));
}
else
{
String fieldName = item.getFieldName();
String fileName = item.getName();
int pos = fileName.lastIndexOf("/");
if (pos == -1) pos = fileName.lastIndexOf("\\");
if (pos != -1)
fileName = fileName.substring(pos+1);
hms.put(fieldName, fileName);
hmb.put(fileName, item.get()); //得到图片的字节数组
}
}
String action = hms.get("action");
if ("updatePersonPhoto".equalsIgnoreCase(action))
{
//调用图片存入的方法,其中可直接将图片的字节数组赋值给数据表中的图片字段(byte[])
updatePersonPhoto(hms, hmb, request, response);
}
else
{
this.logAndOutprintException(new Exception(CommonData.ERROR_ACTION), request, response);
}
return;
}
在上传文件时,有可能出现以下错误信息:
纯文本文件的字符编码未声明。如果该文件包含 US-ASCII 范围之外的字符,该文件将在某些浏览浏览器配置中呈现为乱码。该文件的字符编码需要在传输协议层声明,或者在文件中加入一个 BOM(字节顺序标记)。
处理方法:重启eclipes,同时通过浏览器清空cookie。
2.2.2.9 Java类自定义的数据类型使用方法
在后端可以自定义一些常用的状态,字段值较少的可采用这种方法,如:性别、身份等,代码:
public class Code2MeaningConstant {
private static String JSString = "Code2MeaningConstant = {};" + "\n\n";
//打印状态
public static TreeMap<String, String> TPrintStatus = new TreeMap<String, String>();
static {
TPrintStatus.put("PRINTED", "已打印");
TPrintStatus.put("UNPRINT", "未打印");
createJSString("TPrintStatus", TPrintStatus);
}……
身份等,代码在前端可定义store,来获取这些数据:
UNIT.Common.Data.TPrintStatus =new Ext.data.JsonStore({
data:Code2MeaningConstant.TPrintStatus,
fields:['code','meaning']
});
在前端需要的地方,则可读取这些字段数据,如:
//打印状态 代码——中文
var convertPrintStatus = function(sourceCode) {
var str = sourceCode;
for (var i = 0; i < UNIT.Common.Data.TPrintStatus.data.length; i++) {
var code = UNIT.Common.Data.TPrintStatus.data.items[i].json.code;
if (sourceCode == code) {
str = UNIT.Common.Data.TPrintStatus.data.items[i].json.meaning;
break;
}
}
Extjs 学习笔记1的更多相关文章
- ExtJs学习笔记之学习小结LoginDemo
ExtJs学习小结LoginDemo 1.示例:(登录界面) <!DOCTYPE html> <html> <head> <meta charset=&quo ...
- extjs 学习笔记(二)
EXTJS实用开发指南 1. 要使用ExtJS 框架的页面中一般包括下面几句: <link rel="stylesheet" type="text/css" ...
- EXTJS学习笔记
由于公司统一决策,决定使用EXTJS作为前台的开发UI框架,所以从今天开始学习EXTJS,对学习过程中遇到的问题做记录. 首先第一个问题:为什么要用ExtJS作为公司开发基础架构? 推荐一篇文章: ...
- [转]ExtJS学习笔记(二):handler与listener的区别
原文地址:http://blog.csdn.net/smilingleo/article/details/3733177 ExtJS里handler和listener都是用来对用户的某些输入进行处理的 ...
- ExtJS学习笔记:定义extjs类别
类的定义 Ext.define('Cookbook.Vehicle', { Manufacturer: 'Aston Martin', Model: 'Vanquish', getDetails: f ...
- Extjs学习笔记之九 数据模型(上)-extjs
来源:niutuku.com | vincent上传于2012-07-20 | 1802次浏览 | 0条评论 本文开始进入Extjs最核心最优秀的部分. 标签:Extjs 数据模型 Extjs的数 ...
- ExtJS学习笔记2:响应事件、使用AJAX载入数据
响应事件: 1.设置一个html标记 <div id="my-div">Ext JS 4 Cookbook</div> 2.使用get函数获取此标记对象 v ...
- Extjs学习笔记--(六,选择器)
文档对象dom是javascript与页面元素的桥梁 选择器的作用就是通过元素的标签名,属性名,css属性名对页面进行快速,准确的定位及选择 Extjs的选择器:Ext.DomQuery Ext.qu ...
- Extjs学习笔记--(五,事件)
Extjs中事件包括浏览器事件(单机按钮,鼠标移动等触发)和内部事件(组件之间的联动) 绑定浏览器事件的过程Ext.EventManager 要为元素绑定事件,通常会使用EventManager.on ...
随机推荐
- CSS------当内容超出div宽度后自动换行
<div class="AllReceivers-normal" style="widht:100%;height:100%;word-wrap: break-wo ...
- CSS学习笔记——CSS选择器样式总结
<style type="text/css"> * { padding:0; margin:0; } .box h2 { //内边距左边的距离 padding-left ...
- SVN中Branch和Merge实践
参考资料:http://blog.csdn.net/eggcalm/article/details/6606520 branch主要用于新功能的开发,开发过程中不断从trunk merge revis ...
- JStorm集群的安装和使用
0 JStorm概述 JStorm是一个分布式的实时计算引擎.从应用的角度,JStorm应用是一种遵守某种编程规范的分布式应用:从系统角度, JStorm是一套类似MapReduce的调度系统: 从数 ...
- Java I/O流体系
- python类的高级属性
---恢复内容开始--- 类方法:通过@classmethod装饰器实现,类方法和普通方法的区别是,类方法只能访问类变量,不能访问实例变量,代码如下: class Person(object): de ...
- 一种nodejs的MVC框架
mvc会针对请求进行分发,分发一般有controller(针对模块),action(针对模块中的方法),args(请求的参数). 1.先对http请求的url进行设置,解析url中的各种参数: //c ...
- iOS直播点赞动画,iOS直播心型点赞动画
https://github.com/songxing10000/LikeAnimation-PraiseAnimation
- 用IIS配置反向代理
http://my.oschina.net/tanyixiu/blog/123832 目标服务器:targetServer 配置反向代理的服务器:reveseProxServer 1.确定最终访问的网 ...
- 使用idea开发Springmvc
使用IntelliJ IDEA开发SpringMVC网站(一)开发环境 http://my.oschina.net/gaussik/blog/385697?fromerr=A4EKE0Ix idea1 ...