"use strict"; /** * @Name: 基于layui的异步无限级联选择器 * @Author: 前端喵 * 创建时间: 2019/05/23 * 使用说明: 在主文件里面使用layui.config设置,具体方法看index.html */ /** * 参数说明: * width(可选) :input框宽度 * height(可选):input框高度 */ layui.define(["jquery"], function (exports) { var $ = layui.jquery; // 私有方法,禁止外面调用的方法 function Private() { //页面初始化默认值 this.param = { width: 220, height: 40, prop: { value: "value", label: "label", children: 'children' }, clicklast: false, time: 250, placeholder: "请选择", search: { show: false, minLabel: 10, placeholder: '请输入搜索词' } }, // 定义全局状态仓库 this.store = { showCascader: false, cascaderDom: null, // 当前elem对象 cascaderAll: null, // 生成的Dom主对象 input: null, // input框dom inputI: null, // input框箭头dom model: null, // 下拉菜单的主dom li: null, // li标签 parentNextAll: null, // 当前操作的li标签元素后面所有ul集合 brother: null, // li标签同级dom集合 data: [], // 所有从后端异步请求的数据集合 chooseData: [], // 已选中的数据集 zIndex: 2000 // 显示顺序 }; } // 页面初始化 Private.prototype.init = function (options) { var _this2 = this; var store = this.store; var param = this.param; // dom变量初始化 store.cascaderDom = $(options.elem); // 把用户的参数值进行存储 // 开始存储 for (var _i in options) { if (options[_i].length !== 0) { if (_i == "prop" | _i == 'search') { for (var x in options[_i]) { param[_i][x] = options[_i][x]; } } else { param[_i] = options[_i]; } } } delete param.data; if (options.data) { store.data = options.data; } param.device = this.checkDevice(); // 存储结束 if (store.cascaderDom.next().hasClass('cascader-all')) { store.cascaderDom.next().remove(); } param.className = 'cascader-' + this.param.elem.replace('#', ''); var phoneName = ''; if (param.device === 1) { phoneName = 'cascader-model-phone'; } // 渲染主dom if (typeof this.param.width == "string" &&( this.param.width == "auto" || this.param.width.indexOf("%") > 0)) { this.param.width = this.param.width; } else { this.param.width = this.param.width + " px"; } store.cascaderDom.after("\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n \t\t"); // 判断elem是否存在以及是否正确,elem必填 if (!options.elem || options.elem == "") { layer.msg('请配置有效的elem值 '); } else if ($(options.elem).length == 0) { layer.msg('请配置有效的elem值 '); } store.input = store.cascaderDom.nextAll().find('.cascader-input'); store.inputI = store.input.next(); store.cascaderAll = $(store.cascaderDom.nextAll()[0]); store.model = store.cascaderDom.nextAll().find('.cascader-model'); store.li = store.model.find('li'); // 全局状态初始化 store.model.hide(); if (store.data.length == 0) { param.getChildren(param.value, function (data) { store.data = data; _this2.liHtml(store.data); if (param.checkData) { if (param.checkData.length > 0) { _this2.dataToshow(param.checkData); } } }); } else { this.liHtml(store.data); if (param.checkData) { if (param.checkData.length > 0) { this.dataToshow(param.checkData); } } } // 先进入是否禁用判断事件 // 不禁用则执行下面的事件 this.disabled().then(function (res) { _this2.inputClick(options); _this2.liClick(); _this2.liHover(); _this2.modelHandle(); if (param.search.show) { _this2.handleSearch(); } }); }; // 判断是否禁用 Private.prototype.disabled = function () { var disabled = this.param.disabled; var cascaderAll = this.store.cascaderAll; var input = this.store.input; return new Promise(function (resolve, reject) { if (disabled) { cascaderAll.addClass('cascader-disabled'); } else { resolve(); } }); }; // li标签赋值方法 // key为string类型 Private.prototype.liHtml = function (data, key, choose) { var lis = []; var param = this.param; var store = this.store; var position = []; var key1 = ''; if (!key || key.length == 0) { key = ""; } else { key = key.join('-'); key1 = key; key = key + "-"; } if (data !== "") { for (var _i2 in data) { var li = '
  • ' + data[_i2][param.prop.label] + '
  • '; } else { li = li + '>' + data[_i2][param.prop.label] + ''; } lis.push(li); } } lis = lis.join(''); if (data && data.length > 0) { if (param.search.show && data.length > param.search.minLabel) { lis = '' + lis; } } else { lis = '

    暂无数据

    '; console.log('数据为空,无法进行渲染'); } var ul = $("\n\t\t\t\n\t\t"); ul.fadeIn('fast'); store.model.append(ul); this.liPosition(position); this.ModelPosition(); }; // 当前选中的跳转位置 Private.prototype.liPosition = function (position) { var model = this.store.model.find('ul').last(); }; Private.prototype.handleSearch = function () { var model = this.store.model; var prop = this.param.prop; var _this = this; var value = ''; var flag = true; model.on('compositionstart', function () { flag = false; }); model.on('compositionend', function () { flag = true; }); model.on('input', 'input', function () { var _this3 = this; setTimeout(function () { if (flag) { var data = _this.store.data; if (value == _this3.value) { return; } value = _this3.value; var key = $(_this3).attr('key').split('-'); var key1 = $(_this3).attr('key') + '-'; if ($(_this3).attr('key')) { for (i in key) { if (data[key[i]][prop.children]) { data = data[key[i]][prop.children]; } } } var renderData = []; var lis = ''; for (i in data) { if (data[i][prop.label].indexOf(value) > -1) { if (data[i][prop.children] | data[i].hasChild) { lis += '
  • ' + data[i][prop.label] + '
  • '; } else { lis += '
  • ' + data[i][prop.label] + '
  • '; } renderData.push(data[i]); } } $(_this3.parentNode).find('li').remove(); $(_this3.parentNode).append(lis); } }, 0); }); }; Private.prototype.modelHandle = function () { var _this4 = this; $(window).resize(function () { //当浏览器大小变化时 var model = _this4.store.model; _this4.ModelPosition(); }); }; var modelWidth = 0; Private.prototype.ModelPosition = function () { var model = this.store.model; var input = this.store.input; var BodyWidth = document.documentElement.clientWidth; var positionLeft = 0, left = 0; if (window.getComputedStyle(model[0]).width !== "auto") { modelWidth = window.getComputedStyle(model[0]).width.replace('px', ''); } left = input.offset().left - model.position().left; if (BodyWidth < modelWidth) { positionLeft = BodyWidth - modelWidth; } if (positionLeft < 0) { model.css("left", positionLeft - 30); } else { model.css('left', 0); } }; // 鼠标hover监听事件[li标签] Private.prototype.liHover = function () { var store = this.store; var param = this.param; var _this = this; store.model.on('mouseenter', 'li', function () { var _this5 = this; store.parentNextAll = $(this).parent("ul").nextAll(); store.brother = $(this).siblings(); if ($(this).find('i').length == 0) { store.parentNextAll.fadeOut('fast', function () { store.parentNextAll.remove(); }); } else { (function () { // 增加data的树结点 var DataTreeAdd = function DataTreeAdd(data, newData, keys) { var array = data; for (var k in keys) { if (k < keys.length - 1) { array = array[keys[k]][param.prop.children]; } else { array = array[keys[k]]; } } array.children = newData; }; var DataTreeChange = function DataTreeChange(data, keys) { var array = data; for (var k in keys) { if (k < keys.length - 1) { array = array[keys[k]][param.prop.children]; } else { array = array[keys[k]]; } } array.hasChild = false; }; var keys = $(_this5).attr('key'); var value = $(_this5).attr('value'); var data = _this.store.data; var childrenName = _this.param.prop.children; keys = keys.split('-'); var goodData = data; for (var _i3 in keys) { var key = keys[_i3]; if (goodData) { if (goodData[key]) { goodData = goodData[key][childrenName]; } } } if (!goodData) { param.getChildren(value, function (datax) { goodData = datax; var children = data; if (goodData && goodData.length != 0) { for (var _i4 in keys) { if (_i4 == keys.length - 1) { children = goodData; } else { if (!children[keys[_i4]][childrenName]) { children[keys[_i4]][childrenName] = new Array(); } children = children[keys[_i4]][childrenName]; } } DataTreeAdd(data, goodData, keys); store.parentNextAll = $(_this5).parent("ul").nextAll(); store.parentNextAll.remove(); _this.liHtml(goodData, keys); } else { $(_this5).find('i').remove(); store.parentNextAll.remove(); DataTreeChange(data, keys); } }); } else { store.parentNextAll.remove(); _this.liHtml(goodData, keys); } })(); } $(this).addClass('cascader-choose-active'); store.brother.removeClass('cascader-choose-active'); store.parentNextAll.children().removeClass('cascader-choose-active'); // 获取所有的已选中的数据,并回显至input选择框中 // _this.getChooseData(); }); }; // 鼠标点击监听事件[li标签] Private.prototype.liClick = function () { var _this = this; var store = this.store; var param = this.param; var className = param.className; // store.model为一个自定义dom对象 if (param.clicklast == false) { store.model.on('click', 'li', function () { _this.getChooseData(); store.showCascader = !store.showCascader; if (param.device === 1) {} else { store.model.slideUp(_this.param.time); } store.inputI.removeClass('rotate'); }); } else { store.model.on('click', 'li', function () { store.parentNextAll = $(this).parent("ul").nextAll(); if (store.parentNextAll.length == 0) { _this.getChooseData(); store.showCascader = !store.showCascader; if (param.device === 1) {} else { store.model.slideUp(_this.param.time); } store.inputI.removeClass('rotate'); _this.getThisData(); } }); } }; // 判断当前访问客户端是PC还是移动端 // PC端返回1,移动端返回0 Private.prototype.checkDevice = function () { if (/Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)) { return 1; } else { return 0; } }; // 获取当前层级数据 Private.prototype.getThisData = function () { var value = this.param.prop.value, children = this.param.prop.children, chooseData = this.store.chooseData, data = this.store.data, currentData = []; for (var _i5 in chooseData) { for (var x in data) { if (chooseData[_i5] == data[x][value]) { if (Number(_i5) === chooseData.length - 1) { currentData = JSON.parse(JSON.stringify(data[x])); break; } if (data[x][children]) { data = data[x][children]; } else { data = data[x]; } break; } } } return currentData; }; // 鼠标监听事件[input控件] Private.prototype.inputClick = function (options) { var store = this.store; var param = this.param; var _this = this; $(document).click(function (e) { var className = e.target.className; className = className.split(" "); var other = ['cascader-input', 'cascader-model', "cascader-choose-active", "layui-icon-right", "cascader-ul", "cascader-model-input"]; for (var _i6 in className) { for (var x in other) { if (className[_i6] == other[x]) { return; } } } store.showCascader = false; store.model.slideUp(_this.param.time); store.inputI.removeClass('rotate'); }); store.input.click(function () { store.showCascader = !store.showCascader; if (store.showCascader == true) { store.inputI.addClass('rotate'); var chooseData = _this.store.chooseData; var data = _this.store.data; if (chooseData.length !== 0) { var key = []; _this.clearModel(); for (var _i7 in chooseData) { for (var x in data) { if (data[x][param.prop.value] == chooseData[_i7]) { _this.liHtml(data, key, x); key.unshift(x); data = data[x][param.prop.children]; break; } } } } store.model.slideDown(_this.param.time); _this.ModelPosition(); } else { store.model.slideUp(_this.param.time); store.inputI.removeClass('rotate'); } }); }; // 获取页面中选中的数据 Private.prototype.getChooseData = function () { var store = this.store; var chooseDom = store.model.find('li.cascader-choose-active'); var chooseData = []; var chooseLabel = []; for (var _i8 in chooseDom) { if (chooseDom[_i8].innerText) { chooseData.push($(chooseDom[_i8]).attr('value')); chooseLabel.push(chooseDom[_i8].innerText); } else { break; } } this.store.chooseData = chooseData; this.inputValueChange(chooseLabel); }; Private.prototype.inputValueChange = function (label) { var store = this.store; var param = this.param; if (param.showlast == true) { label = label[label.length - 1]; } else { label = label.join('/'); } store.input.val(label); var fontWidth = store.input.css('font-size').replace('px', ''), inputWidth = store.input.width(), labelWidth = label.length; var maxLabelWidth = Math.floor(inputWidth / fontWidth); if (labelWidth > maxLabelWidth) { store.input.attr('title', label); } else { store.input.attr('title', ""); } }; // 数据回显 Private.prototype.dataToshow = function (checkData) { var _this6 = this; var param = this.param; var store = this.store; var backData = []; //后端数据集合 var chooseLabel = []; //选中的项对应的label值 var keys = []; //checkData在数据源中的位置大全 backData[0] = store.data; var flag = 1; if (param.getChildren) { if (checkData.length === 1) { for (var _i9 in store.data) { if (store.data[_i9][param.prop.value] == checkData[0]) { var label = store.data[_i9][param.prop.label].split(','); this.inputValueChange(label); return; } } } var _loop = function _loop(_i10) { if (_i10 < checkData.length) { param.getChildren(checkData[_i10 - 1], function (data) { backData[_i10] = data; flag++; if (flag == checkData.length) { for (var _i11 = checkData.length - 1; _i11 >= 0; _i11--) { for (var x in backData[_i11]) { if (checkData[_i11] == backData[_i11][x][param.prop.value]) { keys.unshift(x); chooseLabel.unshift(backData[_i11][x][param.prop.label]); if (_i11 < checkData.length - 1) { backData[_i11][x][param.prop.children] = backData[_i11 + 1]; } } } } store.data = backData[0]; // input框数据回显 _this6.inputValueChange(chooseLabel); _this6.clearModel(); // 选择器数据回显 var key = []; for (var _i12 in backData) { if (_i12 !== "0") { key.push(keys[_i12 - 1]); } _this6.liHtml(backData[_i12], key, keys[_i12]); } } }); } }; for (var _i10 = 1; _i10 < checkData.length; _i10++) { _loop(_i10); } } else { var storeData = store.data; for (var x in checkData) { for (var _i13 in storeData) { if (storeData[_i13][param.prop.value] == checkData[x]) { chooseLabel.push(storeData[_i13][param.prop.label]); keys.push(_i13); storeData = storeData[_i13][param.prop.children]; break; } } } // input框数据回显 this.inputValueChange(chooseLabel); this.store.chooseData = checkData; } }; // 清空ul标签 Private.prototype.clearModel = function () { var store = this.store; store.model.html(''); }; // 监听下拉菜单的位置 Private.prototype.handlePosition = function () { // 当前屏幕大小 // let bodyWidth = }; var privates = new Array(); var dom_num = 0; // 暴露给外界使用的方法 var cascader = { // 页面初始化 load: function load(options) { var current = null; for (var _i14 in privates) { if (privates[_i14].elem === options.elem) { current = _i14; } } if (!current) { current = dom_num; dom_num++; privates[current] = new Array(); privates[current].obj = new Private(); } privates[current].elem = options.elem; privates[current].obj.store.zIndex -= current; privates[current].obj.store.data = []; if (options.chooseData) { privates[current].obj.store.chooseData = options.chooseData; } else { privates[current].obj.store.chooseData = []; } privates[current].obj.init(options); }, // 获取页面中选中的数据,数组形式 getChooseData: function getChooseData(elem) { var obj = this.elemCheck(elem); return obj.store.chooseData; }, // 监听方法 on: function on(type, elem, callback) { var obj = this.elemCheck(elem); var className = obj.param.className; if (type == "click") { $(document).on('click', '.' + className + ' li', function () { setTimeout(function () { var data = obj.getThisData(); if (obj.param.clicklast === false) { callback(data); } else { if (obj.store.parentNextAll.length == 0) { callback(data); } } }, 50); }); } else if (type == "hover") { obj.store.model.on('mouseenter', 'li', function () { callback(); }); } }, // elem位置判断,禁止外界调用,因为你调也没啥卵用 elemCheck: function elemCheck(elem) { if (!elem) { return privates[0].obj; } for (var _i15 in privates) { if (privates[_i15].elem == elem) { return privates[_i15].obj; } } } }; //layui.link(layui.cache.base + 'ajaxCascader.css'); layui.link('/js/ajaxCascader/ajaxCascader.css'); exports('ajaxCascader', cascader); });