前期时间,需求说后台管理系统(前端用extjs开发)的省市区三级联动选择太麻烦,能不能做成类似于日期选择控件,直接给用户去选择。当时比较忙,说先凑合着用,最近稍微闲一下,就研究了一下。
对extjs不是很熟,在网上看到了一篇文章,写得不错,http://skirtlesden.com/articles/html-and-extjs-components,
结合Extjs自带的DatePicker源代码,写了一个ProvinceCityAreaPicker,
/**
*
*/
Ext.define('Ext.ux.ProvinceCityAreaPicker', {
extend : 'Ext.panel.Panel',
requires : [ 'Ext.XTemplate', , 'Ext.EventObject', 'Ext.fx.Manager' ],
alias : 'widget.provincecityareapicker',
tpl : [ '<div id="_provincecityareapicker-province", role="presentation" {provinceHide:this.hidePCA}>',// province
// div
'<table class="x-datepicker-inner" cellspacing="0" role="presentation">',// table
'<tbody role="presentation">', // tbody
'<tr role="row">',//
'<td role="gridcell" title="" class="x-datepicker-active x-datepicker-cell"></td>',//
'<td role="gridcell" title="" class="x-datepicker-active x-datepicker-cell"></td>',//
'<td role="gridcell" title="" class="x-datepicker-active x-datepicker-cell"></td>',//
'<td role="gridcell" title="" class="x-datepicker-active x-datepicker-cell"></td>',//
'</tr>', '<tr role="row">',//
'<tpl for="provinceList">',//
'{#:this.isRowEnd}',//
'<td role="gridcell" title="{provinceName}-{provinceCode}" data-code="{provinceCode}" class="x-datepicker-active x-datepicker-cell">',//
'<a role="presentation" hidefocus="on" class="x-datepicker-date" href="#">{provinceName:this.ellipseText}',//
'</td>',//
'</tpl>',//
'</tr>',//
'</tbody>',// tbody
'</table>',// table
'</div>', '<div id="_provincecityareapicker-city", role="presentation" {cityHide:this.hidePCA}>',// city
'<table class="x-datepicker-inner" cellspacing="0" role="presentation">',// table
'<tbody role="presentation">', // tbody
'<tr role="row">',//
'<td role="gridcell" title="" class="x-datepicker-active x-datepicker-cell"></td>',//
'<td role="gridcell" title="" class="x-datepicker-active x-datepicker-cell"></td>',//
'<td role="gridcell" title="" class="x-datepicker-active x-datepicker-cell"></td>',//
'<td role="gridcell" title="" class="x-datepicker-active x-datepicker-cell"></td>',//
'</tr>', '<tr role="row">',//
'<tpl for="cityList">',//
'{#:this.isRowEnd}',//
'<td role="gridcell" title="{cityName}-{cityCode}" data-code="{cityCode}" class="x-datepicker-active x-datepicker-cell">',//
'<a role="presentation" hidefocus="on" class="x-datepicker-date" href="#">{cityName:this.ellipseText}',//
'</td>',//
'</tpl>',//
'</tr>',//
'</tbody>',// tbody
'</table>',// table
'</div>', '<div id="_provincecityareapicker-area", role="presentation" {areaHide:this.hidePCA}>',// areas
'<table class="x-datepicker-inner" cellspacing="0" role="presentation">',// table
'<tbody role="presentation">', // tbody
'<tr role="row">',//
'<td role="gridcell" title="" class="x-datepicker-active x-datepicker-cell"></td>',//
'<td role="gridcell" title="" class="x-datepicker-active x-datepicker-cell"></td>',//
'<td role="gridcell" title="" class="x-datepicker-active x-datepicker-cell"></td>',//
'<td role="gridcell" title="" class="x-datepicker-active x-datepicker-cell"></td>',//
'</tr>', '<tr role="row">',//
'<tpl for="areaList">',//
'{#:this.isRowEnd}',//
'<td role="gridcell" title="{areaName}-{areaCode}" data-code="{areaCode}" class="x-datepicker-active x-datepicker-cell">',//
'<a role="presentation" hidefocus="on" class="x-datepicker-date" href="#">{areaName:this.ellipseText}',//
'</td>',//
'</tpl>',//
'</tr>',//
'</tbody>',// tbody
'</table>',// table
'</div>', {
isRowEnd : function(index) {
index--;
var end = index !== 0 && index % 4 === 0;
return end ? '</tr><tr role="row">' : '';
},
ellipseText : function(text) {
// out.push(values);
return Ext.String.ellipsis(text, 6, true);
},
hidePCA : function(hide) {
return hide ? 'class="x-hidden"' : '';
}
} ],
titleAlign : 'center',
frame : false,
border : true,
bodyStyle : {
padding : '10px'
},
width : 400,
initComponent : function() {
var me = this;
me.callParent();
me.selectedCls = 'x-datepicker-selected';
me.addEvents('select');
},
beforeRender : function() {
var me = this;
me.callParent();
me.sendProvinceData();
Ext.applyIf(me, {
data : {}
});
me.current = {
provinceList : me.provinceList,
cityList : me.cityList,
areaList : me.areaList,
provinceHide : false,
cityHide : true,
areaHide : true
}
Ext.apply(me.data, me.current);
me.protoEl.unselectable();
},
onRender : function(container, position) {
var me = this;
console.log(me.header);
me.callParent(arguments);
me.initProvinceCellEl();
},
initProvinceCellEl : function() {
var me = this;
me.provCt = me.el.select('#_provincecityareapicker-province');
me.provTdCells = me.provCt.select('table tbody tr:visible(true) td');
me.provCells = me.provTdCells.select('a');
},
initCityCellEl : function() {
var me = this;
me.cityCt = me.el.select('#_provincecityareapicker-city');
me.cityTdCells = me.cityCt.select('table tbody tr:visible(true) td');
me.cityCells = me.cityTdCells.select('a');
},
initAreaCellEl : function() {
var me = this;
me.areaCt = me.el.select('#_provincecityareapicker-area');
me.areaTdCells = me.areaCt.select('table tbody tr:visible(true) td');
me.areaCells = me.areaTdCells.select('a');
},
initEvents : function() {
var me = this;
me.callParent();
me.provCells.on('click', me.provClickHandler, me);
},
sendProvinceData : function() {
var me = this;
if (Ext.isEmpty(me.provinceList)) {
Ext.Ajax.request({
method : 'POST',
url : me.contextPath + '/main/findProvince.action',
async : false,
success : function(response, eOpts) {
var data = Ext.decode(response.responseText);
if (data && data.queryList) {
me.provinceList = data.queryList;
}
},
failure : function(response, eOpts) {
me.provinceList = null;
}
});
}
},
sendCityData : function(provinceCode, cache) {
var me = this;
var mustRequest = true;
if (cache && Ext.isEmpty(me.cityList)) {
mustRequest = false;
}
if (mustRequest) {
Ext.Ajax.request({
method : 'POST',
url : me.contextPath + '/main/findCity.action',
async : false,
params : {
code : provinceCode
},
success : function(response, eOpts) {
var data = Ext.decode(response.responseText);
if (data && data.queryList) {
me.cityList = data.queryList
}
},
failure : function(response, eOpts) {
me.cityList = null;
}
});
}
},
sendAreaData : function(cityCode, cache) {
var me = this;
var mustRequest = true;
if (cache && Ext.isEmpty(me.areaList)) {
mustRequest = false;
}
if (mustRequest) {
Ext.Ajax.request({
method : 'POST',
url : me.contextPath + '/main/findArea.action',
async : false,
params : {
code : cityCode
},
success : function(response, eOpts) {
var data = Ext.decode(response.responseText);
if (data && data.queryList) {
me.areaList = data.queryList
}
},
failure : function(response, eOpts) {
me.areaList = null;
}
});
}
},
updateProvince : function() {
var me = this;
Ext.apply(me.current, {
provinceHide : false,
cityHide : true,
areaHide : true
});
me.update(me.current);
me.setTitle('省份选择');
me.initProvinceCellEl();
me.provCells.on('click', me.provClickHandler, me);
},
updateCity : function() {
var me = this;
Ext.apply(me.current, {
provinceHide : true,
cityHide : false,
areaHide : true,
cityList : me.cityList
});
me.update(me.current);
me.setTitle('城市选择');
me.initCityCellEl();
me.cityCells.on('click', me.cityClickHandler, me);
},
updateArea : function() {
var me = this;
Ext.apply(me.current, {
provinceHide : true,
cityHide : true,
areaHide : false,
areaList : me.areaList
});
me.update(me.current);
me.setTitle('区域选择');
me.initAreaCellEl();
me.areaCells.on('click', me.areaClickHandler, me);
},
provClickHandler : function(evt, prov, opts) {
evt.stopEvent();
evt.stopPropagation();
var me = this;
var href = Ext.fly(prov);
if (href && href.getAttribute('href')) {
me.el.select('#_provincecityareapicker-province table tbody tr:visible(true) td').removeCls(me.selectedCls);
var tdEle = href.parent();
var provinceCode = tdEle.getAttribute('data-code');
me.updateCurrentProvince(provinceCode);
tdEle.addCls(me.selectedCls);
me.mask();
me.sendCityData(provinceCode, false);
me.updateCity();
me.unmask();
}
},
updateCurrentProvince : function(provinceCode) {
var me = this;
if (me.current && me.current.provinceList && provinceCode) {
Ext.each(me.provinceList, function(prov, index) {
if (prov && prov.provinceCode == provinceCode) {
Ext.apply(me.current, {
province : prov
});
return;
}
});
}
},
cityClickHandler : function(evt, city, opts) {
evt.stopEvent();
evt.stopPropagation();
var me = this;
var href = Ext.fly(city);
if (href && href.getAttribute('href')) {
me.el.select('#_provincecityareapicker-city table tbody tr:visible(true) td').removeCls(me.selectedCls);
var tdEle = href.parent();
var cityCode = tdEle.getAttribute('data-code');
me.updateCurrentCity(cityCode);
tdEle.addCls(me.selectedCls);
me.mask();
me.sendAreaData(cityCode, false);
me.updateArea();
me.unmask();
}
},
updateCurrentCity : function(cityCode) {
var me = this;
if (me.current && me.current.cityList && cityCode) {
Ext.each(me.cityList, function(city, index) {
if (city && city.cityCode == cityCode) {
Ext.apply(me.current, {
city : city
});
return;
}
});
}
},
areaClickHandler : function(evt, area, opts) {
evt.stopEvent();
evt.stopPropagation();
var me = this, handler = me.handler;
var href = Ext.fly(area);
if (href && href.getAttribute('href')) {
me.el.select('#_provincecityareapicker-area table tbody tr:visible(true) td').removeCls(me.selectedCls);
var tdEle = href.parent();
var areaCode = tdEle.getAttribute('data-code');
me.updateCurrentArea(areaCode);
tdEle.addCls(me.selectedCls);
var c = me.current;
me.setValue({
province : c.province,
city : c.city,
area : c.area
});
me.fireEvent('select', me, me.value);
if (handler) {
handler.call(me.scope || me, me.value);
}
me.onSelect();
}
},
updateCurrentArea : function(areaCode) {
var me = this;
if (me.current && me.current.areaList && areaCode) {
Ext.each(me.areaList, function(area, index) {
if (area && area.areaCode == areaCode) {
Ext.apply(me.current, {
area : area
});
return;
}
});
}
},
onSelect : function() {
if (this.hideOnSelect) {
this.hide();
}
},
setValue : function(value) {
this.value = value;
return this;
},
getValue : function() {
return this.value;
},
prevHandler : function() {
var me = this;
if (!me.current.cityHide && !Ext.isEmpty(me.current.provinceList)) {
me.updateProvince();
} else if (!me.current.areaHide && !Ext.isEmpty(me.current.cityList)) {
me.updateCity();
} else {
return;
}
},
nextHandler : function() {
var me = this;
if (!me.current.provinceHide && !Ext.isEmpty(me.current.cityList)) {
me.updateCity();
} else if (!me.current.cityHide && !Ext.isEmpty(me.current.areaList)) {
me.updateArea();
} else {
return;
}
}
});
代码写的比较粗糙,不过对于后台管理系统来说已经够用了。
最后上几张图: