/**
* cityPicker
* v-2.0.2
* dataJson [Json] json数据,是html显示的列表数据
* selectpattern [Array] 用于存储的字段名和默认提示 { 字段名,默认提示 }
* shorthand [Boolean] 用于城市简写功能,默认是不开启(false)
* storage [Boolean] 存储的值是数字还是中文,默认是(true)数字
* autoSelected [Boolean] 是否自动选择第一项,默认(true)
* renderMode [Boolean] 是模拟的还是原生的;只在type是selector才有效,默认是(true)模拟
* keyboard [Boolean] 是否开启键盘操作事件,默认(false)
* code [Boolean] 是否输出城市区号值,默认(false),开启就是传字段名('cityCode')
* search [Boolean] 是否开启搜索功能,默认(true)
* searchNotStr [String] 没有搜索到的提示语
* streetUrl [String] 街道数据的地址。可以用本地地址或者用'http://passer-by.com/data_location/town/{json}.json'
* level [Number] 多少列 默认是一列/级 (3)
* onInitialized [Attachable] 组件初始化后触发的回调函数
* onClickBefore [Attachable] 组件点击显示列表触发的回调函数(除原生select)
* onForbid [Attachable] 存在class名forbid的禁止点击的回调
* onChoiceEnd [Attachable] 选择结束后执行的回调
* choose-xx [Attachable] 点击组件选项后触发的回调函数 xx(级名称/province/city/district/street)是对应的级的回调
*/
(function ($, window) {
var $selector;
var grade = ['province', 'city', 'district', 'street'];
var defaults = {
dataJson: null,
selectpattern: [
{
field: 'userProvinceId',
placeholder: '请选择分类'
}, {
field: 'userCityId',
placeholder: '请选择预约事项'
}, {
field: 'userDistrictId',
placeholder: '请选择预约业务'
}, {
field: 'userStreet',
placeholder: '请选择四级'
}
],
shorthand: false,
storage: true,
autoSelected: true,
renderMode: true,
keyboard: false,
code: false,
search: true,
searchNotStr: '',
streetUrl: 'town/{json}.json',
level: 3,
onInitialized: function () {},
onClickBefore: function () {},
onChoiceEnd: function () {},
onForbid: function () {}
};
function Citypicker(options, selector) {
this.options = $.extend({}, defaults, options);
this.$selector = $selector = $(selector);
this.values = [];
this.init();
this.bindEvent();
}
//功能模块函数
var effect = {
montage: function (data, pid, reg) {
var self = this,
config = self.options,
leng = data.length,
html = '',
code, name, storage;
for (var i = 0; i < leng; i++) {
if (data[i].parentId === pid) {
//判断是否要输出区号
code = config.code && data[i].cityCode !== '' ? 'data-code=' + data[i].cityCode : '';
//alert(code);
//判断是否开启了简写,是就用输出简写,否则就输出全称
name = config.shorthand ? data[i].shortName : data[i].name;
//存储的是数字还是中文
storage = config.storage ? data[i].id : name;
if (config.renderMode) {
//模拟
html += '
' + name + '';
} else {
//原生
html += '';
}
}
}
return html;
},
seTemplet: function () {
var config = this.options,
selectemplet = '',
placeholder, field, forbid, citygrade, active, hide,
searchStr = config.search ? ''
+''
+'
' : '';
for (var i = 0; i < config.level; i++) { //循环定义的级别
if (i == 0 && config.searchNotStr != '') {
placeholder = config.searchNotStr;
}
else {
placeholder = config.selectpattern[i].placeholder; //默认提示语
}
field = config.selectpattern[i].field; //字段名称
citygrade = grade[i]; //城市级别名称
forbid = i > 0 ? 'forbid' : ''; //添加鼠标不可点击状态
active = i < 1 ? 'active' : ''; //添加选中状态
hide = i > 0 ? ' hide' : ''; //添加隐藏状态
if (config.renderMode) {
//模拟
selectemplet += '';
} else {
//原生
selectemplet += '';
}
}
return selectemplet;
},
obtain: function (event) {
var self = this,
config = self.options,
$selector = self.$selector,
$target = config.renderMode ? event[0].target ? $(event[0].target) : $(event) : $(event.target),
$parent = $target.parents('.listing'),
$selected = $target.find('.caller:selected'),
index = config.renderMode ? $target.parents('.storey').data('index') : $target.data('index'),
id = config.renderMode ? $target.attr('data-id') : $selected.attr('data-id'),
name = config.renderMode ? $target.text() : $selected.text(),
storage = config.storage ? id : name, //存储的是数字还是中文
code = config.renderMode ? $target.data('code') : $selected.data('code'),
$storey = $selector.find('.storey[data-index="'+ index +'"]'),
$listing = $selector.find('.listing').eq(index + 1),
values = { 'id': id || '0', 'name': name, 'cityCode': code || '' },
aselectedIndex = config.autoSelected ? 1 : 0,
placeholder = config.selectpattern[index < 4 ? index + 1 : index].placeholder,
placeholderStr = !config.renderMode ? ''+ effect.montage.apply(self, [config.dataJson, id]) : ''+placeholder+''+ effect.montage.apply(self, [config.dataJson, id]);
// 存储选择的值
if (self.values.length > 0) {
// 判断如果是values 有值,就根据选择的列去替换成新的选择值
self.values.splice(index, config.level - 1, values);
} else {
// values 没有值就直接添加
self.values.push(values);
}
//选择选项后触发自定义事件choose(选择)事件
$selector.trigger('choose-' + grade[index] +'.citypicker', [$target, values]);
//赋值给隐藏域-区号
$selector.find('[role="code"]').val(code);
self.cityCode = code;
// 判断类型
if (config.renderMode) {
//给选中的级-添加值和文字
$storey.find('.reveal').removeClass('df-color forbid').text(name).siblings('.input-price').val(storage);
$listing.data('id', id).find('ul').html(placeholderStr);
index < 2 ? $listing.find('.caller').eq(aselectedIndex).trigger('click') : '';
$listing.find('.caller').eq(0).remove();
// 不是自动选择的事情
!config.autoSelected ? $selector.find('.reveal').eq(index + 1).addClass('df-color') : '';
//模拟: 添加选中的样式
$parent.find('.caller').removeClass('active');
$target.addClass('active');
} else {
//原生: 下一级附上对应的城市选项,执行点击事件
$target.next().html(placeholderStr).find('.caller').eq(aselectedIndex).prop('selected', true);
index < 2 ? $target.next().trigger('change') : '';
}
// 开启四级联动,添加四级城市
if (config.level === 4 && index === 2) {
//self.getStreet(id);
}
// 选择完后执行的回调
if (config.level - 1 === index) {
config.onChoiceEnd.apply(self);
}
$selector.find('.selector-item').removeClass('selector-show');
},
show: function (event) {
var config = this.options,
$target = $(event),
$parent = $target.parent();
$selector = this.$selector;
$parent.addClass('selector-show').siblings('.selector-item').removeClass('selector-show');
// 判断是否开启搜索,是就获取搜索框焦点
if (config.search) {
setTimeout(function() {
$parent.find('.input-search').focus();
}, 400);
}
//点击的回调函数
config.onClickBefore.call($target);
},
hide: function (event) {
var config = this.options,
$target = $(event);
effect.obtain.call(this, $target);
$selector.find('.selector-item').removeClass('selector-show');
return false;
},
search: function (event) {
event.preventDefault();
var self = this,
$target = $(event.target),
$parent = $target.parents('.listing'),
inputVal = $target.val(),
id = $parent.data('id'),
keycode = event.keyCode,
result = [], htmls;
//如果是按下shift/ctr/左右/command键不做事情
if (keycode === 16 || keycode === 17 || keycode === 18 || keycode === 37 || keycode === 39 || keycode === 91 || keycode === 93) {
return false;
}
//如果不是按下enter/上下键的就做搜索事情
if (keycode !== 13 && keycode !== 38 && keycode !== 40) {
$.each(this.options.dataJson, function(key, value) {
//拼音或者名称搜索
if(value.pinyin.toLocaleLowerCase().search(inputVal) > -1 || value.name.search(inputVal) > -1 || value.id.search(inputVal) > -1 ){
result.push(value);
}
});
// 搜索结果返回的html
htmls = effect.montage.apply(self, [result, id]);
// 插入到DOM去
$parent.find('ul').html(htmls ? htmls : ''+ self.options.searchNotStr.replace('{city}', ''+ inputVal +'') +'');
}
},
operation: function (event) {
event.preventDefault();
var $target = $(event.target),
$sibl = $target.hasClass('input-search') ? $target.parents('.listing') : $target.siblings('.listing'),
$items = $sibl.find('.caller'),
inputVal = $sibl.find('.input-search').val(),
keyCode = event.keyCode,
index = 0,
direction,
itemIndex;
//按下enter键
if (keyCode === 13) {
if (!$items.hasClass('active')) { return false; }
effect.hide.call(this, $sibl.find('.caller.active'));
return false;
}
//按下上下键
if (keyCode === 38 || keyCode === 40) {
//方向
direction = keyCode === 38 ? -1 : 1;
//选中的索引
itemIndex = $items.index($sibl.find('.caller.active'));
if (itemIndex < 0) {
index = direction > 0 ? -1 : 0;
} else {
index = itemIndex;
}
//键盘去选择的索引
index = index + direction;
//循环选择
index = index === $items.length ? 0 : index;
$items.removeClass('active').eq(index).addClass('active');
//滚动条跟随定位
effect.position.call(this, $sibl);
}
return false;
},
position: function (event) {
var $target = event,
$caller = $target.find('.caller.active'),
oh = $target.outerHeight(),
ch = $caller.outerHeight(),
dy = $caller.position().top,
sy = $target.find('ul').scrollTop();
$target.find('ul').animate({
scrollTop: dy + ch - oh + sy
}, 200);
},
evaluation: function (arr, arrayVal) {
var self = this,
config = self.options,
$selector = self.$selector;
// 清空原本的值
self.values = [];
// 循环拿到对应的级城市赋值
$.each(arr, function (item, value) {
var $original = $selector.find('.'+grade[item]);
var $forward = $selector.find('.'+grade[item+1]);
var name = config.shorthand ? value.shortName : value.name;
// 两种方式
if (config.renderMode) {
$original.find('.reveal').text(name).removeClass('df-color forbid').siblings('.input-price').val(value.id);
$forward.find('ul').html(effect.montage.apply(self, [config.dataJson, value.id]));
$original.find('.caller[data-id="'+value.id+'"]').addClass('active');
} else {
$forward.html(effect.montage.apply(self, [config.dataJson, value.id]));
$original.find('.caller[data-id="'+value.id+'"]').prop('selected', true);
}
// 存储选择的值
self.values.push({ 'id': value.id, 'name': name, 'cityCode': value.cityCode });
});
// 开启四级联动,取四级城市数据
if (arr.length === 3 && config.level === 4) {
//self.getStreet(arr[2].id, true, arrayVal[3] ? arrayVal[3] : '');
}
}
};
Citypicker.prototype = {
init: function () {
var self = this,
config = self.options,
code = config.code ? '' : '';
//是否开启存储区号,是就加入一个隐藏域
//添加拼接好的模板
$selector.html(effect.seTemplet.call(self) + code);
//html模板
if (config.renderMode) {
//模拟>添加数据
$selector.find('.listing').data('id', '100000').eq(0).find('ul').html(effect.montage.apply(self, [config.dataJson, '100000']));
} else {
//原生>添加数据
$selector.find('.province').append(effect.montage.apply(self, [config.dataJson, '100000']));
}
//初始化后的回调函数
config.onInitialized.call(self);
},
bindEvent: function () {
var self = this,
config = self.options;
//点击显示对应的列表
$selector.on('click.citypicker', '.reveal', function (event) {
event.preventDefault();
var $this = $(this);
if ($this.is('.forbid, .disabled')) {
// 禁止的回调函数
config.onForbid.call($this);
return false;
}
// 显示回调函数
effect.show.call(self, $this);
return false;
});
//点击选项事件
$selector.on('click.citypicker', '.caller', $.proxy(effect.hide, self));
//原生选择事件
$selector.on('change.citypicker', 'select', $.proxy(effect.obtain, self));
//文本框搜索事件
$selector.on('keyup.citypicker', '.input-search', $.proxy(effect.search, self));
//开启键盘操作
if (config.keyboard) {
//键盘选择事件
$selector.on('keyup.citypicker', '.storey', $.proxy(effect.operation, self));
}
},
unBindEvent: function (event) {
var self = this,
config = self.options;
// 处理原生
if (!config.renderMode) {
$selector.off('change.citypicker', 'select');
return false;
}
// 销毁展开列表事件
$selector.off('click.citypicker', '.reveal');
// 销毁选择事件
$selector.off('click.citypicker', '.caller');
// 销毁搜索事件
$selector.off('keyup.citypicker', '.input-search');
// 销毁键盘事件
$selector.off('keyup.citypicker', '.storey');
},
setCityVal: function (val) {
var self = this,
arrayVal = val.split(/\,\s|\,/g),
result = [], resultArray;
// 处理传入的城市数组,然后去查找相同的名称,存储到新的数组上
$.each(arrayVal, function (key, value) {
// 循环数据,去拿到对应的城市名称,存储到新的数组去
$.each(self.options.dataJson, function (item, val) {
var isType = isNaN(value) ? value === val.name : value === val.id;
if (isType) {
result.push(val);
}
});
});
// 反向排序数组
resultArray = result[0].parentId === '100000' ? result.sort() : result.reverse();
// 设置默认值
effect.evaluation.apply(self, [result, arrayVal]);
},
getCityVal: function () {
return this.values[2].cityCode;
},
changeStatus: function (status) {
var self = this,
config = self.options;
if (status === 'disabled') {
self.$selector.find('.reveal').addClass('disabled').siblings('.input-price').prop('disabled', true);
!config.renderMode ? self.$selector.find('select').prop('disabled', true) : '';
} else if (status === 'current') {
self.$selector.find('.reveal').removeClass('disabled forbid').siblings('.input-price').prop('disabled', false);
!config.renderMode ? self.$selector.find('select').prop('disabled', false) : '';
}
},
getStreet: function (id, isSet, name) {
var self = this,
config = self.options,
$street= self.$selector.find('.street'),
placeholder = config.selectpattern[3].placeholder,
index = config.autoSelected ? 1 : 0,
title = name && config.shorthand ? name.replace(/街道|镇|乡/g, '') : name,
converts = isNaN(title) ? 'data-title='+ title : config.renderMode ? 'data-id='+ title : 'value="'+ title +'"',
reults = [], placeholderStr, autoSelectedStr;
// 没有ID值就不做以下事情
if (!id) { return false; }
//alert(id);
// 取街道级数据
$.getJSON(config.streetUrl.replace('{json}', id), function (data) {
// 重新拼接新的数据
$.each(data, function (key, value) {
reults.push({ 'id': key, 'parentId': id, 'name': value, 'shortName': value.replace(/街道|镇|乡/g, ''), 'cityCode': '' });
});
placeholderStr = !config.renderMode ? ''+ effect.montage.apply(self, [reults, id]) : ''+placeholder+''+ effect.montage.apply(self, [reults, id]);
// 数据转化,然后转成html插入到DOM里去
if (config.renderMode) {
$street.find('ul').html(placeholderStr);
// 如果是设置城市的就按照城市名称去选中,否则就选中第一项
if (isSet) {
$street.find('.caller['+ converts +']').trigger('click');
} else {
$street.find('.caller').eq(index).trigger('click');
}
$street.find('.caller').eq(0).remove();
!isSet & !config.autoSelected ? $street.find('.reveal').addClass('df-color') : '';
} else {
$street.html(placeholderStr);
// 如果是设置城市的就按照城市名称去选中,否则就选中第一项
if (isSet) {
$street.find('.caller['+ converts +']').prop('selected', true);
} else {
$street.find('.caller').eq(index).prop('selected', true);
}
$street.trigger('change');
}
});
}
};
//模拟:执行点击区域外的就隐藏列表;
$(document).on('click.citypicker', function (event){
if($selector && $selector.find(event.target).length < 1) {
$selector.find('.selector-item').removeClass('selector-show');
}
});
$.fn.cityPicker = function (options) {
return new Citypicker(options, this);
};
})(jQuery, window);