前期时间,需求说后台管理系统(前端用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; } } });
代码写的比较粗糙,不过对于后台管理系统来说已经够用了。
最后上几张图: