@autofe/carpicker
Version:
A Carpicker library based on jQuery
449 lines (385 loc) • 12.7 kB
JavaScript
import $ from 'jquery';
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'carpicker';
const DATA_KEY = 'fe.carpicker';
const EVENT_KEY = `.${DATA_KEY}`;
const DATA_API_KEY = '.data-api';
const JQUERY_NO_CONFLICT = $.fn[NAME];
// const ESCAPE_KEYCODE = 27;
const Event = {
INIT: `init${EVENT_KEY}`,
SHOW: `show${EVENT_KEY}`,
SHOWN: `shown${EVENT_KEY}`,
HIDE: `hide${EVENT_KEY}`,
HIDDEN: `hidden${EVENT_KEY}`,
CHANGE: `change${EVENT_KEY}`,
CLICK: `click${EVENT_KEY}`,
CLICK_DISMISS: `click.dismiss${EVENT_KEY}`,
CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`
};
const Selector = {
DATA_TOGGLE: '[data-toggle="carpicker"]',
DATA_VALUE: '[data-value]'
};
const Attr = {
DATA_TEXT: 'data-text',
DATA_VALUE: 'data-value'
};
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
function Carpicker(elem, options) {
this.options = $.extend({}, Carpicker.Default, options);
this.$elem = $(elem);
this.$picker = $(this.options.selectPicker, this.$elem);
this.$value = $(this.options.selectValue, this.$elem);
this.$dropdown = $(this.options.selectDropdown, this.$elem);
this.$brand = $(this.options.selectBrand, this.$elem);
this.$series = $(this.options.selectSeries, this.$elem);
this.$spec = $(this.options.selectSpec, this.$elem);
this.$level = this.options.selectLevel;
this.isActive = false;
this._data = null;
this._brandData = null;
this._init();
}
Carpicker.Default = {
selectPicker: '[data-select-picker]',
selectValue: '[data-select-value]',
selectDropdown: '[data-select-dropdown]',
selectBrand: '[data-select-brand]',
selectSeries: '[data-select-series]',
selectSpec: '[data-select-spec]',
selectedClass: 'selected',
disabledClass: 'disabled',
activeClass: 'active',
selectNav: false,
selectLevel: 'brand',
onInitPicker: null,
onBrandPicker: null,
onSeriesPicker: null,
onSpecPicker: null
};
Carpicker.prototype._init = function() {
var that = this;
that.$elem.trigger(Event.INIT, that);
that.$picker.on(Event.CLICK, $.proxy(that.toggle, that));
// 如果设置了品牌车系车型导航,创建导航并绑定点击事件
if (that.options.selectNav) {
if (that.$brand.prev('ul.pop-nav').length == 0) {
that.$brand.parent().prepend('<ul class="pop-nav"></ul>');
}
that.$dropdown.on(Event.CLICK, 'em', function () {
var $item = $(this).parent();
switch ($item.index()) {
case 0 :
that.$brand.show();
that.$series.hide();
that.$spec.hide();
that.$brand.prev('.pop-nav').html(`<li><em>品牌</em></li>`);
break;
case 1 :
that.$brand.hide();
that.$series.show();
that.$spec.hide();
that.$brand.prev('.pop-nav').html(`<li><em>品牌</em></li>
<li><span>></span><em>车系</em></li>`);
break;
case 2 :
break;
default:
break;
}
return false;
});
}
that.$dropdown.on(Event.CLICK, Selector.DATA_VALUE, function () {
var $item = $(this);
var $target = $item.attr('data-target');
// 点击品牌并触发对应回调函数
if ($target === 'brand' && typeof that.options.onBrandPicker === 'function') {
that.options.onBrandPicker($item.attr(Attr.DATA_VALUE), this);
}
// 点击车系并触发对应回调函数
if ($target === 'series' && typeof that.options.onSeriesPicker === 'function') {
that.options.onSeriesPicker($item.attr(Attr.DATA_VALUE), this);
}
// 点击车型并触发对应回调函数
if ($target === 'spec' && typeof that.options.onSpecPicker === 'function'
&& !$item.hasClass('disabled')) {
that.options.onSpecPicker($item.attr(Attr.DATA_VALUE), this);
}
// 设置最多选择到品牌参数时,点击品牌选中并收起下拉框
if ($target === 'brand' && that.options.selectLevel == 'brand') {
that.setValue({
text: $item.attr(Attr.DATA_TEXT),
value: $item.attr(Attr.DATA_VALUE)
});
that.hide();
}
// 车系
if ($target === 'series' && (that.options.selectLevel == 'series' ||
(that.options.selectLevel == 'spec' && !that.options.selectNav))) {
that.setValue({
text: $item.attr(Attr.DATA_TEXT),
value: $item.attr(Attr.DATA_VALUE)
});
that.hide();
}
// 车型
if ($target === 'spec' && !$item.hasClass('disabled')) {
that.setValue({
text: $item.attr(Attr.DATA_TEXT),
value: $item.attr(Attr.DATA_VALUE)
});
that.hide();
}
})
$(document).on(Event.CLICK_DISMISS, function (e) {
var $parent = $(e.target).closest(that.$elem);
if ($parent.length === 0 && that.isActive) {
that.hide();
}
});
if (typeof that.options.onInitPicker === 'function') {
that._brandData = that.options.onInitPicker();
that.setBrand();
}
};
Carpicker.prototype.setValue = function (data) {
const selectedClass = this.options.selectedClass;
const old = this.getValue() || {};
if (old.value !== data.value) {
const $list = this.$dropdown.find(Selector.DATA_VALUE);
const $item = this.$dropdown.find(`[data-value="${data.value}"]`);
$list.removeClass(selectedClass);
$item.addClass(selectedClass);
this.$value.text(data.text);
this._data = {
text: data.text,
value: data.value
};
this.$elem.trigger(Event.CHANGE, [data, $item]);
}
};
Carpicker.prototype.getValue = function () {
return this._data;
};
Carpicker.prototype.toggle = function () {
if (this.isActive) {
this.hide();
} else {
this.show();
}
};
Carpicker.prototype.show = function () {
const activeClass = this.options.activeClass;
const disabledClass = this.options.disabledClass;
if (this.$elem.hasClass(disabledClass)) {
return;
}
const showEvent = $.Event(Event.SHOW);
this.$elem.trigger(showEvent, this);
if (showEvent.isDefaultPrevented()) {
return;
}
this.isActive = true;
this.$elem.addClass(activeClass)
this.$dropdown.show();
this.$elem.trigger(Event.SHOWN, this);
};
Carpicker.prototype.hide = function () {
const activeClass = this.options.activeClass;
const hideEvent = $.Event(Event.HIDE);
this.$elem.trigger(hideEvent, this);
if (hideEvent.isDefaultPrevented()) {
return;
}
this.isActive = false;
this.$elem.removeClass(activeClass);
this.$dropdown.hide();
this.$elem.trigger(Event.HIDDEN, this);
};
Carpicker.prototype.disable = function () {
const disabledClass = this.options.disabledClass;
this.$elem.addClass(disabledClass);
};
Carpicker.prototype.enable = function () {
const disabledClass = this.options.disabledClass;
this.$elem.removeClass(disabledClass);
};
Carpicker.prototype.setBrand = function () {
var that = this;
if (that.options.selectNav) {
that.$brand.prev('.pop-nav').html(`<li><em>品牌</em></li>`);
}
// 字母索引
let alphabeta = [];
that._brandData.forEach(function(elem) {
if (!alphabeta.includes(elem.letter)) {
alphabeta.push(elem.letter);
}
});
if($.trim(that.$brand.html()) === '') {
var brandIndex = `<ol class="index">`;
var brandList = `<dl class="list">`;
for (var i = 0; i < alphabeta.length; i++) {
// 左侧字母
brandIndex += `<li data-target='jump-${alphabeta[i]}'>${alphabeta[i]}</li>`;
// 右侧内容
brandList += `<dt id='jump-${alphabeta[i]}'>${alphabeta[i]}</dt>`;
that._brandData.forEach(function(brand) {
if (brand.letter == alphabeta[i]) {
brandList += `<dd data-value="${brand.id}"
data-text="${brand.name}"
${brand.pinyin ? (`data-pinyin=${brand.pinyin}`) : ''}
data-target="brand">${brand.name}</dd>`;
}
});
}
brandIndex += `</ol>`
brandList += `</dl>`
that.$brand.html(brandIndex + brandList);
that.setIndexLoca();
}
that.$brand.show();
}
Carpicker.prototype.setSeries = function (data, seriesItem) {
var that = this;
if (data.length > 0) {
if (that.options.selectNav) {
that.$brand.prev('.pop-nav').html(`<li><em>品牌</em></li>
<li><span>></span><em>车系</em></li>`);
that.$brand.hide();
}
that.$series.show();
var seriesStr = `<dl class="list">`;
data.forEach(function(series, i) {
// 全部车系项
if (seriesItem && seriesItem.show && i == 0) {
if (seriesItem.link) {
seriesStr += `<dd><a class="all" href="${seriesItem.url}" target="_blank">全部车系</a></dd>`;
} else {
seriesStr += `<dd data-value="${series.brandId}" data-text="全部车系" data-target="series">全部车系</dd>`;
}
}
seriesStr += `<dt>${series.name}</dt>`
series.list.forEach(function(item) {
seriesStr += `<dd data-value="${item.id}"
data-text="${item.name}"
${item.pinyin ? (`data-pinyin=${item.pinyin}`) : ''}
data-target="series">${item.name}</dd>`
})
});
seriesStr += `</dl>`;
that.$series.html(seriesStr);
return;
}
}
Carpicker.prototype.setSpec = function (data, selectSpec) {
var that = this;
if (that.options.selectNav) {
that.$brand.prev('.pop-nav').html(`<li><em>品牌</em></li>
<li><span>></span><em>车系</em></li>
<li><span>></span><em>车型</em></li>`);
that.$brand.hide();
that.$series.hide();
}
that.$spec.show();
var specStr = `<dl class="list">`;
if (data.length > 0) {
data.forEach(function(item) {
specStr += `<dt>${item.name}</dt>`;
item.list.forEach(function(spec) {
specStr += `<dd data-value="${spec.id}"
data-text="${spec.name}"
data-target="spec"
${(selectSpec && $.isArray(selectSpec) && selectSpec.includes(spec.id.toString())) ? `class="disabled"` : ``}>
${spec.name}
${spec.price ? (`<span>${(spec.price > 0 ? (spec.price / 10000.0).toFixed(2) + `万` : `暂无`)}</span>`) : ''}
</dd>`
})
})
specStr += `<dd></dd>`
}else{
specStr += `<dd>暂无相关车型</dd>`
}
specStr += `</dl>`
that.$spec.html(specStr);
return;
}
Carpicker.prototype.setIndexLoca = function () {
var that = this;
that.$brand.on(Event.CLICK, 'li[data-target^="jump"]', function() {
$('.list', that.$brand).scrollTop(0);
$(this).addClass('active').siblings().removeClass('active');
var target = $(this).attr('data-target');
var targetTop = $('dt#' + target, that.$brand).position("list").top;
$('.list', that.$brand).scrollTop(targetTop);
})
}
Carpicker.prototype.destroy = function () {
this.$elem.removeData(DATA_KEY);
this.$picker.off(Event.CLICK);
this.$dropdown.off(Event.CLICK, Selector.DATA_VALUE);
this.onInitPicker = null,
this.onBrandPicker = null,
this.onSeriesPicker = null,
this.onSpecPicker = null
this._brandData = null,
this.setValue({});
};
/**
* ------------------------------------------------------------------------
* Plugin Definition
* ------------------------------------------------------------------------
*/
function Plugin(config) {
return this.each(function () {
const $this = $(this);
let data = $this.data(DATA_KEY);
const _config = $.extend({}, Carpicker.Default, $this.data(), typeof config === 'object' && config);
if (!data) {
data = new Carpicker(this, _config);
$this.data(DATA_KEY, data);
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`);
}
data[config]();
}
});
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
// TODO
// $(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (e) {
// e.preventDefault();
// e.stopPropagation();
// Plugin.call($(this), 'toggle');
// });
$(function () {
Plugin.call($(Selector.DATA_TOGGLE));
});
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Plugin;
$.fn[NAME].Constructor = Carpicker;
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return Plugin;
}
export default Carpicker;